From e8f0fa2d27f0a60b712e9b055d412e8364ce8aeb Mon Sep 17 00:00:00 2001 From: Dario Sassi Date: Mon, 14 Sep 2020 16:42:31 +0000 Subject: [PATCH] Extern : - C3d aggiornamento delle librerie. --- C3d/Include/action.h | 2595 ++++----- C3d/Include/action_analysis.h | 718 +-- C3d/Include/action_b_shaper.h | 853 +-- C3d/Include/action_curve.h | 1521 +++--- C3d/Include/action_curve3d.h | 2495 ++++----- C3d/Include/action_direct.h | 944 ++-- C3d/Include/action_mesh.h | 597 ++- C3d/Include/action_phantom.h | 616 +-- C3d/Include/action_point.h | 1672 +++--- C3d/Include/action_sheet.h | 94 +- C3d/Include/action_shell.h | 1748 ++++--- C3d/Include/action_solid.h | 4476 ++++++++-------- C3d/Include/action_surface.h | 1663 +++--- C3d/Include/action_surface_curve.h | 2165 ++++---- C3d/Include/alg_base.h | 77 +- C3d/Include/alg_circle_curve.h | 73 +- C3d/Include/alg_curve_delete_part.h | 46 +- C3d/Include/alg_curve_distance.h | 11 +- C3d/Include/alg_curve_envelope.h | 20 +- C3d/Include/alg_curve_equid.h | 17 +- C3d/Include/alg_curve_fillet.h | 20 +- C3d/Include/alg_curve_hatch.h | 18 +- C3d/Include/alg_curve_tangent.h | 18 +- C3d/Include/alg_dimension.h | 892 ++-- C3d/Include/alg_diskrete_length_data.h | 2 +- C3d/Include/alg_draw.h | 13 +- C3d/Include/alg_max_distance.h | 330 +- C3d/Include/alg_mesh_to_brep.h | 198 +- C3d/Include/alg_nurbs_conic.h | 754 +-- C3d/Include/assembly.h | 789 +-- C3d/Include/assisting_item.h | 200 +- C3d/Include/attr_color.h | 99 +- C3d/Include/attr_common_attribute.h | 302 ++ C3d/Include/attr_dencity.h | 326 +- C3d/Include/attr_elementary_attribute.h | 68 + C3d/Include/attr_flange_attribute.h | 79 + C3d/Include/attr_geometric_attribute.h | 89 + C3d/Include/attr_identifier.h | 644 +-- C3d/Include/attr_product.h | 901 ++-- C3d/Include/attr_registry.h | 182 +- C3d/Include/attr_selected.h | 308 +- ...b_attribut.h => attr_stamprib_attribute.h} | 186 +- ..._user_attribut.h => attr_user_attribute.h} | 838 ++- C3d/Include/attribute.h | 1093 ++-- C3d/Include/attribute_container.h | 677 +-- C3d/Include/cdet_bool.h | 114 +- C3d/Include/cdet_data.h | 88 +- C3d/Include/cdet_utility.h | 88 +- C3d/Include/check_geometry.h | 52 +- C3d/Include/collection.h | 674 +-- C3d/Include/comanager.h | 116 +- C3d/Include/constraint.h | 466 +- C3d/Include/constraint_item.h | 508 +- C3d/Include/contour_graph.h | 2 +- C3d/Include/conv_annotation_item.h | 69 +- C3d/Include/conv_binary_object.h | 24 + ...rror_result.h => conv_exchange_settings.h} | 398 +- C3d/Include/conv_model_document.h | 575 ++ ...nv_i_converter.h => conv_model_exchange.h} | 642 ++- C3d/Include/conv_model_properties.h | 712 --- C3d/Include/conv_predefined.h | 151 + C3d/Include/conv_topo_mesh.h | 90 + C3d/Include/cr_attribute_provider.h | 306 +- C3d/Include/cr_connecting_curve.h | 376 +- C3d/Include/cr_duplication_solid.h | 207 +- C3d/Include/cr_elementary_solid.h | 30 +- C3d/Include/cr_extension_shell.h | 244 +- C3d/Include/cr_fillet_solid.h | 4 + C3d/Include/cr_intersection_curve.h | 146 +- C3d/Include/cr_join_shell.h | 478 +- C3d/Include/cr_median_shell.h | 231 +- C3d/Include/cr_mesh_shell.h | 206 +- C3d/Include/cr_modified_nurbs_.h | 434 +- C3d/Include/cr_nurbs3d.h | 258 +- C3d/Include/cr_nurbs_surfaces_shell.h | 146 +- C3d/Include/cr_nurbs_surfaces_solid.h | 232 +- C3d/Include/cr_offset_curve.h | 334 +- C3d/Include/cr_patch_creator.h | 22 +- C3d/Include/cr_projection_curve.h | 172 +- C3d/Include/cr_revolution_solid.h | 17 +- C3d/Include/cr_ruled_shell.h | 216 +- C3d/Include/cr_section_shell.h | 137 + C3d/Include/cr_sheet_bend_any_solid.h | 244 +- C3d/Include/cr_sheet_bend_by_edge_solid.h | 2 +- C3d/Include/cr_sheet_bend_over_seg_solid.h | 2 +- C3d/Include/cr_sheet_bend_unbend_solid.h | 2 +- C3d/Include/cr_sheet_builder_solid.h | 114 + C3d/Include/cr_sheet_closed_corner_solid.h | 2 +- C3d/Include/cr_sheet_joint_bend_solid.h | 2 +- C3d/Include/cr_sheet_metal_solid.h | 2 +- C3d/Include/cr_sheet_simplified_flat_solid.h | 206 +- C3d/Include/cr_sheet_union_solid.h | 224 +- C3d/Include/cr_simple_creator.h | 4 +- C3d/Include/cr_split_data.h | 1046 ++-- C3d/Include/cr_stamp_bead_solid.h | 2 +- C3d/Include/cr_stamp_jalousie_solid.h | 2 +- C3d/Include/cr_stamp_jog_solid.h | 24 +- C3d/Include/cr_stamp_remove_solid.h | 220 +- C3d/Include/cr_stamp_rib_solid.h | 270 +- C3d/Include/cr_stamp_solid.h | 2 +- C3d/Include/cr_stamp_spherical_solid.h | 294 +- C3d/Include/cr_stamp_user_solid.h | 256 +- C3d/Include/cr_stitch_solid.h | 56 +- C3d/Include/cr_surface_spline.h | 282 +- C3d/Include/cr_transformed_solid.h | 228 +- C3d/Include/cr_truncated_shell.h | 312 +- C3d/Include/cr_union_solid.h | 38 +- C3d/Include/creator.h | 12 +- C3d/Include/creator_transaction.h | 318 +- C3d/Include/cur_arc.h | 5 +- C3d/Include/cur_arc3d.h | 4 +- C3d/Include/cur_b_spline.h | 216 +- C3d/Include/cur_bezier.h | 37 +- C3d/Include/cur_bezier3d.h | 11 +- C3d/Include/cur_bridge3d.h | 5 +- C3d/Include/cur_character_curve.h | 303 +- C3d/Include/cur_character_curve3d.h | 313 +- C3d/Include/cur_cone_spiral.h | 163 +- C3d/Include/cur_contour.h | 138 +- C3d/Include/cur_contour3d.h | 81 +- C3d/Include/cur_contour_on_plane.h | 18 +- C3d/Include/cur_contour_on_surface.h | 23 +- C3d/Include/cur_contour_with_breaks.h | 1964 +++---- C3d/Include/cur_cosinusoid.h | 8 +- C3d/Include/cur_crooked_spiral.h | 300 +- C3d/Include/cur_cubic_spline.h | 41 +- C3d/Include/cur_cubic_spline3d.h | 54 +- C3d/Include/cur_curve_spiral.h | 44 +- C3d/Include/cur_hermit.h | 24 +- C3d/Include/cur_hermit3d.h | 39 +- C3d/Include/cur_line.h | 24 +- C3d/Include/cur_line3d.h | 1 - C3d/Include/cur_line_segment.h | 8 +- C3d/Include/cur_nurbs.h | 94 +- C3d/Include/cur_nurbs3d.h | 96 +- C3d/Include/cur_nurbs_vector3d.h | 46 +- C3d/Include/cur_offset_curve.h | 34 +- C3d/Include/cur_offset_curve3d.h | 435 +- C3d/Include/cur_plane_curve.h | 6 +- C3d/Include/cur_point_curve.h | 2 +- C3d/Include/cur_polycurve.h | 10 +- C3d/Include/cur_polycurve3d.h | 4 +- C3d/Include/cur_polyline.h | 9 +- C3d/Include/cur_polyline3d.h | 3 +- C3d/Include/cur_projection_curve.h | 2 + C3d/Include/cur_reparam_curve.h | 180 +- C3d/Include/cur_reparam_curve3d.h | 87 +- C3d/Include/cur_silhouette_curve.h | 49 +- C3d/Include/cur_spiral.h | 137 +- C3d/Include/cur_surface_curve.h | 17 +- C3d/Include/cur_surface_intersection.h | 6 +- C3d/Include/cur_trimmed_curve.h | 5 +- C3d/Include/curve.h | 6 +- C3d/Include/curve3d.h | 45 +- C3d/Include/dxf_data.h | 2 +- C3d/Include/func_analytical_function.h | 16 +- C3d/Include/func_cubic_function.h | 8 +- C3d/Include/func_line_function.h | 3 + C3d/Include/func_serve_function.h | 142 + C3d/Include/function.h | 8 +- C3d/Include/function_factory.h | 2 +- C3d/Include/gc_api.h | 23 + C3d/Include/gce_api.h | 65 +- C3d/Include/gce_precision.h | 73 +- C3d/Include/gce_types.h | 13 +- C3d/Include/gcm_blackbox.h | 16 +- C3d/Include/gcm_constraint.h | 4 + C3d/Include/gcm_manager.h | 20 +- C3d/Include/gcm_reposition.h | 3 + C3d/Include/gcm_res_code.h | 2 +- C3d/Include/generic_utility.h | 45 +- C3d/Include/hash32.h | 89 +- C3d/Include/iges_basic.h | 2 +- C3d/Include/instance.h | 370 +- C3d/Include/io_memory_buffer.h | 9 +- C3d/Include/io_tape.h | 163 +- C3d/Include/io_tree.h | 602 +-- C3d/Include/io_version_container.h | 150 +- C3d/Include/io_version_container_rw.h | 58 +- C3d/Include/item_registrator.h | 4 +- C3d/Include/last.h | 76 +- C3d/Include/legend.h | 128 +- C3d/Include/lump.h | 4 +- C3d/Include/m2b_mesh_curvature.h | 80 +- C3d/Include/map_create.h | 36 +- C3d/Include/map_lump.h | 11 +- C3d/Include/map_vestige.h | 30 +- C3d/Include/marker.h | 246 +- C3d/Include/math_cfg.h | 8 + C3d/Include/math_define.h | 49 +- C3d/Include/math_doxigen.h | 842 +-- C3d/Include/math_version.h | 3 + C3d/Include/mb_cart_point.h | 25 +- C3d/Include/mb_cart_point3d.h | 24 +- C3d/Include/mb_class_traits.h | 366 +- C3d/Include/mb_cube.h | 10 +- C3d/Include/mb_data.h | 1154 ++-- C3d/Include/mb_enum.h | 146 +- C3d/Include/mb_homogeneous.h | 2 +- C3d/Include/mb_matrix.h | 2 +- C3d/Include/mb_matrix3d.h | 25 +- C3d/Include/mb_matrixnn.h | 8 +- C3d/Include/mb_nurbs_function.h | 4660 +++++++++-------- C3d/Include/mb_operation_result.h | 44 +- C3d/Include/mb_placement.h | 116 +- C3d/Include/mb_placement3d.h | 8 +- C3d/Include/mb_point_mating.h | 1432 +++-- C3d/Include/mb_property.h | 1762 +++---- C3d/Include/mb_property_title.h | 2389 ++++----- C3d/Include/mb_rect.h | 12 +- C3d/Include/mb_thread.h | 16 +- C3d/Include/mb_variables.h | 332 +- C3d/Include/mb_vector.h | 4 +- C3d/Include/mesh.h | 30 +- C3d/Include/mesh_float_point3d.h | 2 +- C3d/Include/mesh_grid.h | 1808 +++---- C3d/Include/mesh_plane_grid.h | 712 +-- C3d/Include/mesh_polygon.h | 4 +- C3d/Include/mesh_primitive.h | 10 +- C3d/Include/mesh_triangle.h | 984 ++-- C3d/Include/mip_solid_mass_inertia.h | 4 +- C3d/Include/model.h | 1338 ++--- C3d/Include/model_item.h | 1090 ++-- C3d/Include/model_tree.h | 618 +-- C3d/Include/model_tree_data.h | 1631 +++--- C3d/Include/multiline.h | 3632 ++++++------- C3d/Include/name_check.h | 89 +- C3d/Include/name_item.h | 919 ++-- C3d/Include/op_binding_data.h | 80 +- C3d/Include/op_boolean_flags.h | 5 +- C3d/Include/op_duplication_parameter.h | 830 +-- C3d/Include/op_shell_parameter.h | 379 +- C3d/Include/op_swept_parameter.h | 3420 +++++++----- C3d/Include/pars_equation_tree.h | 3122 +++++------ C3d/Include/pars_list.h | 11 +- C3d/Include/pars_tree_variable.h | 464 +- C3d/Include/pars_user_function.h | 228 +- C3d/Include/pars_var.h | 1 - C3d/Include/pars_variable.h | 199 +- C3d/Include/pars_yacc.h | 355 +- C3d/Include/plane_instance.h | 525 +- C3d/Include/plane_item.h | 2 +- C3d/Include/point3d.h | 202 +- C3d/Include/point_frame.h | 44 +- C3d/Include/position_data.h | 376 +- C3d/Include/reference_item.h | 2042 ++++---- C3d/Include/region.h | 40 +- C3d/Include/sheet_metal_param.h | 164 +- C3d/Include/shell_history.h | 8 +- C3d/Include/solid.h | 4 +- C3d/Include/space_instance.h | 242 +- C3d/Include/space_item.h | 3 +- C3d/Include/surf_chamfer_surface.h | 3 + C3d/Include/surf_channel_surface.h | 548 +- C3d/Include/surf_coons_surface.h | 1052 ++-- C3d/Include/surf_corner_surface.h | 692 +-- C3d/Include/surf_cover_surface.h | 6 + C3d/Include/surf_curve_bounded_surface.h | 10 +- C3d/Include/surf_cylinder_surface.h | 2 +- C3d/Include/surf_elementary_surface.h | 6 +- C3d/Include/surf_expansion_surface.h | 4 +- C3d/Include/surf_exploration_surface.h | 388 +- C3d/Include/surf_extrusion_surface.h | 4 +- C3d/Include/surf_fillet_surface.h | 1425 ++--- C3d/Include/surf_grid_surface.h | 825 ++- C3d/Include/surf_join_surface.h | 842 +-- C3d/Include/surf_lofted_surface.h | 1370 ++--- C3d/Include/surf_mesh_surface.h | 222 +- C3d/Include/surf_offset_surface.h | 15 +- C3d/Include/surf_plane.h | 6 +- C3d/Include/surf_polysurface.h | 4 +- C3d/Include/surf_revolution_surface.h | 4 +- C3d/Include/surf_ruled_surface.h | 4 +- C3d/Include/surf_section_surface.h | 473 ++ C3d/Include/surf_sector_surface.h | 4 +- C3d/Include/surf_smooth_surface.h | 820 +-- C3d/Include/surf_spine.h | 272 +- C3d/Include/surf_spiral_surface.h | 801 +-- C3d/Include/surf_spline_surface.h | 158 +- C3d/Include/surface.h | 102 +- C3d/Include/system_atomic.h | 484 +- C3d/Include/system_cpp_standard.h | 253 +- C3d/Include/system_dependency.h | 508 +- C3d/Include/system_types.h | 50 +- C3d/Include/templ_array2.h | 91 +- C3d/Include/templ_balance_tree.h | 18 +- C3d/Include/templ_c_array.h | 17 +- C3d/Include/templ_csp_array.h | 17 +- C3d/Include/templ_dptr.h | 372 +- C3d/Include/templ_fdp_array.h | 66 +- C3d/Include/templ_ifc_array.h | 28 +- C3d/Include/templ_im_array.h | 133 +- C3d/Include/templ_iterator.h | 192 +- C3d/Include/templ_kdtree.h | 1002 ++-- C3d/Include/templ_lis_array.h | 6 +- C3d/Include/templ_multimap.h | 1398 ++--- C3d/Include/templ_p_array.h | 40 +- C3d/Include/templ_pointer.h | 22 +- C3d/Include/templ_psrt_array.h | 2 +- C3d/Include/templ_rp_array.h | 109 +- C3d/Include/templ_rp_stack.h | 2 +- C3d/Include/templ_s_array.h | 169 +- C3d/Include/templ_s_list.h | 136 +- C3d/Include/templ_s_queue.h | 75 +- C3d/Include/templ_sfdp_array.h | 18 +- C3d/Include/templ_sfp_array.h | 41 +- C3d/Include/templ_sp_array.h | 65 +- C3d/Include/templ_sptr.h | 94 +- C3d/Include/templ_ss_array.h | 32 +- C3d/Include/templ_stack.h | 172 +- C3d/Include/tool_cstring.h | 9 +- C3d/Include/tool_enabler.h | 208 +- C3d/Include/tool_err_handling.h | 210 +- C3d/Include/tool_log.h | 248 +- C3d/Include/tool_memory_debug.h | 692 +-- C3d/Include/tool_memory_leaks_check.h | 98 +- C3d/Include/tool_memory_leaks_utils.h | 128 +- C3d/Include/tool_multithreading.h | 1061 ++-- C3d/Include/tool_mutex.h | 1038 ++-- C3d/Include/tool_time_test.h | 10 +- C3d/Include/tool_uuid.h | 576 +- C3d/Include/topology.h | 66 +- C3d/Include/topology_faceset.h | 271 +- C3d/Include/topology_item.h | 52 +- C3d/Include/tri_ball_pivoting.h | 496 +- C3d/Include/tri_face.h | 5 +- C3d/Include/tri_lump.h | 10 +- C3d/Include/wire_frame.h | 160 +- C3d/Lib/x32/Debug/c3d.lib | Bin 7764596 -> 8189322 bytes C3d/Lib/x32/Release/c3d.lib | Bin 8617262 -> 9074700 bytes C3d/Lib/x64/Debug/c3d.lib | Bin 8009102 -> 8446042 bytes C3d/Lib/x64/Release/c3d.lib | Bin 8890488 -> 9361220 bytes 332 files changed, 59308 insertions(+), 52918 deletions(-) create mode 100644 C3d/Include/attr_common_attribute.h create mode 100644 C3d/Include/attr_elementary_attribute.h create mode 100644 C3d/Include/attr_flange_attribute.h create mode 100644 C3d/Include/attr_geometric_attribute.h rename C3d/Include/{attr_stamprib_attribut.h => attr_stamprib_attribute.h} (94%) rename C3d/Include/{attr_user_attribut.h => attr_user_attribute.h} (87%) create mode 100644 C3d/Include/conv_binary_object.h rename C3d/Include/{conv_error_result.h => conv_exchange_settings.h} (50%) create mode 100644 C3d/Include/conv_model_document.h rename C3d/Include/{conv_i_converter.h => conv_model_exchange.h} (65%) delete mode 100644 C3d/Include/conv_model_properties.h create mode 100644 C3d/Include/conv_predefined.h create mode 100644 C3d/Include/conv_topo_mesh.h create mode 100644 C3d/Include/cr_section_shell.h create mode 100644 C3d/Include/cr_sheet_builder_solid.h create mode 100644 C3d/Include/func_serve_function.h create mode 100644 C3d/Include/surf_section_surface.h diff --git a/C3d/Include/action.h b/C3d/Include/action.h index 670ccf1..bd6569e 100644 --- a/C3d/Include/action.h +++ b/C3d/Include/action.h @@ -1,1276 +1,1319 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Функции работы с кривыми, поверхностями, оболочками, телами. - \en Functions for operating with curves, surfaces, shells and solids. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ACTION_H -#define __ACTION_H - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbCurve; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbSurface; -class MATH_CLASS MbCurveEdge; -class MATH_CLASS MbFace; -class MATH_CLASS MbSolid; -class MATH_CLASS MbPlacement3D; -class MATH_CLASS MbPlanarGrid; -class MATH_CLASS MbGrid; -class MATH_CLASS MbSNameMaker; -class IProgressIndicator; - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить оболочку на предмет разделения на отдельные части. - \en Check if the shell can be subdivided into separate parts. \~ - \details \ru Проверить замкнутую оболочку на предмет разделения на отдельные части с анализом вложенности. \n - \en Check if the closed shell can be subdivided into separate parts with inclusion analysis. \n \~ - \param[in] shell - \ru Исходная оболочка. - \en The initial shell. \~ - \result \ru Возвращает true, если оболочка состоит из нескольких частей. - \en Returns 'true' if the shell consists of several parts. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) IsMultiShell( const MbFaceShell * shell, bool checkNesting = true ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Отделить части оболочки. - \en Separate parts from a shell. \~ - \details \ru Отделить части оболочки без анализа вложенности. - Если sort == true, то наибольшая часть оболочки останется в исходной оболочке, - а отделившиеся от неё части будут сложены в parts с сортировкой по убыванию габарита. \n - \en Separate parts from a shell without inclusion analysis. - If 'sort' == 'true', the greatest part of the shell will remain in the initial shell, - separated parts will be collected in array 'parts' sorted by bounding box size in descending order. \n \~ - \param[in] shell - \ru Исходная оболочка. - \en The initial shell. \~ - \param[out] parts - \ru Оболочки, полученные из shell. - \en The shells separated from 'shell'. \~ - \param[in] sort - \ru Выполнять ли сортировку частей оболочки по убыванию габарита? - \en If 'sort' == true, the parts separated from the initial shell will be sorted by bounding box size in descending order. \~ - \result \ru Возвращает количество оболочек в parts. - \en Returns number of shells in 'parts'. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (size_t) DetachShells( MbFaceShell & shell, RPArray & parts, bool sort ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Отделить части оболочки. - \en Separate parts from a shell. \~ - \details \ru Отделить части оболочки без анализа вложенности. - Исходная оболочка всегда остаётся неизменённой. - Если исходная оболочка распадается на части, то все части складываются в parts. \n - \en Separate parts from a shell without inclusion analysis. - The initial shell always remains unchangeable. - If the initial shell is decomposed, all the parts are put into array 'parts'. \n \~ - \param[in] shell - \ru Исходная оболочка. - \en The initial shell. \~ - \param[out] parts - \ru Оболочки, полученные из shell. - \en The shells separated from 'shell'. \~ - \result \ru Возвращает количество оболочек в parts. - \en Returns number of shells in 'parts'. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (size_t) CreateShells( MbFaceShell & shell, RPArray & parts ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить оболочку выдавливанием плоских контуров. - \en Create a shell by extrusion of planar contours. \~ - \details \ru Построить оболочку выдавливанием плоских контуров. \n - \en Create a shell by extrusion of planar contours. \n \~ - \param[in] surface - \ru Поверхность контуров. - \en A surface that contains the contours. \~ - \param[in] contours - \ru Набор двумерных контуров. - \en A set of planar contours. \~ - \param[in] direction - \ru Направление выдавливания. - \en An extrusion direction. \~ - \param[in] params - \ru Параметры построения. - \en Parameters of a shell creation. \~ - \param[in] operNames - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[in] cNames - \ru Набор именователей контуров. - \en A set of objects defining names of the contours. \~ - \param[out] result - \ru Результат операции - оболочка. - \en Result of the operation - a shell. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ExtrusionShell( const MbSurface & surface, - RPArray & contours, - const MbVector3D & direction, - const ExtrusionValues & params, - const MbSNameMaker & operNames, - RPArray & cNames, - MbFaceShell *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить оболочку вращением плоских контуров. - \en Create a shell by revolution of planar contours. \~ - \details \ru Построить оболочку вращением плоских контуров. \n - \en Create a shell by revolution of planar contours. \n \~ - \param[in] surface - \ru Поверхность контуров. - \en A surface that contains the contours. \~ - \param[in] contours - \ru Набор двумерных контуров. - \en A set of planar contours. \~ - \param[in] axis - \ru Ось вращения. - \en Rotation axis. \~ - \param[in] params - \ru Параметры построения. - \en Parameters of a shell creation. \~ - \param[in] operNames - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[in] cNames - \ru Набор именователей контуров. - \en A set of objects defining names of the contours. \~ - \param[out] result - \ru Результат операции - оболочка. - \en Result of the operation - a shell. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) RevolutionShell( const MbSurface & surface, - RPArray & contours, - const MbAxis3D & axis, - const RevolutionValues & params, - const MbSNameMaker & operNames, - RPArray & cNames, - MbFaceShell *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Объединить компланарные грани. - \en Unite complanar faces. \~ - \details \ru Объединить компланарные грани оболочки и проверить оболочку. \n - \en Unite complanar faces of a shell and validate the shell. \n \~ - \param[in] shell - \ru Модифицируемая оболочка. - \en A shell to be modified. \~ - \param[in] nameMaker - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[in] checkBaseSurfaces - \ru Найти и устранить общие поверхности-подложки в гранях. - \en Find and eliminate common underlying surfaces of faces \~ - \return \ru Возвращает true, если оболочка была успешно изменена. - \en Returns 'true' if the shell has been successfully modified. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) UnifyOwnComplanarFaces( MbFaceShell & shell, - const MbSNameMaker & nameMaker, - bool checkBaseSurfaces ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти и устранить общие поверхности-подложки в гранях. - \en Find and eliminate common underlying surfaces of faces \~ - \details \ru Найти и устранить общие поверхности-подложки в гранях оболочки. \n - \en Find and eliminate common underlying surfaces of a shell faces. \n \~ - \param[in] shell - \ru Модифицируемая оболочка. - \en A shell to be modified. \~ - \return \ru Возвращает true, если оболочка была изменена. - \en Returns 'true' if the shell has been modified. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) CheckIdenticalBaseSufaces( MbFaceShell & shell ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Захватить грани одним из способов. - \en Capture the faces in one of proposed methods. \~ - \details \ru Захватить грани одним из способов распространения по связной оболочке. \n - \en Capture the faces in one of methods of propagation in connected shell. \n \~ - \param[in] fp - \ru Cпособ захвата граней. - \en A method of capturing the faces. \~ - \param[in,out] face_set - \ru Набор граней. - \en A set of faces. \~ - \param[in] dir - \ru Направление уклона. - \en A direction of inclination. \~ - \warning \ru Вспомогательная функция операции DraftSolid. - \en An auxillary function of operation DraftSolid. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (void) FacePropagate( const MbeFacePropagation fp, - RPArray & face_set, - const MbVector3D & dir ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Масштабировать каверны литейной формы. - \en Scale cavities of a mold. \~ - \details \ru Масштабировать каверны литейной формы относительно неподвижной точки. \n - \en Scale cavities of a mold relative to a fixed point. \n \~ - \param[in,out] solids - \ru Модифицируемые тела. - \en The solids to be modified. \~ - \param[in] fixedPoint - \ru Неподвижная точка масштабирования. - \en The fixed point of scaling. \~ - \param[in] deltaX - \ru Относительное приращение размера по направлению X. - \en Relative increment of size in X-direction. \~ - \param[in] deltaY - \ru Относительное приращение размера по направлению Y. - \en Relative increment of size in Y-direction. \~ - \param[in] deltaZ - \ru Относительное приращение размера по направлению Z. - \en Relative increment of size in Z-direction. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (void) MouldCavitySolids( RPArray & solids, - MbCartPoint3D * fixedPoint, - double deltaX, - double deltaY, - double deltaZ ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить тела на пересечение. - \en Check intersection of solids. \~ - \details \ru Проверить тела на пересечение без уточнения характера пересечения \n - (проверяем до первого пересечения граней). \n - \en Check if solids intersect each other without definition of intersection type \n - (check until the first intersection is detected). \n \~ - \param[in] solid1 - \ru Первое тело. - \en The first solid. \~ - \param[in] solid2 - \ru Второе тело. - \en The second solid. \~ - \return \ru Возвращает true, если найдено хотя бы одно пересечение. - \en Returns 'true' if at least one intersection is detected. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) IsSolidsIntersection( const MbSolid & solid1, const MbSolid & solid2, const MbSNameMaker & snMaker ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Определить пересечение тел в сборке. - \en Check intersection of solids in an assembly. \~ - \details \ru Определить пересечение тел в сборке. \n - \en Check intersection of solids in an assembly. \n \~ - \param[in] solid1 - \ru Первое тело в локальной системе координат (ЛСК). - \en The first solid in local coordinate system (LCS). \~ - \param[in] matr1 - \ru Матрица преобразования в глобальную СК (ГСК). - \en Matrix of transformation to the global coordinate system (GCS). \~ - \param[in] solid2 - \ru Второе тело в ЛСК. - \en The second solid in LCS. \~ - \param[in] matr2 - \ru Матрица преобразования в ГСК. - \en Matrix of transformation to GCS. \~ - \param[in] checkTangent - \ru Считать касания пересечениями. - \en Consider tangencies as intersections. \~ - \param[in] getIntersectionSolids - \ru Получить не касательные пересечения в виде тел. - \en Get non-tangent intersections in the form of bodies. \~ - \param[in] checkTouchPoints - \ru Искать точки касания. - \en Find touch points. \~ - \param[out] intData - \ru Информация о пересечении двух тел. - \en Information about two solids intersection. \~ - \return \ru Возвращает true, если найдено хотя бы одно пересечение. - \en Returns 'true' if at least one intersection is detected. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) IsSolidsIntersection( const MbSolid & solid1, const MbMatrix3D & matr1, - const MbSolid & solid2, const MbMatrix3D & matr2, - bool checkTangent, // \ru Считать касания пересечениями \en Consider tangencies as intersections - bool getIntersectionSolids, // \ru Получить не касательные пересечения в виде тел \en Get non-tangency intersection as solids - bool checkTouchPoints, // \ru Искать точки касания \en Find touch points - RPArray & intData ); - -//------------------------------------------------------------------------------ -/** \brief \ru Определить минимальное расстояние между телами в сборке. - \en Determine the minimum distance between solids in an assembly. \~ - \details \ru Определить минимальное расстояние между телами в сборке. В случае пересечения или касания тел возвращается нулевая дистанция.\n - При многократном использовании первого тела следует установить isMultipleUseSolid1 = true, иначе false. Аналогично для второго тела.\n - \en Determine the minimum distance between solids in an assembly. In case of intersection or tangent of the shells returns to zero distance.\n - With multiple use of the first body should be set isMultipleUseSolid1 = true, else false. Similarly for the second body.\n \~ - \param[in] solid1 - \ru Первое тело в локальной системе координат (ЛСК). - \en The first solid in local coordinate system (LCS). \~ - \param[in] matr1 - \ru Матрица преобразования в глобальную СК (ГСК). - \en Matrix of transformation to the global coordinate system (GCS). \~ - \param[in] isMultipleUseSolid1 - \ru Множественное использование первого тела. - \en Multiple use of the first body. \~ - \param[in] solid2 - \ru Второе тело в ЛСК. - \en The second solid in LCS. \~ - \param[in] matr2 - \ru Матрица преобразования в ГСК. - \en Matrix of transformation to GCS. \~ - \param[in] isMultipleUseSolid2 - \ru Множественное использование второго тела. - \en Multiple use of the second body. \~ - \param[in] lowerLimitDistance - \ru Минимальное допустимое расстояние. - \en Minimum allowed distance. \~ - \param[in] tillFirstLowerLimit - \ru Искать до первого найденного удовлетворяющего минимально допустимому расстоянию. - \en Search until the first found that satisfies the minimum acceptable distance. \~ - \param[out] shellsDistanceData - \ru Информация о расстоянии между телами. - \en Information about the distance between solids. \~ - \return \ru Возвращает true, если определено хотя бы одно расстояние. - \en Returns 'true' if at least one distance is obtained. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) MinimumSolidsDistance( const MbSolid & solid1, const MbMatrix3D & matr1, bool isMultipleUseSolid1, - const MbSolid & solid2, const MbMatrix3D & matr2, bool isMultipleUseSolid2, - double lowerLimitDistance, bool tillFirstLowerLimit, - std::vector & shellsDistanceData ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти расстояния от контура на плоскости до поверхности. - \en Find the distances from a contour on a plane to a surface. \~ - \details \ru Найти расстояния от контура на плоскости до поверхности. \n - Прямое направление - это направление оси Z системы координат двумерной кривой. \n - Расстояние в прямом направлении найдено, если значение не отрицательное. \n - Расстояние в обратном направлении найдено, если значение не положительное. \n - \en Find the distances from a contour on a plane to a surface. \n - A forward direction is a direction of Z-axis of two-dimensional curve coordinate system. \n - The distance in a forward direction is found if the value is non-negative. \n - The distance in a backward direction is found if the value is non-positive. \n \~ - \param[in] pl - \ru Система координат двумерной кривой. - \en A coordinate system of two-dimensional curve. \~ - \param[in] curve - \ru Двумерная кривая. - \en A two-dimensional curve. \~ - \param[in] surf - \ru Поверхность, до которой проводится поиск расстояний. - \en A surface to measure the distances up to. \~ - \param[out] lPlus - \ru Расстояние в прямом направлении. - \en The distance in a forward direction. \~ - \param[out] lMinus - \ru Расстояние в обратном направлении. - \en The distance in a backward direction. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (void) GetDistanceToSurface( const MbPlacement3D & pl, - const MbCurve * curve, - const MbSurface * surf, - double & lPlus, - double & lMinus ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создание поверхностей сечения выдавливания плоского контура. - \en Create cutter surfaces for extrusion of planar contours. \~ - \details \ru Создание поверхностей сечения выдавливания плоского контура и определение направлений выдавливаний. \n - \en Create cutter surfaces for extrusion of planar contours and define directions of extrusions. \n \~ - \param[in] surface - \ru Поверхность контуров. - \en A surface that contains the contours. \~ - \param[in] contours - \ru Набор двумерных контуров. - \en A set of planar contours. \~ - \param[in] direction - \ru Направление выдавливания. - \en An extrusion direction. \~ - \param[in] params - \ru Параметры построения. - \en Parameters of a shell creation. \~ - \param[in] version - \ru Версия построения. - \en The version of construction. \~ - \param[out] resType - \ru Код результата операции. - \en Operation result code. \~ - \param[out] surfAndDir- \ru Результат операции - поверхности и направление относительно direction. - \en Result of the operation - surfaces and directions relative to parameter direction. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (void) CreateExtrusionCutSurfaces( const MbSurface & surface, - const c3d::PlaneContoursSPtrVector & contours, - const MbVector3D & direction, - ExtrusionValues & params, - VERSION version, - MbResultType & resType, - std::vector< std::pair> & surfAndDir ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти расстояния от контура на поверхности до габаритного куба оболочки. - \en Find the distances from a contour on a surface to the bounding box of a shell. \~ - \details \ru Найти расстояния от контура на поверхности до габаритного куба оболочки. \n - Расстояние в прямом направлении найдено, если значение не отрицательное. \n - Расстояние в обратном направлении найдено, если значение не положительное. \n - \en Find the distances from a contour on a surface to the bounding box of a shell. \n - The distance in a forward direction is found if the value is nonnegative. \n - The distance in a backward direction is found if the value is non-positive. \n \~ - \param[in] surface - \ru Поверхность, на которой лежит двумерная кривая. - \en A surface that contains the two-dimensional curve. \~ - \param[in] direction - \ru Направление поиска (выдавливания) - \en A direction of the distance calculation (an extrusion direction). \~ - \param[in] curve - \ru Двумерная кривая, лежащая на поверхности surface. - \en A two-dimensional curve on surface 'surface'. \~ - \param[in] cube - \ru Габаритный куб оболочки. - \en The bounding box of the shell. \~ - \param[out] lPlus - \ru Расстояние в прямом направлении. - \en The distance in a forward direction. \~ - \param[out] lMinus - \ru Расстояние в обратном направлении. - \en The distance in a backward direction. \~ - \param[out] resType - \ru Код результата операции. - \en Operation result code. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (void) GetDistanceToCube( const MbSurface & surface, - const MbVector3D & direction, - const MbCurve & curve, - const MbCube & cube, - double & lPlus, - double & lMinus, - MbResultType & resType ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти расстояния от набора кривых на поверхности до габаритного куба оболочки. - \en Find the distances from curves on a surface to the bounding box of a shell. \~ - \details \ru Найти расстояния от набора кривых на поверхности до габаритного куба оболочки. \n - Расстояние в прямом направлении найдено, если значение не отрицательное. \n - Расстояние в обратном направлении найдено, если значение не положительное. \n - \en Find the distances from curves on a surface to the bounding box of a shell. \n - The distance in a forward direction is found if the value is nonnegative. \n - The distance in a backward direction is found if the value is nonpositive. \n \~ - \param[in] surface - \ru Поверхность, на которой лежат двумерные кривые. - \en A surface that contains two-dimensional curves. \~ - \param[in] direction - \ru Направление поиска (выдавливания) - \en A direction of the distance calculation (an extrusion direction). \~ - \param[in] curves - \ru Набор двумерных кривых на поверхности surface. - \en A set of two-dimensional curves on the surface 'surface'. \~ - \param[in] cube - \ru Габаритный куб оболочки. - \en The bounding box of the shell. \~ - \param[out] lPlus - \ru Расстояние в прямом направлении. - \en The distance in a forward direction. \~ - \param[out] lMinus - \ru Расстояние в обратном направлении. - \en The distance in a backward direction. \~ - \param[out] resType - \ru Код результата операции. - \en Operation result code. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (void) GetDistanceToCube( const MbSurface & surface, - const MbVector3D & direction, - const RPArray & curves, - const MbCube & cube, - double & lPlus, - double & lMinus, - MbResultType & resType ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти расстояния от плоскости до габаритного куба оболочки. - \en Find the distances from a plane to the bounding box of a shell. \~ - \details \ru Найти расстояния от плоскости до габаритного куба оболочки. \n - Расстояние в прямом направлении найдено, если значение не отрицательное. \n - Расстояние в обратном направлении найдено, если значение не положительное. \n - Использует расчет габарита относительно локальной системы координат. \n - Если система координат плоскости лежит вне габаритного куба, \n - то при взведенном флаге findMax ищется максимальное расстояние до куба. \n - \en Find the distances from a plane to the bounding box of a shell. \n - The distance in a forward direction is found if the value is non-negative. \n - The distance in a backward direction is found if the value is non-positive. \n - Calculation of the distance relative to the local coordinate system is used. \n - If a plane coordinate system is out of the bounding box, \n - then if the flag 'findMax' is set to 'true', the maximal distance to bounding box is calculated. \n \~ - \param[in] pl - \ru Система координат плоскости. - \en The plane coordinate system. \~ - \param[in] shell - \ru Целевая оболочка. - \en A target shell. \~ - \param[out] dPlus - \ru Расстояние в прямом направлении. - \en The distance in a forward direction. \~ - \param[out] dMinus - \ru Расстояние в обратном направлении. - \en The distance in a backward direction. \~ - \param[in] findMax - \ru Искать максимальное расстояние до габаритного куба. - \en The maximal distance to bounding box is to be calculated. \~ - \return \ru Возвращает true, если найдено хотя бы одно из расстояний. - \en Returns 'true' if at least one of the distances has been found. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) GetDistanceToCube( const MbPlacement3D & pl, - const MbFaceShell * shell, - double & dPlus, - double & dMinus, - bool findMax = true ); // \ru Искать максимальное \en Find the maximal distance - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти расстояния/углы от контура до куба или до поверхности. - \en Find the distances/angles from a contour to a bounding box or to a surface. \~ - \details \ru Найти расстояния/углы от контура до куба или до поверхности. \n - Нужно учесть уклон в двух направлениях. \n - \en Find the distances/angles from a contour to a bounding box or to a surface. \n - The inclination in two directions is to be considered. \n \~ - \param[in] curve - \ru Кривая. - \en A curve. \~ - \param[in] direction - \ru Направление выдавливания. - \en An extrusion direction. \~ - \param[in] axis - \ru Ось вращения. - \en Rotation axis. \~ - \param[in] rotation - \ru Вращение (true) или выдавливание (false) - \en Rotation (true) or extrusion (false) \~ - \param[in] operationDirection - \ru Вперед (true) или назад (false) - \en Forward (true) or backward (false) \~ - \param[in] toCube - \ru До куба, если указатель ненулевой - \en Up to a cube if the pointer is not null. \~ - \param[in] toSurface - \ru До поверхности, если указатель ненулевой. - \en Up to a surface if the pointer is not null. \~ - \param[in,out] params: -\ru Должны быть заданы параметры: \n - params.side1.rake - Уклон в направлении direction (для плоской образующей). \n - params.side2.rake - Уклон в направлении direction, обратном direction (для плоской образующей). \n - params.thikness1 - Толщина стенки в прямом направлении - (в положительном направлении нормали объекта (грани, поверхности, плоскости кривой)). \n - params.thikness2 - Толщина стенки в обратном направлении. \n - Заполняются параметры: \n - params.side1.scalarValue - Расстояние выдавливания в направлении direction (если operationDirection == true), - иначе обратном. \n - params.side2.scalarValue - Расстояние выдавливания в направлении, обратном direction (если operationDirection == true), - иначе в прямом. \n -\en The following parameters should be defined: \n - params.side1.rake - The inclination in direction 'direction' (for a planar generatrix). \n - params.side2.rake - The inclination in direction opposite to 'direction' (for a planar generatrix). \n - params.thikness1 - Wall thickness in forward direction. - (in the positive direction of the normal of an object (a face, a surface, the plane of a curve)). \n - params.thikness2 - The wall thickness in the backward direction. \n - The output parameters: \n - params.side1.scalarValue - The extrusion distance in the direction 'direction' (if 'operationDirection' == true), - else in the opposite direction. \n - params.side2.scalarValue - The extrusion distance in the direction opposite to 'direction' (if 'operationDirection' == true), - else in the forward direction. \n \~ - \param[out] resType - \ru Код результата операции. - \en Operation result code. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (void) GetRangeToCubeOrSurface( const MbCurve3D & curve, - const MbVector3D & direction, - const MbAxis3D & axis, - const bool rotation, - bool operationDirection, - const MbCube * toCube, - const MbSurface * toSurface, - SweptValuesAndSides & params, - MbResultType & resType ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти ближайшие тела при выдавливании с опцией "до ближайшего объекта". - \en Find the nearest solids while extruding with option 'up to the nearest object'. \~ - \details \ru Найти ближайшие тела при выдавливании с опцией "до ближайшего объекта". \n - Возвращает номерa (nPlus и nMinus) ближайших тел с положительной и отрицательной стороны эскиза. - \en Find the nearest solids while extruding with option 'up to the nearest object'. \n - Returns the numbers (nPlus and nMinus) of nearest solids on the positive and the negative sides of the sketch. \~ - \param[in] pl - \ru Локальная система координат. - \en A local coordinate system. \~ - \param[in] c - \ru Множество двумерных контуров. - \en An array of two-dimensional contours. \~ - \param[in] direction - \ru Направление выдавливания. - \en An extrusion direction. \~ - \param[in] solids - \ru Целевой набор тел. - \en A target set of solids. \~ - \param[out] nPlus - \ru Номер ближайшего тела в положительном направлении. - \en The number of the nearest solid in the positive direction. \~ - \param[out] nMinus - \ru Номер ближайшего тела в отрицательном направлении. - \en The number of the nearest solid in the negative direction. \~ - \return \ru Возвращает true, если найдено тело хотя бы в одном из направлений. - \en Returns 'true' if a solid is found in at least one of directions. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) GetNearestSolid( const MbPlacement3D & pl, - RPArray & c, - MbSweptLayout::Direction direction, - RPArray & solids, - size_t & nPlus, - size_t & nMinus ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти ближайшие тела при выдавливании с опцией "до ближайшего объекта". - \en Find the nearest solids while extruding with option 'up to the nearest object'. \~ - \details \ru Найти ближайшие тела при выдавливании с опцией "до ближайшего объекта". \n - возвращает номерa (nPlus и nMinus) ближайших тел ближайших тел в прямом и обратном направлении. - \en Find the nearest solids while extruding with option 'up to the nearest object'. \n - returns numbers (nPlus and nMinus) of the nearest solids in the forward and the backward direction. \~ - \param[in] curves - \ru Набор кривых. - \en A set of curves. \~ - \param[in] direction - \ru Направление выдавливания. - \en An extrusion direction. \~ - \param[in] operationDirection - \ru Параметры выдавливания "до ближайшего объекта". - \en Parameters of extrusion 'up to the nearest object'. \~ - \param[in] solids - \ru Целевой набор тел. - \en A target set of solids. \~ - \param[out] nPlus - \ru Номер ближайшего тела в положительном направлении. - \en The number of the nearest solid in the positive direction. \~ - \param[out] nMinus - \ru Номер ближайшего тела в отрицательном направлении. - \en The number of the nearest solid in the negative direction. \~ - \return \ru Возвращает true, если найдено тело хотя бы в одном из направлений. - \en Returns 'true' if a solid is found in at least one of directions. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) GetNearestSolid( RPArray & curves, - const MbVector3D & direction, - MbSweptLayout::Direction operationDirection, - RPArray & solids, - size_t & nPlus, - size_t & nMinus ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить оболочку или тело, состоящее из NURBS поверхностей. - \en Check a shell or a solid that consists of NURBS surfaces. \~ - \details \ru Проверить корректность оболочки или тела, состоящего из NURBS поверхностей. \n - \en Check the correctness of a shell or a solid that consists of NURBS surfaces. \n \~ - \param[in] params - \ru Исходные параметры операции. - \en Initial parameters of the operation. \~ - \param[in] nsSolid - \ru Тело - результат операции. - \en A solid - the result of the operation. \~ - \param[in] progBar - \ru Индикатор прогресса выполнения операции. - \en A progress indicator of the operation. \~ - \return \ru Возвращает rt_Success, если тело успешно прошло проверку. - \en Returns rt_Success if the solid has successfully passed the validation. \~ - \warning \ru Проверочная функция операции NurbsSurfacesShell. - \en A checking function of the operation NurbsSurfacesShell. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (MbResultType) CheckNurbsShell( const NurbsSurfaceValues & params, - const MbSolid & nsSolid, - IProgressIndicator * progBar ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Положить эскиз в массив усекающих объектов. - \en Add a sketch to the array of truncating objects. \~ - \details \ru Положить эскиз в массив усекающих объектов путем создания пространственных кривых. \n - \en Add a sketch to the array of truncating objects by creation of spatial curves. \n \~ - \param[in] sketchPlace - \ru Локальная система координат двумерного эскиза. - \en A local coordinate system of two-dimensional sketch. \~ - \param[in] sketchCurves - \ru Двумерные кривые эскиза. - \en Two-dimensional curves of the sketch. \~ - \param[out] items - \ru Выходной массив пространственных объектов. - \en The output array of spatial objects. \~ - \return \ru - Возвращает true в случае добавления элементов в выходной массив. - \en - Returns 'true' if elements are added into output array. \~ - \warning \ru Вспомогательная функция операции TruncateShell. - \en Auxiliary function of the operation TruncateShell. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) AddTruncatingSketch( const MbPlacement3D & sketchPlace, - RPArray & sketchCurves, - RPArray & items ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Положить кривую в массив усекающих объектов. - \en Add a curve to the array of truncating objects. \~ - \details \ru Положить кривую в массив усекающих объектов - (с разбором кривой на составляющие, в случае необходимости). \n - \en Add a curve to the array of truncating objects - (with decomposition of the curve if necessary). \n \~ - \param[in] curve - \ru Пространственная кривая. - \en A space curve. \~ - \param[out] items - \ru Выходной массив пространственных объектов. - \en The output array of spatial objects. \~ - \warning \ru Вспомогательная функция операции TruncateShell. - \en Auxillary function of the operation TruncateShell. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (void) AddTruncatingCurve( const MbCurve3D & curve, - RPArray & items ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить корректность вскрываемых граней для создания тонкостенного тела. - \en Check the correctness of shelling faces for creation of a thin-walled solid. \~ - \details \ru Проверить корректность набора вскрываемых граней для создания тонкостенного тела. \n - Удаляет из массива не подходящие для операции грани (гладко сопряженные с невыбранными гранями). \n - \en Check the correctness of shelling faces set for creation of a thin-walled solid. \n - Removes unsuitable for the operation faces from the array (Delete faces smoothly connected to unselected faces). \n \~ - \param[in] params - \ru Параметры тонкой стенки. - \en Parameters of a thin wall. \~ - \param[in,out] faces - \ru Множество вскрываемых граней тела. - \en An array of shelling faces of the solid. \~ - \warning \ru Вспомогательная функция операции построения тонкостенного тела. - \en An auxiliary function of thin-walled solid construction operation. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (void) CheckShellingFaces( const SweptValues & params, RPArray & faces ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Вычислить компоненты проекции вектора, заданного в точке на поверхности. - \en Calculate the components of projection of a vector defined at a point on the surface. \~ - \details \ru Вычислить компоненты x и y проекции пространственного вектора, заданного в точке на поверхности. \n - \en Calculate x and y components of projection of a space vector defined at a point on a surface. \n \~ - \param[in] v3d - \ru Пространственный вектор. - \en A space vector. \~ - \param[in] surface - \ru Поверхность. - \en A surface. \~ - \param[in] p2d - \ru Параметрическая точка на поверхности. - \en A parametric point on the surface. \~ - \param[out] v2d - \ru Проекция пространственного вектора на поверхность. - \en The projection of the space vector on the surface. \~ - \return \ru - Возвращает true в случае успешного вычисления проекции вектора. - \en - Returns 'true' if the vector projection has been successfully calculated. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) ProjectVectorOn( const MbVector3D & v3d, const MbSurface & surface, const MbCartPoint & p2d, - MbVector & v2d ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Расширить поверхность для резки тела. - \en Extend a surface for cutting a solid. \~ - \details \ru Расширить поверхность до заданного габарита для резки тела. \n - \en Extend a surface to a given bounding box for cutting a solid. \n \~ - \param[in,out] gabarit - \ru Желаемый габарит расширения. - \en A desirable bounding box of the extended surface. \~ - \param[in] surf - \ru Исходная поверхность. - \en The initial surface. \~ - \param[in] prolongState - \ru Состояние типа продления секущих поверхностей. - \en State of prolongation types of cutter surfaces. \~ - \param[in] version - \ru Версия построения. - \en The version of construction. \~ - \return \ru - Возвращает расширенную поверхность, если получилось ее создать. - \en - Returns the extended surface if it has been successfully created. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (MbSurface *) GetExtendedSurfaceCopy( MbCube & gabarit, - const MbSurface & surf, - const MbShellCuttingParams::ProlongState & prolongState, - VERSION version ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить набор граней с топологией призмы. - \en Create a set of faces with topology of a prism. \~ - \details \ru Построить набор граней с топологией призмы. \n - \en Create a set of faces with topology of a prism. \n \~ - \param[in] place - \ru Локальная система координат (ЛСК). - \en A local coordinate system (LCS). \~ - \param[in] contour - \ru Двумерный контур в ЛСК. - \en A two-dimensional curve in LCS. \~ - \param[in] der - \ru Направление выдавливания. - \en An extrusion direction. \~ - \param[in] sense - \ru Ориентация выходного массива граней как замкнутой оболочки. - \en An orientation of the output array of faces as a closed shell. \~ - \param[in] n - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in,out] initFaces - \ru Множество созданных граней. - \en The array of created faces. \~ - \param[in] useAddCount - \ru Использовать количество граней initFaces на входе для именования новых граней. - \en The number of input faces initFaces is to be used for naming the new faces. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (void) CreateFaces( const MbPlacement3D & place, const MbContour & contour, - const MbVector3D & der, bool sense, const MbSNameMaker & n, - RPArray & initFaces, bool useAddCount = false ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Оценить параметры выдавливания для ребра жёсткости. - \en Estimate parameters of extrusion for a rib. \~ - \details \ru Оценить параметры выдавливания для построения ребра жёсткости. \n - \en Estimate parameters of extrusion for creating a rib. \n \~ - \param[in] shell - \ru Целевая оболочка. - \en A target shell. \~ - \param[in] place - \ru Локальная система координат контура. - \en A local coordinate system of the contour. \~ - \param[in] contour - \ru Двумерный контур. - \en A two-dimensional contour. \~ - \param[in] index - \ru Номер сегмента контура. - \en A number of the contour segment. \~ - \param[out] side - \ru Сторона заполнения пространства телом ребра. - \en The side to place the rib on. \~ - \param[out] origin - \ru Точка. - \en A point. \~ - \param[out] dir3D - \ru Вектор. - \en A vector. \~ - \warning \ru Вспомогательная функция операции RibSolid. - \en An auxillary function of the operation RibSolid. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (void) GetAutoReference( MbFaceShell & shell, - const MbPlacement3D & place, - const MbContour & contour, - ptrdiff_t index, - RibValues::ExtrudeSide & side, - MbCartPoint3D & origin, - MbVector3D & dir3D ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кривую в параметрах поверхности. - \en Create a curve in the parameter space of a surface. \~ - \details \ru Создать кривую в параметрах поверхности, если нужно - проекционную. \n - Если проекционную кривую создавать не нужно, возвращает дубль двумерной кривой в параметрах поверхности. \n - После использования кривую нужно удалить. \n - \en Create a curve in the parameter space of a surface. The projection curve can be created if necessary. \n - If it is not required to create the projection curve, returns a copy of two-dimensional curve in the parameter space of the surface. \n - The curve is to be deleted after use. \n \~ - \param[in] intersectCurve - \ru Кривая пересечения. - \en The intersection curve \~ - \param[in] first - \ru true - Первая поверхность, false - вторая поверхность. - \en True - The first surface, false - the second surface. \~ - \return \ru Возвращает кривую, если ее получилось построить. - \en Returns the curve if it has been successfully created. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (MbCurve *) GetProjCurveOnSurface( const MbSurfaceIntersectionCurve & intersectCurve, bool first ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить неизменность вектора кинематической направляющий в разных версиях. - \en Check the invariance of the vector of spine direction in different versions. \~ - \details \ru Проверить, можно ли сохранить кинематическую направляющую из одной версии в другую без изменения формы. \n - \en Check if the spine direction can be preserved between versions without any change of the shape. \n \~ - \param[in] curve - \ru Направляющая кривая. - \en The spine curve. \~ - \param[in] srcVersion - \ru Рабочая версия. - \en The current version. \~ - \param[in] dstVersion - \ru Целевая версия. - \en The target version. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) IsSameSpineDirection( const MbCurve3D & curve, VERSION srcVersion, VERSION dstVersion ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Классифицировать положения второго контура относительно первого. - \en Classify the position of the second contour relative to the first one. \~ - \details \ru Классифицировать положения второго контура относительно первого: \n - iloc_OutOfItem - снаружи, \n - iloc_OnItem - пересекается, \n - iloc_InItem - внутри. \n - \en Classify the position of the second contour relative to the first one: \n - iloc_OutOfItem - outside, \n - iloc_OnItem - intersects, \n - iloc_InItem - inside. \n \~ - \param[in] contour1 - \ru Первый контур. - \en The first contour. \~ - \param[in] contour2 - \ru Второй контур. - \en The second contour. \~ - \param[in] xEpsilon - \ru Погрешность по x. - \en Tolerance in x direction. \~ - \param[in] yEpsilon - \ru Погрешность по y. - \en Tolerance in y direction. \~ - \return \ru Возвращает результат классификации положения. - \en Returns the result of classification of the relative position. \~ - \ingroup Algorithms_2D -*/ -// --- -MATH_FUNC (MbeItemLocation) SecondContourLocation( const MbContour & contour1, const MbContour & contour2, - double xEpsilon, double yEpsilon ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Определить, близка ли первая кривая ко второй кривой. - \en Determine whether the first curve is close to the second curve. \~ - \details \ru Определить, близка ли кривая curve1 к кривой curve2 с заданной точностью. \n - Близость определяется близостью точек первой кривой, полученных шаганием \n - по кривой с заданным угловым отклонением, ко второй кривой. \n - \en Determine whether curve 'curve1' is close to curve 'curve2' within the given precision. \n - The proximity is defined by the closeness of points of the first curve obtained by sampling \n - with the given turning angle to the second curve. \n \~ - \param[in] curve1 - \ru Первая кривая. - \en The first curve. \~ - \param[in] curve2 - \ru Вторая кривая. - \en The second curve. \~ - \param[in] xEpsilon - \ru Близость по x. - \en Proximity tolerance in x direction. \~ - \param[in] yEpsilon - \ru Близость по y. - \en Proximity tolerance in y direction. \~ - \param[in] devSag - \ru Максимальное угловое отклонение при шагании по кривой. - \en The maximal turning angle for sampling the curve. \~ - \return \ru Возвращает true, если кривые близки. - \en Returns 'true' if the curves are close. \~ - \ingroup Algorithms_2D -*/ -// --- -MATH_FUNC (bool) IsSpaceNear( const MbCurve & curve1, const MbCurve & curve2, double xEpsilon, double yEpsilon, - double devSag = 5.0 * Math::deviateSag ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Определить, близка ли кривая к поверхности. - \en Determine whether a curve is close to a surface. \~ - \details \ru Определить, близка ли кривая к поверхности с заданной точностью. \n - Выполняется проверка по пробным точкам кривой, полученных шаганием по угловому отклонению. \n - \en Determine whether a curve is close to a surface within the given tolerance. \n - The check uses sample points of curves obtained by sampling with maximal turning angle. \n \~ - \param[in] curv - \ru Кривая. - \en A curve. \~ - \param[in] surf - \ru Поверхность. - \en A surface. \~ - \param[in] surfExt - \ru Проверять на расширенной поверхности. - \en Perform the check for the extended surface. \~ - \param[in] mEps - \ru Метрическая близость. - \en The metric proximity tolerance. \~ - \param[in] devSag - \ru Максимальное угловое отклонение при шагании по кривой. - \en The maximal turning angle for sampling the curve. \~ - \return \ru Возвращает true, если кривая близка к поверхности. - \en Returns 'true' if the curve is close to the surface. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) IsSpaceNear( const MbCurve3D & curv, const MbSurface & surf, bool surfExt, - double mEps, double devSag = 5.0 * Math::deviateSag ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать грань по произвольной поверхности. - \en Create the face on the base of arbitrary surface. \~ - \details \ru Создать грань по произвольной поверхности без самопересечений. \n - \en Create the face on the base of arbitrary surface without selfintersections. \n \~ - \param[in] surface - \ru Поверхность. - \en A surface. \~ - \param[out] face - \ru Грань. - \en The face. \~ - \return \ru Возвращает true, если грань создана. - \en Returns 'true' if the face was created. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) SurfaceFace( const MbSurface & surface, SPtr & face ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создание трёхмерной сетки по двумерной сетке. - \en Creating a three-dimensional grid on a two-dimensional grid. \~ - \details \ru Создание трёхмерной сетки по двумерной сетке. \n - \en Creating a three-dimensional grid on a two-dimensional grid. \n \~ - \param[in] place - \ru Локальная система координат в трёхмерном пространстве. - \en Local coordinate system in three dimensional space. \~ - \param[out] planarGrid - \ru Триангуляция двумерной области. - \en Triangulation of a two-dimensional region. \~ - \return \ru Возвращает true, если грань создана. - \en Returns 'true' if the face was created. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (MbGrid *) SpaceGrid( const MbPlacement3D & place, const MbPlanarGrid & planarGrid, bool exact = false ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Заменить элемент на вставку, если расстояние от начала координат до центра - его габарита, превышает размер габаритного куба в заданное число раз \~ - \en Replace an item by an instance if the length from it's bounding box to the - world origin is greater than it's diagonal by specified factor. - \details \ru Объект или его копия смещается на вектор из начала координат до центра габаритного куба объекта - и размещается во вставке, обеспечивающей смещение на вектор противоположного направления. \~ - Обрабатываются только объекты, не являющиеся вставками. \~ - Нулевое или отрицательное значение параметра ratioThreashhold запрещает преобразование. \~ - \en The item or it's replica is moved bey the vector from the origin of the world to the center of the - item's bounding box, then put in the instance, providing the displacement by the reversed vector. \~ - Function processes objects of all types except for instances. \~ - Null or negative value of the ratioThreashhold parameter blocks the transformation. \~ - \param[out] item - \ru Обрабатываемый объект. \~ - \en Processable item. \~ - \param[in] ratioThreashhold - \ru Пороговое значение отношения расстояния до центра габаритного куба и его диагонали, - при превышении которого происходит замена. \~ - \en The replacement threshold value of the ratio of the bounding box's center to the - origin of the world to it's diagonal length. \~ - \param[in] makeCopy - \ru Производить ли трансформацию на копии объекта. \~ - \en Is the copy of the object must be transformed. \~ - \return \ru Возвращает вставку объекта. - \en Returns instance of object. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (MbItem *) ReplaceByInstance( MbItem * item, double ratioThreashhold = -1.0, bool makeCopy = false ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построениe «залитого» объема, расположенного между внутренней поверхностью сосуда и ограничивающей поверхностью или телом. \~ - \en The construction of a "flood fill" volume located between the inner surface of the vessel and the bounding surface or body. \~ - \details \ru На вход подаётся тело, дополнительная поверхность или дополнительное тело и координаты источника. - На выходе получаем объём, построенный от источника и ограниченный со всех сторон оболочкой тела и дополнительными объектами. \~ - \en The body, an additional surface or an additional body and the coordinates of the source are fed to the input. - On the output we get the volume, constructed from the source and bounded from all sides by the shell of the body and by additional objects. \~ - \param[in] vessel - \ru Тело сосуда. \~ - \en The vessel. \~ - \param[in] sameShell - \ru Режим копирования тела сосуда. - \en Whether to copy the vessel. \~ - \param[in] bungData - \ru Поверхность уровня или тело пробки. \~ - \en The surface of the level or body of the bung. \~ - \param[in] origin - \ru Точка внутри сосуда. \~ - \en The point inside the vessel. \~ - \param[in] names - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (MbResultType) FloodFillResult( MbSolid & vessel, - MbeCopyMode sameShell, - const MbSweptData & bungData, - const MbCartPoint3D & origin, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать крепеж по трехмерной точке. НЕ ИСПОЛЬЗОВАТЬ ВНЕ ТЕСТОВОГО ПРИЛОЖЕНИЯ!!! ФУНКЦИЯ НАХОДИТСЯ В РАЗРАБОТКЕ!!! - \en Create fastener using 3D point. \~ - \details \ru Создать крепеж по трехмерной точке. \n - \en Create fastener using 3D point. \n \~ - \param[in] solids - \ru Множество тел для скрепления. - \en An array of bodies to fasten. - \param[in] sameShell - \ru Режим копирования тел. - \en Whether to copy the solids. \~ - \param[in] point - \ru Трехмерная точка, на основе проецирования которой определяется положение крепежа. - \en 3d point. \~ - \param[in] params - \ru Параметры крепежа ( его тип, размеры и т.д. ). - \en Fastener parameters ( type, diameter, etc. ). \~ - \param[in] names - \ru Именователь новых граней. - \en An object defining the name of a new faces. \~ - \param[out] results - \ru Множество тел для скрепления с набором отверстий и набор тел крепежа в отверстиях. - \en Array of bodies with a holes and fastener body. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) CreateFastener ( const RPArray & solids, - MbeCopyMode sameShell, - const MbCartPoint3D & point, - const FastenersValues & params, - const MbSNameMaker & names, - RPArray & results ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Cоздать набор крепежных элементов по трехмерной кривой. НЕ ИСПОЛЬЗОВАТЬ ВНЕ ТЕСТОВОГО ПРИЛОЖЕНИЯ!!! ФУНКЦИЯ НАХОДИТСЯ В РАЗРАБОТКЕ!!! - \en Create an array of fastener elements using 3d curve. \~ - \details \ru Cоздать набор крепежных элементов по трехмерной кривой. - \en Create an array of fastener elements using 3d curve. \~ - \param[in] solids - \ru Множество тел для скрепления. - \en Array of bodies with a hole and fastener body. \~ - \param[in] sameShell - \ru Режим копирования тел. - \en Whether to copy the solids. \~ - \param[in] curve - \ru Трехмерная кривая, на основе проецирования точек которой определяются положения крепежных элементов. - \en 3D curve. \~ - \param[in] number - \ru Количество точек на кривой. Точки расположены равномерно по длине кривой. - \en Number of points on the curve. Points are uniformly located along the length of the curve. \~ - \param[in] params - \ru Параметры крепежа ( его тип, размеры и т.д. ). - \en Fastener parameters ( type, diameter, etc. ). \~ - \param[in] names - \ru Именователь новых граней. - \en An object defining the name of a new faces. \~ - \param[out] results - \ru Множество тел для скрепления с набором отверстий и набор тел крепежа в отверстиях. - \en Array of bodies with a holes and fastener bodies. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) CreateFasteners( const RPArray & solids, - MbeCopyMode sameShell, - const MbCurve3D & curve, - size_t number, - const FastenersValues & params, - const MbSNameMaker & names, - RPArray & results ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Слить несколько граней тела в одну грань. - \en Create a solid with one face instead selected faces. \~ - \details \ru Заменить указанные гладко стыкующиеся грани тела одной геометрически совпадающей гранью. \n - \en To replace these smooth abutting faces to form a single geometrically matching face. \n - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования исходного тела. - \en Whether to copy the source solid. \~ - \param[in] faces - \ru Объединяемые грани тела. - \en The faces of solid to be merged. \~ - \param[in] uParam - \ru Параметры u направления объединяющей поверхности. - \en The operation parameters for common surface in u direction. \~ - \param[in] vParam - \ru Параметры v направления объединяющей поверхности. - \en The operation parameters for common surface in v direction. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] prolong - \ru Параметр добавления гладко стыкующихся граней с faces (prolong>0). - \en The parameter of adding prolong faces (prolong>0). \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) CreateMerging( MbSolid & solid, - MbeCopyMode sameShell, - c3d::FacesVector & faces, - const MbNurbsParameters & uParam, - const MbNurbsParameters & vParam, - const MbSNameMaker & names, - bool prolong, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти грани тел, имеющие контактные площадки. \~ - \en To find contacted faces of bodies. \~ - \details \ru Найти номера контактирующих граней тел, имеющих противоположно направленные нормали, у которых есть общие участки с конечной площадью перекрытия. \~ - \en To find contacted faces of bodies with oppositely directed normals which have a finite overlap area. \~ - \param[in] solid1 - \ru Первое тело. - \en The first solid. \~ - \param[in] solid2 - \ru Второе тело. - \en The second solid. \~ - \param[in] precision - \ru Точность операции. - \en The precision of operation. \~ - \param[out] facesNumbers - \ru Пары номеров касающихся граней с противоположно направленными нормалями. - \en The couples of number of contacted faces with oppositely directed normals. \~ - \return \ru Возвращает true, если грани контакта найдены. - \en Returns true, if contacted faces were found. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) FindTouchedFaces( const MbSolid & solid1, - const MbSolid & solid2, - double precision, - c3d::IndicesPairsVector & facesNumbers ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Разбить контактирующие грани тел. \~ - \en To find contacted faces of bodies. \~ - \details \ru Разбить контактирующие грани тел, выделив общие области с конечной площадью перекрытия в отдельные грани. \~ - \en To find contacted faces of bodies and build a finite overlap contacted area as faces. \~ - \param[in/out] solid1 - \ru Первое тело. - \en The first solid. \~ - \param[in/out] solid2 - \ru Второе тело. - \en The second solid. \~ - \param[in] precision - \ru Точность операции. - \en The precision of operation. \~ - \param[in] facesNumbers - \ru Множество пар номеров соприкасающихся граней, у которых требуется построить общие пятна контакта (может быть пустым). - \en The container with pairs of contact face numbers that need to have common contact spots (it can be empty). \~ - \return \ru Возвращает true, если грани контакта найдены. - \en Returns true, if contacted faces were found. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) SplitTouchedFaces( MbSolid & solid1, - MbSolid & solid2, - double precision, - c3d::IndicesPairsVector & facesNumbers ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Объединить тела, имеющие контактирующие грани. \~ - \en The function performs unite the bodies with contacted faces. \~ - \details \ru Объединить тела, удалив контактирующие грани. \~ - \en The function performs unite the bodies and removing the contacting faces. \~ - \param[in] solid1 - \ru Первое тело. - \en The first solid. \~ - \param[in] sameShell1 - \ru Способ копирования граней первого тела. - \en Method of copying the faces of the first solid. \~ - \param[in] solid2 - \ru Второе тело. - \en The second solid. \~ - \param[in] sameShell2 - \ru Способ копирования граней второго тела. - \en Method of copying the faces of the second solid. \~ - \param[in] precision - \ru Точность операции. - \en The precision of operation. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) TouchedSolidsMerging( MbSolid & solid1, - MbeCopyMode sameShell1, - MbSolid & solid2, - MbeCopyMode sameShell2, - const MbSNameMaker & names, - double precision, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Получить трансформированную копию тела. \~ - \en Get transformed copy of a solid. \~ - \details \ru Получить трансформированную копию тела, если матрица трансформации не единичная или оригинал, если единичная. \~ - \en Get transformed copy of a solid if a matrix is not identity matrix or original of the solid if the matrix is identity matrix. \~ - \param[in] solid - \ru Тело. - \en A solid. \~ - \param[in,out] copyMode - \ru Исходный режим копирования тела. - \en An initial copy mode. \~ - \param[in] matr - \ru Матрица преобразования. - \en Transformation matrix. \~ - \param[in] transformedMainName - \ru Главное имя для операции трансформации. - \en Main name of transformation operation. \~ - \return \ru Возвращает копию или оригинал тела. - \en Returns copy or original of the solid. \~ - \ingroup Algorithms_3D -*/ -// --- -inline -c3d::SolidSPtr GetTransformedSolid( c3d::SolidSPtr & solid, MbeCopyMode & copyMode, const MbMatrix3D & matr, SimpleName transformedMainName = ct_TransformedSolid ) -{ - c3d::SolidSPtr resSolid( solid ); - - if ( (resSolid != NULL) && !matr.IsSingle() ) { - MbSNameMaker n( transformedMainName, MbSNameMaker::i_SideNone, 0 ); - - MbSolid * resSolidPtr = NULL; - TransformValues tv( matr ); - ::TransformedSolid( *solid, cm_Copy, tv, n, resSolidPtr ); - if ( resSolidPtr != NULL ) { - resSolid = resSolidPtr; - copyMode = cm_Same; - } - } - return resSolid; -} - -//------------------------------------------------------------------------------ -/** \brief \ru Получить трансформированную копию объекта. \~ - \en Get transformed copy of object. \~ - \details \ru Получить трансформированную копию объекта, если матрица трансформации не единичная или оригинал, если единичная. \~ - \en Get transformed copy of an object if a matrix is not identity matrix or original of the solid if the matrix is identity matrix. \~ - \param[in] item - \ru Объект. - \en An object. \~ - \param[in] matr - \ru Матрица преобразования. - \en Transformation matrix. \~ - \return \ru Возвращает копию или оригинал объекта. - \en Returns copy or original of the object. \~ - \ingroup Algorithms_3D -*/ -// --- -template -SPtr GetTransformedItem( SPtr & item, const MbMatrix3D & matr, MbRegDuplicate * iDupReg = NULL, MbRegTransform * iTransReg = NULL ) -{ - SPtr resItem( item ); - if ( (resItem != NULL) && !matr.IsSingle() ) { - resItem = static_cast( &item->Duplicate( iDupReg ) ); - resItem->Transform( matr, iTransReg ); - } - return resItem; -} - - -#endif // __ACTION_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Функции работы с кривыми, поверхностями, оболочками, телами. + \en Functions for operating with curves, surfaces, shells and solids. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ACTION_H +#define __ACTION_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class MATH_CLASS MbCurve; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbSurface; +class MATH_CLASS MbCurveEdge; +class MATH_CLASS MbFace; +class MATH_CLASS MbSolid; +class MATH_CLASS MbPlacement3D; +class MATH_CLASS MbPlanarGrid; +class MATH_CLASS MbGrid; +class MATH_CLASS MbSNameMaker; +class IProgressIndicator; + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить оболочку на предмет разделения на отдельные части. + \en Check if the shell can be subdivided into separate parts. \~ + \details \ru Проверить замкнутую оболочку на предмет разделения на отдельные части с анализом вложенности. \n + \en Check if the closed shell can be subdivided into separate parts with inclusion analysis. \n \~ + \param[in] shell - \ru Исходная оболочка. + \en The initial shell. \~ + \result \ru Возвращает true, если оболочка состоит из нескольких частей. + \en Returns 'true' if the shell consists of several parts. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) IsMultiShell( const MbFaceShell * shell, bool checkNesting = true ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Отделить части оболочки. + \en Separate parts from a shell. \~ + \details \ru Отделить части оболочки без анализа вложенности. + Если sort == true, то наибольшая часть оболочки останется в исходной оболочке, + а отделившиеся от неё части будут сложены в parts с сортировкой по убыванию габарита. \n + \en Separate parts from a shell without inclusion analysis. + If 'sort' == 'true', the greatest part of the shell will remain in the initial shell, + separated parts will be collected in array 'parts' sorted by bounding box size in descending order. \n \~ + \param[in] shell - \ru Исходная оболочка. + \en The initial shell. \~ + \param[out] parts - \ru Оболочки, полученные из shell. + \en The shells separated from 'shell'. \~ + \param[in] sort - \ru Выполнять ли сортировку частей оболочки по убыванию габарита? + \en If 'sort' == true, the parts separated from the initial shell will be sorted by bounding box size in descending order. \~ + \result \ru Возвращает количество оболочек в parts. + \en Returns number of shells in 'parts'. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (size_t) DetachShells( MbFaceShell & shell, RPArray & parts, bool sort ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Отделить части оболочки. + \en Separate parts from a shell. \~ + \details \ru Отделить части оболочки без анализа вложенности. + Исходная оболочка всегда остаётся неизменённой. + Если исходная оболочка распадается на части, то все части складываются в parts. \n + \en Separate parts from a shell without inclusion analysis. + The initial shell always remains unchangeable. + If the initial shell is decomposed, all the parts are put into array 'parts'. \n \~ + \param[in] shell - \ru Исходная оболочка. + \en The initial shell. \~ + \param[out] parts - \ru Оболочки, полученные из shell. + \en The shells separated from 'shell'. \~ + \result \ru Возвращает количество оболочек в parts. + \en Returns number of shells in 'parts'. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (size_t) CreateShells( MbFaceShell & shell, RPArray & parts ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить оболочку выдавливанием плоских контуров. + \en Create a shell by extrusion of planar contours. \~ + \details \ru Построить оболочку выдавливанием плоских контуров. \n + \en Create a shell by extrusion of planar contours. \n \~ + \param[in] surface - \ru Поверхность контуров. + \en A surface that contains the contours. \~ + \param[in] contours - \ru Набор двумерных контуров. + \en A set of planar contours. \~ + \param[in] direction - \ru Направление выдавливания. + \en An extrusion direction. \~ + \param[in] params - \ru Параметры построения. + \en Parameters of a shell creation. \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] cNames - \ru Набор именователей контуров. + \en A set of objects defining names of the contours. \~ + \param[out] result - \ru Результат операции - оболочка. + \en Result of the operation - a shell. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ExtrusionShell( const MbSurface & surface, + RPArray & contours, + const MbVector3D & direction, + const ExtrusionValues & params, + const MbSNameMaker & operNames, + RPArray & cNames, + MbFaceShell *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить оболочку вращением плоских контуров. + \en Create a shell by revolution of planar contours. \~ + \details \ru Построить оболочку вращением плоских контуров. \n + \en Create a shell by revolution of planar contours. \n \~ + \param[in] surface - \ru Поверхность контуров. + \en A surface that contains the contours. \~ + \param[in] contours - \ru Набор двумерных контуров. + \en A set of planar contours. \~ + \param[in] axis - \ru Ось вращения. + \en Rotation axis. \~ + \param[in] params - \ru Параметры построения. + \en Parameters of a shell creation. \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] cNames - \ru Набор именователей контуров. + \en A set of objects defining names of the contours. \~ + \param[out] result - \ru Результат операции - оболочка. + \en Result of the operation - a shell. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) RevolutionShell( const MbSurface & surface, + RPArray & contours, + const MbAxis3D & axis, + const RevolutionValues & params, + const MbSNameMaker & operNames, + RPArray & cNames, + MbFaceShell *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Объединить компланарные грани. + \en Unite complanar faces. \~ + \details \ru Объединить компланарные грани оболочки и проверить оболочку. \n + \en Unite complanar faces of a shell and validate the shell. \n \~ + \param[in] shell - \ru Модифицируемая оболочка. + \en A shell to be modified. \~ + \param[in] nameMaker - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] checkBaseSurfaces - \ru Найти и устранить общие поверхности-подложки в гранях. + \en Find and eliminate common underlying surfaces of faces \~ + \return \ru Возвращает true, если оболочка была успешно изменена. + \en Returns 'true' if the shell has been successfully modified. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) UnifyOwnComplanarFaces( MbFaceShell & shell, + const MbSNameMaker & nameMaker, + bool checkBaseSurfaces ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти и устранить общие поверхности-подложки в гранях. + \en Find and eliminate common underlying surfaces of faces \~ + \details \ru Найти и устранить общие поверхности-подложки в гранях оболочки. \n + \en Find and eliminate common underlying surfaces of a shell faces. \n \~ + \param[in] shell - \ru Модифицируемая оболочка. + \en A shell to be modified. \~ + \return \ru Возвращает true, если оболочка была изменена. + \en Returns 'true' if the shell has been modified. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) CheckIdenticalBaseSufaces( MbFaceShell & shell ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Захватить грани одним из способов. + \en Capture the faces in one of proposed methods. \~ + \details \ru Захватить грани одним из способов распространения по связной оболочке. \n + \en Capture the faces in one of methods of propagation in connected shell. \n \~ + \param[in] fp - \ru Cпособ захвата граней. + \en A method of capturing the faces. \~ + \param[in,out] face_set - \ru Набор граней. + \en A set of faces. \~ + \param[in] dir - \ru Направление уклона. + \en A direction of inclination. \~ + \warning \ru Вспомогательная функция операции DraftSolid. + \en An auxillary function of operation DraftSolid. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (void) FacePropagate( const MbeFacePropagation fp, + RPArray & face_set, + const MbVector3D & dir ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Масштабировать каверны литейной формы. + \en Scale cavities of a mold. \~ + \details \ru Масштабировать каверны литейной формы относительно неподвижной точки. \n + \en Scale cavities of a mold relative to a fixed point. \n \~ + \param[in,out] solids - \ru Модифицируемые тела. + \en The solids to be modified. \~ + \param[in] fixedPoint - \ru Неподвижная точка масштабирования. + \en The fixed point of scaling. \~ + \param[in] deltaX - \ru Относительное приращение размера по направлению X. + \en Relative increment of size in X-direction. \~ + \param[in] deltaY - \ru Относительное приращение размера по направлению Y. + \en Relative increment of size in Y-direction. \~ + \param[in] deltaZ - \ru Относительное приращение размера по направлению Z. + \en Relative increment of size in Z-direction. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (void) MouldCavitySolids( RPArray & solids, + MbCartPoint3D * fixedPoint, + double deltaX, + double deltaY, + double deltaZ ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить тела на пересечение. + \en Check intersection of solids. \~ + \details \ru Проверить тела на пересечение без уточнения характера пересечения \n + (проверяем до первого пересечения граней). \n + \en Check if solids intersect each other without definition of intersection type \n + (check until the first intersection is detected). \n \~ + \param[in] solid1 - \ru Первое тело. + \en The first solid. \~ + \param[in] solid2 - \ru Второе тело. + \en The second solid. \~ + \return \ru Возвращает true, если найдено хотя бы одно пересечение. + \en Returns 'true' if at least one intersection is detected. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) IsSolidsIntersection( const MbSolid & solid1, const MbSolid & solid2, const MbSNameMaker & snMaker ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Определить пересечение тел в сборке. + \en Check intersection of solids in an assembly. \~ + \details \ru Определить пересечение тел в сборке. \n + \en Check intersection of solids in an assembly. \n \~ + \param[in] solid1 - \ru Первое тело в локальной системе координат (ЛСК). + \en The first solid in local coordinate system (LCS). \~ + \param[in] matr1 - \ru Матрица преобразования в глобальную СК (ГСК). + \en Matrix of transformation to the global coordinate system (GCS). \~ + \param[in] solid2 - \ru Второе тело в ЛСК. + \en The second solid in LCS. \~ + \param[in] matr2 - \ru Матрица преобразования в ГСК. + \en Matrix of transformation to GCS. \~ + \param[in] checkTangent - \ru Считать касания пересечениями. + \en Consider tangencies as intersections. \~ + \param[in] getIntersectionSolids - \ru Получить не касательные пересечения в виде тел. + \en Get non-tangent intersections in the form of bodies. \~ + \param[in] checkTouchPoints - \ru Искать точки касания. + \en Find touch points. \~ + \param[out] intData - \ru Информация о пересечении двух тел. + \en Information about two solids intersection. \~ + \return \ru Возвращает true, если найдено хотя бы одно пересечение. + \en Returns 'true' if at least one intersection is detected. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) IsSolidsIntersection( const MbSolid & solid1, const MbMatrix3D & matr1, + const MbSolid & solid2, const MbMatrix3D & matr2, + bool checkTangent, // \ru Считать касания пересечениями \en Consider tangencies as intersections + bool getIntersectionSolids, // \ru Получить не касательные пересечения в виде тел \en Get non-tangency intersection as solids + bool checkTouchPoints, // \ru Искать точки касания \en Find touch points + RPArray & intData ); + +//------------------------------------------------------------------------------ +/** \brief \ru Определить минимальное расстояние между телами в сборке. + \en Determine the minimum distance between solids in an assembly. \~ + \details \ru Определить минимальное расстояние между телами в сборке. В случае пересечения или касания тел возвращается нулевая дистанция.\n + При многократном использовании первого тела следует установить isMultipleUseSolid1 = true, иначе false. Аналогично для второго тела.\n + \en Determine the minimum distance between solids in an assembly. In case of intersection or tangent of the shells returns to zero distance.\n + With multiple use of the first body should be set isMultipleUseSolid1 = true, else false. Similarly for the second body.\n \~ + \param[in] solid1 - \ru Первое тело в локальной системе координат (ЛСК). + \en The first solid in local coordinate system (LCS). \~ + \param[in] matr1 - \ru Матрица преобразования в глобальную СК (ГСК). + \en Matrix of transformation to the global coordinate system (GCS). \~ + \param[in] isMultipleUseSolid1 - \ru Множественное использование первого тела. + \en Multiple use of the first body. \~ + \param[in] solid2 - \ru Второе тело в ЛСК. + \en The second solid in LCS. \~ + \param[in] matr2 - \ru Матрица преобразования в ГСК. + \en Matrix of transformation to GCS. \~ + \param[in] isMultipleUseSolid2 - \ru Множественное использование второго тела. + \en Multiple use of the second body. \~ + \param[in] lowerLimitDistance - \ru Минимальное допустимое расстояние. + \en Minimum allowed distance. \~ + \param[in] tillFirstLowerLimit - \ru Искать до первого найденного удовлетворяющего минимально допустимому расстоянию. + \en Search until the first found that satisfies the minimum acceptable distance. \~ + \param[out] shellsDistanceData - \ru Информация о расстоянии между телами. + \en Information about the distance between solids. \~ + \return \ru Возвращает true, если определено хотя бы одно расстояние. + \en Returns 'true' if at least one distance is obtained. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) MinimumSolidsDistance( const MbSolid & solid1, const MbMatrix3D & matr1, bool isMultipleUseSolid1, + const MbSolid & solid2, const MbMatrix3D & matr2, bool isMultipleUseSolid2, + double lowerLimitDistance, bool tillFirstLowerLimit, + std::vector & shellsDistanceData ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Классифицировать набор тел относительно другого набора тел. + \en Classify a set of solids relative to another set of solids. \~ + \details \ru Классифицировать набор тел относительно другого набора тел. Тела находятся в одной системе координат. \n + Каждое классифицируемое тело в результате имеет 3 состояния: 1) Находится вне относительного набора тел; \n + 2) Содержится только в одном теле, тогда есть информация об индексе относительного тела; \n + 3) Во всех остальных случаях тело пересекается с относительным набором тел. \n + \en Classify a set of solids relative to another set of solids. The solids are in the same coordinate system. \n + Each classified solids as result has 3 states: 1) It is outside relative set of bodies; \n + 2) Contained in only one solid, then there is information about the index of the relative body; \n + 3) In all other cases, solid intersects with relative set of solids. \n \~ + \param[in] solids - \ru Классифицируемый набор тел. + \en The classified set of solids. \~ + \param[in] relativeSolids - \ru Набор тел относительно которых осуществляется классификация. + \en A set of solids relative to which classification is performed. \~ + \param[out] info - \ru Информация о нахождении тел относительно другого набора тел. + \en Information about finding solids relative to another set of solids. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC( void ) MultipleSolidsClassification( const RPArray & solids, + const RPArray & relativeSolids, + std::vector & info ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти расстояния от контура на плоскости до поверхности. + \en Find the distances from a contour on a plane to a surface. \~ + \details \ru Найти расстояния от контура на плоскости до поверхности. \n + Прямое направление - это направление оси Z системы координат двумерной кривой. \n + Расстояние в прямом направлении найдено, если значение не отрицательное. \n + Расстояние в обратном направлении найдено, если значение не положительное. \n + \en Find the distances from a contour on a plane to a surface. \n + A forward direction is a direction of Z-axis of two-dimensional curve coordinate system. \n + The distance in a forward direction is found if the value is non-negative. \n + The distance in a backward direction is found if the value is non-positive. \n \~ + \param[in] pl - \ru Система координат двумерной кривой. + \en A coordinate system of two-dimensional curve. \~ + \param[in] curve - \ru Двумерная кривая. + \en A two-dimensional curve. \~ + \param[in] surf - \ru Поверхность, до которой проводится поиск расстояний. + \en A surface to measure the distances up to. \~ + \param[out] lPlus - \ru Расстояние в прямом направлении. + \en The distance in a forward direction. \~ + \param[out] lMinus - \ru Расстояние в обратном направлении. + \en The distance in a backward direction. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (void) GetDistanceToSurface( const MbPlacement3D & pl, + const MbCurve * curve, + const MbSurface * surf, + double & lPlus, + double & lMinus ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создание поверхностей сечения выдавливания плоского контура. + \en Create cutter surfaces for extrusion of planar contours. \~ + \details \ru Создание поверхностей сечения выдавливания плоского контура и определение направлений выдавливаний. \n + \en Create cutter surfaces for extrusion of planar contours and define directions of extrusions. \n \~ + \param[in] surface - \ru Поверхность контуров. + \en A surface that contains the contours. \~ + \param[in] contours - \ru Набор двумерных контуров. + \en A set of planar contours. \~ + \param[in] direction - \ru Направление выдавливания. + \en An extrusion direction. \~ + \param[in] params - \ru Параметры построения. + \en Parameters of a shell creation. \~ + \param[in] version - \ru Версия построения. + \en The version of construction. \~ + \param[out] resType - \ru Код результата операции. + \en Operation result code. \~ + \param[out] surfAndDir- \ru Результат операции - поверхности и направление относительно direction. + \en Result of the operation - surfaces and directions relative to parameter direction. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (void) CreateExtrusionCutSurfaces( const MbSurface & surface, + const c3d::PlaneContoursSPtrVector & contours, + const MbVector3D & direction, + ExtrusionValues & params, + VERSION version, + MbResultType & resType, + std::vector< std::pair> & surfAndDir ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти расстояния от контура на поверхности до габаритного куба оболочки. + \en Find the distances from a contour on a surface to the bounding box of a shell. \~ + \details \ru Найти расстояния от контура на поверхности до габаритного куба оболочки. \n + Расстояние в прямом направлении найдено, если значение не отрицательное. \n + Расстояние в обратном направлении найдено, если значение не положительное. \n + \en Find the distances from a contour on a surface to the bounding box of a shell. \n + The distance in a forward direction is found if the value is nonnegative. \n + The distance in a backward direction is found if the value is non-positive. \n \~ + \param[in] surface - \ru Поверхность, на которой лежит двумерная кривая. + \en A surface that contains the two-dimensional curve. \~ + \param[in] direction - \ru Направление поиска (выдавливания) + \en A direction of the distance calculation (an extrusion direction). \~ + \param[in] curve - \ru Двумерная кривая, лежащая на поверхности surface. + \en A two-dimensional curve on surface 'surface'. \~ + \param[in] cube - \ru Габаритный куб оболочки. + \en The bounding box of the shell. \~ + \param[out] lPlus - \ru Расстояние в прямом направлении. + \en The distance in a forward direction. \~ + \param[out] lMinus - \ru Расстояние в обратном направлении. + \en The distance in a backward direction. \~ + \param[out] resType - \ru Код результата операции. + \en Operation result code. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (void) GetDistanceToCube( const MbSurface & surface, + const MbVector3D & direction, + const MbCurve & curve, + const MbCube & cube, + double & lPlus, + double & lMinus, + MbResultType & resType ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти расстояния от набора кривых на поверхности до габаритного куба оболочки. + \en Find the distances from curves on a surface to the bounding box of a shell. \~ + \details \ru Найти расстояния от набора кривых на поверхности до габаритного куба оболочки. \n + Расстояние в прямом направлении найдено, если значение не отрицательное. \n + Расстояние в обратном направлении найдено, если значение не положительное. \n + \en Find the distances from curves on a surface to the bounding box of a shell. \n + The distance in a forward direction is found if the value is nonnegative. \n + The distance in a backward direction is found if the value is nonpositive. \n \~ + \param[in] surface - \ru Поверхность, на которой лежат двумерные кривые. + \en A surface that contains two-dimensional curves. \~ + \param[in] direction - \ru Направление поиска (выдавливания) + \en A direction of the distance calculation (an extrusion direction). \~ + \param[in] curves - \ru Набор двумерных кривых на поверхности surface. + \en A set of two-dimensional curves on the surface 'surface'. \~ + \param[in] cube - \ru Габаритный куб оболочки. + \en The bounding box of the shell. \~ + \param[out] lPlus - \ru Расстояние в прямом направлении. + \en The distance in a forward direction. \~ + \param[out] lMinus - \ru Расстояние в обратном направлении. + \en The distance in a backward direction. \~ + \param[out] resType - \ru Код результата операции. + \en Operation result code. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (void) GetDistanceToCube( const MbSurface & surface, + const MbVector3D & direction, + const RPArray & curves, + const MbCube & cube, + double & lPlus, + double & lMinus, + MbResultType & resType ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти расстояния от плоскости до габаритного куба оболочки. + \en Find the distances from a plane to the bounding box of a shell. \~ + \details \ru Найти расстояния от плоскости до габаритного куба оболочки. \n + Расстояние в прямом направлении найдено, если значение не отрицательное. \n + Расстояние в обратном направлении найдено, если значение не положительное. \n + Использует расчет габарита относительно локальной системы координат. \n + Если система координат плоскости лежит вне габаритного куба, \n + то при взведенном флаге findMax ищется максимальное расстояние до куба. \n + \en Find the distances from a plane to the bounding box of a shell. \n + The distance in a forward direction is found if the value is non-negative. \n + The distance in a backward direction is found if the value is non-positive. \n + Calculation of the distance relative to the local coordinate system is used. \n + If a plane coordinate system is out of the bounding box, \n + then if the flag 'findMax' is set to 'true', the maximal distance to bounding box is calculated. \n \~ + \param[in] pl - \ru Система координат плоскости. + \en The plane coordinate system. \~ + \param[in] shell - \ru Целевая оболочка. + \en A target shell. \~ + \param[out] dPlus - \ru Расстояние в прямом направлении. + \en The distance in a forward direction. \~ + \param[out] dMinus - \ru Расстояние в обратном направлении. + \en The distance in a backward direction. \~ + \param[in] findMax - \ru Искать максимальное расстояние до габаритного куба. + \en The maximal distance to bounding box is to be calculated. \~ + \return \ru Возвращает true, если найдено хотя бы одно из расстояний. + \en Returns 'true' if at least one of the distances has been found. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) GetDistanceToCube( const MbPlacement3D & pl, + const MbFaceShell * shell, + double & dPlus, + double & dMinus, + bool findMax = true ); // \ru Искать максимальное \en Find the maximal distance + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти расстояния/углы от контура до куба или до поверхности. + \en Find the distances/angles from a contour to a bounding box or to a surface. \~ + \details \ru Найти расстояния/углы от контура до куба или до поверхности. \n + Нужно учесть уклон в двух направлениях. \n + \en Find the distances/angles from a contour to a bounding box or to a surface. \n + The inclination in two directions is to be considered. \n \~ + \param[in] curve - \ru Кривая. + \en A curve. \~ + \param[in] direction - \ru Направление выдавливания. + \en An extrusion direction. \~ + \param[in] axis - \ru Ось вращения. + \en Rotation axis. \~ + \param[in] rotation - \ru Вращение (true) или выдавливание (false) + \en Rotation (true) or extrusion (false) \~ + \param[in] operationDirection - \ru Вперед (true) или назад (false) + \en Forward (true) or backward (false) \~ + \param[in] toCube - \ru До куба, если указатель ненулевой + \en Up to a cube if the pointer is not null. \~ + \param[in] toSurface - \ru До поверхности, если указатель ненулевой. + \en Up to a surface if the pointer is not null. \~ + \param[in,out] params: +\ru Должны быть заданы параметры: \n + params.side1.rake - Уклон в направлении direction (для плоской образующей). \n + params.side2.rake - Уклон в направлении direction, обратном direction (для плоской образующей). \n + params.thikness1 - Толщина стенки в прямом направлении + (в положительном направлении нормали объекта (грани, поверхности, плоскости кривой)). \n + params.thikness2 - Толщина стенки в обратном направлении. \n + Заполняются параметры: \n + params.side1.scalarValue - Расстояние выдавливания в направлении direction (если operationDirection == true), + иначе обратном. \n + params.side2.scalarValue - Расстояние выдавливания в направлении, обратном direction (если operationDirection == true), + иначе в прямом. \n +\en The following parameters should be defined: \n + params.side1.rake - The inclination in direction 'direction' (for a planar generatrix). \n + params.side2.rake - The inclination in direction opposite to 'direction' (for a planar generatrix). \n + params.thikness1 - Wall thickness in forward direction. + (in the positive direction of the normal of an object (a face, a surface, the plane of a curve)). \n + params.thikness2 - The wall thickness in the backward direction. \n + The output parameters: \n + params.side1.scalarValue - The extrusion distance in the direction 'direction' (if 'operationDirection' == true), + else in the opposite direction. \n + params.side2.scalarValue - The extrusion distance in the direction opposite to 'direction' (if 'operationDirection' == true), + else in the forward direction. \n \~ + \param[out] resType - \ru Код результата операции. + \en Operation result code. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (void) GetRangeToCubeOrSurface( const MbCurve3D & curve, + const MbVector3D & direction, + const MbAxis3D & axis, + const bool rotation, + bool operationDirection, + const MbCube * toCube, + const MbSurface * toSurface, + SweptValuesAndSides & params, + MbResultType & resType ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти ближайшие тела при выдавливании с опцией "до ближайшего объекта". + \en Find the nearest solids while extruding with option 'up to the nearest object'. \~ + \details \ru Найти ближайшие тела при выдавливании с опцией "до ближайшего объекта". \n + Возвращает номерa (nPlus и nMinus) ближайших тел с положительной и отрицательной стороны эскиза. + \en Find the nearest solids while extruding with option 'up to the nearest object'. \n + Returns the numbers (nPlus and nMinus) of nearest solids on the positive and the negative sides of the sketch. \~ + \param[in] pl - \ru Локальная система координат. + \en A local coordinate system. \~ + \param[in] c - \ru Множество двумерных контуров. + \en An array of two-dimensional contours. \~ + \param[in] direction - \ru Направление выдавливания. + \en An extrusion direction. \~ + \param[in] solids - \ru Целевой набор тел. + \en A target set of solids. \~ + \param[out] nPlus - \ru Номер ближайшего тела в положительном направлении. + \en The number of the nearest solid in the positive direction. \~ + \param[out] nMinus - \ru Номер ближайшего тела в отрицательном направлении. + \en The number of the nearest solid in the negative direction. \~ + \return \ru Возвращает true, если найдено тело хотя бы в одном из направлений. + \en Returns 'true' if a solid is found in at least one of directions. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) GetNearestSolid( const MbPlacement3D & pl, + RPArray & c, + MbSweptLayout::Direction direction, + RPArray & solids, + size_t & nPlus, + size_t & nMinus ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти ближайшие тела при выдавливании с опцией "до ближайшего объекта". + \en Find the nearest solids while extruding with option 'up to the nearest object'. \~ + \details \ru Найти ближайшие тела при выдавливании с опцией "до ближайшего объекта". \n + возвращает номерa (nPlus и nMinus) ближайших тел ближайших тел в прямом и обратном направлении. + \en Find the nearest solids while extruding with option 'up to the nearest object'. \n + returns numbers (nPlus and nMinus) of the nearest solids in the forward and the backward direction. \~ + \param[in] curves - \ru Набор кривых. + \en A set of curves. \~ + \param[in] direction - \ru Направление выдавливания. + \en An extrusion direction. \~ + \param[in] operationDirection - \ru Параметры выдавливания "до ближайшего объекта". + \en Parameters of extrusion 'up to the nearest object'. \~ + \param[in] solids - \ru Целевой набор тел. + \en A target set of solids. \~ + \param[out] nPlus - \ru Номер ближайшего тела в положительном направлении. + \en The number of the nearest solid in the positive direction. \~ + \param[out] nMinus - \ru Номер ближайшего тела в отрицательном направлении. + \en The number of the nearest solid in the negative direction. \~ + \return \ru Возвращает true, если найдено тело хотя бы в одном из направлений. + \en Returns 'true' if a solid is found in at least one of directions. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) GetNearestSolid( RPArray & curves, + const MbVector3D & direction, + MbSweptLayout::Direction operationDirection, + RPArray & solids, + size_t & nPlus, + size_t & nMinus ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить оболочку или тело, состоящее из NURBS поверхностей. + \en Check a shell or a solid that consists of NURBS surfaces. \~ + \details \ru Проверить корректность оболочки или тела, состоящего из NURBS поверхностей. \n + \en Check the correctness of a shell or a solid that consists of NURBS surfaces. \n \~ + \param[in] params - \ru Исходные параметры операции. + \en Initial parameters of the operation. \~ + \param[in] nsSolid - \ru Тело - результат операции. + \en A solid - the result of the operation. \~ + \param[in] progBar - \ru Индикатор прогресса выполнения операции. + \en A progress indicator of the operation. \~ + \return \ru Возвращает rt_Success, если тело успешно прошло проверку. + \en Returns rt_Success if the solid has successfully passed the validation. \~ + \warning \ru Проверочная функция операции NurbsSurfacesShell. + \en A checking function of the operation NurbsSurfacesShell. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (MbResultType) CheckNurbsShell( const NurbsSurfaceValues & params, + const MbSolid & nsSolid, + IProgressIndicator * progBar ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Положить эскиз в массив усекающих объектов. + \en Add a sketch to the array of truncating objects. \~ + \details \ru Положить эскиз в массив усекающих объектов путем создания пространственных кривых. \n + \en Add a sketch to the array of truncating objects by creation of spatial curves. \n \~ + \param[in] sketchPlace - \ru Локальная система координат двумерного эскиза. + \en A local coordinate system of two-dimensional sketch. \~ + \param[in] sketchCurves - \ru Двумерные кривые эскиза. + \en Two-dimensional curves of the sketch. \~ + \param[out] items - \ru Выходной массив пространственных объектов. + \en The output array of spatial objects. \~ + \return \ru - Возвращает true в случае добавления элементов в выходной массив. + \en - Returns 'true' if elements are added into output array. \~ + \warning \ru Вспомогательная функция операции TruncateShell. + \en Auxiliary function of the operation TruncateShell. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) AddTruncatingSketch( const MbPlacement3D & sketchPlace, + RPArray & sketchCurves, + RPArray & items ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Положить кривую в массив усекающих объектов. + \en Add a curve to the array of truncating objects. \~ + \details \ru Положить кривую в массив усекающих объектов + (с разбором кривой на составляющие, в случае необходимости). \n + \en Add a curve to the array of truncating objects + (with decomposition of the curve if necessary). \n \~ + \param[in] curve - \ru Пространственная кривая. + \en A space curve. \~ + \param[out] items - \ru Выходной массив пространственных объектов. + \en The output array of spatial objects. \~ + \warning \ru Вспомогательная функция операции TruncateShell. + \en Auxillary function of the operation TruncateShell. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (void) AddTruncatingCurve( const MbCurve3D & curve, + RPArray & items ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить корректность вскрываемых граней для создания тонкостенного тела. + \en Check the correctness of shelling faces for creation of a thin-walled solid. \~ + \details \ru Проверить корректность набора вскрываемых граней для создания тонкостенного тела. \n + Удаляет из массива не подходящие для операции грани (гладко сопряженные с невыбранными гранями). \n + \en Check the correctness of shelling faces set for creation of a thin-walled solid. \n + Removes unsuitable for the operation faces from the array (Delete faces smoothly connected to unselected faces). \n \~ + \param[in] params - \ru Параметры тонкой стенки. + \en Parameters of a thin wall. \~ + \param[in,out] faces - \ru Множество вскрываемых граней тела. + \en An array of shelling faces of the solid. \~ + \warning \ru Вспомогательная функция операции построения тонкостенного тела. + \en An auxiliary function of thin-walled solid construction operation. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (void) CheckShellingFaces( const SweptValues & params, RPArray & faces ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Вычислить компоненты проекции вектора, заданного в точке на поверхности. + \en Calculate the components of projection of a vector defined at a point on the surface. \~ + \details \ru Вычислить компоненты x и y проекции пространственного вектора, заданного в точке на поверхности. \n + \en Calculate x and y components of projection of a space vector defined at a point on a surface. \n \~ + \param[in] v3d - \ru Пространственный вектор. + \en A space vector. \~ + \param[in] surface - \ru Поверхность. + \en A surface. \~ + \param[in] p2d - \ru Параметрическая точка на поверхности. + \en A parametric point on the surface. \~ + \param[out] v2d - \ru Проекция пространственного вектора на поверхность. + \en The projection of the space vector on the surface. \~ + \return \ru - Возвращает true в случае успешного вычисления проекции вектора. + \en - Returns 'true' if the vector projection has been successfully calculated. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) ProjectVectorOn( const MbVector3D & v3d, const MbSurface & surface, const MbCartPoint & p2d, + MbVector & v2d ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Расширить поверхность для резки тела. + \en Extend a surface for cutting a solid. \~ + \details \ru Расширить поверхность до заданного габарита для резки тела. \n + \en Extend a surface to a given bounding box for cutting a solid. \n \~ + \param[in,out] gabarit - \ru Желаемый габарит расширения. + \en A desirable bounding box of the extended surface. \~ + \param[in] surf - \ru Исходная поверхность. + \en The initial surface. \~ + \param[in] prolongState - \ru Состояние типа продления секущих поверхностей. + \en State of prolongation types of cutter surfaces. \~ + \param[in] version - \ru Версия построения. + \en The version of construction. \~ + \return \ru - Возвращает расширенную поверхность, если получилось ее создать. + \en - Returns the extended surface if it has been successfully created. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (MbSurface *) GetExtendedSurfaceCopy( MbCube & gabarit, + const MbSurface & surf, + const MbShellCuttingParams::ProlongState & prolongState, + VERSION version ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить набор граней с топологией призмы. + \en Create a set of faces with topology of a prism. \~ + \details \ru Построить набор граней с топологией призмы. \n + \en Create a set of faces with topology of a prism. \n \~ + \param[in] place - \ru Локальная система координат (ЛСК). + \en A local coordinate system (LCS). \~ + \param[in] contour - \ru Двумерный контур в ЛСК. + \en A two-dimensional curve in LCS. \~ + \param[in] der - \ru Направление выдавливания. + \en An extrusion direction. \~ + \param[in] sense - \ru Ориентация выходного массива граней как замкнутой оболочки. + \en An orientation of the output array of faces as a closed shell. \~ + \param[in] n - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in,out] initFaces - \ru Множество созданных граней. + \en The array of created faces. \~ + \param[in] useAddCount - \ru Использовать количество граней initFaces на входе для именования новых граней. + \en The number of input faces initFaces is to be used for naming the new faces. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (void) CreateFaces( const MbPlacement3D & place, const MbContour & contour, + const MbVector3D & der, bool sense, const MbSNameMaker & n, + RPArray & initFaces, bool useAddCount = false ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Оценить параметры выдавливания для ребра жёсткости. + \en Estimate parameters of extrusion for a rib. \~ + \details \ru Оценить параметры выдавливания для построения ребра жёсткости. \n + \en Estimate parameters of extrusion for creating a rib. \n \~ + \param[in] shell - \ru Целевая оболочка. + \en A target shell. \~ + \param[in] place - \ru Локальная система координат контура. + \en A local coordinate system of the contour. \~ + \param[in] contour - \ru Двумерный контур. + \en A two-dimensional contour. \~ + \param[in] index - \ru Номер сегмента контура. + \en A number of the contour segment. \~ + \param[out] side - \ru Сторона заполнения пространства телом ребра. + \en The side to place the rib on. \~ + \param[out] origin - \ru Точка. + \en A point. \~ + \param[out] dir3D - \ru Вектор. + \en A vector. \~ + \warning \ru Вспомогательная функция операции RibSolid. + \en An auxillary function of the operation RibSolid. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (void) GetAutoReference( MbFaceShell & shell, + const MbPlacement3D & place, + const MbContour & contour, + ptrdiff_t index, + RibValues::ExtrudeSide & side, + MbCartPoint3D & origin, + MbVector3D & dir3D ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кривую в параметрах поверхности. + \en Create a curve in the parameter space of a surface. \~ + \details \ru Создать кривую в параметрах поверхности, если нужно - проекционную. \n + Если проекционную кривую создавать не нужно, возвращает дубль двумерной кривой в параметрах поверхности. \n + После использования кривую нужно удалить. \n + \en Create a curve in the parameter space of a surface. The projection curve can be created if necessary. \n + If it is not required to create the projection curve, returns a copy of two-dimensional curve in the parameter space of the surface. \n + The curve is to be deleted after use. \n \~ + \param[in] intersectCurve - \ru Кривая пересечения. + \en The intersection curve \~ + \param[in] first - \ru true - Первая поверхность, false - вторая поверхность. + \en True - The first surface, false - the second surface. \~ + \return \ru Возвращает кривую, если ее получилось построить. + \en Returns the curve if it has been successfully created. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (MbCurve *) GetProjCurveOnSurface( const MbSurfaceIntersectionCurve & intersectCurve, bool first ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить неизменность вектора кинематической направляющий в разных версиях. + \en Check the invariance of the vector of spine direction in different versions. \~ + \details \ru Проверить, можно ли сохранить кинематическую направляющую из одной версии в другую без изменения формы. \n + \en Check if the spine direction can be preserved between versions without any change of the shape. \n \~ + \param[in] curve - \ru Направляющая кривая. + \en The spine curve. \~ + \param[in] srcVersion - \ru Рабочая версия. + \en The current version. \~ + \param[in] dstVersion - \ru Целевая версия. + \en The target version. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) IsSameSpineDirection( const MbCurve3D & curve, VERSION srcVersion, VERSION dstVersion ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Классифицировать положения второго контура относительно первого. + \en Classify the position of the second contour relative to the first one. \~ + \details \ru Классифицировать положения второго контура относительно первого: \n + iloc_OutOfItem - снаружи, \n + iloc_OnItem - пересекается, \n + iloc_InItem - внутри. \n + \en Classify the position of the second contour relative to the first one: \n + iloc_OutOfItem - outside, \n + iloc_OnItem - intersects, \n + iloc_InItem - inside. \n \~ + \param[in] contour1 - \ru Первый контур. + \en The first contour. \~ + \param[in] contour2 - \ru Второй контур. + \en The second contour. \~ + \param[in] xEpsilon - \ru Погрешность по x. + \en Tolerance in x direction. \~ + \param[in] yEpsilon - \ru Погрешность по y. + \en Tolerance in y direction. \~ + \return \ru Возвращает результат классификации положения. + \en Returns the result of classification of the relative position. \~ + \ingroup Algorithms_2D +*/ +// --- +MATH_FUNC (MbeItemLocation) SecondContourLocation( const MbContour & contour1, const MbContour & contour2, + double xEpsilon, double yEpsilon ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Определить, близка ли первая кривая ко второй кривой. + \en Determine whether the first curve is close to the second curve. \~ + \details \ru Определить, близка ли кривая curve1 к кривой curve2 с заданной точностью. \n + Близость определяется близостью точек первой кривой, полученных шаганием \n + по кривой с заданным угловым отклонением, ко второй кривой. \n + \en Determine whether curve 'curve1' is close to curve 'curve2' within the given precision. \n + The proximity is defined by the closeness of points of the first curve obtained by sampling \n + with the given turning angle to the second curve. \n \~ + \param[in] curve1 - \ru Первая кривая. + \en The first curve. \~ + \param[in] curve2 - \ru Вторая кривая. + \en The second curve. \~ + \param[in] xEpsilon - \ru Близость по x. + \en Proximity tolerance in x direction. \~ + \param[in] yEpsilon - \ru Близость по y. + \en Proximity tolerance in y direction. \~ + \param[in] devSag - \ru Максимальное угловое отклонение при шагании по кривой. + \en The maximal turning angle for sampling the curve. \~ + \return \ru Возвращает true, если кривые близки. + \en Returns 'true' if the curves are close. \~ + \ingroup Algorithms_2D +*/ +// --- +MATH_FUNC (bool) IsSpaceNear( const MbCurve & curve1, const MbCurve & curve2, double xEpsilon, double yEpsilon, + double devSag = 5.0 * Math::deviateSag ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Определить, близка ли кривая к поверхности. + \en Determine whether a curve is close to a surface. \~ + \details \ru Определить, близка ли кривая к поверхности с заданной точностью. \n + Выполняется проверка по пробным точкам кривой, полученных шаганием по угловому отклонению. \n + \en Determine whether a curve is close to a surface within the given tolerance. \n + The check uses sample points of curves obtained by sampling with maximal turning angle. \n \~ + \param[in] curv - \ru Кривая. + \en A curve. \~ + \param[in] surf - \ru Поверхность. + \en A surface. \~ + \param[in] surfExt - \ru Проверять на расширенной поверхности. + \en Perform the check for the extended surface. \~ + \param[in] mEps - \ru Метрическая близость. + \en The metric proximity tolerance. \~ + \param[in] devSag - \ru Максимальное угловое отклонение при шагании по кривой. + \en The maximal turning angle for sampling the curve. \~ + \return \ru Возвращает true, если кривая близка к поверхности. + \en Returns 'true' if the curve is close to the surface. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) IsSpaceNear( const MbCurve3D & curv, const MbSurface & surf, bool surfExt, + double mEps, double devSag = 5.0 * Math::deviateSag ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать грань по произвольной поверхности. + \en Create the face on the base of arbitrary surface. \~ + \details \ru Создать грань по произвольной поверхности без самопересечений. \n + \en Create the face on the base of arbitrary surface without selfintersections. \n \~ + \param[in] surface - \ru Поверхность. + \en A surface. \~ + \param[out] face - \ru Грань. + \en The face. \~ + \return \ru Возвращает true, если грань создана. + \en Returns 'true' if the face was created. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) SurfaceFace( const MbSurface & surface, SPtr & face ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создание трёхмерной сетки по двумерной сетке. + \en Creating a three-dimensional grid on a two-dimensional grid. \~ + \details \ru Создание трёхмерной сетки по двумерной сетке. \n + \en Creating a three-dimensional grid on a two-dimensional grid. \n \~ + \param[in] place - \ru Локальная система координат в трёхмерном пространстве. + \en Local coordinate system in three dimensional space. \~ + \param[out] planarGrid - \ru Триангуляция двумерной области. + \en Triangulation of a two-dimensional region. \~ + \return \ru Возвращает true, если грань создана. + \en Returns 'true' if the face was created. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (MbGrid *) SpaceGrid( const MbPlacement3D & place, const MbPlanarGrid & planarGrid, bool exact = false ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Заменить элемент на вставку, если расстояние от начала координат до центра + его габарита, превышает размер габаритного куба в заданное число раз \~ + \en Replace an item by an instance if the length from it's bounding box to the + world origin is greater than it's diagonal by specified factor. + \details \ru Объект или его копия смещается на вектор из начала координат до центра габаритного куба объекта + и размещается во вставке, обеспечивающей смещение на вектор противоположного направления. \~ + Обрабатываются только объекты, не являющиеся вставками. \~ + Нулевое или отрицательное значение параметра ratioThreashhold запрещает преобразование. \~ + \en The item or it's replica is moved bey the vector from the origin of the world to the center of the + item's bounding box, then put in the instance, providing the displacement by the reversed vector. \~ + Function processes objects of all types except for instances. \~ + Null or negative value of the ratioThreashhold parameter blocks the transformation. \~ + \param[out] item - \ru Обрабатываемый объект. \~ + \en Processable item. \~ + \param[in] ratioThreashhold - \ru Пороговое значение отношения расстояния до центра габаритного куба и его диагонали, + при превышении которого происходит замена. \~ + \en The replacement threshold value of the ratio of the bounding box's center to the + origin of the world to it's diagonal length. \~ + \param[in] makeCopy - \ru Производить ли трансформацию на копии объекта. \~ + \en Is the copy of the object must be transformed. \~ + \return \ru Возвращает вставку объекта. + \en Returns instance of object. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (MbItem *) ReplaceByInstance( MbItem * item, double ratioThreashhold = -1.0, bool makeCopy = false ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построениe «залитого» объема, расположенного между внутренней поверхностью сосуда и ограничивающей поверхностью или телом. \~ + \en The construction of a "flood fill" volume located between the inner surface of the vessel and the bounding surface or body. \~ + \details \ru На вход подаётся тело, дополнительная поверхность или дополнительное тело и координаты источника. + На выходе получаем объём, построенный от источника и ограниченный со всех сторон оболочкой тела и дополнительными объектами. \~ + \en The body, an additional surface or an additional body and the coordinates of the source are fed to the input. + On the output we get the volume, constructed from the source and bounded from all sides by the shell of the body and by additional objects. \~ + \param[in] vessel - \ru Тело сосуда. \~ + \en The vessel. \~ + \param[in] sameShell - \ru Режим копирования тела сосуда. + \en Whether to copy the vessel. \~ + \param[in] bungData - \ru Поверхность уровня или тело пробки. \~ + \en The surface of the level or body of the bung. \~ + \param[in] origin - \ru Точка внутри сосуда. \~ + \en The point inside the vessel. \~ + \param[in] names - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (MbResultType) FloodFillResult( MbSolid & vessel, + MbeCopyMode sameShell, + const MbSweptData & bungData, + const MbCartPoint3D & origin, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать крепеж по трехмерной точке. НЕ ИСПОЛЬЗОВАТЬ ВНЕ ТЕСТОВОГО ПРИЛОЖЕНИЯ!!! ФУНКЦИЯ НАХОДИТСЯ В РАЗРАБОТКЕ!!! + \en Create fastener using 3D point. \~ + \details \ru Создать крепеж по трехмерной точке. \n + \en Create fastener using 3D point. \n \~ + \param[in] solids - \ru Множество тел для скрепления. + \en An array of bodies to fasten. + \param[in] sameShell - \ru Режим копирования тел. + \en Whether to copy the solids. \~ + \param[in] point - \ru Трехмерная точка, на основе проецирования которой определяется положение крепежа. + \en 3d point. \~ + \param[in] params - \ru Параметры крепежа ( его тип, размеры и т.д. ). + \en Fastener parameters ( type, diameter, etc. ). \~ + \param[in] names - \ru Именователь новых граней. + \en An object defining the name of a new faces. \~ + \param[out] results - \ru Множество тел для скрепления с набором отверстий и набор тел крепежа в отверстиях. + \en Array of bodies with a holes and fastener body. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) CreateFastener ( const RPArray & solids, + MbeCopyMode sameShell, + const MbCartPoint3D & point, + const FastenersValues & params, + const MbSNameMaker & names, + RPArray & results ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Cоздать набор крепежных элементов по трехмерной кривой. НЕ ИСПОЛЬЗОВАТЬ ВНЕ ТЕСТОВОГО ПРИЛОЖЕНИЯ!!! ФУНКЦИЯ НАХОДИТСЯ В РАЗРАБОТКЕ!!! + \en Create an array of fastener elements using 3d curve. \~ + \details \ru Cоздать набор крепежных элементов по трехмерной кривой. + \en Create an array of fastener elements using 3d curve. \~ + \param[in] solids - \ru Множество тел для скрепления. + \en Array of bodies with a hole and fastener body. \~ + \param[in] sameShell - \ru Режим копирования тел. + \en Whether to copy the solids. \~ + \param[in] curve - \ru Трехмерная кривая, на основе проецирования точек которой определяются положения крепежных элементов. + \en 3D curve. \~ + \param[in] number - \ru Количество точек на кривой. Точки расположены равномерно по длине кривой. + \en Number of points on the curve. Points are uniformly located along the length of the curve. \~ + \param[in] params - \ru Параметры крепежа ( его тип, размеры и т.д. ). + \en Fastener parameters ( type, diameter, etc. ). \~ + \param[in] names - \ru Именователь новых граней. + \en An object defining the name of a new faces. \~ + \param[out] results - \ru Множество тел для скрепления с набором отверстий и набор тел крепежа в отверстиях. + \en Array of bodies with a holes and fastener bodies. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) CreateFasteners( const RPArray & solids, + MbeCopyMode sameShell, + const MbCurve3D & curve, + size_t number, + const FastenersValues & params, + const MbSNameMaker & names, + RPArray & results ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Слить несколько граней тела в одну грань. + \en Create a solid with one face instead selected faces. \~ + \details \ru Заменить указанные гладко стыкующиеся грани тела одной геометрически совпадающей гранью. \n + \en To replace these smooth abutting faces to form a single geometrically matching face. \n + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования исходного тела. + \en Whether to copy the source solid. \~ + \param[in] faces - \ru Объединяемые грани тела. + \en The faces of solid to be merged. \~ + \param[in] uParam - \ru Параметры u направления объединяющей поверхности. + \en The operation parameters for common surface in u direction. \~ + \param[in] vParam - \ru Параметры v направления объединяющей поверхности. + \en The operation parameters for common surface in v direction. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] prolong - \ru Параметр добавления гладко стыкующихся граней с faces (prolong>0). + \en The parameter of adding prolong faces (prolong>0). \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) CreateMerging( MbSolid & solid, + MbeCopyMode sameShell, + c3d::FacesVector & faces, + const MbNurbsParameters & uParam, + const MbNurbsParameters & vParam, + const MbSNameMaker & names, + bool prolong, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти грани тел, имеющие контактные площадки. \~ + \en To find contacted faces of bodies. \~ + \details \ru Найти номера контактирующих граней тел, имеющих противоположно направленные нормали, у которых есть общие участки с конечной площадью перекрытия. \~ + \en To find contacted faces of bodies with oppositely directed normals which have a finite overlap area. \~ + \param[in] solid1 - \ru Первое тело. + \en The first solid. \~ + \param[in] solid2 - \ru Второе тело. + \en The second solid. \~ + \param[in] precision - \ru Точность операции. + \en The precision of operation. \~ + \param[out] facesNumbers - \ru Пары номеров касающихся граней с противоположно направленными нормалями. + \en The couples of number of contacted faces with oppositely directed normals. \~ + \return \ru Возвращает true, если грани контакта найдены. + \en Returns true, if contacted faces were found. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) FindTouchedFaces( const MbSolid & solid1, + const MbSolid & solid2, + double precision, + c3d::IndicesPairsVector & facesNumbers ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Разбить контактирующие грани тел. \~ + \en To find contacted faces of bodies. \~ + \details \ru Разбить контактирующие грани тел, выделив общие области с конечной площадью перекрытия в отдельные грани. \~ + \en To find contacted faces of bodies and build a finite overlap contacted area as faces. \~ + \param[in/out] solid1 - \ru Первое тело. + \en The first solid. \~ + \param[in/out] solid2 - \ru Второе тело. + \en The second solid. \~ + \param[in] precision - \ru Точность операции. + \en The precision of operation. \~ + \param[in] facesNumbers - \ru Множество пар номеров соприкасающихся граней, у которых требуется построить общие пятна контакта (может быть пустым). + \en The container with pairs of contact face numbers that need to have common contact spots (it can be empty). \~ + \return \ru Возвращает true, если грани контакта найдены. + \en Returns true, if contacted faces were found. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) SplitTouchedFaces( MbSolid & solid1, + MbSolid & solid2, + double precision, + c3d::IndicesPairsVector & facesNumbers ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Объединить тела, имеющие контактирующие грани. \~ + \en The function performs unite the bodies with contacted faces. \~ + \details \ru Объединить тела, удалив контактирующие грани. \~ + \en The function performs unite the bodies and removing the contacting faces. \~ + \param[in] solid1 - \ru Первое тело. + \en The first solid. \~ + \param[in] sameShell1 - \ru Способ копирования граней первого тела. + \en Method of copying the faces of the first solid. \~ + \param[in] solid2 - \ru Второе тело. + \en The second solid. \~ + \param[in] sameShell2 - \ru Способ копирования граней второго тела. + \en Method of copying the faces of the second solid. \~ + \param[in] precision - \ru Точность операции. + \en The precision of operation. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) TouchedSolidsMerging( MbSolid & solid1, + MbeCopyMode sameShell1, + MbSolid & solid2, + MbeCopyMode sameShell2, + const MbSNameMaker & names, + double precision, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Починить тело. \~ + \en Solid repairing. \~ + \details \ru Найти дефекты и починить грани, рёбра, вершины тела. \~ + \en Find defects and repair faces, edges, vertices of the solid. \~ + \param[in] solid - \ru Тело для ремонта. + \en The initial solid. \~ + \param[in] accuracy - \ru Точность для поиска дефектов тела. + \en The accuracy for finding defects of the solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SolidRepairing( MbSolid & solid, double accuracy ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Получить трансформированную копию тела. \~ + \en Get transformed copy of a solid. \~ + \details \ru Получить трансформированную копию тела, если матрица трансформации не единичная или оригинал, если единичная. \~ + \en Get transformed copy of a solid if a matrix is not identity matrix or original of the solid if the matrix is identity matrix. \~ + \param[in] solid - \ru Тело. + \en A solid. \~ + \param[in,out] copyMode - \ru Исходный режим копирования тела. + \en An initial copy mode. \~ + \param[in] matr - \ru Матрица преобразования. + \en Transformation matrix. \~ + \param[in] transformedMainName - \ru Главное имя для операции трансформации. + \en Main name of transformation operation. \~ + \return \ru Возвращает копию или оригинал тела. + \en Returns copy or original of the solid. \~ + \ingroup Algorithms_3D +*/ +// --- +inline +c3d::SolidSPtr GetTransformedSolid( c3d::SolidSPtr & solid, MbeCopyMode & copyMode, const MbMatrix3D & matr, SimpleName transformedMainName = ct_TransformedSolid ) +{ + c3d::SolidSPtr resSolid( solid ); + + if ( (resSolid != NULL) && !matr.IsSingle() ) { + MbSNameMaker n( transformedMainName, MbSNameMaker::i_SideNone, 0 ); + + MbSolid * resSolidPtr = NULL; + TransformValues tv( matr ); + ::TransformedSolid( *solid, cm_Copy, tv, n, resSolidPtr ); + if ( resSolidPtr != NULL ) { + resSolid = resSolidPtr; + copyMode = cm_Same; + } + } + return resSolid; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Получить трансформированную копию объекта. \~ + \en Get transformed copy of object. \~ + \details \ru Получить трансформированную копию объекта, если матрица трансформации не единичная или оригинал, если единичная. \~ + \en Get transformed copy of an object if a matrix is not identity matrix or original of the solid if the matrix is identity matrix. \~ + \param[in] item - \ru Объект. + \en An object. \~ + \param[in] matr - \ru Матрица преобразования. + \en Transformation matrix. \~ + \return \ru Возвращает копию или оригинал объекта. + \en Returns copy or original of the object. \~ + \ingroup Algorithms_3D +*/ +// --- +template +SPtr GetTransformedItem( SPtr & item, const MbMatrix3D & matr, MbRegDuplicate * iDupReg = NULL, MbRegTransform * iTransReg = NULL ) +{ + SPtr resItem( item ); + if ( (resItem != NULL) && !matr.IsSingle() ) { + resItem = static_cast( &item->Duplicate( iDupReg ) ); + resItem->Transform( matr, iTransReg ); + } + return resItem; +} + + +#endif // __ACTION_H diff --git a/C3d/Include/action_analysis.h b/C3d/Include/action_analysis.h index f84c5a9..068ba44 100644 --- a/C3d/Include/action_analysis.h +++ b/C3d/Include/action_analysis.h @@ -1,334 +1,384 @@ -//////////////////////////////////////////////////////////////////////////////// -/** -\file -\brief \ru Функции для анализа кривизны поверхности. -\en Functions for surface curvature analysis. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ACTION_CURVATURE_ANALYSIS_H -#define __ACTION_CURVATURE_ANALYSIS_H - - -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Алгоритмы поиска экстремумов на поверхности. - \en Algorithms for finding extremes on the surface. \~ - \details \ru Константы, задающие вызываемый алгоритм поиска экстремальных значений функции на поверхности. - \en Constants defining the called algorithm for searching for extreme values of a function on a surface. \~ - \ingroup Algorithms_3D -*/ -enum MbeExtremsSearchingMethod -{ - esm_GradientDescent = 1, ///< \ru Mетод градиентного спуска. \en Gradient Descent Method. - esm_LineSegregation = 2 ///< \ru Mетод выделения линий смены убывания / возрастания функции по u и по v. \en The method of segregation of lines of change of decrease / increase of the function in u and v directions. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Функция, заданная на поверхности. - \en The function define on the surface. \~ - \details \ru Рассчитывает значение самой функции и ее градиент. - \en Calculates the value of the function itself and its gradient. \~ - \ingroup Algorithms_3D -*/ -typedef void( *SurfaceFunction )( const MbSurface & surf, // Поверхность, - const MbCartPoint & pnt, // точка на поверхности - double & func, // рассчитываемое значение функции, - MbVector * der );// рассчитываемое значение вектора градиента (если указатель не нулевой). - -//------------------------------------------------------------------------------ -/** \brief \ru Вычислить в точке поверхности минимальную нормальную кривизну, а также ее градиент. - \en Calculate at the point of the surface the minimum normal curvature, as well as its gradient. \~ - \details \ru Вычисляется значение кривизны и опционально значение ее градиента в точке (если передается ненулевой указатель). - \en The curvature value is calculated and, optionally, its gradient value at a point (if a non-zero pointer is passed). \~ - \param[in] surf - \ru Поверхность. - \en Surface. \~ - \param[in] pnt - \ru Точка расчета. - \en Point of calculation. \~ - \param[out] func - \ru Рассчитываемое значение кривизны. - \en Calculated curvature value. \~ - \param[out] der - \ru Рассчитываемое значение градиента кривизны. - \en The calculated value of the curvature gradient. \~ - \ingroup Algorithms_3D -*/ -MATH_FUNC( void ) MinSurfaceCurvature( const MbSurface & surf, const MbCartPoint & pnt, double & func, MbVector * der = NULL ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Вычислить в точке поверхности максимальну нормальную кривизну, а также ее градиент. - \en Calculate at the point of the surface the maximum normal curvature, as well as its gradient. \~ - \details \ru Вычисляется значение кривизны и опционально значение ее градиента в точке (если передается ненулевой указатель). - \en The curvature value is calculated and, optionally, its gradient value at a point (if a non-zero pointer is passed). \~ - \param[in] surf - \ru Поверхность. - \en Surface. \~ - \param[in] pnt - \ru Точка расчета. - \en Point of calculation. \~ - \param[out] func - \ru Рассчитываемое значение кривизны. - \en Calculated curvature value. \~ - \param[out] der - \ru Рассчитываемое значение градиента кривизны. - \en The calculated value of the curvature gradient. \~ - \ingroup Algorithms_3D -*/ -MATH_FUNC( void ) MaxSurfaceCurvature( const MbSurface & surf, const MbCartPoint & pnt, double & func, MbVector * der = NULL ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Вычислить в точке поверхности гауссову кривизну, а также ее градиент. - \en Calculate at a surface point the Gaussian curvature, as well as its gradient. \~ - \details \ru Вычисляется значение кривизны и опционально значение ее градиента в точке (если передается ненулевой указатель). - \en The curvature value is calculated and, optionally, its gradient value at a point (if a non-zero pointer is passed). \~ - \param[in] surf - \ru Поверхность. - \en Surface. \~ - \param[in] pnt - \ru Точка расчета. - \en Point of calculation. \~ - \param[out] func - \ru Рассчитываемое значение кривизны. - \en Calculated curvature value. \~ - \param[out] der - \ru Рассчитываемое значение градиента кривизны. - \en The calculated value of the curvature gradient. \~ - \ingroup Algorithms_3D -*/ -MATH_FUNC( void ) GaussCurvature( const MbSurface & surf, const MbCartPoint & pnt, double & func, MbVector * der = NULL ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Вычислить в точке поверхности среднюю кривизну, а также ее градиент. - \en Calculate at the point of the surface the mean curvature, as well as its gradient. \~ - \details \ru Вычисляется значение кривизны и опционально значение ее градиента в точке (если передается ненулевой указатель). - \en The curvature value is calculated and, optionally, its gradient value at a point (if a non-zero pointer is passed). \~ - \param[in] surf - \ru Поверхность. - \en Surface. \~ - \param[in] pnt - \ru Точка расчета. - \en Point of calculation. \~ - \param[out] func - \ru Рассчитываемое значение кривизны. - \en Calculated curvature value. \~ - \param[out] der - \ru Рассчитываемое значение градиента кривизны. - \en The calculated value of the curvature gradient. \~ - \ingroup Algorithms_3D -*/ -MATH_FUNC( void ) MeanCurvature( const MbSurface & surf, const MbCartPoint & pnt, double & func, MbVector * der = NULL ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Вычислить в точке поверхности нормальную кривизна в направлении u , а также ее градиент. - \en Calculate at the surface point the normal curvature in the direction of u, as well as its gradient. \~ - \details \ru Вычисляется значение кривизны и опционально значение ее градиента в точке (если передается ненулевой указатель). - \en The curvature value is calculated and, optionally, its gradient value at a point (if a non-zero pointer is passed). \~ - \param[in] surf - \ru Поверхность. - \en Surface. \~ - \param[in] pnt - \ru Точка расчета. - \en Point of calculation. \~ - \param[out] func - \ru Рассчитываемое значение кривизны. - \en Calculated curvature value. \~ - \param[out] der - \ru Рассчитываемое значение градиента кривизны. - \en The calculated value of the curvature gradient. \~ - \ingroup Algorithms_3D -*/ -MATH_FUNC( void ) UNormalCurvature( const MbSurface & surf, const MbCartPoint & pnt, double & func, MbVector * der = NULL ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Вычислить в точке поверхности нормальную кривизна в направлении v, а также ее градиент. - \en Calculate at the surface point the normal curvature in the direction of v, as well as its gradient. \~ - \details \ru Вычисляется значение кривизны и опционально значение ее градиента в точке (если передается ненулевой указатель). - \en The curvature value is calculated and, optionally, its gradient value at a point (if a non-zero pointer is passed). \~ - \param[in] surf - \ru Поверхность. - \en Surface. \~ - \param[in] pnt - \ru Точка расчета. - \en Point of calculation. \~ - \param[out] func - \ru Рассчитываемое значение кривизны. - \en Calculated curvature value. \~ - \param[out] der - \ru Рассчитываемое значение градиента кривизны. - \en The calculated value of the curvature gradient. \~ - \ingroup Algorithms_3D -*/ -MATH_FUNC( void ) VNormalCurvature( const MbSurface & surf, const MbCartPoint & pnt, double & func, MbVector * der = NULL ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точки поверхности, в которых выбранная кривизна принимает наибольшие по модулю значения. - \en Find the points of the surface at which the selected curvature takes the largest in modulus values. \~ - \details \ru Ищутся точки, в которых выбранная кривизна принимает на поверхности наибольшее положительное и наименьшее отрицательное значение. - \en Looks for points at which the selected curvature takes on the surface the greatest positive and least negative value. \~ - \param[in] surf - \ru Исследуемая поверхность. - \en Test surface. \~ - \param[in] func - \ru Функция расчета кривизны в точке. - \en The function of calculating the curvature at a point. \~ - \param[out] maxNegCurv - \ru Наибольшее по модулю отрицательное значение кривизны (0, если нет такого). - \en The largest in modulus value negative curvature (0, if there is no such). \~ - \param[out] maxNegLoc - \ru Точка, в которой кривизна принимает наибольшее по модулю отрицательное значение. - \en The point at which the curvature takes the largest in modulus negative value. \~ - \param[out] maxPosCurv - \ru Наибольшее положительное значение кривизны (0, если нет такого). - \en The greatest positive value of curvature (0, if there is no such). \~ - \param[out] maxPosLoc - \ru Точка, в которой кривизна принимает наибольшее положительное значение. - \en The point at which the curvature takes the most positive value. \~ - \param[in] method - \ru Алгоритм поиска экстремумов. - \en Extremum search algorithm. \~ - \ingroup Algorithms_3D -*/ -MATH_FUNC( void ) SurfaceMinMaxCurvature(const MbSurface & surface, SurfaceFunction func, double & maxNegCurv, MbCartPoint & maxNegLoc, - double & maxPosCurv, MbCartPoint & maxPosLoc, MbeExtremsSearchingMethod method = esm_LineSegregation ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точки оболочки, в которых выбранная кривизна принимает наибольшие по модулю значения. - \en Find the points of the shell at which the selected curvature takes the most modulo values. \~ - \details \ru Ищутся точки на оболочке, в которых выбранная кривизна принимают наибольшее положительное и наименьшее отрицательное значение. - \en Finds points on the shell at which the selected curvature takes the largest positive and lowest negative values. \~ - \param[in] faces - \ru Грани оболочки. - \en Faces of the shell. \~ - \param[in] func - \ru Функция расчета кривизны в точке. - \en The function of calculating the curvature at a point. \~ - \param[out] maxNegCurv - \ru Наибольшее по модулю отрицательное значение кривизны (0, если нет такого). - \en The largest in modulus value negative curvature (0, if there is no such). \~ - \param[out] maxNegFace - \ru Грань, в которой кривизна принимает наибольшее по модулю отрицательное значение. - \en The face at which the curvature takes the largest in modulus negative value. \~ - \param[out] maxNegLoc - \ru Точка, в которой кривизна принимает наибольшее по модулю отрицательное значение. - \en The point at which the curvature takes the largest in modulus negative value. \~ - \param[out] maxPosCurv - \ru Наибольшее положительное значение кривизны (0, если нет такого). - \en The greatest positive value of curvature (0, if there is no such). \~ - \param[out] maxPosFace - \ru Грань, в которой кривизна принимает наибольшее положительное значение. - \en The face at which the curvature takes the most positive value. \~ - \param[out] maxPosLoc - \ru Точка, в которой кривизна принимает наибольшее положительное значение. - \en The point at which the curvature takes the most positive value. \~ - \param[in] borderControl - \ru Учитывать границы граней при поиске экстремумов. - \en Take into account the boundaries of the faces when searching for extrema. \~ - \param[in] method - \ru Алгоритм поиска экстремумов. - \en Extremum search algorithm. \~ - \ingroup Algorithms_3D -*/ -MATH_FUNC( void ) FacesMinMaxCurvature( const RPArray & faces, SurfaceFunction func, double & maxNegCurv, MbFace *& maxNegFace, MbCartPoint & maxNegLoc, - double & maxPosCurv, MbFace *& maxPosFace, MbCartPoint & maxPosLoc, bool borderControl = false, - MbeExtremsSearchingMethod method = esm_LineSegregation ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точки на поверхности, в которых главные нормальные кривизны принимают наибольшие по модулю значения. - \en Find the points on the surface at which the major normal curvatures take the largest values in the module. \~ - \details \ru Ищутся точки на поверхности, в которых главные нормальные кривизны принимают наибольшее положительное и наименьшее отрицательное значение. - \en Looks for points on the surface at which the major normal curvatures take the largest positive and smallest negative values. \~ - \param[in] surf - \ru Исследуемая поверхность. - \en Test surface. \~ - \param[out] maxNegCurv - \ru Наибольшее по модулю отрицательное значение кривизны (0, если нет такого). - \en The largest in modulus value negative curvature (0, if there is no such). \~ - \param[out] maxNegLoc - \ru Точка, в которой кривизна принимает наибольшее по модулю отрицательное значение. - \en The point at which the curvature takes the largest in modulus negative value. \~ - \param[out] maxPosCurv - \ru Наибольшее положительное значение кривизны (0, если нет такого). - \en The greatest positive value of curvature (0, if there is no such). \~ - \param[out] maxPosLoc - \ru Точка, в которой кривизна принимает наибольшее положительное значение. - \en The point at which the curvature takes the most positive value. \~ - \param[in] method - \ru Алгоритм поиска экстремумов. - \en Extremum search algorithm. \~ - \ingroup Algorithms_3D -*/ -MATH_FUNC( void ) SurfaceMinMaxCurvature(const MbSurface & surface, double & maxNegCurv, MbCartPoint & maxNegLoc, - double & maxPosCurv, MbCartPoint & maxPosLoc, MbeExtremsSearchingMethod method = esm_LineSegregation ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точки на оболочке, в которых главные нормальные кривизны принимают наибольшие по модулю значения. - \en Find the points on the shell at which the major normal curvatures take the largest values in the module. \~ - \details \ru Ищутся точки на оболочке, в которых главные нормальные кривизны принимают наибольшее положительное и наименьшее отрицательное значение. - \en Looks for points on the shell at which the major normal curvatures take the largest positive and smallest negative values. \~ - \param[in] faces - \ru Грани оболочки. - \en Faces of the shell. \~ - \param[out] maxNegCurv - \ru Наибольшее по модулю отрицательное значение кривизны (0, если нет такого). - \en The largest in modulus value negative curvature (0, if there is no such). \~ - \param[out] maxNegFace - \ru Грань, в которой кривизна принимает наибольшее по модулю отрицательное значение. - \en The face at which the curvature takes the largest in modulus negative value. \~ - \param[out] maxNegLoc - \ru Точка, в которой кривизна принимает наибольшее по модулю отрицательное значение. - \en The point at which the curvature takes the largest in modulus negative value. \~ - \param[out] maxPosCurv - \ru Наибольшее положительное значение кривизны (0, если нет такого). - \en The greatest positive value of curvature (0, if there is no such). \~ - \param[out] maxPosFace - \ru Грань, в которой кривизна принимает наибольшее положительное значение. - \en The face at which the curvature takes the most positive value. \~ - \param[out] maxPosLoc - \ru Точка, в которой кривизна принимает наибольшее положительное значение. - \en The point at which the curvature takes the most positive value. \~ - \param[in] borderControl - \ru Учитывать границы граней при поиске экстремумов. - \en Take into account the boundaries of the faces when searching for extrema. \~ - \param[in] method - \ru Алгоритм поиска экстремумов. - \en Extremum search algorithm. \~ - \ingroup Algorithms_3D -*/ -MATH_FUNC( void ) FacesMinMaxCurvature( const RPArray & faces, double & maxNegCurv, MbFace *& maxNegFace, MbCartPoint & maxNegLoc, - double & maxPosCurv, MbFace *& maxPosFace, MbCartPoint & maxPosLoc, bool borderControl = false, - MbeExtremsSearchingMethod method = esm_LineSegregation ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Ориентированная кривизна для плоской кривой. - \en Oriented curvature for a plane curve. \~ - \details \ru Для плоской кривой функция возвращает кривизну в точке, ориентированную относительно нормали плоскости, - в которой она лежит. Для неплоской кривой функция просто возвращает кривизну в точке. - \en For a flat curve, the function returns the curvature at a point oriented relative to the normal to the plane, - in which she lies. For a non-flat curve, the function simply returns the curvature at the point. \~ - \param[in] curve - \ru Исследуемая кривая. - \en Test curve. \~ - \param[in] param - \ru Параметр на кривой. - \en Parameter on the curve. \~ - \param[in] planeNorm - \ru Нормаль плоскости, в которой лежит кривая. Если нормаль не передается в функцию, алгоритм самостоятельно - выполняет проверку, лежит ли кривая в плоскости, и вычисляет нормаль, если проверка выполняется. - \en The normal of the plane in which the curve lies. If the normal is not passed to the function, the algorithm itself - checks if the curve is in the plane and calculates normal if the test is being performed. \~ - \return \ru Возвращается значение ориентированной кривизны в точке. - \en The value of the oriented curvature at the point is returned. \~ - - \ingroup Algorithms_3D -*/ -MATH_FUNC( double ) CurveOrientedCurvature(const MbCurve3D & curve, double & param, const MbVector3D * planeNorm = NULL ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точки на кривой, в которых кривизна принимает наибольшее и наименьшее значения. - \en Find the points on the curve at which the curvature takes the largest and smallest values. \~ - \details \ru Для плоской кривой наибольшее и наименьшее значение может уходить в отрицательную область. - Для неплоской кривой наибольшее и наименьшее значение всегда неотрицательны. - \en For a flat curve, the largest and smallest value may go into the negative region. - For a non-planar curve, the largest and smallest values are always non-negative. \~ - \param[in] curve - \ru Исследуемая кривая. - \en Test curve. \~ - \param[out] maxCurv - \ru Наибольшее значение кривизны. - \en The greatest value of curvature. \~ - \param[out] maxParam - \ru Точка, в которой кривизна принимает наибольшее значение. - \en The point at which the curvature takes the largest value. \~ - \param[out] minCurv - \ru Наименьшее значение кривизны. - \en The smallest value of curvature. \~ - \param[out] minParam - \ru Точка, в которой кривизна принимает наибольшее значение. - \en The point at which the curvature takes the smallest value. \~ - \param[out] bendPoints - \ru Mассив параметров точек перегиба. - \en Array of parameters of bend points. \~ - \param[out] maxPoints - \ru Mассив параметров, в которых достигается локальный максимум кривизны по модулю. - \en An array of parameters in which the local maximum curvature modulo is reached. \~ - \param[out] minPoints - \ru Mассив параметров, в которых достигается локальный минимум кривизны по модулю. - \en An array of parameters in which the local minimum curvature modulo is reached. \~ - \param[out] rapPoints - \ru Mассив параметров, в которых кривизна терпит разрыв. - Для каждого разрыва вставляются две точки, до и после. - \en Array of parameters in which curvature breaks. - For each break two points are inserted, before and after. \~ - \ingroup Algorithms_3D -*/ -MATH_FUNC( void ) CurveMinMaxCurvature( const MbCurve3D & curve, double & maxCurv, double & maxParam, double & minCurv, double & minParam, - std::vector * bendPoints = NULL, std::vector * maxPoints = NULL, - std::vector * minPoints = NULL, std::vector * rapPoints = NULL ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Направление максимальной нормальной кривизны поверхности. - \en The direction of the maximum normal surface curvature. \~ - \details \ru Вычисляется направление на поверхности, в котором нормальная кривизна поверхности принимает максимальное значение. - \en The direction on the surface is calculated in which the normal curvature of the surface takes a maximum value. \~ - \param[in] surf - \ru Поверхность. - \en Surface. \~ - \param[in] pnt - \ru Точка расчета. - \en Point of calculation. \~ - \param[out] dir - \ru Рассчитываемое направление. - \en The calculated direction. \~ - \ingroup Algorithms_3D -*/ -MATH_FUNC( void ) SurfaceMaxCurvatureDirection( const MbSurface & surf, const MbCartPoint & pnt, MbVector & dir ); - -#endif // __ACTION_CURVATURE_ANALYSIS_H +//////////////////////////////////////////////////////////////////////////////// +/** +\file +\brief \ru Функции для анализа нормалей и кривизны поверхностей и кривых. + \en Functions for normals and curvature analysis of surfaces and curves. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ACTION_CURVATURE_ANALYSIS_H +#define __ACTION_CURVATURE_ANALYSIS_H + + +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Алгоритмы поиска экстремумов на поверхности. + \en Algorithms for finding extremes on the surface. \~ + \details \ru Константы, задающие вызываемый алгоритм поиска экстремальных значений функции на поверхности. + \en Constants defining the called algorithm for searching for extreme values of a function on a surface. \~ + \ingroup Algorithms_3D +*/ +enum MbeExtremsSearchingMethod +{ + esm_GradientDescent = 1, ///< \ru Mетод градиентного спуска. \en Gradient Descent Method. + esm_LineSegregation = 2 ///< \ru Mетод выделения линий смены убывания / возрастания функции по u и по v. \en The method of segregation of lines of change of decrease / increase of the function in u and v directions. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Функция, заданная на поверхности. + \en The function define on the surface. \~ + \details \ru Рассчитывает значение самой функции и ее градиент. + \en Calculates the value of the function itself and its gradient. \~ + \ingroup Algorithms_3D +*/ +typedef void( *SurfaceFunction )( const MbSurface & surf, // Поверхность, + const MbCartPoint & pnt, // точка на поверхности + double & func, // рассчитываемое значение функции, + MbVector * der );// рассчитываемое значение вектора градиента (если указатель не нулевой). + +//------------------------------------------------------------------------------ +/** \brief \ru Вычислить в точке поверхности минимальную нормальную кривизну, а также ее градиент. + \en Calculate at the point of the surface the minimum normal curvature, as well as its gradient. \~ + \details \ru Вычисляется значение кривизны и опционально значение ее градиента в точке (если передается ненулевой указатель). + \en The curvature value is calculated and, optionally, its gradient value at a point (if a non-zero pointer is passed). \~ + \param[in] surf - \ru Поверхность. + \en Surface. \~ + \param[in] pnt - \ru Точка расчета. + \en Point of calculation. \~ + \param[out] func - \ru Рассчитываемое значение кривизны. + \en Calculated curvature value. \~ + \param[out] der - \ru Рассчитываемое значение градиента кривизны. + \en The calculated value of the curvature gradient. \~ + \ingroup Algorithms_3D +*/ +MATH_FUNC( void ) MinSurfaceCurvature( const MbSurface & surf, + const MbCartPoint & pnt, + double & func, + MbVector * der = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Вычислить в точке поверхности максимальную нормальную кривизну, а также ее градиент. + \en Calculate at the point of the surface the maximum normal curvature, as well as its gradient. \~ + \details \ru Вычисляется значение кривизны и опционально значение ее градиента в точке (если передается ненулевой указатель). + \en The curvature value is calculated and, optionally, its gradient value at a point (if a non-zero pointer is passed). \~ + \param[in] surf - \ru Поверхность. + \en Surface. \~ + \param[in] pnt - \ru Точка расчета. + \en Point of calculation. \~ + \param[out] func - \ru Рассчитываемое значение кривизны. + \en Calculated curvature value. \~ + \param[out] der - \ru Рассчитываемое значение градиента кривизны. + \en The calculated value of the curvature gradient. \~ + \ingroup Algorithms_3D +*/ +MATH_FUNC( void ) MaxSurfaceCurvature( const MbSurface & surf, + const MbCartPoint & pnt, + double & func, + MbVector * der = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Вычислить в точке поверхности гауссову кривизну, а также ее градиент. + \en Calculate at a surface point the Gaussian curvature, as well as its gradient. \~ + \details \ru Вычисляется значение кривизны и опционально значение ее градиента в точке (если передается ненулевой указатель). + \en The curvature value is calculated and, optionally, its gradient value at a point (if a non-zero pointer is passed). \~ + \param[in] surf - \ru Поверхность. + \en Surface. \~ + \param[in] pnt - \ru Точка расчета. + \en Point of calculation. \~ + \param[out] func - \ru Рассчитываемое значение кривизны. + \en Calculated curvature value. \~ + \param[out] der - \ru Рассчитываемое значение градиента кривизны. + \en The calculated value of the curvature gradient. \~ + \ingroup Algorithms_3D +*/ +MATH_FUNC( void ) GaussCurvature( const MbSurface & surf, + const MbCartPoint & pnt, + double & func, + MbVector * der = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Вычислить в точке поверхности среднюю кривизну, а также ее градиент. + \en Calculate at the point of the surface the mean curvature, as well as its gradient. \~ + \details \ru Вычисляется значение кривизны и опционально значение ее градиента в точке (если передается ненулевой указатель). + \en The curvature value is calculated and, optionally, its gradient value at a point (if a non-zero pointer is passed). \~ + \param[in] surf - \ru Поверхность. + \en Surface. \~ + \param[in] pnt - \ru Точка расчета. + \en Point of calculation. \~ + \param[out] func - \ru Рассчитываемое значение кривизны. + \en Calculated curvature value. \~ + \param[out] der - \ru Рассчитываемое значение градиента кривизны. + \en The calculated value of the curvature gradient. \~ + \ingroup Algorithms_3D +*/ +MATH_FUNC( void ) MeanCurvature( const MbSurface & surf, + const MbCartPoint & pnt, + double & func, + MbVector * der = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Вычислить в точке поверхности нормальную кривизна в направлении u , а также ее градиент. + \en Calculate at the surface point the normal curvature in the direction of u, as well as its gradient. \~ + \details \ru Вычисляется значение кривизны и опционально значение ее градиента в точке (если передается ненулевой указатель). + \en The curvature value is calculated and, optionally, its gradient value at a point (if a non-zero pointer is passed). \~ + \param[in] surf - \ru Поверхность. + \en Surface. \~ + \param[in] pnt - \ru Точка расчета. + \en Point of calculation. \~ + \param[out] func - \ru Рассчитываемое значение кривизны. + \en Calculated curvature value. \~ + \param[out] der - \ru Рассчитываемое значение градиента кривизны. + \en The calculated value of the curvature gradient. \~ + \ingroup Algorithms_3D +*/ +MATH_FUNC( void ) UNormalCurvature( const MbSurface & surf, + const MbCartPoint & pnt, + double & func, + MbVector * der = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Вычислить в точке поверхности нормальную кривизна в направлении v, а также ее градиент. + \en Calculate at the surface point the normal curvature in the direction of v, as well as its gradient. \~ + \details \ru Вычисляется значение кривизны и опционально значение ее градиента в точке (если передается ненулевой указатель). + \en The curvature value is calculated and, optionally, its gradient value at a point (if a non-zero pointer is passed). \~ + \param[in] surf - \ru Поверхность. + \en Surface. \~ + \param[in] pnt - \ru Точка расчета. + \en Point of calculation. \~ + \param[out] func - \ru Рассчитываемое значение кривизны. + \en Calculated curvature value. \~ + \param[out] der - \ru Рассчитываемое значение градиента кривизны. + \en The calculated value of the curvature gradient. \~ + \ingroup Algorithms_3D +*/ +MATH_FUNC( void ) VNormalCurvature( const MbSurface & surf, + const MbCartPoint & pnt, + double & func, + MbVector * der = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точки поверхности, в которых выбранная кривизна принимает наибольшие по модулю значения. + \en Find the points of the surface at which the selected curvature takes the largest in modulus values. \~ + \details \ru Ищутся точки, в которых выбранная кривизна принимает на поверхности наибольшее положительное и наименьшее отрицательное значение. + \en Looks for points at which the selected curvature takes on the surface the greatest positive and least negative value. \~ + \param[in] surf - \ru Исследуемая поверхность. + \en Test surface. \~ + \param[in] func - \ru Функция расчета кривизны в точке. + \en The function of calculating the curvature at a point. \~ + \param[out] maxNegCurv - \ru Наибольшее по модулю отрицательное значение кривизны (0, если нет такого). + \en The largest in modulus value negative curvature (0, if there is no such). \~ + \param[out] maxNegLoc - \ru Точка, в которой кривизна принимает наибольшее по модулю отрицательное значение. + \en The point at which the curvature takes the largest in modulus negative value. \~ + \param[out] maxPosCurv - \ru Наибольшее положительное значение кривизны (0, если нет такого). + \en The greatest positive value of curvature (0, if there is no such). \~ + \param[out] maxPosLoc - \ru Точка, в которой кривизна принимает наибольшее положительное значение. + \en The point at which the curvature takes the most positive value. \~ + \param[in] method - \ru Алгоритм поиска экстремумов. + \en Extremum search algorithm. \~ + \ingroup Algorithms_3D +*/ +MATH_FUNC( void ) SurfaceMinMaxCurvature( const MbSurface & surface, + SurfaceFunction func, + double & maxNegCurv, + MbCartPoint & maxNegLoc, + double & maxPosCurv, + MbCartPoint & maxPosLoc, + MbeExtremsSearchingMethod method = esm_LineSegregation ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точки оболочки, в которых выбранная кривизна принимает наибольшие по модулю значения. + \en Find the points of the shell at which the selected curvature takes the most modulo values. \~ + \details \ru Ищутся точки на оболочке, в которых выбранная кривизна принимают наибольшее положительное и наименьшее отрицательное значение. + \en Finds points on the shell at which the selected curvature takes the largest positive and lowest negative values. \~ + \param[in] faces - \ru Грани оболочки. + \en Faces of the shell. \~ + \param[in] func - \ru Функция расчета кривизны в точке. + \en The function of calculating the curvature at a point. \~ + \param[out] maxNegCurv - \ru Наибольшее по модулю отрицательное значение кривизны (0, если нет такого). + \en The largest in modulus value negative curvature (0, if there is no such). \~ + \param[out] maxNegFace - \ru Грань, в которой кривизна принимает наибольшее по модулю отрицательное значение. + \en The face at which the curvature takes the largest in modulus negative value. \~ + \param[out] maxNegLoc - \ru Точка, в которой кривизна принимает наибольшее по модулю отрицательное значение. + \en The point at which the curvature takes the largest in modulus negative value. \~ + \param[out] maxPosCurv - \ru Наибольшее положительное значение кривизны (0, если нет такого). + \en The greatest positive value of curvature (0, if there is no such). \~ + \param[out] maxPosFace - \ru Грань, в которой кривизна принимает наибольшее положительное значение. + \en The face at which the curvature takes the most positive value. \~ + \param[out] maxPosLoc - \ru Точка, в которой кривизна принимает наибольшее положительное значение. + \en The point at which the curvature takes the most positive value. \~ + \param[in] borderControl - \ru Учитывать границы граней при поиске экстремумов. + \en Take into account the boundaries of the faces when searching for extrema. \~ + \param[in] method - \ru Алгоритм поиска экстремумов. + \en Extremum search algorithm. \~ + \ingroup Algorithms_3D +*/ +MATH_FUNC( void ) FacesMinMaxCurvature( const RPArray & faces, + SurfaceFunction func, + double & maxNegCurv, + MbFace *& maxNegFace, + MbCartPoint & maxNegLoc, + double & maxPosCurv, + MbFace *& maxPosFace, + MbCartPoint & maxPosLoc, + bool borderControl = false, + MbeExtremsSearchingMethod method = esm_LineSegregation ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точки на поверхности, в которых главные нормальные кривизны принимают наибольшие по модулю значения. + \en Find the points on the surface at which the major normal curvatures take the largest values in the module. \~ + \details \ru Ищутся точки на поверхности, в которых главные нормальные кривизны принимают наибольшее положительное и наименьшее отрицательное значение. + \en Looks for points on the surface at which the major normal curvatures take the largest positive and smallest negative values. \~ + \param[in] surf - \ru Исследуемая поверхность. + \en Test surface. \~ + \param[out] maxNegCurv - \ru Наибольшее по модулю отрицательное значение кривизны (0, если нет такого). + \en The largest in modulus value negative curvature (0, if there is no such). \~ + \param[out] maxNegLoc - \ru Точка, в которой кривизна принимает наибольшее по модулю отрицательное значение. + \en The point at which the curvature takes the largest in modulus negative value. \~ + \param[out] maxPosCurv - \ru Наибольшее положительное значение кривизны (0, если нет такого). + \en The greatest positive value of curvature (0, if there is no such). \~ + \param[out] maxPosLoc - \ru Точка, в которой кривизна принимает наибольшее положительное значение. + \en The point at which the curvature takes the most positive value. \~ + \param[in] method - \ru Алгоритм поиска экстремумов. + \en Extremum search algorithm. \~ + \ingroup Algorithms_3D +*/ +MATH_FUNC( void ) SurfaceMinMaxCurvature( const MbSurface & surface, + double & maxNegCurv, + MbCartPoint & maxNegLoc, + double & maxPosCurv, + MbCartPoint & maxPosLoc, + MbeExtremsSearchingMethod method = esm_LineSegregation ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точки на оболочке, в которых главные нормальные кривизны принимают наибольшие по модулю значения. + \en Find the points on the shell at which the major normal curvatures take the largest values in the module. \~ + \details \ru Ищутся точки на оболочке, в которых главные нормальные кривизны принимают наибольшее положительное и наименьшее отрицательное значение. + \en Looks for points on the shell at which the major normal curvatures take the largest positive and smallest negative values. \~ + \param[in] faces - \ru Грани оболочки. + \en Faces of the shell. \~ + \param[out] maxNegCurv - \ru Наибольшее по модулю отрицательное значение кривизны (0, если нет такого). + \en The largest in modulus value negative curvature (0, if there is no such). \~ + \param[out] maxNegFace - \ru Грань, в которой кривизна принимает наибольшее по модулю отрицательное значение. + \en The face at which the curvature takes the largest in modulus negative value. \~ + \param[out] maxNegLoc - \ru Точка, в которой кривизна принимает наибольшее по модулю отрицательное значение. + \en The point at which the curvature takes the largest in modulus negative value. \~ + \param[out] maxPosCurv - \ru Наибольшее положительное значение кривизны (0, если нет такого). + \en The greatest positive value of curvature (0, if there is no such). \~ + \param[out] maxPosFace - \ru Грань, в которой кривизна принимает наибольшее положительное значение. + \en The face at which the curvature takes the most positive value. \~ + \param[out] maxPosLoc - \ru Точка, в которой кривизна принимает наибольшее положительное значение. + \en The point at which the curvature takes the most positive value. \~ + \param[in] borderControl - \ru Учитывать границы граней при поиске экстремумов. + \en Take into account the boundaries of the faces when searching for extrema. \~ + \param[in] method - \ru Алгоритм поиска экстремумов. + \en Extremum search algorithm. \~ + \ingroup Algorithms_3D +*/ +MATH_FUNC( void ) FacesMinMaxCurvature( const RPArray & faces, + double & maxNegCurv, + MbFace *& maxNegFace, + MbCartPoint & maxNegLoc, + double & maxPosCurv, + MbFace *& maxPosFace, + MbCartPoint & maxPosLoc, + bool borderControl = false, + MbeExtremsSearchingMethod method = esm_LineSegregation ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Ориентированная кривизна для плоской кривой. + \en Oriented curvature for a plane curve. \~ + \details \ru Для плоской кривой функция возвращает кривизну в точке, ориентированную относительно нормали плоскости, + в которой она лежит. Для неплоской кривой функция просто возвращает кривизну в точке. + \en For a flat curve, the function returns the curvature at a point oriented relative to the normal to the plane, + in which she lies. For a non-flat curve, the function simply returns the curvature at the point. \~ + \param[in] curve - \ru Исследуемая кривая. + \en Test curve. \~ + \param[in] param - \ru Параметр на кривой. + \en Parameter on the curve. \~ + \param[in] planeNorm - \ru Нормаль плоскости, в которой лежит кривая. Если нормаль не передается в функцию, алгоритм самостоятельно + выполняет проверку, лежит ли кривая в плоскости, и вычисляет нормаль, если проверка выполняется. + \en The normal of the plane in which the curve lies. If the normal is not passed to the function, the algorithm itself + checks if the curve is in the plane and calculates normal if the test is being performed. \~ + \return \ru Возвращается значение ориентированной кривизны в точке. + \en The value of the oriented curvature at the point is returned. \~ + + \ingroup Algorithms_3D +*/ +MATH_FUNC( double ) CurveOrientedCurvature( const MbCurve3D & curve, + double & param, + const MbVector3D * planeNorm = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точки на кривой, в которых кривизна принимает наибольшее и наименьшее значения. + \en Find the points on the curve at which the curvature takes the largest and smallest values. \~ + \details \ru Для плоской кривой наибольшее и наименьшее значение может уходить в отрицательную область. + Для неплоской кривой наибольшее и наименьшее значение всегда неотрицательны. + \en For a flat curve, the largest and smallest value may go into the negative region. + For a non-planar curve, the largest and smallest values are always non-negative. \~ + \param[in] curve - \ru Исследуемая кривая. + \en Test curve. \~ + \param[out] maxCurv - \ru Наибольшее значение кривизны. + \en The greatest value of curvature. \~ + \param[out] maxParam - \ru Точка, в которой кривизна принимает наибольшее значение. + \en The point at which the curvature takes the largest value. \~ + \param[out] minCurv - \ru Наименьшее значение кривизны. + \en The smallest value of curvature. \~ + \param[out] minParam - \ru Точка, в которой кривизна принимает наибольшее значение. + \en The point at which the curvature takes the smallest value. \~ + \param[out] bendPoints - \ru Mассив параметров точек перегиба. + \en Array of parameters of bend points. \~ + \param[out] maxPoints - \ru Mассив параметров, в которых достигается локальный максимум кривизны по модулю. + \en An array of parameters in which the local maximum curvature modulo is reached. \~ + \param[out] minPoints - \ru Mассив параметров, в которых достигается локальный минимум кривизны по модулю. + \en An array of parameters in which the local minimum curvature modulo is reached. \~ + \param[out] rapPoints - \ru Mассив параметров, в которых кривизна терпит разрыв. + Для каждого разрыва вставляются две точки, до и после. + \en Array of parameters in which curvature breaks. + For each break two points are inserted, before and after. \~ + \ingroup Algorithms_3D +*/ +MATH_FUNC( void ) CurveMinMaxCurvature( const MbCurve3D & curve, + double & maxCurv, + double & maxParam, + double & minCurv, + double & minParam, + c3d::DoubleVector * bendPoints = NULL, + c3d::DoubleVector * maxPoints = NULL, + c3d::DoubleVector * minPoints = NULL, + c3d::DoublePairsVector * rapPoints = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Направление максимальной нормальной кривизны поверхности. + \en The direction of the maximum normal surface curvature. \~ + \details \ru Вычисляется направление на поверхности, в котором нормальная кривизна поверхности принимает максимальное значение по модулю. + \en The direction on the surface is calculated in which the normal curvature of the surface takes a maximum absolute value. \~ + \param[in] surf - \ru Поверхность. + \en Surface. \~ + \param[in] pnt - \ru Точка расчета. + \en Point of calculation. \~ + \param[out] dir - \ru Рассчитываемое направление. + \en The calculated direction. \~ + \ingroup Algorithms_3D +*/ +MATH_FUNC( void ) SurfaceMaxCurvatureDirection( const MbSurface & surf, + const MbCartPoint & pnt, + MbVector & dir ); + +#endif // __ACTION_CURVATURE_ANALYSIS_H diff --git a/C3d/Include/action_b_shaper.h b/C3d/Include/action_b_shaper.h index 208f375..42be300 100644 --- a/C3d/Include/action_b_shaper.h +++ b/C3d/Include/action_b_shaper.h @@ -1,396 +1,457 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Методы преобразования полигональных геометрических объектов в объекты BRep. - \en Functions for conversion of the polygonal geometric object to BRep objects. \~ - \details \ru Методы преобразования полигональных геометрических объектов в объекты BRep. - \en Functions for conversion of the polygonal geometric object to BRep objects. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ACTION_B_SHAPER_H -#define __ACTION_B_SHAPER_H - - -#include -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbPlacement3D; -class MATH_CLASS MbMesh; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbSurface; -class MATH_CLASS MbSNameMaker; -class MATH_CLASS MbFace; -class MATH_CLASS MbCollection; - - -//------------------------------------------------------------------------------ -/** \brief \ru Режим распознавания поверхностей. - \en Surface reconstruction mode. \~ - \details \ru Режим распознавания поверхностей. - \en Surface reconstruction mode. \~ - \ingroup Polygonal_Objects -*/ -// --- -enum MbeSurfReconstructMode -{ - srm_All = 0, ///< \ru Строить все поверхности. \en Build all surfaces. - srm_NoGrids = 1, ///< \ru Не строить поверхности на базе триангуляции. \en Not build surfaces based on triangulation. - srm_CanonicOnly = 2, ///< \ru Строить только элементарные поверхности. \en Build elementary surfaces only. - srm_Default = srm_NoGrids ///< \ru Режим по умолчанию. \en Default mode. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Параметры построения оболочки тела по полигональной сетке. - \en Parameters of BRep shell construction from polygonal mesh. \~ - \ingroup Polygonal_Objects -*/ -// --- -class MATH_CLASS MbMeshProcessorValues { -public: - - /** \brief \ru Использовать относительную точность (true). - \en Use relative tolerance (true). \~ - \details \ru При использовании относительной точности отклонение граней тела от сетки проверяется относительно размера модели. - \en While use of relative tolerance distance from shell to mesh is checked relative to model size. \~ - */ - bool useRelativeTolerance; - - /** \brief \ru Точность. - \en Tolerance. \~ - \details \ru Точность работы метода: допустимое отклонение граней тела от вершин сетки. - \en Tolerance: maximum distance from BRep faces to mesh vertices. \~ - */ - double tolerance; - - /** \brief \ru Режим распознавания поверхностей. - \en Surface reconstruction mode. \~ - */ - MbeSurfReconstructMode surfReconstructMode; - - /// \ru Конструктор по умолчанию. \en Default constructor. - explicit MbMeshProcessorValues( bool useRelTol = true, double tol = 0.01, MbeSurfReconstructMode mode = srm_Default ) - : useRelativeTolerance( useRelTol ) - , tolerance ( tol ) - , surfReconstructMode ( mode ) - {} -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Класс для создания оболочки в граничном представлении по полигональной сетке. - \en Class for creating a BRep shell by polygonal mesh. \~ - \details \ru Предоставить интерфейс для управления преобразованием сетки в - оболочку в граничном представлении. \n - \en Provide an interface for managing of "Mesh to BRep" conversion. \n \~ - \ingroup Polygonal_Objects -*/ -// --- -class MATH_CLASS MbMeshProcessor : public MbRefItem -{ -protected: - /// \ru Конструктор. \en Constructor. - MbMeshProcessor(); - -public: - /** \brief \ru Создать экземпляр процессора по коллекции. - \en Create mesh processor by collection. \~ - \details \ru Создать экземпляр процессора по коллекции. Пользователь должен сам удалить объект. - \en Create mesh processor by collection. User must delete created object. \~ - \param[in] collection - \ru Входная коллекция, содержащая треугольную сетку. \n - \en Input collection containing triangle mesh. \~ - \return \ru Возвращает указатель на созданный объект. - \en Returns pointer to created object. \~ - \ingroup Polygonal_Objects - */ - static MbMeshProcessor * Create( const MbCollection & collection ); - - /// \ru Деструктор. \en Destructor. - virtual ~MbMeshProcessor(); - - /** \brief \ru Установить относительную точность. - \en Set relative tolerance. \~ - \details \ru Установить относительную точность по габаритам текущей сетки. - \en Set relative tolerance by current mesh box. \~ - \param[in] tolerance - \ru Относительная точность. \n - \en Relative tolerance to set. \~ - \ingroup Polygonal_Objects - */ - virtual void SetRelativeTolerance( double tolerance ) = 0; - - /** \brief \ru Установить точность. - \en Set tolerance. \~ - \details \ru Установить точность распознавания поверхностей и расширения сегментов сетки. - Метод должен быть вызван перед вызовом SegmentMesh. - Точность по умолчанию равна 0.1. - \en Set tolerance of surface reconstruction and segments extension. - Method should be called before call to SegmentMesh. - Default tolerance is 0.1. \n \~ - \param[in] tolerance - \ru Точность. \n - \en Tolerance to set. \~ - \ingroup Polygonal_Objects - */ - virtual void SetTolerance( double tolerance ) = 0; - - /** \brief \ru Получить точность. - \en Get tolerance. \~ - \details \ru Получить текущую точность, используемую при распознавании поверхностей и расширения сегментов сетки. - \en Get current tolerance used in surface reconstruction and segments extension. \~ - \return \ru Возвращает абсолютную точность. - \en Returns absolute tolerance. \~ - \ingroup Polygonal_Objects - */ - virtual double GetTolerance() const = 0; - - //------------------------------------------------------------------------------ - /** \brief \ru Установить режим распознавания поверхностей. - \en Set the surfaces reconstruction mode. \~ - \details \ru Задать типы поверхностей, генерируемых на сегментах. Поверхности неподдерживаемых типов строиться не будут. \n - \en Set types of surfaces which will be generated on segments. The surfaces of unsupoprted type will not be built. \n \~ - \param[in] mode - \ru Режим распознавания поверхностей. - \en Surface reconstruction mode. - \ingroup Polygonal_Objects - */ - virtual void SetReconstructionMode( MbeSurfReconstructMode mode ) = 0; - - //------------------------------------------------------------------------------ - /** \brief \ru Установить флаг сглаживания входной сетки. - \en Set flag to use smoothing of input mesh. \~ - \details \ru Установить флаг сглаживания входной сетки. Если флаг установлен в true, - то перед запуском основного алгоритма сегментации будет выполнено сглаживание входной сетки. - Рекомендуется использовать сглаживание на неточных сетках, например, полученных методом сканирования. \n - \en Set flag to use smoothing of input mesh. If the flag set to true, then run smoothing of input mesh - before main segmentation algorithm start. - It is recommended to use mesh smoothing on inexact meshes, e.g. meshes obtained by scanning. \n \~ - \param[in] useSmoothing - \ru Флаг использования сглаживания входной сетки. По-умолчанию false. - \en The flag to use smoothing of input mesh. Default false. - \ingroup Polygonal_Objects - */ - // --- - virtual void SetUseMeshSmoothing( bool useSmoothing ) = 0; - - /** \brief \ru Получить исправленную (упрощенную) копию входной полигональной сетки. - \en Get fixed (simplified) copy of the input mesh. \~ - \details \ru Получить исправленную копию входной сетки, на которой выполняются операции MbMeshProcessor: - подсчет кривизн, сегментация, построение оболочки. Все индексы в выходных данных соответствуют - индексам вершин и треугольников упрощенной сетки, возвращаемой данным методом. \n - \en Get fixed copy of the input mesh. All further operations of MbMehsProcessor are - performed for simplified mesh: curvature calculation, segmentation, shell creation. - All indices in the output of these operations corresponds to indices of vertices and - triangles of the simplified mesh returned from this function. \n \~ - \return \ru Возвращает исправленную версию входной полигональной сетки. - \en Returns a fixed version of the input mesh. \~ - \ingroup Polygonal_Objects - */ - virtual const MbCollection & GetSimplifiedMesh() = 0; - - /** \brief \ru Получить сегментированную копию входной полигональной сетки. - \en Get segmented copy of the input mesh. \~ - \details \ru Получить сегменитрованную копию входной сетки, на которой выполняются операции MbMeshProcessor: - подсчет кривизн, сегментация, построение оболочки. - Сегментация доступна внутри коллекции. \n - \en Get segmented copy of the input mesh. All further operations of MbMehsProcessor are - performed for simplified mesh: curvature calculation, segmentation, shell creation. - Segmentation is stored inside collection. \n \~ - \return \ru Возвращает сегментированную версию входной полигональной сетки. - \en Returns a segmented version of the input mesh. \~ - \ingroup Polygonal_Objects - */ - virtual const MbCollection & GetSegmentedMesh() = 0; - - /** \brief \ru Рассчитать главные кривизны и главные направления изменения кривизн в точках сетки. - \en Calculate the principal curvatures and principal curvature directions at mesh points. \~ - \details \ru Рассчитать главные кривизны и главные направления изменения кривизн в точках сетки. \n - \en Calculate the principal curvatures and principal curvature directions at mesh points. \n \~ - \return \ru Возвращает главные кривизны и главные направления в точках сетки. - \en Returns principal curvatures and principal curvature directions at mesh points. \~ - \ingroup Polygonal_Objects - */ - virtual const std::vector & CalculateCurvatures() = 0; - - /** \brief \ru Сегментровать полигональную сетку. - \en Segment a polygonal mesh. \~ - \details \ru Выполнить сегментацию полигональной сетки. \n - \en Perform segmentation of a polygonal mesh. \n \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \param[in] createSurfaces - \ru Создавать ли поверхности на сегментах. - \en Create surfaces on segments or not. \~ - \ingroup Polygonal_Objects - */ - virtual MbResultType SegmentMesh( bool createSurfaces = true ) = 0; - - /** \brief \ru Создать оболочку. - \en Create shell. \~ - \details \ru Создать оболочку в граничном представлении, соответствующее модели, заданной полигональной сеткой. - Используется текущая сегментация. - Если сегментация не была вычислена, но вычисляется автоматическая сегментация (с параметрами по умолчанию). \n - \en Create BRep shell that represents input mesh model. - Current segmentation is used. - If segmentation is not computed yet, then automatic segmentation is performed (with default paramters). \n \~ - \param[out] pShell - \ru Указатель на созданную оболочку. - \en The pointer to created shell. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Polygonal_Objects - */ - virtual MbResultType CreateBRepShell( MbFaceShell *& pShell ) = 0; - - /** \brief \ru Вписать поверхность. - \en Fit surface to segment . \~ - \details \ru Распознать поверхность по сегменту сетки с заданным индексом. - Распознанная поверхность может быть получена с помощью метода GetSegmentSurface. \n - \en Recognize surface for mesh segment with a given index. - Recognized surface is available through GetSegmentSurface method. \n \~ - \param[in] idxSegment - \ru Индекс сегмента полигональной сетки. - \en Index of a mesh segment. \~ - \ingroup Polygonal_Objects - */ - virtual void FitSurfaceToSegment( size_t idxSegment ) = 0; - - /** \brief \ru Вписать поверхность заданного типа. - \en Fit surface of a given type to a segment. \~ - \details \ru Построить поверхность заданного типа, аппроксимирующиую сегмент сетки с заданным индексом. - Распознанная поверхность может быть получена с помощью метода GetSegmentSurface. \n - \en Find surface of a given type approximating mesh segment with a given index. - Recognized surface is available through GetSegmentSurface method. \n \~ - \param[in] idxSegment - \ru Индекс сегмента полигональной сетки. - \en Index of a mesh segment. \~ - \param[in] surfaceType - \ru Тип вписываемой поверхности. - \en Type of fitted surface. \~ - \ingroup Polygonal_Objects - */ - virtual void FitSurfaceToSegment( size_t idxSegment, MbeSpaceType surfaceType ) = 0; - - /** \brief \ru Получить поверхность для сегмента. - \en Get surface of segment. \~ - \details \ru Получить поверхность, вписанную в сегмент. - Чтобы поверхность была определена предварительно должны быть вызваны методы - SegmentMesh или FitSurfaceToSegment. - Распознанная поверхность с помощью метода GetSegmentSurface. \n - \en Get surface that approximates segment. - To fit surface use corresponding methods SegmentMesh or FitSurfaceToSegment. \n \~ - \param[in] idxSegment - \ru Индекс сегмента полигональной сетки. - \en Index of a mesh segment. \~ - \return \ru Возвращает указатель на поверхность для сегмента, если поверхность определена, иначе - NULL. - \en Returns pointer to segment surface if it exists, else - NULL. \~ - \ingroup Polygonal_Objects - */ - virtual const MbSurface * GetSegmentSurface( size_t idxSegment ) const = 0; - - /** \brief \ru Очистить сегментацию полигональной сетки. - \en Reset segmentation of the polygonal mesh. \~ - \details \ru Очистить сегментацию полигональной сетки, хранящуюся внутри MbMeshProcessor. \n - \en Reset segmentation of the polygonal mesh stored inside MbMeshProcessor. \n \~ - \ingroup Polygonal_Objects - */ - virtual void ResetSegmentation() = 0; - - /** \brief \ru Найти ближайший путь между двумя вершинами коллекции. - \en Find shortest path between two vertices. \~ - \details \ru Найти ближайший путь, проходящий по вершинам и ребрам коллекции, соединяющий две заданные вершины. \n - \en Find shortest path between two vertices. The path should pass through collection vertices and edges. \n \~ - \param[in] v1 - \ru Индекс первой вершины. - \en The index of first vertex. \~ - \param[in] v2 - \ru Индекс второй вершины. - \en The index of second vertex. \~ - \param[out] path - \ru Путь из первой вершины во вторую. - Массив содержит последовательные индексы всех вершин пути. - \en The path from the first vertex to the second one. - The array contains successive indices of path vertices. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Polygonal_Objects - */ - virtual bool FindShortestVertexPath( uint v1, uint v2, std::vector & path ) = 0; - -private: // UNDER DEVELOPMENT - /** \} */ - /** \ru \name Функции для работы с разбиением сетки на сегменты и распознаванием поверхностей для сегментов. - \en \name Functions for editting of mesh segmentation and reconstruction of surfaces for the segments. - \{ */ - - /** \brief \ru Объединить два сегмента в текущей сегментации. - \en Unite two segments in current segmentation. \~ - \details \ru Объединение сегментов в текущей сегментации. - Результат объединения доступен через коллекцию, возвращаемую методом GetSegmentedMesh. \n - \en Union of segments in current mesh segmentation. - Result segmentation is available through collection returned by GetSegmentedMesh. \n \~ - \param[in] firstSegmentIdx - \ru Индекс первого сегмента для объединения. \n - \en Index of the first segment for union. \~ - \param[in] secondSegmentIdx - \ru Индекс второго сегмента для объединения. \n - \en Index of the second segment for union. \~ - \ingroup Polygonal_Objects - */ - virtual void UniteSegments( size_t firstSegmentIdx, size_t secondSegmentIdx ) = 0; - - /** \brief \ru Сегментровать полигональную сетку по разделителям сегментов. - \en Segment a polygonal mesh by segment separators. \~ - \details \ru Выполнить сегментацию полигональной сетки по заданным разделителям сегментов. \n - \en Perform segmentation of a polygonal mesh by segment separators. \n \~ - \param[in] separators - \ru Массив разделителей. - Каждый разделитель содержит путь по вершинам сетки, ребра которого разделяют сегменты. - \en The array of segment separators. - Each separator contains a path by mesh vertices. Edges of that path split mesh to segments. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Polygonal_Objects - */ - virtual MbResultType SegmentMeshBySeparators( const std::vector> & separators ) = 0; - - OBVIOUS_PRIVATE_COPY( MbMeshProcessor ) -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать оболочку по полигональной сетке c автоматическим распознаванием поверхностей. - \en Create shell from mesh with automatic surface reconstruction. \~ - \details \ru Создать оболочку в граничном представлении, соответствующее модели, заданной полигональной сеткой. - Алгоритм в автоматическом режиме распознает и реконструирует грани, соответствующие элементарным - поверхностям (плоскость, цилиндр, сфера, конус, тор). \n - \en Create BRep shell that represents input mesh model. - Algorithm automatically detect and reconstruct faces based on elementary surfaces (plane, cylinder, sphere, cone, torus). \n \~ - \param[in] mesh - \ru Входная сетка. - \en The input mesh. \~ - \param[out] shell - \ru Указатель на созданную оболочку. - \en The pointer to created shell. \~ - \param[in] params - \ru Параметры построения оболочки тела. - \en Parameters of BRep shell construction. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Polygonal_Objects -*/ -// --- -MATH_FUNC( MbResultType ) ConvertMeshToShell( MbMesh & mesh, MbFaceShell *& shell, const MbMeshProcessorValues & params = MbMeshProcessorValues() ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать оболочку по коллекции, содержащей полигональную сетку c автоматическим распознаванием поверхностей. - \en Create shell from collection with automatic surface reconstruction. \~ - \details \ru Создать оболочку в граничном представлении, соответствующую модели, заданной полигональной сеткой. - Алгоритм в автоматическом режиме распознает и реконструирует грани, соответствующие элементарным - поверхностям (плоскость, цилиндр, сфера, конус, тор). \n - \en Create BRep shell that represents input mesh model from collection. - Algorithm automatically detect and reconstruct faces based on elementary surfaces (plane, cylinder, sphere, cone, torus). \n \~ - \param[in] collection - \ru Коллекция, содержащая входную сетку. - \en The input collection. \~ - \param[out] shell - \ru Указатель на созданную оболочку. - \en The pointer to created shell. \~ - \param[in] params - \ru Параметры построения оболочки тела. - \en Parameters of BRep shell construction. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Polygonal_Objects -*/ -// --- -MATH_FUNC( MbResultType ) ConvertCollectionToShell( MbCollection & collection, MbFaceShell *& shell, const MbMeshProcessorValues & params = MbMeshProcessorValues() ); - -#endif // __ACTION_B_SHAPER_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Методы преобразования полигональных геометрических объектов в объекты BRep. + \en Functions for conversion of the polygonal geometric object to BRep objects. \~ + \details \ru Методы преобразования полигональных геометрических объектов в объекты BRep. + \en Functions for conversion of the polygonal geometric object to BRep objects. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ACTION_B_SHAPER_H +#define __ACTION_B_SHAPER_H + + +#include +#include +#include +#include +#include +#include +#include + + +class MATH_CLASS MbPlacement3D; +class MATH_CLASS MbMesh; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbSurface; +class MATH_CLASS MbSNameMaker; +class MATH_CLASS MbFace; +class MATH_CLASS MbCollection; + + +//------------------------------------------------------------------------------ +/** \brief \ru Режим распознавания поверхностей. + \en Surface reconstruction mode. \~ + \details \ru Режим распознавания поверхностей. + \en Surface reconstruction mode. \~ + \ingroup Polygonal_Objects +*/ +// --- +enum MbeSurfReconstructMode +{ + srm_All = 0, ///< \ru Строить все поверхности. \en Build all surfaces. + srm_NoGrids = 1, ///< \ru Не строить поверхности на базе триангуляции. \en Not build surfaces based on triangulation. + srm_CanonicOnly = 2, ///< \ru Строить только элементарные поверхности. \en Build elementary surfaces only. + srm_Default = srm_NoGrids ///< \ru Режим по умолчанию. \en Default mode. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Режим построения модели BRep. + \en BRep creation mode. \~ + \details \ru Режим построения модели BRep. + \en BRep creation mode. \~ + \ingroup Polygonal_Objects +*/ +// --- +enum MbeCreateBRepMode +{ + cbm_Strict = 0, ///< \ru Соседние грани пересекаются по общей кривой. \en Adjancent faces have a common edge. + cbm_Weak = 1, ///< \ru Все ребра каждой из граней - граничные. \en All edges of each face are a boundary edges. + cbm_Default = cbm_Strict ///< \ru Режим по умолчанию. \en Default mode. +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Параметры построения оболочки тела по полигональной сетке. + \en Parameters of BRep shell construction from polygonal mesh. \~ + \ingroup Polygonal_Objects +*/ +// --- +class MATH_CLASS MbMeshProcessorValues { +public: + + /** \brief \ru Использовать относительную точность (true). + \en Use relative tolerance (true). \~ + \details \ru При использовании относительной точности отклонение граней тела от сетки проверяется относительно размера модели. + \en While use of relative tolerance distance from shell to mesh is checked relative to model size. \~ + */ + bool useRelativeTolerance; + + /** \brief \ru Флаг сглаживания краевых ребер. + \en Smoothing flag of boundary edges. \~ + \details \ru Сглаживать краевые ребра результирующей оболочки или строить их кусочно-линейными. + \en Smooth boundary edges of the resultant shell or build them piecewise linear. \~ + */ + bool smoothBoundaryEdges; + + /** \brief \ru Предельное значение угла между соседними внешними ребрами сетки в радианах. + \en The treshold value of angle between two adjanced external edges of mesh (in radians). \~ + \details \ru Предельное значение угла между соседними ребрами сетки используется при построении граничных ребер оболочки: граничные ребра оболочки + будут разделяться в вершинах, где наименьший угол между соседними внешними ребрами сетки менее данного предельного. + \en The treshold value of angle between two adjanced edges of mesh is used for building boundary edges of the shell: + boundary edge will be divied at the vertices at which minimum angle between two adjanced edges of mesh is less + then a given treshold value. \~ + */ + double bAngle; + + /** \brief \ru Точность. + \en Tolerance. \~ + \details \ru Точность работы метода: допустимое отклонение граней тела от вершин сетки. + \en Tolerance: maximum distance from BRep faces to mesh vertices. \~ + */ + double tolerance; + + /** \brief \ru Режим распознавания поверхностей. + \en Surface reconstruction mode. \~ + */ + MbeSurfReconstructMode surfReconstructMode; + + /** \brief \ru Режим построения модели BRep. + \en BRep creation mode. \~ + */ + MbeCreateBRepMode brepCreationMode; + + /// \ru Конструктор по умолчанию. \en Default constructor. + explicit MbMeshProcessorValues( bool useRelTol = true, + bool smoothBoundary = true, + double tol = 0.01, + double angle = M_PI_2, + MbeSurfReconstructMode reconMode = srm_Default, + MbeCreateBRepMode bMode = cbm_Default ) + : useRelativeTolerance( useRelTol ) + , smoothBoundaryEdges ( smoothBoundary ) + , tolerance ( tol ) + , bAngle ( angle ) + , surfReconstructMode ( reconMode ) + , brepCreationMode ( bMode ) + {} +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Класс для создания оболочки в граничном представлении по полигональной сетке. + \en Class for creating a BRep shell by polygonal mesh. \~ + \details \ru Предоставить интерфейс для управления преобразованием сетки в + оболочку в граничном представлении. \n + \en Provide an interface for managing of "Mesh to BRep" conversion. \n \~ + \ingroup Polygonal_Objects +*/ +// --- +class MATH_CLASS MbMeshProcessor : public MbRefItem +{ +protected: + /// \ru Конструктор. \en Constructor. + MbMeshProcessor(); + +public: + /** \brief \ru Создать экземпляр процессора по коллекции. + \en Create mesh processor by collection. \~ + \details \ru Создать экземпляр процессора по коллекции. Пользователь должен сам удалить объект. + \en Create mesh processor by collection. User must delete created object. \~ + \param[in] collection - \ru Входная коллекция, содержащая треугольную сетку. \n + \en Input collection containing triangle mesh. \~ + \return \ru Возвращает указатель на созданный объект. + \en Returns pointer to created object. \~ + \ingroup Polygonal_Objects + */ + static MbMeshProcessor * Create( const MbCollection & collection ); + + /// \ru Деструктор. \en Destructor. + virtual ~MbMeshProcessor(); + + /** \brief \ru Установить относительную точность. + \en Set relative tolerance. \~ + \details \ru Установить относительную точность по габаритам текущей сетки. + \en Set relative tolerance by current mesh box. \~ + \param[in] tolerance - \ru Относительная точность. \n + \en Relative tolerance to set. \~ + \ingroup Polygonal_Objects + */ + virtual void SetRelativeTolerance( double tolerance ) = 0; + + /** \brief \ru Установить точность. + \en Set tolerance. \~ + \details \ru Установить точность распознавания поверхностей и расширения сегментов сетки. + Метод должен быть вызван перед вызовом SegmentMesh. + Точность по умолчанию равна 0.1. + \en Set tolerance of surface reconstruction and segments extension. + Method should be called before call to SegmentMesh. + Default tolerance is 0.1. \n \~ + \param[in] tolerance - \ru Точность. \n + \en Tolerance to set. \~ + \ingroup Polygonal_Objects + */ + virtual void SetTolerance( double tolerance ) = 0; + + /** \brief \ru Получить точность. + \en Get tolerance. \~ + \details \ru Получить текущую точность, используемую при распознавании поверхностей и расширения сегментов сетки. + \en Get current tolerance used in surface reconstruction and segments extension. \~ + \return \ru Возвращает абсолютную точность. + \en Returns absolute tolerance. \~ + \ingroup Polygonal_Objects + */ + virtual double GetTolerance() const = 0; + + //------------------------------------------------------------------------------ + /** \brief \ru Установить режим распознавания поверхностей. + \en Set the surfaces reconstruction mode. \~ + \details \ru Задать типы поверхностей, генерируемых на сегментах. Поверхности неподдерживаемых типов строиться не будут. \n + \en Set types of surfaces which will be generated on segments. The surfaces of unsupoprted type will not be built. \n \~ + \param[in] mode - \ru Режим распознавания поверхностей. + \en Surface reconstruction mode. + \ingroup Polygonal_Objects + */ + virtual void SetReconstructionMode( MbeSurfReconstructMode mode ) = 0; + + + //------------------------------------------------------------------------------ + /** \brief \ru Установить режим построения модели BRep. + \en Set the BRep creation mode. \~ + \details \ru Задать степень связности граней в результирующей модели BRep. \n + \en Set connectivity type of faces of resultant BRep model. \n \~ + \param[in] mode - \ru Режим построения модели BRep. + \en BRep creation mode. + \ingroup Polygonal_Objects + */ + virtual void SetBrepCreationMode( MbeCreateBRepMode mode ) = 0; + + //------------------------------------------------------------------------------ + /** \brief \ru Установить флаг сглаживания входной сетки. + \en Set flag to use smoothing of input mesh. \~ + \details \ru Установить флаг сглаживания входной сетки. Если флаг установлен в true, + то перед запуском основного алгоритма сегментации будет выполнено сглаживание входной сетки. + Рекомендуется использовать сглаживание на неточных сетках, например, полученных методом сканирования. \n + \en Set flag to use smoothing of input mesh. If the flag set to true, then run smoothing of input mesh + before main segmentation algorithm start. + It is recommended to use mesh smoothing on inexact meshes, e.g. meshes obtained by scanning. \n \~ + \param[in] useSmoothing - \ru Флаг использования сглаживания входной сетки. По-умолчанию false. + \en The flag to use smoothing of input mesh. Default false. + \ingroup Polygonal_Objects + */ + // --- + virtual void SetUseMeshSmoothing( bool useSmoothing ) = 0; + + /** \brief \ru Получить исправленную (упрощенную) копию входной полигональной сетки. + \en Get fixed (simplified) copy of the input mesh. \~ + \details \ru Получить исправленную копию входной сетки, на которой выполняются операции MbMeshProcessor: + подсчет кривизн, сегментация, построение оболочки. Все индексы в выходных данных соответствуют + индексам вершин и треугольников упрощенной сетки, возвращаемой данным методом. \n + \en Get fixed copy of the input mesh. All further operations of MbMehsProcessor are + performed for simplified mesh: curvature calculation, segmentation, shell creation. + All indices in the output of these operations corresponds to indices of vertices and + triangles of the simplified mesh returned from this function. \n \~ + \return \ru Возвращает исправленную версию входной полигональной сетки. + \en Returns a fixed version of the input mesh. \~ + \ingroup Polygonal_Objects + */ + virtual const MbCollection & GetSimplifiedMesh() = 0; + + /** \brief \ru Получить сегментированную копию входной полигональной сетки. + \en Get segmented copy of the input mesh. \~ + \details \ru Получить сегменитрованную копию входной сетки, на которой выполняются операции MbMeshProcessor: + подсчет кривизн, сегментация, построение оболочки. + Сегментация доступна внутри коллекции. \n + \en Get segmented copy of the input mesh. All further operations of MbMehsProcessor are + performed for simplified mesh: curvature calculation, segmentation, shell creation. + Segmentation is stored inside collection. \n \~ + \return \ru Возвращает сегментированную версию входной полигональной сетки. + \en Returns a segmented version of the input mesh. \~ + \ingroup Polygonal_Objects + */ + virtual const MbCollection & GetSegmentedMesh() = 0; + + /** \brief \ru Рассчитать главные кривизны и главные направления изменения кривизн в точках сетки. + \en Calculate the principal curvatures and principal curvature directions at mesh points. \~ + \details \ru Рассчитать главные кривизны и главные направления изменения кривизн в точках сетки. \n + \en Calculate the principal curvatures and principal curvature directions at mesh points. \n \~ + \return \ru Возвращает главные кривизны и главные направления в точках сетки. + \en Returns principal curvatures and principal curvature directions at mesh points. \~ + \ingroup Polygonal_Objects + */ + virtual const std::vector & CalculateCurvatures() = 0; + + /** \brief \ru Сегментровать полигональную сетку. + \en Segment a polygonal mesh. \~ + \details \ru Выполнить сегментацию полигональной сетки. \n + \en Perform segmentation of a polygonal mesh. \n \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \param[in] createSurfaces - \ru Создавать ли поверхности на сегментах. + \en Create surfaces on segments or not. \~ + \ingroup Polygonal_Objects + */ + virtual MbResultType SegmentMesh( bool createSurfaces = true ) = 0; + + /** \brief \ru Создать оболочку. + \en Create shell. \~ + \details \ru Создать оболочку в граничном представлении, соответствующее модели, заданной полигональной сеткой. + Используется текущая сегментация. + Если сегментация не была вычислена, то вычисляется автоматическая сегментация (с параметрами по умолчанию). \n + \en Create BRep shell that represents input mesh model. + Current segmentation is used. + If segmentation is not computed yet, then automatic segmentation is performed (with default paramters). \n \~ + \param[out] pShell - \ru Указатель на созданную оболочку. + \en The pointer to created shell. \~ + \param[in] smoothBoundaryEdges - \ru Флаг сглаживания краевых ребер. + \en Smoothing flag of boundary edges. \~ + \param[in] bondThresholdAngle - \ru Предельное значение угла между соседними внешними ребрами сетки в радианах. + \en The treshold value of angle between two adjanced external edges of mesh (in radians). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Polygonal_Objects + */ + virtual MbResultType CreateBRepShell( MbFaceShell *& pShell, bool smoothBoundaryEdges, double bondThresholdAngle ) = 0; + + /** \brief \ru Вписать поверхность. + \en Fit surface to segment . \~ + \details \ru Распознать поверхность по сегменту сетки с заданным индексом. + Распознанная поверхность может быть получена с помощью метода GetSegmentSurface. \n + \en Recognize surface for mesh segment with a given index. + Recognized surface is available through GetSegmentSurface method. \n \~ + \param[in] idxSegment - \ru Индекс сегмента полигональной сетки. + \en Index of a mesh segment. \~ + \ingroup Polygonal_Objects + */ + virtual void FitSurfaceToSegment( size_t idxSegment ) = 0; + + /** \brief \ru Вписать поверхность заданного типа. + \en Fit surface of a given type to a segment. \~ + \details \ru Построить поверхность заданного типа, аппроксимирующиую сегмент сетки с заданным индексом. + Распознанная поверхность может быть получена с помощью метода GetSegmentSurface. \n + \en Find surface of a given type approximating mesh segment with a given index. + Recognized surface is available through GetSegmentSurface method. \n \~ + \param[in] idxSegment - \ru Индекс сегмента полигональной сетки. + \en Index of a mesh segment. \~ + \param[in] surfaceType - \ru Тип вписываемой поверхности. + \en Type of fitted surface. \~ + \ingroup Polygonal_Objects + */ + virtual void FitSurfaceToSegment( size_t idxSegment, MbeSpaceType surfaceType ) = 0; + + /** \brief \ru Получить поверхность для сегмента. + \en Get surface of segment. \~ + \details \ru Получить поверхность, вписанную в сегмент. + Чтобы поверхность была определена предварительно должны быть вызваны методы + SegmentMesh или FitSurfaceToSegment. + Распознанная поверхность с помощью метода GetSegmentSurface. \n + \en Get surface that approximates segment. + To fit surface use corresponding methods SegmentMesh or FitSurfaceToSegment. \n \~ + \param[in] idxSegment - \ru Индекс сегмента полигональной сетки. + \en Index of a mesh segment. \~ + \return \ru Возвращает указатель на поверхность для сегмента, если поверхность определена, иначе - NULL. + \en Returns pointer to segment surface if it exists, else - NULL. \~ + \ingroup Polygonal_Objects + */ + virtual const MbSurface * GetSegmentSurface( size_t idxSegment ) const = 0; + + /** \brief \ru Очистить сегментацию полигональной сетки. + \en Reset segmentation of the polygonal mesh. \~ + \details \ru Очистить сегментацию полигональной сетки, хранящуюся внутри MbMeshProcessor. \n + \en Reset segmentation of the polygonal mesh stored inside MbMeshProcessor. \n \~ + \ingroup Polygonal_Objects + */ + virtual void ResetSegmentation() = 0; + + /** \brief \ru Найти ближайший путь между двумя вершинами коллекции. + \en Find shortest path between two vertices. \~ + \details \ru Найти ближайший путь, проходящий по вершинам и ребрам коллекции, соединяющий две заданные вершины. \n + \en Find shortest path between two vertices. The path should pass through collection vertices and edges. \n \~ + \param[in] v1 - \ru Индекс первой вершины. + \en The index of first vertex. \~ + \param[in] v2 - \ru Индекс второй вершины. + \en The index of second vertex. \~ + \param[out] path - \ru Путь из первой вершины во вторую. + Массив содержит последовательные индексы всех вершин пути. + \en The path from the first vertex to the second one. + The array contains successive indices of path vertices. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Polygonal_Objects + */ + virtual bool FindShortestVertexPath( uint v1, uint v2, std::vector & path ) = 0; + +private: // UNDER DEVELOPMENT + /** \} */ + /** \ru \name Функции для работы с разбиением сетки на сегменты и распознаванием поверхностей для сегментов. + \en \name Functions for editting of mesh segmentation and reconstruction of surfaces for the segments. + \{ */ + + /** \brief \ru Объединить два сегмента в текущей сегментации. + \en Unite two segments in current segmentation. \~ + \details \ru Объединение сегментов в текущей сегментации. + Результат объединения доступен через коллекцию, возвращаемую методом GetSegmentedMesh. \n + \en Union of segments in current mesh segmentation. + Result segmentation is available through collection returned by GetSegmentedMesh. \n \~ + \param[in] firstSegmentIdx - \ru Индекс первого сегмента для объединения. \n + \en Index of the first segment for union. \~ + \param[in] secondSegmentIdx - \ru Индекс второго сегмента для объединения. \n + \en Index of the second segment for union. \~ + \ingroup Polygonal_Objects + */ + virtual void UniteSegments( size_t firstSegmentIdx, size_t secondSegmentIdx ) = 0; + + /** \brief \ru Сегментровать полигональную сетку по разделителям сегментов. + \en Segment a polygonal mesh by segment separators. \~ + \details \ru Выполнить сегментацию полигональной сетки по заданным разделителям сегментов. \n + \en Perform segmentation of a polygonal mesh by segment separators. \n \~ + \param[in] separators - \ru Массив разделителей. + Каждый разделитель содержит путь по вершинам сетки, ребра которого разделяют сегменты. + \en The array of segment separators. + Each separator contains a path by mesh vertices. Edges of that path split mesh to segments. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Polygonal_Objects + */ + virtual MbResultType SegmentMeshBySeparators( const std::vector> & separators ) = 0; + + OBVIOUS_PRIVATE_COPY( MbMeshProcessor ) +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать оболочку по полигональной сетке c автоматическим распознаванием поверхностей. + \en Create shell from mesh with automatic surface reconstruction. \~ + \details \ru Создать оболочку в граничном представлении, соответствующее модели, заданной полигональной сеткой. + Алгоритм в автоматическом режиме распознает и реконструирует грани, соответствующие элементарным + поверхностям (плоскость, цилиндр, сфера, конус, тор). \n + \en Create BRep shell that represents input mesh model. + Algorithm automatically detect and reconstruct faces based on elementary surfaces (plane, cylinder, sphere, cone, torus). \n \~ + \param[in] mesh - \ru Входная сетка. + \en The input mesh. \~ + \param[out] shell - \ru Указатель на созданную оболочку. + \en The pointer to created shell. \~ + \param[in] params - \ru Параметры построения оболочки тела. + \en Parameters of BRep shell construction. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Polygonal_Objects +*/ +// --- +MATH_FUNC( MbResultType ) ConvertMeshToShell( MbMesh & mesh, MbFaceShell *& shell, const MbMeshProcessorValues & params = MbMeshProcessorValues() ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать оболочку по коллекции, содержащей полигональную сетку c автоматическим распознаванием поверхностей. + \en Create shell from collection with automatic surface reconstruction. \~ + \details \ru Создать оболочку в граничном представлении, соответствующую модели, заданной полигональной сеткой. + Алгоритм в автоматическом режиме распознает и реконструирует грани, соответствующие элементарным + поверхностям (плоскость, цилиндр, сфера, конус, тор). \n + \en Create BRep shell that represents input mesh model from collection. + Algorithm automatically detect and reconstruct faces based on elementary surfaces (plane, cylinder, sphere, cone, torus). \n \~ + \param[in] collection - \ru Коллекция, содержащая входную сетку. + \en The input collection. \~ + \param[out] shell - \ru Указатель на созданную оболочку. + \en The pointer to created shell. \~ + \param[in] params - \ru Параметры построения оболочки тела. + \en Parameters of BRep shell construction. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Polygonal_Objects +*/ +// --- +MATH_FUNC( MbResultType ) ConvertCollectionToShell( MbCollection & collection, MbFaceShell *& shell, const MbMeshProcessorValues & params = MbMeshProcessorValues() ); + +#endif // __ACTION_B_SHAPER_H diff --git a/C3d/Include/action_curve.h b/C3d/Include/action_curve.h index 8d4af22..312d158 100644 --- a/C3d/Include/action_curve.h +++ b/C3d/Include/action_curve.h @@ -1,757 +1,764 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Методы построения двумерных кривых. - \en Functions for two-dimensional curves construction. \~ - \details \ru Двумерные кривые могут быть построены с помощью аналитических функций, - по набору точек, на базе других двумерных кривых. - \en Two-dimensional curves can be constructed using analytical functions, - for a point set or on the basis of other two-dimensional curves. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ACTION_CURVE_H -#define __ACTION_CURVE_H - - -#include -#include -#include -#include -#include - - -class MATH_CLASS MbPlacement3D; -class MATH_CLASS MbCurve; -class MATH_CLASS MbContour; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbSurface; -class MATH_CLASS MbSNameMaker; -class MATH_CLASS MbFace; - - -//------------------------------------------------------------------------------ -/** \brief \ru Перечисление способов создания эллипса (окружности) или их дуг в двумерном пространстве. - \en Enumeration of ways to create an ellipse (circle) or their arcs in two-dimensional space. \~ -\ingroup Curve_Modeling -*/ -// --- -enum MbeArcCreateWay -{ - /** - \ru Окружность по центру и радиусу, задается 'center' и радиус в 'c'. - \en Circle by center and radius, set the 'center' and radius in 'c'. - */ - acw_CircleByCenterAndRadius, - - /** - \ru Дуга окружности по центру и двум точкам. - Задается: 'center', две точки в 'points', направление в 'option' (true - по часовой стрелке). - Возвращается: начальный угол дуги в 'а', конечный угол в 'b', радиус в 'c'. - \en Circular arc by center and two points. - Set: 'center', two points in 'points', direction in 'option' (true - clockwise direction). - Return: start angle in 'a', end in 'b', radius in 'c'. - */ - acw_ArcByCenterAnd2Points, - - /** - \ru Дуга окружности по центру и двум углам. - Задается: 'center', начальный угол в 'a', конечный в 'b', радиус в 'c', - направление в 'option' (true - по часовой стрелке). Углы задаются в радианах. - \en Circular arc by center and two angles, - Set: 'center', start angle in 'a', end in 'b', radius in 'c', - direction in 'option' (true - clockwise direction). The angles are given in radians. - */ - acw_ArcByCenterAnd2Angles, - /** - \ru Дуга окружности по трем точкам, заданным в 'points', точки points[0] и points[2] конечные. - Возвращается: начальный угол дуги в 'а', конечный угол в 'b', радиус в 'c'. - \en Circular arc by three points specified in 'points', points[0] and points[2] are the end point. - Return: start angle in 'a', end in 'b', radius in 'c'. - */ - acw_ArcBy3Points, - - /** - \ru Эллипс с заданными полуосями и углом наклона. - Задается 'center', X полуось в 'a', Y полуось в 'b', угол наклона в 'с'. Угол задается в радианах. - \en Ellipse by semiaxes and angle. - Set : 'center', X semiaxis in 'a', Y semiaxis in 'b', angle in 'c'. The angle are given in radians. - */ - acw_EllipseByCenterAndSemiaxis, - - /** - \ru Эллипс по центру и трем точкам на нем. - Задается 'center' и 3 точки в 'points'. - Возвращаются: X полуось в 'a', Y полуось в 'b', угол наклона в 'с'. Угол задается в радианах. - \en Ellipse by centre and three points on ellipse. - Set: 'center', 3 points in 'points'. - Return: X semiaxis in 'a', Y semiaxis in 'b', angle in 'c'. The angle are given in radians. - */ - acw_EllipseByCenterAnd3Points, - - /** - \ru Дуга эллипса, обрезанная двумя лучами из центра к заданным точкам. - Задается 'center' и 2 точки в 'points', X полуось в 'a', Y полуось в 'b', угол наклона в 'с', - направление в 'option' (true - по часовой стрелке). Угол задается в радианах. - \en Elliptical arc is trimmed by two rays, starting from the center and passing through points. - Set: 'center', 2 points in 'points', X semiaxis in 'a', Y semiaxis in 'b', angle in 'c', - direction in 'option' (true - clockwise direction). The angle are given in radians. - */ - acw_EArcByCenterAnd2Points -}; - -//------------------------------------------------------------------------------ -/** \brief \ru Создать прямую. - \en Create a line. \~ - \details \ru Создать прямую по двум точкам. \n - \en Create a line given two points. \n \~ - \param[in] point1 - \ru Первая точка. - \en The first point. \~ - \param[in] point2 - \ru Вторая точка. - \en The second point. \~ - \param[out] result - \ru Прямая. - \en The line. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC (MbResultType) Line( const MbCartPoint & point1, - const MbCartPoint & point2, - MbCurve *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать отрезок прямой. - \en Create a line segment. \~ - \details \ru Создать отрезок прямой по двум точкам. \n - \en Create a line segment given two points. \n \~ - \param[in] point1 - \ru Первая точка. - \en The first point. \~ - \param[in] point2 - \ru Вторая точка. - \en The second point. \~ - \param[out] result - \ru Отрезок. - \en The segment. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC (MbResultType) Segment( const MbCartPoint & point1, - const MbCartPoint & point2, - MbCurve *& result ); - - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать эллипс (окружность) или его дугу указанным способом. - \en Create an ellipse (circle) or an elliptical (circular) arc in the specified way. \~ -\details \ru Создать эллипс (окружность) или его дугу указанным способом.\n Входные параметры интерпретируются в соответствии с выбранным путем создания. - \en Create an ellipse (circle) or an elliptical (circular) arc in the specified way. \n The input parameters are interpreted according to the selected create way. -\param[in] createWay - \ru Способ создания. Определяет как интерпретировать входные параметры. - \en Create way. Defines how to interpret the input parameters.\~ -\param[in] center - \ru Центр - \en Сenter. \~ -\param[in] points - \ru Конечные точки или точки через которые проходит кривая. - \en Endpoints or points through which the curve passes. \~ -\param[in,out] a - \ru Интерпретация параметра зависит от способа создания дуги, см. enum #ArcCreateWay - \en Interpretation of parameter depends on a way of creation of an arc, see enum #ArcCreateWay. \~ -\param[in,out] b - \ru Интерпретация параметра зависит от способа создания дуги, см. enum #ArcCreateWay - \en Interpretation of parameter depends on a way of creation of an arc, see enum #ArcCreateWay. \~ -\param[in,out] с - \ru Интерпретация параметра зависит от способа создания дуги, см. enum #ArcCreateWay - \en Interpretation of parameter depends on a way of creation of an arc, see enum #ArcCreateWay. \~ -\param[in] option - \ru Интерпретация параметра зависит от способа создания дуги, см. enum #ArcCreateWay - \en Interpretation of parameter depends on a way of creation of an arc, see enum #ArcCreateWay. \~ -\param[out] result - \ru Эллипс (окружность) или его дуга. - \en The ellipse (circle) or the elliptical (circular) arc. \~ -\return \ru Возвращает код результата операции. - \en Returns operation result code. \~ -\ingroup Curve_Modeling -*/ -//--- - -MATH_FUNC( MbResultType ) Arc( MbeArcCreateWay createWay, - const MbCartPoint & center, - const std::vector & points, - double & a, double & b, double & c, - bool option, - MbCurve *& result ); - -//------------------------------------------------------------------------------ -/**\attention \ru Функция устарела. Вместо неё применять #Arc. - \en The function is deprecated. Use #Arc instead. \~ -\ingroup Curve_Modeling -*/ -// 2018 -//--- -MATH_FUNC( MbResultType ) Arc( const MbCartPoint & centre, - const SArray & points, - bool curveClosed, double angle, - double & a, double & b, - MbCurve *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кривую, проходящую по набору точек. - \en Create a curve passing through a set of points. \~ - \details \ru Создать кривую, проходящую по набору точек, следующего типа: \n - - curveType == pt_LineSegment - отрезок, \n - - curveType == pt_Arc - окружность или дуга, \n - - curveType == pt_Polyline - ломаная, \n - - curveType == pt_Bezier - кривая Безье, \n - - curveType == pt_CubicSpline - кубический сплайн, \n - - curveType == pt_Hermit - составной кубический сплайн Эрмита, \n - - curveType == pt_Nurbs - неоднородный рациональный B-сплайн четвертого порядка (кубический). \n - \en Create a curve passing through a set of points that has the following type: \n - - curveType == pt_LineSegment - a line segment, \n - - curveType == pt_Arc - a circle or an arc, \n - - curveType == pt_Polyline - a polyline, \n - - curveType == pt_Bezier - a Bezier curve, \n - - curveType == pt_CubicSpline - a cubic spline, \n - - curveType == pt_Hermit - a cubic Hermite spline, \n - - curveType == pt_Nurbs - a nonuniform rational B-spline of fourth order (cubic). \n \~ - \param[in] pointList - \ru Набор точек. - \en A point set. \~ - \param[in] curveClosed - \ru Замкнутость кривой. - \en A curve closedness. \~ - \param[in] curveType - \ru Тип кривой. - \en A curve type. \~ - \param[out] result - \ru Кривая. - \en The curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SplineCurve( const SArray & pointList, - bool curveClosed, MbePlaneType curveType, - MbCurve *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать NURBS-кривую. - \en Create a NURBS-curve. \~ - \details \ru Создать NURBS-кривую, построенную по набору контрольных точек. \n - Контейнер weightList может быть пустым. \n - Контейнер knotList может быть пустым. \n - \en Create a NURBS-curve given a sequence of control points. \n - Container 'weightList' can be empty. \n - Container 'knotList' can be empty. \n \~ - \param[in] pointList - \ru Множество точек. - \en An array of points. \~ - \param[in] weightList - \ru Множество весов. - \en An array of weights. \~ - \param[in] degree - \ru Порядок сплайна. - \en A spline degree. \~ - \param[in] knotList - \ru Множество параметрических узлов (Узловой вектор). - \en An array of parametric knots (A knot vector). \~ - \param[in] curveClosed - \ru Замкнутость кривой. - \en A curve closedness. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve_Modeling -*/ -//--- -MATH_FUNC (MbResultType) NurbsCurve( const SArray & pointList, - const SArray & weightList, size_t degree, - const SArray & knotList, bool curveClosed, - MbCurve *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать копию кривой в виде NURBS. - \en Create a copy of a curve as a NURBS-curve. \~ - \details \ru Создать копию кривой в виде NURBS. \n - \en Create a copy of a curve as a NURBS-curve. \n \~ - \param[in] curve - \ru Исходная кривая. - \en The initial curve. \~ - \param[out] result - \ru Сплайновая копия кривой. - \en The spline copy of the curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve_Modeling -*/ -//--- -MATH_FUNC (MbResultType) NurbsCopy( const MbCurve & curve, - MbCurve *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать правильный многоугольник, вписанный в окружность или описанный вокруг окружности. - \en Create a regular polygon inscribed in a circle or circumscribed around a circle. \~ - \details \ru Создать правильный многоугольник, вписанный в окружность (describe == false) или - описанный вокруг окружности (describe == true) с центром centre, проходящей через point: \n - - при vertexCount == 0 строится окружность с центром centre, проходящая через point, \n - - при vertexCount == 1 строится отрезок c крайними точками centre и point, \n - - при vertexCount == 2 строится прямоугольник со сторонами, параллельными глобальным осям и противоположными вершинами в centre и point, \n - - при vertexCount >= 3 строится правильный многоугольник с заданным числом сторон, - вписанный в окружность (describe == false) или описанный вокруг окружности (describe == true) с центром centre, проходящей через point: \n - \en Create a regular polygon inscribed in a circle (describe == false) or - circumscribed around a circle (describe == true) with the specified centre and passing through the given point: \n - - if vertexCount == 0, a circle with center 'center' passing through 'point' is created, \n - - if vertexCount == 1, a line segment with end points at 'centre' and 'point' is created, \n - - if vertexCount, == 2 a rectangle aligned with the global axes with the opposite vertices at points 'centre' and 'point' is created, \n - - if vertexCount >= 3, a regular polygon is created with a given number of sides, - inscribed in a circle (describe == false) or circumscribed around a circle (describe == true) with the specified centre and passing through the given point: \n \~ - \param[in] centre - \ru Центр фигуры. - \en A figure centre. \~ - \param[in] point - \ru Точка для построения. - \en A point for the curve construction. \~ - \param[in] vertexCount - \ru Количество вершин правильного многоугольника. - \en The number of vertices of a regular polygon. \~ - \param[in] describe - \ru Флаг построения многоугольника: описать вокруг окружности (true), вписать в окружность (false). - \en A polygon construction flag: circumscribe the polygon around the circle, inscribe the polygon in the circle (false). \~ - \param[out] result - \ru Результат построения. - \en The curve creation result. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve_Modeling -*/ -//--- -MATH_FUNC (MbResultType) RegularPolygon( const MbCartPoint & centre, - const MbCartPoint & point, - size_t vertexCount, - bool describe, - MbCurve *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать косинусоиду. - \en Create a cosine curve. \~ - \details \ru Создать косинусоиду по точкам, фазе и длине волны. \n - \en Create a cosine curve given the points, phase and wave length. \n \~ - \param[in] point0 - \ru Начало локальной системы координат (ЛСК). - \en The origin of local coordinate system (LCS). \~ - \param[in] point1 - \ru Точка на оси X ЛСК. - \en A point on the X-axis of LCS. \~ - \param[in] point2 - \ru Точка на оси Y ЛСК. - \en A point on the Y-axis of LCS. \~ - \param[in] phase - \ru Фаза. - \en The phase. \~ - \param[in] waveLength - \ru Длина Волны. - \en The wave length. \~ - \param[out] result - \ru Косинусоида. - \en The cosine curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve_Modeling -*/ -//--- -MATH_FUNC (MbResultType) Cosinusoid( const MbCartPoint & point0, - const MbCartPoint & point1, - const MbCartPoint & point2, - double phase, - double waveLength, - MbCurve *& result ); - -//------------------------------------------------------------------------------ -/** \brief \ru Создать косинусоиду. - \en Create a cosine curve. \~ - \details \ru Создать косинусоиду по точкам, фазе и длине волны. \n - \en Create a cosine curve given the points, phase and wave length. \n \~ - \param[in] origin - \ru Начало локальной системы координат (ЛСК). - \en The origin of local coordinate system (LCS). \~ - \param[in] amplitude - \ru Амплитуда волны. - \en The amplitude of the wave. \~ - \param[in] waveLength - \ru Длина Волны. - \en The wave length. \~ - \param[in] wavesCount - \ru Количество волн. - \en The number of waves. \~ - \param[in] phase - \ru Фаза. - \en The phase. \~ - \param[out] result - \ru Косинусоида. - \en The cosine curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve_Modeling -*/ -//--- -MATH_FUNC (MbResultType) Cosinusoid( const MbCartPoint & origin, - double amplitude, - double waveLength, - double wavesCount, - double phase, - MbCurve *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать составную кривую (контур). - \en Create a composite curve (contour). \~ - \details \ru Создать составную кривую (контур) на базе исходной кривой. \n - \en Create a composite curve (contour) on the basis of the given curve. \n \~ - \param[in] curve - \ru Исходная кривая. - \en The initial curve. \~ - \param[out] result - \ru Контур на основе кривой. - \en The contour created on the basis of the curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve_Modeling -*/ -//--- -MATH_FUNC (MbResultType) CreateContour( MbCurve & curve, - MbContour *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать копию кривой. - \en Create a copy of a curve. \~ - \details \ru Создать копию кривой с заменой некоторых кривых. \n - \en Create a copy of a curve with substitution of some curves. \n \~ - \param[in] curve - \ru Исходная кривая. - \en The initial curve. \~ - \return \ru Возвращает модифицированную копию кривой, если получилось ее создать. - \en Returns a modified copy of the curve if it has been successfully created. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC (MbCurve *) DuplicateCurve( const MbCurve & curve ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать копию контура. - \en Create a copy of a contour. \~ - \details \ru Создать копию контура с заменой некоторых кривых и его модификацией по флагу. - Модификация - слияние подобных кривых и удаление вырожденных. \n - \en Create a copy of a contour with substitution of some curves and its modification according to the flag. - Modification is a merging of similar curves and deleting of degenerate ones. \n \~ - \param[in] cntr - \ru Исходный контур. - \en The initial contour. \~ - \param[in] modifySegments - \ru Флаг разрешения замены и слияния сегментов. - \en The flag determines whether segments can be replaced or merged. \~ - \param[in] names - \ru Именователь, синхронизированный с контуром. - \en An object defining the names synchronized with contour. \~ - \return \ru Возвращает модифицированнную копию контура, если получилось его создать. - \en Returns a modified copy of the contour if it has been successfully created. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC (MbContour *) DuplicateContour( const MbContour & cntr, - bool modifySegments, - MbSNameMaker * names = NULL ); - - -//------------------------------------------------------------------------------ -// Создание эквидистантной кривой. -/** \brief \ru Создать эквидистантную кривую. - \en Create an offset curve. \~ - \details \ru Создать эквидистантную кривую по базовой кривой и смещению в крайних точках. \n - \en Create the offset curve for a given curve with offset in the begin and the end points. \n \~ - \param[in] curve - \ru Базовая кривая. - \en Base curve. \~ - \param[in] offset1 - \ru Смещение в точке Tmin базовой кривой. - \en Offset distance on point Tmin of base curve. \~ - \param[in] offset2 - \ru Смещение в точке Tmax базовой кривой. - \en Offset distance on point Tmax of base curve. \~ - \param[in] type - \ru Тип смещения точек: константный, линейный или кубический. - \en The offset type: constant, or linear, or cubic. \~ - \return \ru Возвращает эквидистантную кривую. - \en Returns the offset curve. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC (MbCurve *) OffsetCurve( const MbCurve & curve, - double offset1, - double offset2, - MbeOffsetType type ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать эквидистантный контур. - \en Create an offset contour. \~ - \details \ru Создать эквидистантный контур к исходному контуру. \n - \en Create the offset contour for a given contour. \n \~ - \param[in] cntr - \ru Исходный контур. - \en The initial contour. \~ - \param[in] rad - \ru Величина эквидистантного смещения. - \en The offset value. \~ - \param[in] xEpsilon - \ru Точность по x. - \en Tolerance in x direction. \~ - \param[in] yEpsilon - \ru Точность по y. - \en Tolerance in y direction. \~ - \param[in] modifySegments - \ru Флаг разрешения замены и слияния сегментов. - \en The flag determines whether segments can be replaced or merged. \~ - \param[in] version - \ru Версия исполнения. - \en The version. \~ - \return \ru Возвращает эквидистантный контур, если получилось его создать. - \en Returns the offset contour if it has been successfully created. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC (MbContour *) OffsetContour( const MbContour & cntr, - double rad, - double xEpsilon, - double yEpsilon, - bool modifySegments, - VERSION version = Math::DefaultMathVersion()/*BUG_61694*/ ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать эквидистантный контур, начинающийся и оканчивающийся на оси вращения. - \en Create an offset contour with start and end points on the rotation axis. \~ - \details \ru Создать незамкнутый эквидистантный контур, начинающийся и оканчивающийся на оси вращения. \n - Cчитается, что, если контур замкнуть, то он будет ориентирован против движения часовой стрелки. \n - \en Create an open offset contour with start and end points on the rotation axis. \n - It is considered that if one closes the contour, it will be oriented counterclockwise. \n \~ - \param[in] cntr - \ru Исходный контур. - \en The initial contour. \~ - \param[in] q1 - \ru Начальная точка оси вращения. - \en The start point of the rotation axis. \~ - \param[in] q2 - \ru Конечная точка оси вращения. - \en The end point of the rotation axis. \~ - \param[in] rad - \ru Величина эквидистантного смещения. - \en The offset value. \~ - \param[in] xEpsilon - \ru Точность по x. - \en Tolerance in x direction. \~ - \param[in] yEpsilon - \ru Точность по y. - \en Tolerance in y direction. \~ - \return \ru Возвращает эквидистантный контур, если получилось его создать. - \en Returns the offset contour if it has been successfully created. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC (MbContour *) AxisOffsetOpenContour( const MbContour & cntr, - const MbCartPoint & q1, - const MbCartPoint & q2, - double rad, - double xEpsilon, - double yEpsilon ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Инициализировать кривую по новым параметрам. - \en Initialize a curve with new parameters. \~ - \details \ru Инициализировать кривую по новым параметрам. \n - \en Initialize a curve with new parameters. \n \~ - \param[in,out] curve - \ru Изменяемая кривая. - \en The curve to be modified. \~ - \param[in] t1 - \ru Новый начальный параметр. - \en A new start parameter. \~ - \param[in] t2 - \ru Новый конечный параметр. - \en A new end parameter. \~ - \param[in] eps - \ru Точность. - \en Tolerance. \~ - \return \ru Возвращает true, если получилось модифицировать кривую. - \en Returns 'true' if the curve has been successfully modified. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC (bool) CurveTrim( MbCurve & curve, - double t1, - double t2, - double eps = METRIC_PRECISION ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Добавить кривую в составную кривую (контур). - \en Add a curve to a composite curve (a contour). \~ - \details \ru Добавить кривую curve в составную кривую (контур) contour. \n - Если toEnd == true, то добавить в конец. \n - Если toEnd == false, то добавить в начало. \n - \en Add a curve to a composite curve (a contour) 'contour'. \n - If toEnd == true, the curve is to be added to the end. \n - If toEnd == true, the curve is to be added to the beginning. \n \~ - \param[in] curve - \ru Добавляемая кривая - \en A curve to be added. \~ - \param[in,out] contour - \ru Модифицируемый контур. - \en A contour to be modified. \~ - \param[in] toEnd - \ru Флаг места добавления кривой. - \en The flag determines the place of the curve in the contour. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve_Modeling -*/ -//--- -MATH_FUNC (MbResultType) AddCurveToContour( MbCurve & curve, - MbContour & contour, - bool toEnd ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти пересечения кривой с плоскостью. - \en Calculate the intersections of a curve and a surface. \~ - \details \ru Найти пересечения кривой с плоскостью. \n - Результат - массив точек или массив двумерных кривых на плоскости. - \en Calculate the intersections of a curve and a surface. \n - The result is an array of points or an array of two-dimensional curves on the plane. \~ - \param[in] curve - \ru Кривая. - \en The curve. \~ - \param[in] place - \ru Система координат плоскости. - \en The plane coordinate system. \~ - \param[out] result - \ru Множество точек на плоскости. - \en The array of points on the plane. \~ - \param[out] resultCurve - \ru Множество кривых на плоскости. - \en The array of curves on the plane. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC (void) CurveSection( const MbCurve3D & curve, - const MbPlacement3D & place, - SArray & result, - RPArray & resultCurve ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти пересечения поверхности с плоскостью. - \en Calculate the intersections of a surface and a plane. \~ - \details \ru Найти пересечения поверхности с плоскостью. \n - Результат - массив кривых на поверхности и двумерных кривых на плоскости. - \en Calculate the intersections of a surface and a plane. \n - The result is an array of curves on the surface and two-dimensional curves on the plane. \~ - \param[in] surface - \ru Поверхность. - \en A surface. \~ - \param[in] place - \ru Система координат плоскости. - \en The plane coordinate system. \~ - \param[out] result - \ru Множество кривых на плоскости. - \en The array of curves on the plane. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC (void) SurfaceSection( const MbSurface & surface, - const MbPlacement3D & place, - RPArray & result, - VERSION version = Math::DefaultMathVersion() ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать двумерный сегмент поверхности проецированием ориентированного ребра. - \en Create a two-dimensional segment on a surface by projection of an oriented edge. \~ - \details \ru Создать двумерный сегмент поверхности проецированием ориентированного ребра. \n - Результатом является двумерная кривая в параметрической области поверхности surface. - \en Create a two-dimensional segment on a surface by projection of an oriented edge. \n - The result is a two-dimensional curve in the parametric domain of the given surface. \~ - \param[in] face - \ru Грань поверхности. - \en A face defined on the surface. \~ - \param[in] loopInd - \ru Номер цикла в грани. - \en The number of a loop in the face. \~ - \param[in] edgeInd - \ru Номер проецируемого ребра в цикле. - \en Index of the edge in the loop to be projected. \~ - \param[in] surface - \ru Поверхность проецирования. - \en A surface to project on. \~ - \param[in] version - \ru Версия изготовления. - \en Version. \~ - \param[out] result - \ru Двумерная кривая. - \en A two-dimensional curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve_Modeling -*/ -//--- -MATH_FUNC (MbResultType) FaceBoundSegment( const MbFace & face, - size_t loopInd, - size_t edgeInd, // \ru Проецируемое ребро грани \en The edge of face to be pojected. - const MbSurface & surface, // \ru На поверхность \en On the surface - VERSION version, - MbCurve *& result ); - -//------------------------------------------------------------------------------ -/** \brief \ru Создать двумерную границу поверхности проецированием пространственной кривой. - \en Create a two-dimension boundary of a surface by projection of a space curve. \~ - \details \ru Создать двумерную границу поверхности проецированием пространственной кривой \n - (предполагается, что пространственные граничные кривые лежат на поверхности). \n - \en Create a two-dimension boundary of a surface by projection of a space curve \n - (the boundary space curves are considered to belong to the surface) \n \~ - \param[in] surface - \ru Поверхность. - \en A surface. \~ - \param[in] spaceCurve - \ru Пространственная кривая. - \en A space curve. \~ - \param[in] version - \ru Версия изготовления. - \en Version. \~ - \param[out] result - \ru Двумерный контур на поверхности. - \en The two-dimensional contour on the surface. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SurfaceBoundContour( const MbSurface & surface, - const MbCurve3D & spaceCurve, - VERSION version, - MbContour *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Скорректировать начальную точку. - \en Correct the start point. \~ - \details \ru Изменить начальную точку кривой на новую.\n - Меняет начальную точку у кривых типа:\n - pt_Nurbs, pt_Hermit, pt_Polyline, pt_Bezier, - pt_CubicSpline, pt_LineSegment, pt_ReparamCurve,\n - или у контура pt_Contour, если первый его сегмент одного из перечисленных типов. - \en Change the start point of curve with a new one.\n - Changes the start point for curves of types:\n - pt_Nurbs, pt_Hermit, pt_Polyline, pt_Bezier, - pt_CubicSpline, pt_LineSegment, pt_ReparamCurve,\n - or for contour pt_Contour if its first segment is of one of the listed types. \~ - \param[in] segment - \ru Изменяемая кривая. - \en The modified curve. \~ - \param[in] p1 - \ru Новая начальная точка. - \en A new start point. \~ - \ingroup Algorithms_2D -*/ -// --- -MATH_FUNC (bool) ChangeFirstPoint( MbCurve * segment, const MbCartPoint & p1 ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Скорректировать конечную точку. - \en Correct the last point. \~ - \details \ru Изменить конечную точку кривой на новую.\n - Меняет начальную точку у кривых типа:\n - pt_Nurbs, pt_Hermit, pt_Polyline, pt_Bezier, - pt_CubicSpline, pt_LineSegment, pt_ReparamCurve,\n - или у контура pt_Contour, если последний его сегмент одного из перечисленных типов. - \en Change the end point of curve with a new one.\n - Changes the end point for curves of types:\n - pt_Nurbs, pt_Hermit, pt_Polyline, pt_Bezier, - pt_CubicSpline, pt_LineSegment, pt_ReparamCurve,\n - or for contour pt_Contour if its last segment is of one of the listed types. \~ - \param[in] segment - \ru Изменяемая кривая. - \en The modified curve. \~ - \param[in] p1 - \ru Новая начальная точка. - \en A new start point. \~ - \ingroup Algorithms_2D -*/ -// --- -MATH_FUNC (bool) ChangeLastPoint( MbCurve * segment, const MbCartPoint & p2 ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Является ли кривая прямолинейной независимо от ее параметризации. - \en Whether the curve is like straight-line regardless of its parameterisation. \~ - \details \ru Является ли кривая прямолинейной независимо от ее параметризации.\n - \en Whether the curve is like straight-line regardless of its parameterisation. \~ - \param[in] curve - \ru Кривая. - \en Curve. \~ - \param[in] eps - \ru Точность. - \en Accuracy. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC (bool) IsLikeStraightLine( const MbCurve & curve, double eps ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Удалить вырожденные сегменты из контура. - \en Delete degenerate segments from contour. \~ - \details \ru Удалить вырожденные сегменты из контура с заменой некоторых кривых и модификацией по флагу. \n - \en Delete degenerate segments from contour with substitution of some curves and its modification according to the flag. \n \~ - \param[in] cntr - \ru Исходный контур. - \en The initial contour. \~ - \param[in] modifySegments - \ru Флаг разрешения замены сегментов. - \en The flag determines whether segments can be replaced. \~ - \param[in] names - \ru Именователь, синхронизированный с контуром. - \en An object defining the names synchronized with contour. \~ - \return \ru Возвращает модифицированнную копию контура, если получилось его создать. - \en Returns a modified copy of the contour if it has been successfully created. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC( MbContour * ) DeleteDegenerateSegments( const MbContour & cntr, - bool modifySegments, - MbSNameMaker * names = NULL ); - - -#endif // __ACTION_CURVE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Методы построения двумерных кривых. + \en Functions for two-dimensional curves construction. \~ + \details \ru Двумерные кривые могут быть построены с помощью аналитических функций, + по набору точек, на базе других двумерных кривых. + \en Two-dimensional curves can be constructed using analytical functions, + for a point set or on the basis of other two-dimensional curves. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ACTION_CURVE_H +#define __ACTION_CURVE_H + + +#include +#include +#include +#include +#include + + +class MATH_CLASS MbPlacement3D; +class MATH_CLASS MbCurve; +class MATH_CLASS MbContour; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbSurface; +class MATH_CLASS MbSNameMaker; +class MATH_CLASS MbFace; + + +//------------------------------------------------------------------------------ +/** \brief \ru Перечисление способов создания эллипса (окружности) или их дуг в двумерном пространстве. + \en Enumeration of ways to create an ellipse (circle) or their arcs in two-dimensional space. \~ +\ingroup Curve_Modeling +*/ +// --- +enum MbeArcCreateWay +{ + /** + \ru Окружность по центру и радиусу, задается 'center' и радиус в 'c'. + \en Circle by center and radius, set the 'center' and radius in 'c'. + */ + acw_CircleByCenterAndRadius, + + /** + \ru Дуга окружности по центру и двум точкам. + Задается: 'center', две точки в 'points', направление в 'option' (true - по часовой стрелке). + Возвращается: начальный угол дуги в 'а', конечный угол в 'b', радиус в 'c'. + \en Circular arc by center and two points. + Set: 'center', two points in 'points', direction in 'option' (true - clockwise direction). + Return: start angle in 'a', end in 'b', radius in 'c'. + */ + acw_ArcByCenterAnd2Points, + + /** + \ru Дуга окружности по центру и двум углам. + Задается: 'center', начальный угол в 'a', конечный в 'b', радиус в 'c', + направление в 'option' (true - по часовой стрелке). Углы задаются в радианах. + \en Circular arc by center and two angles, + Set: 'center', start angle in 'a', end in 'b', radius in 'c', + direction in 'option' (true - clockwise direction). The angles are given in radians. + */ + acw_ArcByCenterAnd2Angles, + /** + \ru Дуга окружности по трем точкам, заданным в 'points', точки points[0] и points[2] конечные. + Возвращается: начальный угол дуги в 'а', конечный угол в 'b', радиус в 'c'. + \en Circular arc by three points specified in 'points', points[0] and points[2] are the end point. + Return: start angle in 'a', end in 'b', radius in 'c'. + */ + acw_ArcBy3Points, + + /** + \ru Эллипс с заданными полуосями и углом наклона. + Задается 'center', X полуось в 'a', Y полуось в 'b', угол наклона в 'с'. Угол задается в радианах. + \en Ellipse by semiaxes and angle. + Set : 'center', X semiaxis in 'a', Y semiaxis in 'b', angle in 'c'. The angle are given in radians. + */ + acw_EllipseByCenterAndSemiaxis, + + /** + \ru Эллипс по центру и трем точкам на нем. + Задается 'center' и 3 точки в 'points'. + Возвращаются: X полуось в 'a', Y полуось в 'b', угол наклона в 'с'. Угол задается в радианах. + \en Ellipse by centre and three points on ellipse. + Set: 'center', 3 points in 'points'. + Return: X semiaxis in 'a', Y semiaxis in 'b', angle in 'c'. The angle are given in radians. + */ + acw_EllipseByCenterAnd3Points, + + /** + \ru Дуга эллипса, обрезанная двумя лучами из центра к заданным точкам. + Задается 'center' и 2 точки в 'points', X полуось в 'a', Y полуось в 'b', угол наклона в 'с', + направление в 'option' (true - по часовой стрелке). Угол задается в радианах. + \en Elliptical arc is trimmed by two rays, starting from the center and passing through points. + Set: 'center', 2 points in 'points', X semiaxis in 'a', Y semiaxis in 'b', angle in 'c', + direction in 'option' (true - clockwise direction). The angle are given in radians. + */ + acw_EArcByCenterAnd2Points +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Создать прямую. + \en Create a line. \~ + \details \ru Создать прямую по двум точкам. \n + \en Create a line given two points. \n \~ + \param[in] point1 - \ru Первая точка. + \en The first point. \~ + \param[in] point2 - \ru Вторая точка. + \en The second point. \~ + \param[out] result - \ru Прямая. + \en The line. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC (MbResultType) Line( const MbCartPoint & point1, + const MbCartPoint & point2, + MbCurve *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать отрезок прямой. + \en Create a line segment. \~ + \details \ru Создать отрезок прямой по двум точкам. \n + \en Create a line segment given two points. \n \~ + \param[in] point1 - \ru Первая точка. + \en The first point. \~ + \param[in] point2 - \ru Вторая точка. + \en The second point. \~ + \param[out] result - \ru Отрезок. + \en The segment. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC (MbResultType) Segment( const MbCartPoint & point1, + const MbCartPoint & point2, + MbCurve *& result ); + + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать эллипс (окружность) или его дугу указанным способом. + \en Create an ellipse (circle) or an elliptical (circular) arc in the specified way. \~ +\details \ru Создать эллипс (окружность) или его дугу указанным способом.\n Входные параметры интерпретируются в соответствии с выбранным путем создания. + \en Create an ellipse (circle) or an elliptical (circular) arc in the specified way. \n The input parameters are interpreted according to the selected create way. +\param[in] createWay - \ru Способ создания. Определяет как интерпретировать входные параметры. + \en Create way. Defines how to interpret the input parameters.\~ +\param[in] center - \ru Центр + \en Сenter. \~ +\param[in] points - \ru Конечные точки или точки через которые проходит кривая. + \en Endpoints or points through which the curve passes. \~ +\param[in,out] a - \ru Интерпретация параметра зависит от способа создания дуги, см. enum #ArcCreateWay + \en Interpretation of parameter depends on a way of creation of an arc, see enum #ArcCreateWay. \~ +\param[in,out] b - \ru Интерпретация параметра зависит от способа создания дуги, см. enum #ArcCreateWay + \en Interpretation of parameter depends on a way of creation of an arc, see enum #ArcCreateWay. \~ +\param[in,out] с - \ru Интерпретация параметра зависит от способа создания дуги, см. enum #ArcCreateWay + \en Interpretation of parameter depends on a way of creation of an arc, see enum #ArcCreateWay. \~ +\param[in] option - \ru Интерпретация параметра зависит от способа создания дуги, см. enum #ArcCreateWay + \en Interpretation of parameter depends on a way of creation of an arc, see enum #ArcCreateWay. \~ +\param[out] result - \ru Эллипс (окружность) или его дуга. + \en The ellipse (circle) or the elliptical (circular) arc. \~ +\return \ru Возвращает код результата операции. + \en Returns operation result code. \~ +\ingroup Curve_Modeling +*/ +//--- + +MATH_FUNC( MbResultType ) Arc( MbeArcCreateWay createWay, + const MbCartPoint & center, + const std::vector & points, + double & a, double & b, double & c, + bool option, + MbCurve *& result ); + +//------------------------------------------------------------------------------ +/**\attention \ru Функция устарела. Вместо неё применять #Arc. + \en The function is deprecated. Use #Arc instead. \~ +\ingroup Curve_Modeling +*/ +// 2018 +//--- +MATH_FUNC( MbResultType ) Arc( const MbCartPoint & centre, + const SArray & points, + bool curveClosed, double angle, + double & a, double & b, + MbCurve *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кривую, проходящую по набору точек. + \en Create a curve passing through a set of points. \~ + \details \ru Создать кривую, проходящую по набору точек, следующего типа: \n + - curveType == pt_LineSegment - отрезок, \n + - curveType == pt_Arc - окружность или дуга, \n + - curveType == pt_Polyline - ломаная, \n + - curveType == pt_Bezier - кривая Безье, \n + - curveType == pt_CubicSpline - кубический сплайн, \n + - curveType == pt_Hermit - составной кубический сплайн Эрмита, \n + - curveType == pt_Nurbs - неоднородный рациональный B-сплайн четвертого порядка (кубический). \n + \en Create a curve passing through a set of points that has the following type: \n + - curveType == pt_LineSegment - a line segment, \n + - curveType == pt_Arc - a circle or an arc, \n + - curveType == pt_Polyline - a polyline, \n + - curveType == pt_Bezier - a Bezier curve, \n + - curveType == pt_CubicSpline - a cubic spline, \n + - curveType == pt_Hermit - a cubic Hermite spline, \n + - curveType == pt_Nurbs - a nonuniform rational B-spline of fourth order (cubic). \n \~ + \param[in] pointList - \ru Набор точек. + \en A point set. \~ + \param[in] curveClosed - \ru Замкнутость кривой. + \en A curve closedness. \~ + \param[in] curveType - \ru Тип кривой. + \en A curve type. \~ + \param[out] result - \ru Кривая. + \en The curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SplineCurve( const SArray & pointList, + bool curveClosed, MbePlaneType curveType, + MbCurve *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать NURBS-кривую. + \en Create a NURBS-curve. \~ + \details \ru Создать NURBS-кривую, построенную по набору контрольных точек. \n + Контейнер weightList может быть пустым. \n + Контейнер knotList может быть пустым. \n + \en Create a NURBS-curve given a sequence of control points. \n + Container 'weightList' can be empty. \n + Container 'knotList' can be empty. \n \~ + \param[in] pointList - \ru Множество точек. + \en An array of points. \~ + \param[in] weightList - \ru Множество весов. + \en An array of weights. \~ + \param[in] degree - \ru Порядок сплайна. + \en A spline degree. \~ + \param[in] knotList - \ru Множество параметрических узлов (Узловой вектор). + \en An array of parametric knots (A knot vector). \~ + \param[in] curveClosed - \ru Замкнутость кривой. + \en A curve closedness. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve_Modeling +*/ +//--- +MATH_FUNC (MbResultType) NurbsCurve( const SArray & pointList, + const SArray & weightList, size_t degree, + const SArray & knotList, bool curveClosed, + MbCurve *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать копию кривой в виде NURBS. + \en Create a copy of a curve as a NURBS-curve. \~ + \details \ru Создать копию кривой в виде NURBS. \n + \en Create a copy of a curve as a NURBS-curve. \n \~ + \param[in] curve - \ru Исходная кривая. + \en The initial curve. \~ + \param[out] result - \ru Сплайновая копия кривой. + \en The spline copy of the curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve_Modeling +*/ +//--- +MATH_FUNC (MbResultType) NurbsCopy( const MbCurve & curve, + MbCurve *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать правильный многоугольник, вписанный в окружность или описанный вокруг окружности. + \en Create a regular polygon inscribed in a circle or circumscribed around a circle. \~ + \details \ru Создать правильный многоугольник, вписанный в окружность (describe == false) или + описанный вокруг окружности (describe == true) с центром centre, проходящей через point: \n + - при vertexCount == 0 строится окружность с центром centre, проходящая через point, \n + - при vertexCount == 1 строится отрезок c крайними точками centre и point, \n + - при vertexCount == 2 строится прямоугольник со сторонами, параллельными глобальным осям и противоположными вершинами в centre и point, \n + - при vertexCount >= 3 строится правильный многоугольник с заданным числом сторон, + вписанный в окружность (describe == false) или описанный вокруг окружности (describe == true) с центром centre, проходящей через point: \n + \en Create a regular polygon inscribed in a circle (describe == false) or + circumscribed around a circle (describe == true) with the specified centre and passing through the given point: \n + - if vertexCount == 0, a circle with center 'center' passing through 'point' is created, \n + - if vertexCount == 1, a line segment with end points at 'centre' and 'point' is created, \n + - if vertexCount, == 2 a rectangle aligned with the global axes with the opposite vertices at points 'centre' and 'point' is created, \n + - if vertexCount >= 3, a regular polygon is created with a given number of sides, + inscribed in a circle (describe == false) or circumscribed around a circle (describe == true) with the specified centre and passing through the given point: \n \~ + \param[in] centre - \ru Центр фигуры. + \en A figure centre. \~ + \param[in] point - \ru Точка для построения. + \en A point for the curve construction. \~ + \param[in] vertexCount - \ru Количество вершин правильного многоугольника. + \en The number of vertices of a regular polygon. \~ + \param[in] describe - \ru Флаг построения многоугольника: описать вокруг окружности (true), вписать в окружность (false). + \en A polygon construction flag: circumscribe the polygon around the circle, inscribe the polygon in the circle (false). \~ + \param[out] result - \ru Результат построения. + \en The curve creation result. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve_Modeling +*/ +//--- +MATH_FUNC (MbResultType) RegularPolygon( const MbCartPoint & centre, + const MbCartPoint & point, + size_t vertexCount, + bool describe, + MbCurve *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать косинусоиду. + \en Create a cosine curve. \~ + \details \ru Создать косинусоиду по точкам, фазе и длине волны. \n + \en Create a cosine curve given the points, phase and wave length. \n \~ + \param[in] point0 - \ru Начало локальной системы координат (ЛСК). + \en The origin of local coordinate system (LCS). \~ + \param[in] point1 - \ru Точка на оси X ЛСК. + \en A point on the X-axis of LCS. \~ + \param[in] point2 - \ru Точка на оси Y ЛСК. + \en A point on the Y-axis of LCS. \~ + \param[in] phase - \ru Фаза. + \en The phase. \~ + \param[in] waveLength - \ru Длина Волны. + \en The wave length. \~ + \param[out] result - \ru Косинусоида. + \en The cosine curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve_Modeling +*/ +//--- +MATH_FUNC (MbResultType) Cosinusoid( const MbCartPoint & point0, + const MbCartPoint & point1, + const MbCartPoint & point2, + double phase, + double waveLength, + MbCurve *& result ); + +//------------------------------------------------------------------------------ +/** \brief \ru Создать косинусоиду. + \en Create a cosine curve. \~ + \details \ru Создать косинусоиду по точкам, фазе и длине волны. \n + \en Create a cosine curve given the points, phase and wave length. \n \~ + \param[in] origin - \ru Начало локальной системы координат (ЛСК). + \en The origin of local coordinate system (LCS). \~ + \param[in] amplitude - \ru Амплитуда волны. + \en The amplitude of the wave. \~ + \param[in] waveLength - \ru Длина Волны. + \en The wave length. \~ + \param[in] wavesCount - \ru Количество волн. + \en The number of waves. \~ + \param[in] phase - \ru Фаза. + \en The phase. \~ + \param[out] result - \ru Косинусоида. + \en The cosine curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve_Modeling +*/ +//--- +MATH_FUNC (MbResultType) Cosinusoid( const MbCartPoint & origin, + double amplitude, + double waveLength, + double wavesCount, + double phase, + MbCurve *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать составную кривую (контур). + \en Create a composite curve (contour). \~ + \details \ru Создать составную кривую (контур) на базе исходной кривой. \n + \en Create a composite curve (contour) on the basis of the given curve. \n \~ + \param[in] curve - \ru Исходная кривая. + \en The initial curve. \~ + \param[out] result - \ru Контур на основе кривой. + \en The contour created on the basis of the curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve_Modeling +*/ +//--- +MATH_FUNC (MbResultType) CreateContour( MbCurve & curve, + MbContour *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать копию кривой. + \en Create a copy of a curve. \~ + \details \ru Создать копию кривой с заменой некоторых кривых. \n + \en Create a copy of a curve with substitution of some curves. \n \~ + \param[in] curve - \ru Исходная кривая. + \en The initial curve. \~ + \return \ru Возвращает модифицированную копию кривой, если получилось ее создать. + \en Returns a modified copy of the curve if it has been successfully created. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC (MbCurve *) DuplicateCurve( const MbCurve & curve ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать копию контура. + \en Create a copy of a contour. \~ + \details \ru Создать копию контура с заменой некоторых кривых и его модификацией по флагу. + Модификация - слияние подобных кривых и удаление вырожденных. \n + \en Create a copy of a contour with substitution of some curves and its modification according to the flag. + Modification is a merging of similar curves and deleting of degenerate ones. \n \~ + \param[in] cntr - \ru Исходный контур. + \en The initial contour. \~ + \param[in] modifySegments - \ru Флаг разрешения замены и слияния сегментов. + \en The flag determines whether segments can be replaced or merged. \~ + \param[in] names - \ru Именователь, синхронизированный с контуром. + \en An object defining the names synchronized with contour. \~ + \return \ru Возвращает модифицированнную копию контура, если получилось его создать. + \en Returns a modified copy of the contour if it has been successfully created. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC (MbContour *) DuplicateContour( const MbContour & cntr, + bool modifySegments, + MbSNameMaker * names = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать эквидистантную кривую. + \en Create an offset curve. \~ + \details \ru Создать эквидистантную кривую по базовой кривой и смещению в крайних точках. \n + \en Create the offset curve for a given curve with offset in the begin and the end points. \n \~ + \param[in] curve - \ru Базовая кривая. + \en Base curve. \~ + \param[in] offset1 - \ru Смещение в точке Tmin базовой кривой. + \en Offset distance on point Tmin of base curve. \~ + \param[in] offset2 - \ru Смещение в точке Tmax базовой кривой. + \en Offset distance on point Tmax of base curve. \~ + \param[in] type - \ru Тип смещения точек: константный, линейный или кубический. + \en The offset type: constant, or linear, or cubic. \~ + \return \ru Возвращает эквидистантную кривую. + \en Returns the offset curve. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC (MbCurve *) OffsetCurve( const MbCurve & curve, + double offset1, + double offset2, + MbeOffsetType type ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать эквидистантный контур. + \en Create an offset contour. \~ + \details \ru Создать эквидистантный контур к исходному контуру. \n + \en Create the offset contour for a given contour. \n \~ + \param[in] cntr - \ru Исходный контур. + \en The initial contour. \~ + \param[in] rad - \ru Величина эквидистантного смещения. + \en The offset value. \~ + \param[in] xEpsilon - \ru Точность по x. + \en Tolerance in x direction. \~ + \param[in] yEpsilon - \ru Точность по y. + \en Tolerance in y direction. \~ + \param[in] modifySegments - \ru Флаг разрешения замены и слияния сегментов. + \en The flag determines whether segments can be replaced or merged. \~ + \param[in] version - \ru Версия исполнения. + \en The version. \~ + \return \ru Возвращает эквидистантный контур, если получилось его создать. + \en Returns the offset contour if it has been successfully created. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC (MbContour *) OffsetContour( const MbContour & cntr, + double rad, + double xEpsilon, + double yEpsilon, + bool modifySegments, + VERSION version = Math::DefaultMathVersion()/*BUG_61694*/ ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать эквидистантный контур, начинающийся и оканчивающийся на оси вращения. + \en Create an offset contour with start and end points on the rotation axis. \~ + \details \ru Создать незамкнутый эквидистантный контур, начинающийся и оканчивающийся на оси вращения. \n + Cчитается, что, если контур замкнуть, то он будет ориентирован против движения часовой стрелки. \n + \en Create an open offset contour with start and end points on the rotation axis. \n + It is considered that if one closes the contour, it will be oriented counterclockwise. \n \~ + \param[in] cntr - \ru Исходный контур. + \en The initial contour. \~ + \param[in] q1 - \ru Начальная точка оси вращения. + \en The start point of the rotation axis. \~ + \param[in] q2 - \ru Конечная точка оси вращения. + \en The end point of the rotation axis. \~ + \param[in] rad - \ru Величина эквидистантного смещения. + \en The offset value. \~ + \param[in] xEpsilon - \ru Точность по x. + \en Tolerance in x direction. \~ + \param[in] yEpsilon - \ru Точность по y. + \en Tolerance in y direction. \~ + \param[in] version - \ru Версия исполнения. + \en The version. \~ + \return \ru Возвращает эквидистантный контур, если получилось его создать. + \en Returns the offset contour if it has been successfully created. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC (MbContour *) AxisOffsetOpenContour( const MbContour & cntr, + const MbCartPoint & q1, + const MbCartPoint & q2, + double rad, + double xEpsilon, + double yEpsilon, + VERSION version );// = Math::DefaultMathVersion() ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Инициализировать кривую по новым параметрам. + \en Initialize a curve with new parameters. \~ + \details \ru Инициализировать кривую по новым параметрам. \n + \en Initialize a curve with new parameters. \n \~ + \param[in,out] curve - \ru Изменяемая кривая. + \en The curve to be modified. \~ + \param[in] t1 - \ru Новый начальный параметр. + \en A new start parameter. \~ + \param[in] t2 - \ru Новый конечный параметр. + \en A new end parameter. \~ + \param[in] eps - \ru Точность. + \en Tolerance. \~ + \return \ru Возвращает true, если получилось модифицировать кривую. + \en Returns 'true' if the curve has been successfully modified. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC (bool) CurveTrim( MbCurve & curve, + double t1, + double t2, + double eps = METRIC_PRECISION ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Добавить кривую в составную кривую (контур). + \en Add a curve to a composite curve (a contour). \~ + \details \ru Добавить кривую curve в составную кривую (контур) contour. \n + Если toEnd == true, то добавить в конец. \n + Если toEnd == false, то добавить в начало. \n + \en Add a curve to a composite curve (a contour) 'contour'. \n + If toEnd == true, the curve is to be added to the end. \n + If toEnd == true, the curve is to be added to the beginning. \n \~ + \param[in] curve - \ru Добавляемая кривая + \en A curve to be added. \~ + \param[in,out] contour - \ru Модифицируемый контур. + \en A contour to be modified. \~ + \param[in] toEnd - \ru Флаг места добавления кривой. + \en The flag determines the place of the curve in the contour. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve_Modeling +*/ +//--- +MATH_FUNC (MbResultType) AddCurveToContour( MbCurve & curve, + MbContour & contour, + bool toEnd ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти пересечения кривой с плоскостью. + \en Calculate the intersections of a curve and a surface. \~ + \details \ru Найти пересечения кривой с плоскостью. \n + Результат - массив точек или массив двумерных кривых на плоскости. + \en Calculate the intersections of a curve and a surface. \n + The result is an array of points or an array of two-dimensional curves on the plane. \~ + \param[in] curve - \ru Кривая. + \en The curve. \~ + \param[in] place - \ru Система координат плоскости. + \en The plane coordinate system. \~ + \param[out] result - \ru Множество точек на плоскости. + \en The array of points on the plane. \~ + \param[out] resultCurve - \ru Множество кривых на плоскости. + \en The array of curves on the plane. \~ + \param[in] version - \ru Версия исполнения. + \en The version. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC (void) CurveSection( const MbCurve3D & curve, + const MbPlacement3D & place, + SArray & result, + RPArray & resultCurve, + VERSION version = Math::DefaultMathVersion() ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти пересечения поверхности с плоскостью. + \en Calculate the intersections of a surface and a plane. \~ + \details \ru Найти пересечения поверхности с плоскостью. \n + Результат - массив кривых на поверхности и двумерных кривых на плоскости. + \en Calculate the intersections of a surface and a plane. \n + The result is an array of curves on the surface and two-dimensional curves on the plane. \~ + \param[in] surface - \ru Поверхность. + \en A surface. \~ + \param[in] place - \ru Система координат плоскости. + \en The plane coordinate system. \~ + \param[out] result - \ru Множество кривых на плоскости. + \en The array of curves on the plane. \~ + \param[in] version - \ru Версия исполнения. + \en The version. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC (void) SurfaceSection( const MbSurface & surface, + const MbPlacement3D & place, + RPArray & result, + VERSION version = Math::DefaultMathVersion() ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать двумерный сегмент поверхности проецированием ориентированного ребра. + \en Create a two-dimensional segment on a surface by projection of an oriented edge. \~ + \details \ru Создать двумерный сегмент поверхности проецированием ориентированного ребра. \n + Результатом является двумерная кривая в параметрической области поверхности surface. + \en Create a two-dimensional segment on a surface by projection of an oriented edge. \n + The result is a two-dimensional curve in the parametric domain of the given surface. \~ + \param[in] face - \ru Грань поверхности. + \en A face defined on the surface. \~ + \param[in] loopInd - \ru Номер цикла в грани. + \en The number of a loop in the face. \~ + \param[in] edgeInd - \ru Номер проецируемого ребра в цикле. + \en Index of the edge in the loop to be projected. \~ + \param[in] surface - \ru Поверхность проецирования. + \en A surface to project on. \~ + \param[in] version - \ru Версия изготовления. + \en Version. \~ + \param[out] result - \ru Двумерная кривая. + \en A two-dimensional curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve_Modeling +*/ +//--- +MATH_FUNC (MbResultType) FaceBoundSegment( const MbFace & face, + size_t loopInd, + size_t edgeInd, // \ru Проецируемое ребро грани \en The edge of face to be pojected. + const MbSurface & surface, // \ru На поверхность \en On the surface + VERSION version, + MbCurve *& result ); + +//------------------------------------------------------------------------------ +/** \brief \ru Создать двумерную границу поверхности проецированием пространственной кривой. + \en Create a two-dimension boundary of a surface by projection of a space curve. \~ + \details \ru Создать двумерную границу поверхности проецированием пространственной кривой \n + (предполагается, что пространственные граничные кривые лежат на поверхности). \n + \en Create a two-dimension boundary of a surface by projection of a space curve \n + (the boundary space curves are considered to belong to the surface) \n \~ + \param[in] surface - \ru Поверхность. + \en A surface. \~ + \param[in] spaceCurve - \ru Пространственная кривая. + \en A space curve. \~ + \param[in] version - \ru Версия изготовления. + \en Version. \~ + \param[out] result - \ru Двумерный контур на поверхности. + \en The two-dimensional contour on the surface. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SurfaceBoundContour( const MbSurface & surface, + const MbCurve3D & spaceCurve, + VERSION version, + MbContour *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Скорректировать начальную точку. + \en Correct the start point. \~ + \details \ru Изменить начальную точку кривой на новую.\n + Меняет начальную точку у кривых типа:\n + pt_Nurbs, pt_Hermit, pt_Polyline, pt_Bezier, + pt_CubicSpline, pt_LineSegment, pt_ReparamCurve,\n + или у контура pt_Contour, если первый его сегмент одного из перечисленных типов. + \en Change the start point of curve with a new one.\n + Changes the start point for curves of types:\n + pt_Nurbs, pt_Hermit, pt_Polyline, pt_Bezier, + pt_CubicSpline, pt_LineSegment, pt_ReparamCurve,\n + or for contour pt_Contour if its first segment is of one of the listed types. \~ + \param[in] segment - \ru Изменяемая кривая. + \en The modified curve. \~ + \param[in] p1 - \ru Новая начальная точка. + \en A new start point. \~ + \ingroup Algorithms_2D +*/ +// --- +MATH_FUNC (bool) ChangeFirstPoint( MbCurve * segment, const MbCartPoint & p1 ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Скорректировать конечную точку. + \en Correct the last point. \~ + \details \ru Изменить конечную точку кривой на новую.\n + Меняет начальную точку у кривых типа:\n + pt_Nurbs, pt_Hermit, pt_Polyline, pt_Bezier, + pt_CubicSpline, pt_LineSegment, pt_ReparamCurve,\n + или у контура pt_Contour, если последний его сегмент одного из перечисленных типов. + \en Change the end point of curve with a new one.\n + Changes the end point for curves of types:\n + pt_Nurbs, pt_Hermit, pt_Polyline, pt_Bezier, + pt_CubicSpline, pt_LineSegment, pt_ReparamCurve,\n + or for contour pt_Contour if its last segment is of one of the listed types. \~ + \param[in] segment - \ru Изменяемая кривая. + \en The modified curve. \~ + \param[in] p1 - \ru Новая начальная точка. + \en A new start point. \~ + \ingroup Algorithms_2D +*/ +// --- +MATH_FUNC (bool) ChangeLastPoint( MbCurve * segment, const MbCartPoint & p2 ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Является ли кривая прямолинейной независимо от ее параметризации. + \en Whether the curve is like straight-line regardless of its parameterisation. \~ + \details \ru Является ли кривая прямолинейной независимо от ее параметризации.\n + \en Whether the curve is like straight-line regardless of its parameterisation. \~ + \param[in] curve - \ru Кривая. + \en Curve. \~ + \param[in] eps - \ru Точность. + \en Accuracy. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC (bool) IsLikeStraightLine( const MbCurve & curve, double eps ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Удалить вырожденные сегменты из контура. + \en Delete degenerate segments from contour. \~ + \details \ru Удалить вырожденные сегменты из контура с заменой некоторых кривых и модификацией по флагу. \n + \en Delete degenerate segments from contour with substitution of some curves and its modification according to the flag. \n \~ + \param[in] cntr - \ru Исходный контур. + \en The initial contour. \~ + \param[in] modifySegments - \ru Флаг разрешения замены сегментов. + \en The flag determines whether segments can be replaced. \~ + \param[in] names - \ru Именователь, синхронизированный с контуром. + \en An object defining the names synchronized with contour. \~ + \return \ru Возвращает модифицированнную копию контура, если получилось его создать. + \en Returns a modified copy of the contour if it has been successfully created. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC( MbContour * ) DeleteDegenerateSegments( const MbContour & cntr, + bool modifySegments, + MbSNameMaker * names = NULL ); + + +#endif // __ACTION_CURVE_H diff --git a/C3d/Include/action_curve3d.h b/C3d/Include/action_curve3d.h index e0201a0..dd300ff 100644 --- a/C3d/Include/action_curve3d.h +++ b/C3d/Include/action_curve3d.h @@ -1,1226 +1,1269 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Методы построения трехмерных кривых. - \en Functions for three-dimensional curves construction. \~ - \details \ru На базе кривых строятся рёбра. Рёбра используются в твёрдотельной и каркасной модели. - Кроме того, кривые используются для построения поверхностей, а также могут служить - вспомогательными элементами модели. - \en Edges are created on the basis of curves. Edges are used in solid and wireframe model. - In addition curves are used for construction of surfaces as well as can be used - as auxiliary elements of a model. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ACTION_CURVE3D_H -#define __ACTION_CURVE3D_H - - -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbCurve; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbNurbs3D; -class MATH_CLASS MbContour3D; -class MATH_CLASS MbFace; -class MATH_CLASS MbSurface; -class MATH_CLASS MbWireFrame; -class MATH_CLASS MbItem; -class MATH_CLASS MbName; -class MATH_CLASS MbSweptData; -struct MATH_CLASS EvolutionValues; - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать прямую. - \en Create a line. \~ - \details \ru Создать прямую по двум точкам. \n - \en Create a line given two points. \n \~ - \param[in] point1 - \ru Первая точка. - \en The first point. \~ - \param[in] point2 - \ru Вторая точка. - \en The second point. \~ - \param[out] result - \ru Прямая. - \en The line. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) Line( const MbCartPoint3D & point1, - const MbCartPoint3D & point2, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать отрезок прямой. - \en Create a line segment. \~ - \details \ru Создать отрезок прямой по двум точкам. \n - \en Create a line segment given two points. \n \~ - \param[in] point1 - \ru Первая точка. - \en The first point. \~ - \param[in] point2 - \ru Вторая точка. - \en The second point. \~ - \param[out] result - \ru Отрезок. - \en The segment. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) Segment( const MbCartPoint3D & point1, - const MbCartPoint3D & point2, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать эллипс (окружность) или его дугу. - \en Create an ellipse (circle) or an elliptical (circular) arc. \~ - \details \ru Создать эллипс (окружность) или его дугу. \n - Контейнер points может содержать 0, 2 элементов. \n - \en Create an ellipse (circle) or an elliptical (circular) arc. \n - The container 'points' should contain 0 or 2 elements. \n \~ - \param[in] centre - \ru Центр эллипса (окружности) - \en The center of an ellipse (circle). \~ - \param[in] points - \ru Точки дуги. - \en Points on the arc. \~ - \param[in] curveClosed - \ru Замкнутость дуги. - \en The closedness of the arc. \~ - \param[in] angle - \ru Угол наклона. - \en The inclination angle. \~ - \param[in,out] a - \ru Длина большой полуоси. - \en The major axis length. \~ - \param[in,out] b - \ru Длина малой полуоси. - \en The minor axis length. \~ - \param[out] result - \ru Эллипс (окружность) или его дуга. - \en The ellipse (circle) or the elliptical (circular) arc. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) Arc( const MbCartPoint3D & centre, - const SArray & points, - bool curveClosed, double angle, - double & a, double & b, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кривую, проходящую по набору точек. - \en Create a curve passing through a set of points. \~ - \details \ru Создать кривую, проходящую по набору точек, следующего типа: \n - curveType == st_LineSegment3D - отрезок, \n - curveType == st_Arc3D - окружность или дуга, \n - curveType == st_Polyline3D - ломаная, \n - curveType == st_Bezier3D - кривая Безье, \n - curveType == st_CubicSpline3D - кубический сплайн, \n - curveType == st_Hermit3D - составной кубический сплайн Эрмита, \n - curveType == st_Nurbs3D - неоднородный рациональный B-сплайн четвертого порядка (кубический). \n - \en Create a curve passing through a set of points that has the following type: \n - curveType == st_LineSegment3D - a line segment, \n - curveType == st_Arc3D - a circle or an arc, \n - curveType == st_Polyline3D - a polyline, \n - curveType == st_Bezier3D - a Bezier curve, \n - curveType == st_CubicSpline3D - a cubic spline, \n - curveType == st_Hermit3D - a cubic Hermite spline, \n - curveType == st_Nurbs3D - a nonuniform rational B-spline of fourth order (cubic). \n \~ - \param[in] pointList - \ru Набор точек. - \en A point set. \~ - \param[in] curveClosed - \ru Замкнутость кривой. - \en A curve closedness. \~ - \param[in] curveType - \ru Тип кривой. - \en A curve type. \~ - \param[out] result - \ru Кривая. - \en The curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SplineCurve( const SArray & pointList, - bool curveClosed, - MbeSpaceType curveType, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать NURBS-кривую. - \en Create a NURBS-curve. \~ - \details \ru Создать NURBS-кривую, построенную по набору контрольных точек. \n - Контейнер weightList может быть пустым. \n - Контейнер knotList может быть пустым. \n - \en Create a NURBS-curve given a sequence of control points. \n - Container 'weightList' can be empty. \n - Container 'knotList' can be empty. \n \~ - \param[in] pointList - \ru Множество точек. - \en An array of points. \~ - \param[in] weightList - \ru Множество весов. - \en An array of weights. \~ - \param[in] degree - \ru Порядок сплайна. - \en A spline degree. \~ - \param[in] knotList - \ru Множество параметрических узлов (Узловой вектор). - \en An array of parametric knots (A knot vector). \~ - \param[in] curveClosed - \ru Замкнутость кривой. - \en A curve closedness. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) NurbsCurve( const SArray & pointList, - const SArray & weightList, size_t degree, - const SArray & knotList, bool curveClosed, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать копию кривой в виде NURBS. - \en Create a copy of a curve as a NURBS-curve. \~ - \details \ru Создать копию кривой в виде NURBS. \n - \en Create a copy of a curve as a NURBS-curve. \n \~ - \param[in] curve - \ru Исходная кривая. - \en The initial curve. \~ - \param[out] result - \ru Сплайновая копия кривой. - \en The spline copy of the curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) NurbsCopy( const MbCurve3D & curve, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать правильный многоугольник, вписанный в окружность. - \en Create a regular polygon inscribed in a circle. \~ - \details \ru Создать правильный многоугольник, вписанный в окружность \n - (при describe == true описанного вокруг окружности) с центром centre и проходящей через point: \n - - при vertexCount <= 1 строится окружность с центром centre и проходящая через point, \n - - при vertexCount == 2 строится прямоугольник со сторонами, параллельными глобальным осям - и противоположными вершинами в centre и point. \n - \en Create a regular polygon inscribed in a circle \n - (if 'describe' == true circumscribed around a circle) with the specified centre and passing through the given point: \n - - if vertexCount <= 1, a circle with center 'center' passing through 'point' is created, \n - - if vertexCount == 2, a rectangle aligned with the global axes - with the opposite vertices at points 'centre' and 'point' is created. \n \~ - \param[in] centre - \ru Центр. - \en The center. \~ - \param[in] point - \ru Точка. - \en A point. \~ - \param[in] axisZ - \ru Ось Z для создания ЛСК кривой. - \en Z-axis for curve LCS creation. \~ - \param[in] vertexCount - \ru Количество вершин. - \en Number of vertices. \~ - \param[in] describe - \ru Описанный вокруг окружности. - \en Circumscribing around the circle. \~ - \param[out] result - \ru Правильный многоугольник. - \en The regular polygon. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) RegularPolygon( const MbCartPoint3D & centre, - const MbCartPoint3D & point, - const MbVector3D & axisZ, - size_t vertexCount, - bool describe, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать спираль. - \en Create a spiral. \~ - \details \ru Создать спираль. \n - Если spiralAxis == true, то lawCurve - определяет плоскую ось спирали. \n - Если spiralAxis == false, то lawCurve - определяет закон изменения радиуса спирали. \n - \en Create a spiral. \n - If 'spiralAxis' == true, 'lawCurve' determines the axis of a spiral. \n - If spiralAxis == false, then 'lawCurve' - determines a radius law. \n \~ - \param[in] place - \ru Локальная система координат. - \en A local coordinate system. \~ - \param[in] radius - \ru Радиус спирали. - \en A spiral radius. \~ - \param[in] step - \ru Шаг спирали. - \en A pitch. \~ - \param[in] lawCurve - \ru Формообразующая кривая. - \en A guide curve. \~ - \param[in] spiralAxis - \ru Выбор режима формообразования - \en A spiral construction mode. \~ - \param[out] result - \ru Спиральная кривая. - \en The spiral curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SpiralCurve( const MbPlacement3D & place, - double radius, - double step, - MbCurve & lawCurve, - bool spiralAxis, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать спираль. - \en Create a spiral. \~ - \details \ru Создать спираль. \n - Если spiralAxis == true, то lawCurve - определяет плоскую ось спирали. \n - Если spiralAxis == false, то lawCurve - определяет закон изменения радиуса спирали. \n - Если lawCurve == NULL, то строится коническая спираль с углом конусности angle. \n - \en Create a spiral. \n - If 'spiralAxis' == true, 'lawCurve' determines the axis of a spiral. \n - If spiralAxis == false, then 'lawCurve' - determines a radius law. \n - If lawCurve == NULL, a conical spiral is created with the specified taper angle. \n \~ - \param[in] point0 - \ru Начало локальной системы координат (ЛСК). - \en The origin of local coordinate system (LCS). \~ - \param[in] point1 - \ru Точка на оси Z ЛСК. - \en A point on Z-axis of LCS. \~ - \param[in] point2 - \ru Точка на оси X ЛСК. - \en A point on the X-axis of LCS. \~ - \param[in] radius - \ru Радиус спирали. - \en A spiral radius. \~ - \param[in] step - \ru Шаг спирали. - \en A pitch. \~ - \param[in] angle - \ru Угол коничности спирали. - \en A taper angle. \~ - \param[in] lawCurve - \ru Формообразующая кривая. - \en A guide curve. \~ - \param[in] spiralAxis - \ru Выбор режима формообразования - \en A spiral construction mode. \~ - \param[out] result - \ru Спиральная кривая. - \en The spiral curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SpiralCurve( const MbCartPoint3D & point0, - const MbCartPoint3D & point1, - const MbCartPoint3D & point2, - double radius, - double step, - double angle, - MbCurve * lawCurve, - bool spiralAxis, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать составную кривую (контур). - \en Create a composite curve (contour). \~ - \details \ru Создать составную кривую (контур) на базе исходной кривой. \n - \en Create a composite curve (contour) on the basis of the given curve. \n \~ - \param[in] curve - \ru Исходная кривая. - \en The initial curve. \~ - \param[out] result - \ru Контур на основе кривой. - \en The contour created on the basis of the curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) CreateContour( MbCurve3D & curve, - MbContour3D *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать копию кривой. - \en Create a copy of a curve. \~ - \details \ru Создать копию кривой с разбивкой ломаной линии и заменой некоторых кривых. \n - \en Create a copy of a curve with splitting of a polyline and replacement of some curves. \n \~ - \param[in] curve - \ru Исходная кривая. - \en The initial curve. \~ - \param[in] version - \ru Версия исполнения. - \en The version. \~ - \return \ru Возвращает модифицированную копию кривой, если получилось ее создать. - \en Returns a modified copy of the curve if it has been successfully created. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbCurve3D *) DuplicateCurve( const MbCurve3D & curve, - VERSION version = Math::DefaultMathVersion() ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Добавить кривую в составную кривую (контур). - \en Add a curve to a composite curve (a contour). \~ - \details \ru Добавить кривую curve в составную кривую (контур) contour. \n - Если toEnd == true, то добавить в конец. \n - Если toEnd == false, то добавить в начало. \n - \en Add a curve to a composite curve (a contour) 'contour'. \n - If toEnd == true, the curve is to be added to the end. \n - If toEnd == true, the curve is to be added to the beginning. \n \~ - \param[in] curve - \ru Добавляемая кривая - \en A curve to be added. \~ - \param[in,out] contour - \ru Модифицируемый контур. - \en A contour to be modified. \~ - \param[in] toEnd - \ru Флаг места добавления кривой. - \en The flag determines the place of the curve in the contour. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) AddCurveToContour( MbCurve3D & curve, - MbCurve3D & contour, - bool toEnd ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить кривую в пространстве по двумерной кривой. - \en Create a space curve from a two-dimensional curve. \~ - \details \ru Построить кривую в пространстве по двумерной кривой сurve на плоскости place. \n - Построение выполняется на оригинале кривой. - \en Create a space curve from a two-dimensional curve 'curve' lying on plane 'place'. \n - The construction is performed on the source curve. \~ - \param[in] place - \ru Система координат плоскости. - \en The plane coordinate system. \~ - \param[in] curve - \ru Двумерная кривая - \en A two-dimensional curve \~ - \param[out] result - \ru Плоская кривая. - \en The planar curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) PlaneCurve( const MbPlacement3D & place, - const MbCurve & curve, - MbCurve3D *& result ); - -//------------------------------------------------------------------------------ -/** \brief \ru Построить кривую на поверхности по двумерной кривой. - \en Create a curve on a surface given a two-dimensional curve. \~ - \details \ru Построить кривую на поверхности surface по двумерной кривой сurve. \n - Построение выполняется на оригиналах кривой и поверхности. - \en Create a curve on a surface 'surface' given a two-dimensional curve 'curve'. \n - The construction is performed on the original curve and surface. \~ - \param[in] surface - \ru Поверхность. - \en A surface. \~ - \param[in] curve - \ru Двумерная кривая - \en A two-dimensional curve \~ - \param[out] result - \ru Поверхностная кривая. - \en The curve on the specified surface. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SurfaceCurve( const MbSurface & surface, - const MbCurve & curve, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать поверхностную кривую, если пространственная кривая лежит на поверхности. - \en Create a curve on a surface from a space curve lying on the surface. \~ - \details \ru Создать поверхностную кривую, если пространственная кривая лежит на поверхности. \n - Разбираются частные случае точной принадлежности кривой выбранной поверхности. - В общем случае пространственная кривая считается принадлежащей поверхности, если группа точек, - полученная шаганием по параметру кривой по угловому отклонению, принадлежит поверхности. - В этом случае создается двумерная проекционная кривая этой кривой на поверхности. \n - \en Create a curve on a surface from a space curve lying on the surface. \n - The special cases of curves exactly lying on the specified surfaces are treated. - In the general case a space curve is considered to lie on the surface if a group of points - obtained by sampling the curve with the given turning angle belongs to the surface. - In this case a two-dimensional curve is created as the projection of the curve on the surface. \n \~ - \param[in] curve - \ru Пространственная кривая. - \en A space curve. \~ - \param[in] surf - \ru Поверхность. - \en A surface. \~ - \param[in] sameSurf - \ru Использовать оригинал поверхности. - \en Whether to use the source surface. \~ - \param[in] extSurf - \ru Искать на расширенной поверхности. - \en Whether to use the extended surface. \~ - \param[in] strictOnSurface - \ru Все точки кривой лежат на поверхности (true) или часть точек лежит на поверхности (false). - \en All the points of the curve belong to the surface (true) or a part of the points belong to the surface (false). \~ - \param[out] result - \ru Поверхностная кривая. - \en The curve on the specified surface. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) CurveOnSurface( const MbCurve3D & curve, - const MbSurface & surf, - bool sameSurf, bool extSurf, - MbCurve3D *& result, - bool strictOnSurface = false ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Лежит ли кривая на поверхности. - \en Determine whether the curve lies on the surface. \~ - \details \ru Проверить, лежит ли кривая полностью на поверхности. \n - \en Determine whether the curve entirely lies on the surface. \n \~ - \param[in] curve - \ru Пространственная кривая. - \en A space curve. \~ - \param[in] surf - \ru Поверхность. - \en A surface. \~ - \param[in] ext - \ru Искать на расширенной поверхности. - \en Whether to use the extended surface. \~ - \param[in] strictOnSurface - \ru Все точки кривой лежат на поверхности (true) или часть точек лежит на поверхности (false). - \en All the points of the curve belong to the surface (true) or a part of the points belong to the surface (false). \~ - \return \ru Возвращает true, если кривая лежит на поверхности. - \en Returns true if the curve lies on the surface. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (bool) IsCurveOnSurface( const MbCurve3D & curve, - const MbSurface & surf, bool ext, - bool strictOnSurface = false ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать массив контуров по массиву кривых. - \en Create an array of contours given an array of curves. \~ - \details \ru Создать массив контуров по массиву кривых (на оригиналах кривых). \n - \en Create an array of contours given an array of curves (using the original curves). \n \~ - \param[in] curves - \ru Множество кривых. - \en An array of curves. \~ - \param[in] metricEps - \ru Радиус захвата для стыковки кривых. - \en The radius for curves joining. \~ - \param[out] result - \ru Множество контуров. - \en The array of contours. \~ - \param[in] onlySmoothConnected - \ru Добавлять в контур только гладко стыкующиеся сегменты. - \en Whether to add only smoothly connected segments. \~ - \param[in] version - \ru Версия исполнения. - \en Version. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) CreateContours( RPArray & curves, - double metricEps, - RPArray & result, - bool onlySmoothConnected = false, - VERSION version = Math::DefaultMathVersion() ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать контуры по набору кривых с удалением вырожденных. - \en Create contours given a set of curves with elimination of degenerate ones. \~ - \details \ru Создать контуры по набору кривых с удалением вырожденных (на оригиналах кривых). \n - \en Create contours given a set of curves with elimination of degenerate ones (the original curves are used). \n \~ - \param[in, out] curves - \ru Множество кривых. - \en An array of curves. \~ - \param[in] metricAcc - \ru Радиус захвата для стыковки кривых. - \en The radius for curves joining. \~ - \param[in] onlySmoothConnected - \ru Добавлять в контур только гладко стыкующиеся сегменты. - \en Whether to add only smoothly connected segments. \~ - \param[in] version - \ru Версия исполнения. - \en Version. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) CreateContours( RPArray & curves, - double metricAcc, - bool onlySmoothConnected = false, - VERSION version = Math::DefaultMathVersion() ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать именованный трехмерный каркас. - \en Create a named three-dimensional wireframe. \~ - \details \ru Создать именованный трехмерный каркас по кривой. \n - \en Create a named three-dimensional wireframe from a curve. \n \~ - \param[in] curve - \ru Кривая. - \en The curve. \~ - \param[in] curveName - \ru Имя кривой. - \en The curve name. \~ - \param[in] mainName - \ru Главное имя операции. - \en The operation main name. \~ - \param[out] result - \ru Каркас. - \en The wireframe. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) WireFrame( const MbCurve3D & curve, - const MbName & curveName, - SimpleName mainName, - MbWireFrame *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать именованный трехмерный каркас. - \en Create a named three-dimensional wireframe. \~ - \details \ru Создать именованный трехмерный каркас по массиву кривых. \n - \en Create a named three-dimensional wireframe from a given array of curves. \n \~ - \param[in] curves - \ru Множество кривых. - \en An array of curves. \~ - \param[in] curveNames - \ru Множество имен кривых. - \en An array of the curves names. \~ - \param[in] mainName - \ru Главное имя операции. - \en The operation main name. \~ - \param[out] result - \ru Каркас. - \en The wireframe. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) WireFrame( const RPArray & curves, - const RPArray & curveNames, - SimpleName mainName, - MbWireFrame *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Аппроксимировать контур дугами и отрезками. - \en Approximate a contour with arcs and line segments. \~ - \details \ru Аппроксимировать контур дугами и отрезками. \n - Производится аппроксимация каждого из сегментов контура. \n - \en Approximate a contour with arcs and line segments. \n - The approximation is performed for each segment of the contour. \n \~ - \param[in] curve - \ru Кривая или контур, которую надо аппроксимировать. - \en A curve or a contour to approximate. \~ - \param[out] result - \ru Результат аппроксимации. - \en The approximation result. \~ - \param[in] eps - \ru Ошибка аппроксимации. - \en The approximation precision. \~ - \param[in] minRad - \ru Минимально допустимый радиус окружностей, используемых для аппроксимации. - \en The minimal acceptable radius of the circles used for the approximation. \~ - \param[in] maxRad - \ru Максимально допустимый радиус окружностей, используемых для аппроксимации. - \en The maximal acceptable radius of the circles used for the approximation. \~ - \return \ru - Возращает код результата операции. - \en - Returns operation result code. \~ -\ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) CreatePolyArcCurve3D( const MbCurve3D & curve, - MbCurve3D *& result, - double & eps, - double minRad = Math::minRadius, - double maxRad = Math::maxRadius ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Получить или создать пространственную кривую. - \en Get or create a space curve. \~ - \details \ru Получить или создать пространственную кривую из данных модельного объекта - c опциональным сохранением несущей плоскости в случае плоского объекта. \n - Двумерный контур на плоскости преобразуется не в контур на плоскости, - а в пространственный контур. \n - \en Get or create a space curve from a model object data. - with optional keeping of supporting plane in the case of a planar object. \n - A two-dimensional contour is to be converted not to a contour on a plane - but to a three-dimensional contour. \n \~ - \param[in] item - \ru Модельный объект. - \en A model object. \~ - \param[in] keepPlacement - \ru Сохранять несущую плоскость. - \en Whether to keep the supporting plane. \~ - \param[out] curve0 - \ru Пространственная кривая. - \en A space curve. \~ - \param[out] curves - \ru Дополнительные пространственные кривые (могут быть в объекте MbPlaneInstance). - \en Additional space curve (It's possible if a object "MbItem" is the object "MbPlaneInstance"). \~ - \return \ru - Возвращает успешность результата операции. - \en - Returns whether the result is successful. \~ -\ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (bool) GetSpaceCurve( const MbItem & item, - bool keepPlacement, - SPtr & curve0, - std::vector< SPtr > * curves = NULL ); - - -//------------------------------------------------------------------------------- -/** \brief \ru Построить развертку кривой/контура на плоскость. - \en Construct a unwrapping curve/contour. \~ - \details \ru Построение развертки кривой/контура на плоскость. Контур разворачивается посегментно, функцией UnwrapCurve. - Первым разворачивается сегмент, который лежит в плоскости и находиться ближе всех к точке, - если лежащих в плоскости нет, то ближащий к точке из тех что пересекают контур, если и таких нет то просто ближайший к точке. - Первый сегмент разворачивается так, чтобы точка пересечения или ближайшей проекции была неподвижной. \n - \en Construction unwrapping of the curve/contour on a plane. Each segment of the contour is unwrapped by the function UnwrapCurve. - Unwrapping starting with a segment which lies in the plane or crosses the plane and which is closest to the given point. - Unwrapping of the first segment is constructed in the manner, that cross point or closest projection of the segment on the plane were stationary. \n \~ - \param[in] curve - \ru Разворачиваемая кривая/контур. - \en Original curve/contour. \~ - \param[in] placement - \ru Локальная система координат плоскости. - \en The placement of the plane. \~ - \param[in] point - \ru Точка для определения первого сегмента. - \en The point for determine first segment. \~ - \param[in] deviationAngle - \ru Параметру точности. - \en The parameter of accuracy. \~ - \return \ru Возвращает указатель на построенную кривую с нулевум счетчиком ссылок \n - или NULL, если не удалось построить развертку для заданных параметров. - \en The pointer to the constructed curve with zero counter of references\n - return NULL, if unwrap curve can't be construvted for this parameters -\ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbCurve3D *) UnwrapCurve( const MbCurve3D & curve, - const MbPlacement3D & placement, - const MbCartPoint3D & point, - double deviationAngle = DEVIATION_SAG); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать сечение кинематического тела для заданного параметра на направляющей. - \en Create a section of evolution solid for defined parameter on the guide curve. \~ - \details \ru Создать сечение кинематического тела для заданного параметра на направляющей. - Если направляющая является контуром, то стыки между сегментами контура должны быть гладкими. \n - \en Create a section of evolution solid (sweep solid with guide curve) for defined parameter on guide curve. - If the guide curve is a contour, then this contour have to be smooth. \n \~ - \param[in] generCurves - \ru Множество плоских образующих. - \en An array of forming curves. \~ - \param[in] guideCurve - \ru Направляющая кривая (или контур). - \en An guide curve (or contour). \~ - \param[in] guideParam - \ru Параметр, заданный на направляющей кривой. - \en A parameter on the guide curve. \~ - \param[in] angleEpsilon - \ru Желаемая угловая точность параллельности касательных в точке стыка сегментов контура. - \en The desired angular precision parallel to the tangent at the point of junction path segments. \~ - \param[out] result - \ru Кривые сечения кинематического тела. - \en Curves of evolution solid section. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) EvolutionSection( const MbSweptData & generCurves, - const MbCurve3D & guideCurve, - double guideParam, - const EvolutionValues & params, - MbSweptData & result, - VERSION version = Math::DefaultMathVersion(), - double angleEpsilon = ANGLE_EPSILON ); - - -//------------------------------------------------------------------------------ -// Является ли кривая прямолинейной независимо от ее параметризации. -/** \brief \ru Является ли кривая прямолинейной независимо от ее параметризации. - \en Whether the curve is like straight-line regardless of its parameterisation. \~ - \details \ru Является ли кривая прямолинейной независимо от ее параметризации.\n - \en Whether the curve is like straight-line regardless of its parameterisation. \~ - \param[in] curve - \ru Кривая. - \en Curve. \~ - \param[in] eps - \ru Точность. - \en Accuracy. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (bool) IsLikeStraightLine( const MbCurve & curve, double eps ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать осевые (центральные) линии для грани оболочки. - \en Create center lines of shell face. \~ - \details \ru Создать осевые (центральные) линии для грани оболочки. \n - \en Create center lines of shell face. \n \~ - \param[in] face - \ru Исходная грань. - \en The initial face. \~ - \param[out] clCurves - \ru Набор построенных осевых линий. - \en The set of created center lines. \~ - \return \ru Возвращает true, если осевые кривые удалось создать. - \en Returns true if the center lines was created. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC( bool ) CreateCenterLineCurves( const MbFace & face, - std::vector & clCurves ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Создать плавную B-сплайновую кривую на опорной ломаной. - \en Create a fair B-spline on base polyline. \~ - \details \ru Создать плавную V-кривую на опорной ломаной и аппроксимировать B-сплайновой кривой. - Степень сплайна m, (m = 3, 4, ... , 9, 10) устанавливается в переменной degreeBSpline. - Для гармоничного перераспределения точек значение переменной arrange == 1. В противном сучае == 0. - Для уплотнения кривой значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного). - Направление вектора в точках перегиба учитывается по значению переменной accountInflexVector (0 - как направление звена S-полигона, - 1 - как направление касательного вектора). \n - \en Create a fair V-curve on the base polyline and approximate by a B-spline curve. - The degree of spline m, (m = 3, 4, ... , 9, 10) is set by variable degreeBSpline. - For harmonious redistribution of points, the value of the variable arrange == 1. Otherwise, == 0. - To subdivide a curve, the value of the variable subdivision > 0 (1 for a single subdivision, 2 for a double subdivision). - The direction of the vector at the inflection points is taken into account by the value of the variable InflexVector (0 - as the direction of the S-polygon segment, - 1 - as the direction of the tangent vector). \n \~ - \param[in] polyline - \ru Исходная ломаная. - \en An initial polyline. \~ - \param[in] data - \ru Данные построения кривой. - \en The curve construction data. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC (MbResultType) CreateFairBSplineCurveOnBasePolyline( MbCurve3D * polyline, - MbFairCurveData & data, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Создать плавную B-сплайновую кривую на касательной ломаной. - \en Create a fair B-spline on tangent polyline. \~ - \details \ru Создать плавную V-кривую на касательной ломаной и аппроксимировать B-сплайновой кривой. - Степень сплайна m, (m = 3, 4, ... , 9, 10) устанавливается в переменной degreeBSpline. - Для уплотнения кривой устанавливается значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного). - Направление вектора в точках перегиба кривой совпадает с направлением звена перегиба касательной ломаной. \n - \en Create a fair V-curve on the base polyline and approximate by a B-spline curve. - The degree of spline m, (m = 3, 4, ... , 9, 10) is set by variable degreeBSpline. - To subdivide a curve, the value of the variable subdivision > 0 (1 for a single subdivision, 2 for a double subdivision). - The direction of the tangent vector at the inflection points coincides with the direction of the inflection segment of tangent polyline). \n \~ - \param[in] polyline - \ru Исходная ломаная. - \en An initial polyline. \~ - \param[in] data - \ru Данные построения кривой. - \en The curve construction data. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC (MbResultType) CreateFairBSplineCurveOnTangentPolyline( MbCurve3D * polyline, - MbFairCurveData & data, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Создать плавную кривую Безье на опорной ломаной. - \en Create a fair Bezier curve on base polyline. \~ - \details \ru Создать плавную V-кривую на опорной ломаной и аппроксимировать рациональной кубической кривой Безье. - Для гармоничного перераспределения точек значение переменной arrange == 1. В противном сучае == 0. - Для уплотнения кривой значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного).\n - \en Create a smooth V-curve on the reference polyline and approximate a rational cubic Bezier curve. - For harmonious redistribution of points, the value of the variable arrange == 1. Otherwise, == 0. - To subdivide a curve, the value of the variable subdivision > 0 (1 for a single subdivision, 2 for a double subdivision). \n \~ - \param[in] polyline - \ru Исходная ломаная. - \en An initial polyline. \~ - \param[in] data - \ru Данные построения кривой. - \en The curve construction data. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC(MbResultType) CreateFairBezierCurveOnBasePolyline( MbCurve3D * polyline, - MbFairCurveData & data, - MbCurve3D *& result); - - -//------------------------------------------------------------------------------ - /** \brief \ru Создать плавную кривую Безье на касательной ломаной. - \en Create a fair Bezier curve on tangent polyline. \~ - \details \ru Создать плавную V-кривую на касательной ломаной и аппроксимировать рациональной кубической кривой Безье. - Для уплотнения кривой значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного). \n - \en Create a smooth V-curve on a tangent polyline and approximate a rational cubic Bezier curve. - To subdivide a curve, the value of the variable subdivision > 0 (1 for a single subdivision, 2 for a double subdivision). \n \~ - \param[in] polyline - \ru Исходная ломаная. - \en An initial polyline. \~ - \param[in] data - \ru Данные построения кривой. - \en The curve construction data. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке из списка сообщений метода MessageError. - \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number.from the message list of the MessageError method. \~ - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC (MbResultType) CreateFairBezierCurveOnTangentPolyline( MbCurve3D * polyline, - MbFairCurveData & data, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Создать ГО Эрмита на кривой Безье. - \en Create Hermite GD on a Bezier curve. \~ - \details \ru Создать геометрический определитель Эрмита в виде ломаной линии на рациональной сплайновой кривой Безье. \n - На кривой определяется опорная ломаная, вершины которой принадлежат узловым точкам сплайна. - Определяются векторы касательных и векторы кривизны. Вектор касательной может иметь произвольную длину. - Вектор кривизны должет иметь длину, равную значению кривизны в данной вершине опорной ломаной. - Если значение кривизны равно нулю в точке перегиба, то вектор кривизны откладывается по направлению касательного вектора в данной вершине - и имеет произвольную ненулевую длину, не превышающую длину касательного вектора. - Направленная ломаная ГО Эрмита последовательно обходит вершины опорной ломаной, концы касательных векторов и концы векторов кривизны. - Обход происходит следующим образом: от вершины опорной к концу касательного вектора, затем возврат к вершине опорной ломаной, - переход к концу вектора кривизны, возврат к вершине опорной ломаной, затем переход к следующей вершине опорной ломаной и т.д. - Вершина с номером 1 и вершины с номерами через 5 (1, 6, 11, ... ) принадлежат концам касательных векторов. - Вершина с номером 3 и вершины с номерами через 5 (3, 8, 13, ... ) принадлежат концам векторов кривизны. - \en Create a Hermite geometric determinant in the form of a polyline on a rational Bezier spline curve. \n - A base polyline is defined on the curve, the vertices of which belong to the nodal points of the spline. - The tangent vectors and the curvature vectors are determined. The tangent vector can be of arbitrary length. - The curvature vector must have a length equal to the value of curvature at a given vertex of the base polyline. - If the curvature value is zero at the inflection point, then the curvature vector is set in the direction of the tangent vector at the given vertex - and has an arbitrary nonzero length not exceeding the length of the tangent vector. \ n - A directional polyline of Hermite GD sequentially bypasses the vertices of the base polyline, the ends of the tangent vectors and the ends of the curvature vectors. - Bypass occurs as follows: from the vertex of the base polyline to the end of the tangent vector, then return to the vertex of the base polyline, - transition to the end of the curvature vector, return to the vertex of the base polyline, then transition to the next vertex of the base polyline, etc. - The vertex with number 1 and the vertices with numbers in 5 (1, 6, 11, ...) belong to the ends of the tangent vectors. - The vertex with number 3 and the vertices with numbers in 5 (3, 8, 13, ...) belong to the ends of the curvature vectors. \~ - \param[in] curve - \ru Исходная кривая. - \en An initial curve. \~ - \param[in] data - \ru Данные построения кривой. - \en The curve construction data. \~ - \param[out] polyline - \ru 3D ломаная ГО Эрмита. - \en 3D polyline of Hermite GD. \~ - \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке из списка сообщений метода MessageError. - \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number from the message list of the MessageError method.\~ - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC(MbResultType) KernelCreateHermiteGDOnBezierCurve( MbNurbs3D * curve, - MbFairCurveData & data, - MbCurve3D *& polyline ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Создать кривую Безье на ГО Эрмита. - \en Create a Bezier on Hermite GD. \~ - \details \ru Изогеометрически построить рациональную кубическую кривую Безье на ГО Эрмита второго порядка фиксации. - Используются все параметры ГО Эрмита второго порядка фиксации: точки, касательные векторы и значения кривизны. - \en Isogeometrically create a cubic rational Bezier curve on a Hermite GD of second-order fixation. - Used all params of Hermite GD of second-order fixation: points, tangent vectors and values of curvature. \~ - \param[in] polyline - \ru Исходная ломаная. - \en An initial polyline. \~ - \param[in] data - \ru Данные построения кривой. - \en The curve construction data. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке из списка сообщений метода MessageError. - \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number from the message list of the MessageError method.\~ - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC (MbResultType) CreateBezierCurveOnHermiteGD( MbCurve3D * polyline, - MbFairCurveData & data, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Создать B-сплайновую кривую на ГО Эрмита. - \en Create a B-spline on Hermite GD. \~ - \details \ru Создать B-сплайную кривую на ГО Эрмита первого порядка фиксации. \n - Направления касательных векторов ГО Эрмита определяют направления звеньев S-полигона. - Направление вектора в точках перегиба учитывается по значению переменной accountInflexVector (0 - как направление звена S-полигона, \n - 1 - как направление касательного вектора). \n - Кривизна в концевых точках учитывается по значению accountCurvature (0 - не учитывается, 1 - в начальной точке, 2 - в конечной точке, 3 - учитываются на обоих концах). - \en Create a B-spline curve on Hermite's GD of first-order fixation. \ n -               The directions of the tangent vectors of the Hermite GD determine the directions of the S-polygon segmentss. \n -               The direction of the vector at the inflection points is taken into account by the value of the variable InflexVector (0 - as the direction of the S-polygon segment, -               1 - as the direction of the tangent vector). \n -               The curvature at the end points is taken into account by the value of accountCurvature (0 - not taken into account, 1 - at the start point, 2 - at the end point, 3 - are taken into account at both ends). \~ - \param[in] polyline - \ru Исходная ломаная. - \en An initial polyline. \~ - \param[in] data - \ru Данные построения кривой. - \en The curve construction data. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке из списка сообщений метода MessageError. - \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number from the message list of the MessageError method.\~ - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC(MbResultType) CreateBSplineCurveOnHermiteGD( MbCurve3D * polyline, - MbFairCurveData & data, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Создать плавную кривую Безье на опорной ломаной ГО Эрмита. - \en Create a fair Bezier curve on base polyline of Hermite GD. \~ - \details \ru Создать плавную V-кривую на опорной ломаной ГО Эрмита и аппроксимировать рациональной кубической кривой Безье. \n - Учитываются концевые значения кривизны и направления касательных в точках перегиба ГО Эрмита. - Для гармоничного перераспределения точек значение переменной arrange == 1. В противном сучае == 0. - Для уплотнения кривой значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного). - \en Create a fair V-curve on the base polyline and approximate by a rational cubic Bezier curve. \ n - The end values of the curvature and directions of the tangents at the inflection points of the Hermite GO are taken into account. - For harmonious redistribution of points, the value of the variable arrange == 1. Otherwise, == 0. - To subdivide a curve, the value of the variable subdivision> 0 (1 for a single subdivision, 2 for a double subdivision).\~ - \param[in] polyline - \ru Исходная ломаная. - \en An initial polyline. \~ - \param[in] data - \ru Данные построения кривой. - \en The curve construction data. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке из списка сообщений метода MessageError. - \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number from the message list of the MessageError method.\~ - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC(MbResultType) CreateFairBezierCurveOnBasePolylineOfHermiteGD( MbCurve3D * polyline, - MbFairCurveData & data, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Создать плавную кривую Безье на касательных прямых ГО Эрмита. - \en Create a fair Bezier curve on tangent lines of Hermite GD. \~ - \details \ru Создать плавную V-кривую на касательных прямых ГО Эрмита и аппроксимировать рациональной кубической кривой Безье. \n - Учитываются концевые значения кривизны и положения точек перегиба ГО Эрмита. - Для уплотнения кривой значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного). - \en Create a fair V-curve on the base polyline and approximate by a rational cubic Bezier curve. \ n -  The end values of the curvature and directions of the tangents at the inflection points of the Hermite GO are taken into account. - To subdivide a curve, set the value of the variable subdivision > 0 (1 for a single subdivision, 2 for a double subdivision).\~ - \param[in] polyline - \ru Исходная ломаная. - \en An initial polyline. \~ - \param[in] data - \ru Данные построения кривой. - \en The curve construction data. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке \n - из списка сообщений метода MessageError. - \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number \ n -              from the message list of the MessageError method.\~ - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC (MbResultType) CreateFairBezierCurveOnTangentsOfHermiteGD( MbCurve3D * polyline, - MbFairCurveData & data, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Создать плавную B-сплайновую кривую на опорной ломаной ГО Эрмита. - \en Create a fair B-spline curve on base polyline of Hermite GD. \~ - \details \ru Создать плавную V-кривую на опорной ломаной и аппроксимировать B-сплайновой кривой. \n - Степень сплайна m, (m = 3, 4, ... , 9, 10) устанавливается в переменной degreeBSpline. - Для гармоничного перераспределения точек значение переменной arrange == 1. В противном сучае == 0. - Для уплотнения кривой значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного). - Направление вектора в точках перегиба учитывается по значению переменной accountInflexVector (0 - как направление звена S-полигона, - 1 - как направление касательного вектора). \n - Кривизна в концевых точках учитывается по значению accountCurvature (0 - не учитывается, 1 - в начальной точке, 2 - в конечной точке, 3 - учитываются на обоих концах). - \en Create a fair V-curve on the base polyline and approximate by a B-spline curve. \ n - The degree of spline m, (m = 3, 4, ... , 9, 10) is set by variable degreeBSpline. - For harmonious redistribution of points, the value of the variable arrange == 1. Otherwise, == 0. - To subdivide a curve, the value of the variable subdivision > 0 (1 for a single subdivision, 2 for a double subdivision). - The directions of the tangent vectors of the Hermite GD determine the directions of the S-polygon segmentss. \n - The direction of the vector at the inflection points is taken into account by the value of the variable InflexVector (0 - as the direction of the S-polygon segment, \ n - 1 - as the direction of the tangent vector). \n - The curvature at the end points is taken into account by the value of accountCurvature (0 - not taken into account, 1 - at the start point, 2 - at the end point, 3 - are taken into account at both ends). \~ - \param[in] polyline - \ru Исходная ломаная. - \en An initial polyline. \~ - \param[in] data - \ru Данные построения кривой. - \en The curve construction data. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке из списка сообщений метода MessageError. - \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number from the message list of the MessageError method.\~ - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC (MbResultType) CreateFairBSplineCurveOnBasePolylineOfHermiteGD( MbCurve3D * polyline, - MbFairCurveData & data, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Создать плавную B-сплайновую кривую на касательных прямых ГО Эрмита. - \en Create a fair B-spline curve on tangent lines of Hermite GD. \~ - \details \ru Создать плавную V-кривую на касательных прямых ГО Эрмита и аппроксимировать B-сплайновой кривой. \n - Степень сплайна m, (m = 3, 4, ... , 9, 10) устанавливается в переменной degreeBSpline. - Для гармоничного перераспределения точек значение переменной arrange == 1. В противном сучае == 0. - Для уплотнения кривой установите значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного). - Кривизна в концевых точках учитывается по значению accountCurvature (0 - не учитывается, 1 - в начальной точке, 2 - в конечной точке, 3 - учитываются на обоих концах). - \en Create a fair V-curve on the base polyline of Hermite GD and approximate by a B-spline curve. \ n - The degree of spline m, (m = 3, 4, ... , 9, 10) is set by variable degree. - For harmonious redistribution of points, the value of the variable arrange == 1. Otherwise, == 0. - To subdivide a curve, set the value of the variable subdivision > 0 (1 for a single subdivision, 2 for a double subdivision). - The curvature at the end points is taken into account by the value of accountCurvature (0 - not taken into account, 1 - at the start point, 2 - at the end point, 3 - are taken into account at both ends). \~ - \param[in] polyline - \ru Исходная ломаная. - \en An initial polyline. \~ - \param[in] data - \ru Данные построения кривой. - \en The curve construction data. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке из списка сообщений метода MessageError. - \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number from the message list of the MessageError method.\~ - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC(MbResultType) CreateFairBSplineCurveOnTangentsOfHermiteGD( MbCurve3D * polyline, - MbFairCurveData & data, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Создать клотоиду. - \en Create a clothoid. \~ - \details \ru Создать начальный участок клотоиды. \n - В переменных data задаются параметры построения клотоиды. Максимальная длина начального участка клотоиды в переменной - clothoidMax и минимальный радиус на конце участка в переменной clothoidRMin. В переменной clothoidSegms задается количество - сегментов аппроксимирующей клотоиду сплайновой кривой Безье. - \en Create the starting part of the clothoid. \ n -             The parameters to create the clothoid are set in the data. The maximum length of the initial section of the clothoid in the variable clothoidMax and the minimum radius at the end of the section in the variable clothoidRMin. - The variable clothoidSegms sets the number of segments of the Bezier spline curve approximating the clothoid .\~ - \param[in] data - \ru Данные построения кривой. - \en The curve construction data. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает значение результата операции. - \en Returns operation result value. - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC(MbResultType) KernelCreateClothoid( MbFairCurveData & data, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Создать сектрису. - \en Create a sectrix. \~ - \details \ru Создать сектрису Маклорена. \n - На касательной ломаной из двух звеньев генерируются точки сектрисы Маклорена - кривой с монотонным изменением кривизны. - Сектриса Маклорена изогеометрически с сохранением монотонности аппроксимируется сплайновой кривой Безье. - \en Create a sectrix of Maclourin. - On a tangent polyline of two links, points of the Maclaurin sectrix — a curve with a monotonic change in curvature — are generated. -             The Maclaurin sectrix is isometrically approximated with the preservation of monotony by the Bezier spline curve.\~ -            - \param[in] polyline - \ru Исходная ломаная. - \en An initial polyline. \~ - \param[in] data - \ru Данные построения кривой. - \en The curve construction data. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает значение результата операции. - \en Returns operation result value. - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC(MbResultType) KernelCreateSectrix( MbCurve3D * polyline, - MbFairCurveData & data, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Увеличить степень NURBzS кривой. - \en Elevate the degree of the NURBzS curve. \~ - \details \ru Увеличить степень NURBzS кривой. \n - Степень кубической кривой увеличивается до шестой степени. - Степени больше 6 увеличиваются на единцу. Максимальная степень - 10. - \en The increasing the degree of the NURBzS curve. \n - The degree of the cubic curve increases to the sixth degree. -             Degrees greater than 6 increase by one. The maximum degree is 10.\~ - \param[in] curve - \ru Исходная кривая. - \en An initial curve. \~ - \param[in] data - \ru Данные построения кривой. - \en The curve construction data. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает значение результата операции. - \en Returns operation result value. - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC(MbResultType) ElevateDegreeNurbzs( MbNurbs3D * curve, - MbFairCurveData & data, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Уплотнить NURBS кривую. - \en Subdivide the NURBS curve. \~ - \details \ru Однократно уплотнить NURBS кривую. \n - \en Single subdivision of NURBS curve. \~ - \param[in] curve - \ru Исходная кривая. - \en An initial curve. \~ - \param[in] data - \ru Данные построения и преобразования кривой. - \en Curve construction and transformation data. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает значение результата операции. - \en Returns operation result value. - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC(MbResultType) SubdivideNurbs( MbNurbs3D * curve, - MbFairCurveData & data, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Выделить участок NURBS кривой / изменить формат NURBS кривой. - \en To extrct part of NURBS curve / change the format of the NURBS curve. \~ - \details \ru Выделить участок NURBS кривой / изменить формат NURBS кривой. \n - В data устанавливаются переменые преобразований. Выделямый участок определяется номером начального сегмента в переменной numSegment - и количеством сегментов в переменной numSegments. Выходной формат сплайна определяется в переменной outFormat. - При значении 2 - NURBS кривая с управляющим S-полигоном. При значении 3 - рациональная кривая Безье - с управляющим GB-полигоном. - \en To extrct part of NURBS curve / change the format of the NURBS curve. \n - Transformation variables are set in data. The extracting part is determined by the number of the starting segment in the variable numSegment and the number of segments in the variable numSegments. - The output spline format is defined in the outFormat variable.    If the value is 2 - NURBS curve with control S-polygon. If the value is 3 - rational Bezier curve with GB-control polygon.\~ - \param[in] curve - \ru Исходная кривая. - \en An initial curve. \~ - \param[in] data - \ru Данные построения и преобразования кривой. - \en Curve construction and transformation data. \~ - \param[out] result - \ru Сплайновая кривая. - \en The spline curve. \~ - \return \ru Возвращает значение результата операции. - \en Returns operation result value. - \ingroup Curve3D_Modeling - */ -// --- -MATH_FUNC(MbResultType) ExtractChangeNurbs( MbNurbs3D * curve, - MbFairCurveData & data, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ - /** \brief \ru Вставить узел в NURBS. - \en Insert node in NURBS. \~ - \details \ru Вставить узел в NURBS. \n - В data устанавливаются переменые преобразований. Место вставки узла определяется переменными nSegment и tParam. - В переменной nSegment устанавливается номер сегмента, в переменной tParam (0.1 < tParam < 0.9) задается значение внутреннего параметра на сегменте сплайна. - \en Insert node in NURBS. Transformation variables are set in data. The insertion positon of the node is determined by the variables nSegment and tParam. -             The nSegment variable sets the segment number, the tParam variable (0.1 +#include +#include +#include +#include +#include + + +class MATH_CLASS MbCurve; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbNurbs3D; +class MATH_CLASS MbContour3D; +class MATH_CLASS MbFace; +class MATH_CLASS MbSurface; +class MATH_CLASS MbWireFrame; +class MATH_CLASS MbItem; +class MATH_CLASS MbName; +class MATH_CLASS MbSweptData; +struct MATH_CLASS EvolutionValues; + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать прямую. + \en Create a line. \~ + \details \ru Создать прямую по двум точкам. \n + \en Create a line given two points. \n \~ + \param[in] point1 - \ru Первая точка. + \en The first point. \~ + \param[in] point2 - \ru Вторая точка. + \en The second point. \~ + \param[out] result - \ru Прямая. + \en The line. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) Line( const MbCartPoint3D & point1, + const MbCartPoint3D & point2, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать отрезок прямой. + \en Create a line segment. \~ + \details \ru Создать отрезок прямой по двум точкам. \n + \en Create a line segment given two points. \n \~ + \param[in] point1 - \ru Первая точка. + \en The first point. \~ + \param[in] point2 - \ru Вторая точка. + \en The second point. \~ + \param[out] result - \ru Отрезок. + \en The segment. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) Segment( const MbCartPoint3D & point1, + const MbCartPoint3D & point2, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать эллипс (окружность) или его дугу. + \en Create an ellipse (circle) or an elliptical (circular) arc. \~ + \details \ru Создать эллипс (окружность) или его дугу. \n + Контейнер points может содержать 0, 2 элементов. \n + \en Create an ellipse (circle) or an elliptical (circular) arc. \n + The container 'points' should contain 0 or 2 elements. \n \~ + \param[in] centre - \ru Центр эллипса (окружности) + \en The center of an ellipse (circle). \~ + \param[in] points - \ru Точки дуги. + \en Points on the arc. \~ + \param[in] curveClosed - \ru Замкнутость дуги. + \en The closedness of the arc. \~ + \param[in] angle - \ru Угол наклона. + \en The inclination angle. \~ + \param[in,out] a - \ru Длина большой полуоси. + \en The major axis length. \~ + \param[in,out] b - \ru Длина малой полуоси. + \en The minor axis length. \~ + \param[out] result - \ru Эллипс (окружность) или его дуга. + \en The ellipse (circle) or the elliptical (circular) arc. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) Arc( const MbCartPoint3D & centre, + const SArray & points, + bool curveClosed, double angle, + double & a, double & b, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кривую, проходящую по набору точек. + \en Create a curve passing through a set of points. \~ + \details \ru Создать кривую, проходящую по набору точек, следующего типа: \n + curveType == st_LineSegment3D - отрезок, \n + curveType == st_Arc3D - окружность или дуга, \n + curveType == st_Polyline3D - ломаная, \n + curveType == st_Bezier3D - кривая Безье, \n + curveType == st_CubicSpline3D - кубический сплайн, \n + curveType == st_Hermit3D - составной кубический сплайн Эрмита, \n + curveType == st_Nurbs3D - неоднородный рациональный B-сплайн четвертого порядка (кубический). \n + \en Create a curve passing through a set of points that has the following type: \n + curveType == st_LineSegment3D - a line segment, \n + curveType == st_Arc3D - a circle or an arc, \n + curveType == st_Polyline3D - a polyline, \n + curveType == st_Bezier3D - a Bezier curve, \n + curveType == st_CubicSpline3D - a cubic spline, \n + curveType == st_Hermit3D - a cubic Hermite spline, \n + curveType == st_Nurbs3D - a nonuniform rational B-spline of fourth order (cubic). \n \~ + \param[in] pointList - \ru Набор точек. + \en A point set. \~ + \param[in] curveClosed - \ru Замкнутость кривой. + \en A curve closedness. \~ + \param[in] curveType - \ru Тип кривой. + \en A curve type. \~ + \param[out] result - \ru Кривая. + \en The curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SplineCurve( const SArray & pointList, + bool curveClosed, + MbeSpaceType curveType, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать NURBS-кривую. + \en Create a NURBS-curve. \~ + \details \ru Создать NURBS-кривую, построенную по набору контрольных точек. \n + Контейнер weightList может быть пустым. \n + Контейнер knotList может быть пустым. \n + \en Create a NURBS-curve given a sequence of control points. \n + Container 'weightList' can be empty. \n + Container 'knotList' can be empty. \n \~ + \param[in] pointList - \ru Множество точек. + \en An array of points. \~ + \param[in] weightList - \ru Множество весов. + \en An array of weights. \~ + \param[in] degree - \ru Порядок сплайна. + \en A spline degree. \~ + \param[in] knotList - \ru Множество параметрических узлов (Узловой вектор). + \en An array of parametric knots (A knot vector). \~ + \param[in] curveClosed - \ru Замкнутость кривой. + \en A curve closedness. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) NurbsCurve( const SArray & pointList, + const SArray & weightList, size_t degree, + const SArray & knotList, bool curveClosed, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать копию кривой в виде NURBS. + \en Create a copy of a curve as a NURBS-curve. \~ + \details \ru Создать копию кривой в виде NURBS. \n + \en Create a copy of a curve as a NURBS-curve. \n \~ + \param[in] curve - \ru Исходная кривая. + \en The initial curve. \~ + \param[out] result - \ru Сплайновая копия кривой. + \en The spline copy of the curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) NurbsCopy( const MbCurve3D & curve, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать правильный многоугольник, вписанный в окружность. + \en Create a regular polygon inscribed in a circle. \~ + \details \ru Создать правильный многоугольник, вписанный в окружность \n + (при describe == true описанного вокруг окружности) с центром centre и проходящей через point: \n + - при vertexCount <= 1 строится окружность с центром centre и проходящая через point, \n + - при vertexCount == 2 строится прямоугольник со сторонами, параллельными глобальным осям + и противоположными вершинами в centre и point. \n + \en Create a regular polygon inscribed in a circle \n + (if 'describe' == true circumscribed around a circle) with the specified centre and passing through the given point: \n + - if vertexCount <= 1, a circle with center 'center' passing through 'point' is created, \n + - if vertexCount == 2, a rectangle aligned with the global axes + with the opposite vertices at points 'centre' and 'point' is created. \n \~ + \param[in] centre - \ru Центр. + \en The center. \~ + \param[in] point - \ru Точка. + \en A point. \~ + \param[in] axisZ - \ru Ось Z для создания ЛСК кривой. + \en Z-axis for curve LCS creation. \~ + \param[in] vertexCount - \ru Количество вершин. + \en Number of vertices. \~ + \param[in] describe - \ru Описанный вокруг окружности. + \en Circumscribing around the circle. \~ + \param[out] result - \ru Правильный многоугольник. + \en The regular polygon. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) RegularPolygon( const MbCartPoint3D & centre, + const MbCartPoint3D & point, + const MbVector3D & axisZ, + size_t vertexCount, + bool describe, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать спираль. + \en Create a spiral. \~ + \details \ru Создать спираль. \n + Если spiralAxis == true, то lawCurve - определяет плоскую ось спирали. \n + Если spiralAxis == false, то lawCurve - определяет закон изменения радиуса спирали. \n + \en Create a spiral. \n + If 'spiralAxis' == true, 'lawCurve' determines the axis of a spiral. \n + If spiralAxis == false, then 'lawCurve' - determines a radius law. \n \~ + \param[in] place - \ru Локальная система координат. + \en A local coordinate system. \~ + \param[in] radius - \ru Радиус спирали. + \en A spiral radius. \~ + \param[in] step - \ru Шаг спирали. + \en A pitch. \~ + \param[in] lawCurve - \ru Формообразующая кривая. + \en A guide curve. \~ + \param[in] spiralAxis - \ru Выбор режима формообразования + \en A spiral construction mode. \~ + \param[out] result - \ru Спиральная кривая. + \en The spiral curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SpiralCurve( const MbPlacement3D & place, + double radius, + double step, + MbCurve & lawCurve, + bool spiralAxis, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать спираль. + \en Create a spiral. \~ + \details \ru Создать спираль. \n + Если spiralAxis == true, то lawCurve - определяет плоскую ось спирали. \n + Если spiralAxis == false, то lawCurve - определяет закон изменения радиуса спирали. \n + Если lawCurve == NULL, то строится коническая спираль с углом конусности angle. \n + \en Create a spiral. \n + If 'spiralAxis' == true, 'lawCurve' determines the axis of a spiral. \n + If spiralAxis == false, then 'lawCurve' - determines a radius law. \n + If lawCurve == NULL, a conical spiral is created with the specified taper angle. \n \~ + \param[in] point0 - \ru Начало локальной системы координат (ЛСК). + \en The origin of local coordinate system (LCS). \~ + \param[in] point1 - \ru Точка на оси Z ЛСК. + \en A point on Z-axis of LCS. \~ + \param[in] point2 - \ru Точка на оси X ЛСК. + \en A point on the X-axis of LCS. \~ + \param[in] radius - \ru Радиус спирали. + \en A spiral radius. \~ + \param[in] step - \ru Шаг спирали. + \en A pitch. \~ + \param[in] angle - \ru Угол коничности спирали. + \en A taper angle. \~ + \param[in] lawCurve - \ru Формообразующая кривая. + \en A guide curve. \~ + \param[in] spiralAxis - \ru Выбор режима формообразования + \en A spiral construction mode. \~ + \param[out] result - \ru Спиральная кривая. + \en The spiral curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SpiralCurve( const MbCartPoint3D & point0, + const MbCartPoint3D & point1, + const MbCartPoint3D & point2, + double radius, + double step, + double angle, + MbCurve * lawCurve, + bool spiralAxis, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать составную кривую (контур). + \en Create a composite curve (contour). \~ + \details \ru Создать составную кривую (контур) на базе исходной кривой. \n + \en Create a composite curve (contour) on the basis of the given curve. \n \~ + \param[in] curve - \ru Исходная кривая. + \en The initial curve. \~ + \param[out] result - \ru Контур на основе кривой. + \en The contour created on the basis of the curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) CreateContour( MbCurve3D & curve, + MbContour3D *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать копию кривой. + \en Create a copy of a curve. \~ + \details \ru Создать копию кривой с разбивкой ломаной линии и заменой некоторых кривых. \n + \en Create a copy of a curve with splitting of a polyline and replacement of some curves. \n \~ + \param[in] curve - \ru Исходная кривая. + \en The initial curve. \~ + \param[in] version - \ru Версия исполнения. + \en The version. \~ + \return \ru Возвращает модифицированную копию кривой, если получилось ее создать. + \en Returns a modified copy of the curve if it has been successfully created. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbCurve3D *) DuplicateCurve( const MbCurve3D & curve, + VERSION version = Math::DefaultMathVersion() ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Добавить кривую в составную кривую (контур). + \en Add a curve to a composite curve (a contour). \~ + \details \ru Добавить кривую curve в составную кривую (контур) contour. \n + Если toEnd == true, то добавить в конец. \n + Если toEnd == false, то добавить в начало. \n + \en Add a curve to a composite curve (a contour) 'contour'. \n + If toEnd == true, the curve is to be added to the end. \n + If toEnd == true, the curve is to be added to the beginning. \n \~ + \param[in] curve - \ru Добавляемая кривая + \en A curve to be added. \~ + \param[in,out] contour - \ru Модифицируемый контур. + \en A contour to be modified. \~ + \param[in] toEnd - \ru Флаг места добавления кривой. + \en The flag determines the place of the curve in the contour. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) AddCurveToContour( MbCurve3D & curve, + MbCurve3D & contour, + bool toEnd ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить кривую в пространстве по двумерной кривой. + \en Create a space curve from a two-dimensional curve. \~ + \details \ru Построить кривую в пространстве по двумерной кривой сurve на плоскости place. \n + Построение выполняется на оригинале кривой. + \en Create a space curve from a two-dimensional curve 'curve' lying on plane 'place'. \n + The construction is performed on the source curve. \~ + \param[in] place - \ru Система координат плоскости. + \en The plane coordinate system. \~ + \param[in] curve - \ru Двумерная кривая + \en A two-dimensional curve \~ + \param[out] result - \ru Плоская кривая. + \en The planar curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) PlaneCurve( const MbPlacement3D & place, + const MbCurve & curve, + MbCurve3D *& result ); + +//------------------------------------------------------------------------------ +/** \brief \ru Построить кривую на поверхности по двумерной кривой. + \en Create a curve on a surface given a two-dimensional curve. \~ + \details \ru Построить кривую на поверхности surface по двумерной кривой сurve. \n + Построение выполняется на оригиналах кривой и поверхности. + \en Create a curve on a surface 'surface' given a two-dimensional curve 'curve'. \n + The construction is performed on the original curve and surface. \~ + \param[in] surface - \ru Поверхность. + \en A surface. \~ + \param[in] curve - \ru Двумерная кривая + \en A two-dimensional curve \~ + \param[out] result - \ru Поверхностная кривая. + \en The curve on the specified surface. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SurfaceCurve( const MbSurface & surface, + const MbCurve & curve, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать поверхностную кривую, если пространственная кривая лежит на поверхности. + \en Create a curve on a surface from a space curve lying on the surface. \~ + \details \ru Создать поверхностную кривую, если пространственная кривая лежит на поверхности. \n + Разбираются частные случае точной принадлежности кривой выбранной поверхности. + В общем случае пространственная кривая считается принадлежащей поверхности, если группа точек, + полученная шаганием по параметру кривой по угловому отклонению, принадлежит поверхности. + В этом случае создается двумерная проекционная кривая этой кривой на поверхности. \n + \en Create a curve on a surface from a space curve lying on the surface. \n + The special cases of curves exactly lying on the specified surfaces are treated. + In the general case a space curve is considered to lie on the surface if a group of points + obtained by sampling the curve with the given turning angle belongs to the surface. + In this case a two-dimensional curve is created as the projection of the curve on the surface. \n \~ + \param[in] curve - \ru Пространственная кривая. + \en A space curve. \~ + \param[in] surf - \ru Поверхность. + \en A surface. \~ + \param[in] sameSurf - \ru Использовать оригинал поверхности. + \en Whether to use the source surface. \~ + \param[in] extSurf - \ru Искать на расширенной поверхности. + \en Whether to use the extended surface. \~ + \param[in] strictOnSurface - \ru Все точки кривой лежат на поверхности (true) или часть точек лежит на поверхности (false). + \en All the points of the curve belong to the surface (true) or a part of the points belong to the surface (false). \~ + \param[out] result - \ru Поверхностная кривая. + \en The curve on the specified surface. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) CurveOnSurface( const MbCurve3D & curve, + const MbSurface & surf, + bool sameSurf, bool extSurf, + MbCurve3D *& result, + bool strictOnSurface = false ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Лежит ли кривая на поверхности. + \en Determine whether the curve lies on the surface. \~ + \details \ru Проверить, лежит ли кривая полностью на поверхности. \n + \en Determine whether the curve entirely lies on the surface. \n \~ + \param[in] curve - \ru Пространственная кривая. + \en A space curve. \~ + \param[in] surf - \ru Поверхность. + \en A surface. \~ + \param[in] ext - \ru Искать на расширенной поверхности. + \en Whether to use the extended surface. \~ + \param[in] strictOnSurface - \ru Все точки кривой лежат на поверхности (true) или часть точек лежит на поверхности (false). + \en All the points of the curve belong to the surface (true) or a part of the points belong to the surface (false). \~ + \return \ru Возвращает true, если кривая лежит на поверхности. + \en Returns true if the curve lies on the surface. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (bool) IsCurveOnSurface( const MbCurve3D & curve, + const MbSurface & surf, bool ext, + bool strictOnSurface, // = false + VERSION version = Math::DefaultMathVersion() ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать массив контуров по массиву кривых. + \en Create an array of contours given an array of curves. \~ + \details \ru Создать массив контуров по массиву кривых (на оригиналах кривых). \n + \en Create an array of contours given an array of curves (using the original curves). \n \~ + \param[in] curves - \ru Множество кривых. + \en An array of curves. \~ + \param[in] metricEps - \ru Радиус захвата для стыковки кривых. + \en The radius for curves joining. \~ + \param[out] result - \ru Множество контуров. + \en The array of contours. \~ + \param[in] onlySmoothConnected - \ru Добавлять в контур только гладко стыкующиеся сегменты. + \en Whether to add only smoothly connected segments. \~ + \param[in] version - \ru Версия исполнения. + \en Version. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) CreateContours( RPArray & curves, + double metricEps, + RPArray & result, + bool onlySmoothConnected = false, + VERSION version = Math::DefaultMathVersion() ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать контуры по набору кривых с удалением вырожденных. + \en Create contours given a set of curves with elimination of degenerate ones. \~ + \details \ru Создать контуры по набору кривых с удалением вырожденных (на оригиналах кривых). \n + \en Create contours given a set of curves with elimination of degenerate ones (the original curves are used). \n \~ + \param[in, out] curves - \ru Множество кривых. + \en An array of curves. \~ + \param[in] metricAcc - \ru Радиус захвата для стыковки кривых. + \en The radius for curves joining. \~ + \param[in] onlySmoothConnected - \ru Добавлять в контур только гладко стыкующиеся сегменты. + \en Whether to add only smoothly connected segments. \~ + \param[in] version - \ru Версия исполнения. + \en Version. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) CreateContours( RPArray & curves, + double metricAcc, + bool onlySmoothConnected = false, + VERSION version = Math::DefaultMathVersion() ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать именованный трехмерный каркас. + \en Create a named three-dimensional wireframe. \~ + \details \ru Создать именованный трехмерный каркас по кривой. \n + \en Create a named three-dimensional wireframe from a curve. \n \~ + \param[in] curve - \ru Кривая. + \en The curve. \~ + \param[in] curveName - \ru Имя кривой. + \en The curve name. \~ + \param[in] mainName - \ru Главное имя операции. + \en The operation main name. \~ + \param[out] result - \ru Каркас. + \en The wireframe. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) WireFrame( const MbCurve3D & curve, + const MbName & curveName, + SimpleName mainName, + MbWireFrame *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать именованный трехмерный каркас. + \en Create a named three-dimensional wireframe. \~ + \details \ru Создать именованный трехмерный каркас по массиву кривых. \n + \en Create a named three-dimensional wireframe from a given array of curves. \n \~ + \param[in] curves - \ru Множество кривых. + \en An array of curves. \~ + \param[in] curveNames - \ru Множество имен кривых. + \en An array of the curves names. \~ + \param[in] mainName - \ru Главное имя операции. + \en The operation main name. \~ + \param[out] result - \ru Каркас. + \en The wireframe. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) WireFrame( const RPArray & curves, + const RPArray & curveNames, + SimpleName mainName, + MbWireFrame *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Аппроксимировать контур дугами и отрезками. + \en Approximate a contour with arcs and line segments. \~ + \details \ru Аппроксимировать контур дугами и отрезками. \n + Производится аппроксимация каждого из сегментов контура. \n + \en Approximate a contour with arcs and line segments. \n + The approximation is performed for each segment of the contour. \n \~ + \param[in] curve - \ru Кривая или контур, которую надо аппроксимировать. + \en A curve or a contour to approximate. \~ + \param[out] result - \ru Результат аппроксимации. + \en The approximation result. \~ + \param[in] eps - \ru Ошибка аппроксимации. + \en The approximation precision. \~ + \param[in] minRad - \ru Минимально допустимый радиус окружностей, используемых для аппроксимации. + \en The minimal acceptable radius of the circles used for the approximation. \~ + \param[in] maxRad - \ru Максимально допустимый радиус окружностей, используемых для аппроксимации. + \en The maximal acceptable radius of the circles used for the approximation. \~ + \return \ru - Возращает код результата операции. + \en - Returns operation result code. \~ +\ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) CreatePolyArcCurve3D( const MbCurve3D & curve, + MbCurve3D *& result, + double & eps, + double minRad = Math::minRadius, + double maxRad = Math::maxRadius ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Получить или создать пространственную кривую. + \en Get or create a space curve. \~ + \details \ru Получить или создать пространственную кривую из данных модельного объекта + c опциональным сохранением несущей плоскости в случае плоского объекта. \n + Двумерный контур на плоскости преобразуется не в контур на плоскости, + а в пространственный контур. \n + \en Get or create a space curve from a model object data. + with optional keeping of supporting plane in the case of a planar object. \n + A two-dimensional contour is to be converted not to a contour on a plane + but to a three-dimensional contour. \n \~ + \param[in] item - \ru Модельный объект. + \en A model object. \~ + \param[in] keepPlacement - \ru Сохранять несущую плоскость. + \en Whether to keep the supporting plane. \~ + \param[out] curve0 - \ru Пространственная кривая. + \en A space curve. \~ + \param[out] curves - \ru Дополнительные пространственные кривые (могут быть в объекте MbPlaneInstance). + \en Additional space curve (It's possible if a object "MbItem" is the object "MbPlaneInstance"). \~ + \return \ru - Возвращает успешность результата операции. + \en - Returns whether the result is successful. \~ +\ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (bool) GetSpaceCurve( const MbItem & item, + bool keepPlacement, + SPtr & curve0, + std::vector< SPtr > * curves = NULL ); + + +//------------------------------------------------------------------------------- +/** \brief \ru Построить развертку кривой/контура на плоскость. + \en Construct a unwrapping curve/contour. \~ + \details \ru Построение развертки кривой/контура на плоскость. Контур разворачивается посегментно, функцией UnwrapCurve. + Первым разворачивается сегмент, который лежит в плоскости и находиться ближе всех к точке, + если лежащих в плоскости нет, то ближащий к точке из тех что пересекают контур, если и таких нет то просто ближайший к точке. + Первый сегмент разворачивается так, чтобы точка пересечения или ближайшей проекции была неподвижной. \n + \en Construction unwrapping of the curve/contour on a plane. Each segment of the contour is unwrapped by the function UnwrapCurve. + Unwrapping starting with a segment which lies in the plane or crosses the plane and which is closest to the given point. + Unwrapping of the first segment is constructed in the manner, that cross point or closest projection of the segment on the plane were stationary. \n \~ + \param[in] curve - \ru Разворачиваемая кривая/контур. + \en Original curve/contour. \~ + \param[in] placement - \ru Локальная система координат плоскости. + \en The placement of the plane. \~ + \param[in] point - \ru Точка для определения первого сегмента. + \en The point for determine first segment. \~ + \param[in] deviationAngle - \ru Параметру точности. + \en The parameter of accuracy. \~ + \return \ru Возвращает указатель на построенную кривую с нулевум счетчиком ссылок \n + или NULL, если не удалось построить развертку для заданных параметров. + \en The pointer to the constructed curve with zero counter of references\n + return NULL, if unwrap curve can't be construvted for this parameters +\ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbCurve3D *) UnwrapCurve( const MbCurve3D & curve, + const MbPlacement3D & placement, + const MbCartPoint3D & point, + double deviationAngle = DEVIATION_SAG); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать сечение кинематического тела для заданного параметра на направляющей. + \en Create a section of evolution solid for defined parameter on the guide curve. \~ + \details \ru Создать сечение кинематического тела для заданного параметра на направляющей. + Если направляющая является контуром, то стыки между сегментами контура должны быть гладкими. \n + \en Create a section of evolution solid (sweep solid with guide curve) for defined parameter on guide curve. + If the guide curve is a contour, then this contour have to be smooth. \n \~ + \param[in] generCurves - \ru Множество плоских образующих. + \en An array of forming curves. \~ + \param[in] guideCurve - \ru Направляющая кривая (или контур). + \en An guide curve (or contour). \~ + \param[in] guideParam - \ru Параметр, заданный на направляющей кривой. + \en A parameter on the guide curve. \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[out] result - \ru Кривые сечения кинематического тела. + \en Curves of evolution solid section. \~ + \param[in] version - \ru Версия исполнения. + \en The version. \~ + \param[in] angleEpsilon - \ru Желаемая угловая точность параллельности касательных в точке стыка сегментов контура. + \en The desired angular precision parallel to the tangent at the point of junction path segments. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) EvolutionSection( const MbSweptData & generCurves, + const MbCurve3D & guideCurve, + double guideParam, + const EvolutionValues & params, + MbSweptData & result, + VERSION version = Math::DefaultMathVersion(), + double angleEpsilon = ANGLE_EPSILON ); + + +//------------------------------------------------------------------------------ +// Является ли кривая прямолинейной независимо от ее параметризации. +/** \brief \ru Является ли кривая прямолинейной независимо от ее параметризации. + \en Whether the curve is like straight-line regardless of its parameterisation. \~ + \details \ru Является ли кривая прямолинейной независимо от ее параметризации.\n + \en Whether the curve is like straight-line regardless of its parameterisation. \~ + \param[in] curve - \ru Кривая. + \en Curve. \~ + \param[in] eps - \ru Точность. + \en Accuracy. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (bool) IsLikeStraightLine( const MbCurve3D & curve, double eps ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать осевые (центральные) линии для грани оболочки. + \en Create center lines of shell face. \~ + \details \ru Создать осевые (центральные) линии для грани оболочки. \n + \en Create center lines of shell face. \n \~ + \param[in] face - \ru Исходная грань. + \en The initial face. \~ + \param[out] clCurves - \ru Набор построенных осевых линий. + \en The set of created center lines. \~ + \return \ru Возвращает true, если осевые кривые удалось создать. + \en Returns true if the center lines was created. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC( bool ) CreateCenterLineCurves( const MbFace & face, + std::vector & clCurves ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Создать плавную B-сплайновую кривую на опорной ломаной. + \en Create a fair B-spline on base polyline. \~ + \details \ru Создать плавную V-кривую на опорной ломаной и аппроксимировать B-сплайновой кривой. + Степень сплайна m, (m = 3, 4, ... , 9, 10) устанавливается в переменной degreeBSpline. + Для гармоничного перераспределения точек значение переменной arrange == true. В противном случае == false. + Для уплотнения кривой значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного). + Направление вектора в точках перегиба учитывается по значению переменной accountInflexVector (0 - как направление звена S-полигона, + 1 - как направление касательного вектора). \n + \en Create a fair V-curve on the base polyline and approximate by a B-spline curve. + The degree of spline m, (m = 3, 4, ... , 9, 10) is set by variable degreeBSpline. + For harmonious redistribution of points, the value of the variable arrange == true. Otherwise, == false. + To subdivide a curve, the value of the variable subdivision > 0 (1 for a single subdivision, 2 for a double subdivision). + The direction of the vector at the inflection points is taken into account by the value of the variable InflexVector (0 - as the direction of the S-polygon segment, + 1 - as the direction of the tangent vector). \n \~ + \param[in] polyline - \ru Исходная ломаная. + \en An initial polyline. \~ + \param[in] data - \ru Данные построения кривой. + \en The curve construction data. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC (MbResultType) CreateFairBSplineCurveOnBasePolyline( MbCurve3D * polyline, + MbFairCurveData & data, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Создать плавную B-сплайновую кривую на касательной ломаной. + \en Create a fair B-spline on tangent polyline. \~ + \details \ru Создать плавную V-кривую на касательной ломаной и аппроксимировать B-сплайновой кривой. + Степень сплайна m, (m = 3, 4, ... , 9, 10) устанавливается в переменной degreeBSpline. + Для уплотнения кривой устанавливается значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного). + Направление вектора в точках перегиба кривой совпадает с направлением звена перегиба касательной ломаной. \n + \en Create a fair V-curve on the base polyline and approximate by a B-spline curve. + The degree of spline m, (m = 3, 4, ... , 9, 10) is set by variable degreeBSpline. + To subdivide a curve, the value of the variable subdivision > 0 (1 for a single subdivision, 2 for a double subdivision). + The direction of the tangent vector at the inflection points coincides with the direction of the inflection segment of tangent polyline). \n \~ + \param[in] polyline - \ru Исходная ломаная. + \en An initial polyline. \~ + \param[in] data - \ru Данные построения кривой. + \en The curve construction data. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC (MbResultType) CreateFairBSplineCurveOnTangentPolyline( MbCurve3D * polyline, + MbFairCurveData & data, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Создать плавную кривую Безье на опорной ломаной. + \en Create a fair Bezier curve on base polyline. \~ + \details \ru Создать плавную V-кривую на опорной ломаной и аппроксимировать рациональной кубической кривой Безье. + Для гармоничного перераспределения точек значение переменной arrange == true. В противном случае == false. + Для уплотнения кривой значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного).\n + \en Create a smooth V-curve on the reference polyline and approximate a rational cubic Bezier curve. + For harmonious redistribution of points, the value of the variable arrange == true. Otherwise, == false. + To subdivide a curve, the value of the variable subdivision > 0 (1 for a single subdivision, 2 for a double subdivision). \n \~ + \param[in] polyline - \ru Исходная ломаная. + \en An initial polyline. \~ + \param[in] data - \ru Данные построения кривой. + \en The curve construction data. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC(MbResultType) CreateFairBezierCurveOnBasePolyline( MbCurve3D * polyline, + MbFairCurveData & data, + MbCurve3D *& result); + + +//------------------------------------------------------------------------------ + /** \brief \ru Создать плавную кривую Безье на касательной ломаной. + \en Create a fair Bezier curve on tangent polyline. \~ + \details \ru Создать плавную V-кривую на касательной ломаной и аппроксимировать рациональной кубической кривой Безье. + Для уплотнения кривой значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного). \n + \en Create a smooth V-curve on a tangent polyline and approximate a rational cubic Bezier curve. + To subdivide a curve, the value of the variable subdivision > 0 (1 for a single subdivision, 2 for a double subdivision). \n \~ + \param[in] polyline - \ru Исходная ломаная. + \en An initial polyline. \~ + \param[in] data - \ru Данные построения кривой. + \en The curve construction data. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке из списка сообщений метода MessageError. + \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number.from the message list of the MessageError method. \~ + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC (MbResultType) CreateFairBezierCurveOnTangentPolyline( MbCurve3D * polyline, + MbFairCurveData & data, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Создать ГО Эрмита на кривой Безье. + \en Create Hermite GD on a Bezier curve. \~ + \details \ru Создать геометрический определитель Эрмита в виде ломаной линии на рациональной сплайновой кривой Безье. \n + На кривой определяется опорная ломаная, вершины которой принадлежат узловым точкам сплайна. + Определяются векторы касательных и векторы кривизны. Вектор касательной может иметь произвольную длину. + Вектор кривизны должет иметь длину, равную значению кривизны в данной вершине опорной ломаной. + Если значение кривизны равно нулю в точке перегиба, то вектор кривизны откладывается по направлению касательного вектора в данной вершине + и имеет произвольную ненулевую длину, не превышающую длину касательного вектора. + Направленная ломаная ГО Эрмита последовательно обходит вершины опорной ломаной, концы касательных векторов и концы векторов кривизны. + Обход происходит следующим образом: от вершины опорной к концу касательного вектора, затем возврат к вершине опорной ломаной, + переход к концу вектора кривизны, возврат к вершине опорной ломаной, затем переход к следующей вершине опорной ломаной и т.д. + Вершина с номером 1 и вершины с номерами через 5 (1, 6, 11, ... ) принадлежат концам касательных векторов. + Вершина с номером 3 и вершины с номерами через 5 (3, 8, 13, ... ) принадлежат концам векторов кривизны. + \en Create a Hermite geometric determinant in the form of a polyline on a rational Bezier spline curve. \n + A base polyline is defined on the curve, the vertices of which belong to the nodal points of the spline. + The tangent vectors and the curvature vectors are determined. The tangent vector can be of arbitrary length. + The curvature vector must have a length equal to the value of curvature at a given vertex of the base polyline. + If the curvature value is zero at the inflection point, then the curvature vector is set in the direction of the tangent vector at the given vertex + and has an arbitrary nonzero length not exceeding the length of the tangent vector. \ n + A directional polyline of Hermite GD sequentially bypasses the vertices of the base polyline, the ends of the tangent vectors and the ends of the curvature vectors. + Bypass occurs as follows: from the vertex of the base polyline to the end of the tangent vector, then return to the vertex of the base polyline, + transition to the end of the curvature vector, return to the vertex of the base polyline, then transition to the next vertex of the base polyline, etc. + The vertex with number 1 and the vertices with numbers in 5 (1, 6, 11, ...) belong to the ends of the tangent vectors. + The vertex with number 3 and the vertices with numbers in 5 (3, 8, 13, ...) belong to the ends of the curvature vectors. \~ + \param[in] curve - \ru Исходная кривая. + \en An initial curve. \~ + \param[in] data - \ru Данные построения кривой. + \en The curve construction data. \~ + \param[out] polyline - \ru 3D ломаная ГО Эрмита. + \en 3D polyline of Hermite GD. \~ + \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке из списка сообщений метода MessageError. + \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number from the message list of the MessageError method.\~ + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC(MbResultType) KernelCreateHermiteGDOnBezierCurve( MbNurbs3D * curve, + MbFairCurveData & data, + MbCurve3D *& polyline ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Создать кривую Безье на ГО Эрмита. + \en Create a Bezier on Hermite GD. \~ + \details \ru Изогеометрически построить рациональную кубическую кривую Безье на ГО Эрмита второго порядка фиксации. + Используются все параметры ГО Эрмита второго порядка фиксации: точки, касательные векторы и значения кривизны. + \en Isogeometrically create a cubic rational Bezier curve on a Hermite GD of second-order fixation. + Used all params of Hermite GD of second-order fixation: points, tangent vectors and values of curvature. \~ + \param[in] polyline - \ru Исходная ломаная. + \en An initial polyline. \~ + \param[in] data - \ru Данные построения кривой. + \en The curve construction data. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке из списка сообщений метода MessageError. + \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number from the message list of the MessageError method.\~ + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC (MbResultType) CreateBezierCurveOnHermiteGD( MbCurve3D * polyline, + MbFairCurveData & data, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Создать B-сплайновую кривую на ГО Эрмита. + \en Create a B-spline on Hermite GD. \~ + \details \ru Создать B-сплайную кривую на ГО Эрмита первого порядка фиксации. \n + Направления касательных векторов ГО Эрмита определяют направления звеньев S-полигона. + Направление вектора в точках перегиба учитывается по значению переменной accountInflexVector (0 - как направление звена S-полигона, \n + 1 - как направление касательного вектора). \n + Кривизна в концевых точках учитывается по значению accountCurvature (0 - не учитывается, 1 - в начальной точке, 2 - в конечной точке, 3 - учитываются на обоих концах). + \en Create a B-spline curve on Hermite's GD of first-order fixation. \ n +               The directions of the tangent vectors of the Hermite GD determine the directions of the S-polygon segmentss. \n +               The direction of the vector at the inflection points is taken into account by the value of the variable InflexVector (0 - as the direction of the S-polygon segment, +               1 - as the direction of the tangent vector). \n +               The curvature at the end points is taken into account by the value of accountCurvature (0 - not taken into account, 1 - at the start point, 2 - at the end point, 3 - are taken into account at both ends). \~ + \param[in] polyline - \ru Исходная ломаная. + \en An initial polyline. \~ + \param[in] data - \ru Данные построения кривой. + \en The curve construction data. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке из списка сообщений метода MessageError. + \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number from the message list of the MessageError method.\~ + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC(MbResultType) CreateBSplineCurveOnHermiteGD( MbCurve3D * polyline, + MbFairCurveData & data, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Создать плавную кривую Безье на опорной ломаной ГО Эрмита. + \en Create a fair Bezier curve on base polyline of Hermite GD. \~ + \details \ru Создать плавную V-кривую на опорной ломаной ГО Эрмита и аппроксимировать рациональной кубической кривой Безье. \n + Учитываются концевые значения кривизны и направления касательных в точках перегиба ГО Эрмита. + Для гармоничного перераспределения точек значение переменной arrange == true. В противном случае == false. + Для уплотнения кривой значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного). + \en Create a fair V-curve on the base polyline and approximate by a rational cubic Bezier curve. \ n + The end values of the curvature and directions of the tangents at the inflection points of the Hermite GO are taken into account. + For harmonious redistribution of points, the value of the variable arrange == true. Otherwise, == false. + To subdivide a curve, the value of the variable subdivision> 0 (1 for a single subdivision, 2 for a double subdivision).\~ + \param[in] polyline - \ru Исходная ломаная. + \en An initial polyline. \~ + \param[in] data - \ru Данные построения кривой. + \en The curve construction data. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке из списка сообщений метода MessageError. + \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number from the message list of the MessageError method.\~ + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC(MbResultType) CreateFairBezierCurveOnBasePolylineOfHermiteGD( MbCurve3D * polyline, + MbFairCurveData & data, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Создать плавную кривую Безье на касательных прямых ГО Эрмита. + \en Create a fair Bezier curve on tangent lines of Hermite GD. \~ + \details \ru Создать плавную V-кривую на касательных прямых ГО Эрмита и аппроксимировать рациональной кубической кривой Безье. \n + Учитываются концевые значения кривизны и положения точек перегиба ГО Эрмита. + Для уплотнения кривой значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного). + \en Create a fair V-curve on the base polyline and approximate by a rational cubic Bezier curve. \ n +  The end values of the curvature and directions of the tangents at the inflection points of the Hermite GO are taken into account. + To subdivide a curve, set the value of the variable subdivision > 0 (1 for a single subdivision, 2 for a double subdivision).\~ + \param[in] polyline - \ru Исходная ломаная. + \en An initial polyline. \~ + \param[in] data - \ru Данные построения кривой. + \en The curve construction data. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке \n + из списка сообщений метода MessageError. + \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number \ n +              from the message list of the MessageError method.\~ + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC (MbResultType) CreateFairBezierCurveOnTangentsOfHermiteGD( MbCurve3D * polyline, + MbFairCurveData & data, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Создать плавную B-сплайновую кривую на опорной ломаной ГО Эрмита. + \en Create a fair B-spline curve on base polyline of Hermite GD. \~ + \details \ru Создать плавную V-кривую на опорной ломаной и аппроксимировать B-сплайновой кривой. \n + Степень сплайна m, (m = 3, 4, ... , 9, 10) устанавливается в переменной degreeBSpline. + Для гармоничного перераспределения точек значение переменной arrange == true. В противном случае == false. + Для уплотнения кривой значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного). + Направление вектора в точках перегиба учитывается по значению переменной accountInflexVector (0 - как направление звена S-полигона, + 1 - как направление касательного вектора). \n + Кривизна в концевых точках учитывается по значению accountCurvature (0 - не учитывается, 1 - в начальной точке, 2 - в конечной точке, 3 - учитываются на обоих концах). + \en Create a fair V-curve on the base polyline and approximate by a B-spline curve. \ n + The degree of spline m, (m = 3, 4, ... , 9, 10) is set by variable degreeBSpline. + For harmonious redistribution of points, the value of the variable arrange == true. Otherwise, == false. + To subdivide a curve, the value of the variable subdivision > 0 (1 for a single subdivision, 2 for a double subdivision). + The directions of the tangent vectors of the Hermite GD determine the directions of the S-polygon segmentss. \n + The direction of the vector at the inflection points is taken into account by the value of the variable InflexVector (0 - as the direction of the S-polygon segment, \ n + 1 - as the direction of the tangent vector). \n + The curvature at the end points is taken into account by the value of accountCurvature (0 - not taken into account, 1 - at the start point, 2 - at the end point, 3 - are taken into account at both ends). \~ + \param[in] polyline - \ru Исходная ломаная. + \en An initial polyline. \~ + \param[in] data - \ru Данные построения кривой. + \en The curve construction data. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке из списка сообщений метода MessageError. + \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number from the message list of the MessageError method.\~ + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC (MbResultType) CreateFairBSplineCurveOnBasePolylineOfHermiteGD( MbCurve3D * polyline, + MbFairCurveData & data, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Создать плавную B-сплайновую кривую на касательных прямых ГО Эрмита. + \en Create a fair B-spline curve on tangent lines of Hermite GD. \~ + \details \ru Создать плавную V-кривую на касательных прямых ГО Эрмита и аппроксимировать B-сплайновой кривой. \n + Степень сплайна m, (m = 3, 4, ... , 9, 10) устанавливается в переменной degreeBSpline. + Для гармоничного перераспределения точек значение переменной arrange == true. В противном случае == false. + Для уплотнения кривой установите значение переменной subdivision > 0 (1 - для однократного уплотнения, 2 - для двукратного). + Кривизна в концевых точках учитывается по значению accountCurvature (0 - не учитывается, 1 - в начальной точке, 2 - в конечной точке, 3 - учитываются на обоих концах). + \en Create a fair V-curve on the base polyline of Hermite GD and approximate by a B-spline curve. \ n + The degree of spline m, (m = 3, 4, ... , 9, 10) is set by variable degree. + For harmonious redistribution of points, the value of the variable arrange == true. Otherwise, == false. + To subdivide a curve, set the value of the variable subdivision > 0 (1 for a single subdivision, 2 for a double subdivision). + The curvature at the end points is taken into account by the value of accountCurvature (0 - not taken into account, 1 - at the start point, 2 - at the end point, 3 - are taken into account at both ends). \~ + \param[in] polyline - \ru Исходная ломаная. + \en An initial polyline. \~ + \param[in] data - \ru Данные построения кривой. + \en The curve construction data. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает значение результата операции. 0 - при успешном построении кривой. При > 0 значение равно номеру сообщения об ошибке из списка сообщений метода MessageError. + \en Returns operation result value. 0 - upon successful creation of the curve. If > 0, the value is equal to the error message number from the message list of the MessageError method.\~ + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC(MbResultType) CreateFairBSplineCurveOnTangentsOfHermiteGD( MbCurve3D * polyline, + MbFairCurveData & data, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Создать клотоиду. + \en Create a clothoid. \~ + \details \ru Создать начальный участок клотоиды. \n + В переменных data задаются параметры построения клотоиды. Максимальная длина начального участка клотоиды в переменной + clothoidMax и минимальный радиус на конце участка в переменной clothoidRMin. В переменной clothoidSegms задается количество + сегментов аппроксимирующей клотоиду сплайновой кривой Безье. + \en Create the starting part of the clothoid. \ n +             The parameters to create the clothoid are set in the data. The maximum length of the initial section of the clothoid in the variable clothoidMax and the minimum radius at the end of the section in the variable clothoidRMin. + The variable clothoidSegms sets the number of segments of the Bezier spline curve approximating the clothoid .\~ + \param[in] data - \ru Данные построения кривой. + \en The curve construction data. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает значение результата операции. + \en Returns operation result value. + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC(MbResultType) KernelCreateClothoid( MbFairCurveData & data, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Создать сектрису. + \en Create a sectrix. \~ + \details \ru Создать сектрису Маклорена. \n + На касательной ломаной из двух звеньев генерируются точки сектрисы Маклорена - кривой с монотонным изменением кривизны. + Сектриса Маклорена изогеометрически с сохранением монотонности аппроксимируется сплайновой кривой Безье. + \en Create a sectrix of Maclourin. + On a tangent polyline of two links, points of the Maclaurin sectrix — a curve with a monotonic change in curvature — are generated. +             The Maclaurin sectrix is isometrically approximated with the preservation of monotony by the Bezier spline curve.\~ +            + \param[in] polyline - \ru Исходная ломаная. + \en An initial polyline. \~ + \param[in] data - \ru Данные построения кривой. + \en The curve construction data. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает значение результата операции. + \en Returns operation result value. + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC(MbResultType) KernelCreateSectrix( MbCurve3D * polyline, + MbFairCurveData & data, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Увеличить степень NURBzS кривой. + \en Elevate the degree of the NURBzS curve. \~ + \details \ru Увеличить степень NURBzS кривой. \n + Степень кубической кривой увеличивается до шестой степени. + Степени больше 6 увеличиваются на единцу. Максимальная степень - 10. + \en The increasing the degree of the NURBzS curve. \n + The degree of the cubic curve increases to the sixth degree. +             Degrees greater than 6 increase by one. The maximum degree is 10.\~ + \param[in] curve - \ru Исходная кривая. + \en An initial curve. \~ + \param[in] data - \ru Данные построения кривой. + \en The curve construction data. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает значение результата операции. + \en Returns operation result value. + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC(MbResultType) ElevateDegreeNurbzs( MbNurbs3D * curve, + MbFairCurveData & data, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Уплотнить NURBS кривую. + \en Subdivide the NURBS curve. \~ + \details \ru Однократно уплотнить NURBS кривую. \n + \en Single subdivision of NURBS curve. \~ + \param[in] curve - \ru Исходная кривая. + \en An initial curve. \~ + \param[in] data - \ru Данные построения и преобразования кривой. + \en Curve construction and transformation data. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает значение результата операции. + \en Returns operation result value. + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC(MbResultType) SubdivideNurbs( MbNurbs3D * curve, + MbFairCurveData & data, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Выделить участок NURBS кривой / изменить формат NURBS кривой. + \en To extrct part of NURBS curve / change the format of the NURBS curve. \~ + \details \ru Выделить участок NURBS кривой / изменить формат NURBS кривой. \n + В data устанавливаются переменые преобразований. Выделямый участок определяется номером начального сегмента в переменной numSegment + и количеством сегментов в переменной numSegments. Выходной формат сплайна определяется в переменной outFormat. + При значении 2 - NURBS кривая с управляющим S-полигоном. При значении 3 - рациональная кривая Безье + с управляющим GB-полигоном. + \en To extract part of NURBS curve / change the format of the NURBS curve. \n + Transformation variables are set in data. The extracting part is determined by the number of the starting segment in the variable numSegment and the number of segments in the variable numSegments. + The output spline format is defined in the outFormat variable.    If the value is 2 - NURBS curve with control S-polygon. If the value is 3 - rational Bezier curve with GB-control polygon.\~ + \param[in] curve - \ru Исходная кривая. + \en An initial curve. \~ + \param[in] data - \ru Данные построения и преобразования кривой. + \en Curve construction and transformation data. \~ + \param[out] result - \ru Сплайновая кривая. + \en The spline curve. \~ + \return \ru Возвращает значение результата операции. + \en Returns operation result value. + \ingroup Curve3D_Modeling + */ +// --- +MATH_FUNC(MbResultType) ExtractChangeNurbs( MbNurbs3D * curve, + MbFairCurveData & data, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ + /** \brief \ru Вставить узел в NURBS. + \en Insert node in NURBS. \~ + \details \ru Вставить узел в NURBS. \n + В data устанавливаются переменые преобразований. Место вставки узла определяется переменными nSegment и tParam. + В переменной nSegment устанавливается номер сегмента, в переменной tParam (0.1 < tParam < 0.9) задается значение внутреннего параметра на сегменте сплайна. + \en Insert node in NURBS. Transformation variables are set in data. The insertion positon of the node is determined by the variables nSegment and tParam. +             The nSegment variable sets the segment number, the tParam variable (0.1 -#include -#include -#include -#include -#include - - -class MATH_CLASS MbCurve; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbSurface; -class MATH_CLASS MbSplineSurface; -class MATH_CLASS MbCurveEdge; -class MATH_CLASS MbFace; -class MATH_CLASS MbSolid; -class MATH_CLASS MbSNameMaker; - - -//------------------------------------------------------------------------------ -/** \brief \ru Модифицировать тело по матрице. - \en Modify a solid by the matrix. \~ - \details \ru Выполнить трансформацию копии исходного тела по матрице, рассчитанной по габаритному кубу. \n - \en Transform a copy of the solid using the matrix calculated by bounding box of solid. \n \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] sameShell - \ru Режим копирования исходного тела. - \en The mode of copying of the initial solid. \~ - \param[in] p - \ru Параметры трансформации. - \en The transformation parameters. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Модифицированное тело. - \en The modified solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Direct_Modeling -*/ -// --- -MATH_FUNC (MbResultType) TransformedSolid( MbSolid & solid, - MbeCopyMode sameShell, - const TransformValues & p, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Собрать грани оболочки для методов прямого моделирования. - \en Modify a shell by the methods of direct modeling. \~ - \details \ru Функция собирает грани оболочки для методов прямого моделирования: \n - удаление из тела выбранных граней с окружением (way==dmt_Remove), \n - удаление выбранных граней скругления тела (way==dmt_Purify). \n - Для удаления граней собираются замкнутые цилиндрические, конические, тороидальные, сферические грани тела, - а также грани вращения, радиус которых не превосходит указанный радиус. - Для удаления граней скругления собираются незамкнутые цилиндрические, тороидальные, сферические грани, - а также грани скругления, радиус которых не превосходит указанный радиус. \n - \en The method collects the faces of the shell for direct modeling methods: \n - removal of the faces from a shell (way==dmt_Remove), \n - removal of the fillet faces from a shell (way==dmt_Purify). \n - The cylindrical, conical, toroidal, spherical, and revolution periodic faces are collect to remove way. - The cylindrical, toroidal, spherical non-periodic, and fillet faces are collect to purify way. \n \~ - \param[in] shell - \ru Исходная оболочка тела. - \en The initial faces set. \~ - \param[in] way - \ru Способ модификации. - \en Way of the modification. \~ - \param[in] radius - \ru Радиус собираемых граней. - \en Radius of collected faces. \~ - \param[in] faces - \ru Найденные грани для дальнейшей модификации. - \en Found faces to be modified. \~ - \return \ru Возвращает код результата действий. - \en Returns action result code. \~ - \ingroup Direct_Modeling -*/ -// --- -MATH_FUNC (MbResultType) CollectFacesForModification( MbFaceShell * shell, - MbeModifyingType way, - double radius, - RPArray & faces ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Модифицировать или построить тело методами прямого моделирования. - \en Modify a solid by the methods of direct modeling. \~ - \details \ru В зависимости от параметров модификации метод выполняет одно из следующих действий: \n - 1. Удаление из тела выбранных граней с окружением (param.way==dmt_Remove). \n - 2. Создание тела из выбранных граней с окружением (param.way==dmt_Create). \n - 3. Перемещение выбранных граней с окружением относительно оставшихся граней тела (param.way==dmt_Action). \n - 4. Замена выбранных граней тела эквидистантными гранями (param.way==dmt_Offset). \n - 5. Изменение радиуса выбранных граней скругления (param.way==dmt_Fillet). \n - 6. Замена выбранных граней тела деформируемыми гранями для редактирования (param.way==dmt_Supple). \n - 7. Удаление выбранных граней скругления тела (param.way==dmt_Purify). - \en The method is for one of listed actions below depends of parameters: \n - 1. Removal of the specified faces with the neighborhood from a solid (param.way==dmt_Remove). \n - 2. Creation of a solid from the specified faces with the neighborhood (param.way==dmt_Create). \n - 3. Translation of the specified faces with neighborhood relative to the other faces of the solid (param.way==dmt_Action). \n - 4. Replacement of the specified faces of a solid with the offset faces (param.way==dmt_Offset). \n - 5. Changing of the radius of the specified fillet faces (param.way==dmt_Fillet). \n - 6. Replacement of the specified faces of a solid with a deformable faces for editing (param.way==dmt_Supple). \n - 7. Removal of the specified fillet faces from a solid (param.way==dmt_Purify). \n \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] sameShell - \ru Режим копирования исходного тела. - \en The mode of copying of the initial solid. \~ - \param[in] params - \ru Параметры модификации. - \en Parameters of the modification. \~ - \param[in] faces - \ru Изменяемые грани тела. - \en Faces to be modified. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Модифицированное тело. - \en The modified solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Direct_Modeling -*/ -// --- -MATH_FUNC (MbResultType) FaceModifiedSolid( MbSolid & solid, - MbeCopyMode sameShell, - const ModifyValues & params, - const RPArray & faces, - const MbSNameMaker & names, - MbSolid *& result ); - -//------------------------------------------------------------------------------ -/** \brief \ru Модифицировать или построить тело методами прямого моделирования. - \en Modify a solid by the methods of direct modeling. \~ - \details \ru Метод выполняет удаление указанных рёбер, слияние их вершин и модификацию окружающих граней (param.way==dmt_Merger). - По направлению вектора "param.direction" определяется: начальная ли вершина ребра будет слита с конечной вершиной, или конечная вершина ребра будет слита с начальной вершиной. \n - \en The method performs the deletion of selectsd edges, merging their vertices and modification of surrounding faces (param.way==dmt_Merger). - The direction of the vector "params.direction" determines whether the start vertex of an edge is merged with the end vertex, or whether the end vertex of an edge is merged with the start vertex. \n - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] sameShell - \ru Режим копирования исходного тела. - \en The mode of copying of the initial solid. \~ - \param[in] params - \ru Параметры модификации, способ должен быть равен param.way==dmt_Merger. - \en Parameters of the modification, the way must be equal to param.way==dmt_Merger. \~ - \param[in] edges - \ru Удаляемые рёьра тела. - \en Edges to be removed. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Модифицированное тело. - \en The modified solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Direct_Modeling -*/ -// --- - -MATH_FUNC (MbResultType) EdgeModifiedSolid( MbSolid & solid, - MbeCopyMode sameShell, - const ModifyValues & params, - const RPArray & edges, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Заменить выбранные грани тела деформируемыми гранями. - \en Replace the specified faces of solid with deformable faces. \~ - \details \ru Заменить выбранные грани тела деформируемыми гранями (превращение в NURBS для редактирования). \n - \en Replace the specified faces of the solid with deformable faces (conversion to NURBS for editing). \n \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] sameShell - \ru Режим копирования исходного тела. - \en The mode of copying of the initial solid. \~ - \param[in] p - \ru Параметры преобразования. - \en The transformation parameters. \~ - \param[in] faces - \ru Заменяемые грани тела. - \en Faces to replace. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Модифицированное тело. - \en The modified solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Direct_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ModifiedNurbsItem( MbSolid & solid, - MbeCopyMode sameShell, - const NurbsValues & p, - const RPArray & faces, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Заменить выбранную грань тела деформируемой гранью. - \en Replace the specified face of the solid by a deformable face. \~ - \details \ru Заменить выбранную грань тела деформируемой гранью (превращение в NURBS для редактирования). \n - \en Replace the specified face of the solid by a deformable face (conversion to NURBS for editing). \n \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] sameShell - \ru Режим копирования исходного тела. - \en The mode of copying of the initial solid. \~ - \param[in] p - \ru Параметры преобразования. - \en The transformation parameters. \~ - \param[in] face - \ru Заменяемая грань тела. - \en A face to replace. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Модифицированное тело. - \en The modified solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Direct_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ModifiedNurbsItem( MbSolid & solid, - MbeCopyMode sameShell, - const NurbsValues & p, - const MbFace & face, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Получить NURBS-поверхности грани. - \en Get the NURBS-surfaces of a face. \~ - \details \ru Выполнить построение деформируемой поверхности для исходной грани. \n - \en Create a deformable surface for the initial face. \n \~ - \param[in] face - \ru Исходная грань. - \en The initial face. \~ - \return \ru Возвращает NURBS-поверхности грани. - \en Returns NURBS-surfaces of the face. \~ - \ingroup Direct_Modeling -*/ -// --- -MATH_FUNC (MbSurface *) GetControlSurface( const MbFace & face ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Получить контрольные точки NURBS-поверхности грани. - \en Get the control points of the NURBS-surface of a face. \~ - \details \ru Получить множество контрольных точек NURBS-поверхности грани и множества их весов. \n - \en Get a set of the control points of a NURBS-surface of a face and a set of their weights. \n \~ - \param[in] face - \ru Исходная грань. - \en The initial face. \~ - \param[out] controlPoints - \ru Контрольные точки NURBS-поверхности грани. - \en The control points of the NURBS-surface of the face. \~ - \param[out] result - \ru Веса контрольных точек. - \en The weights of the control points. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Direct_Modeling -*/ -// --- -MATH_FUNC (MbResultType) FaceControlPoints( const MbFace & face, - Array2 & controlPoints, - Array2 & result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Деформировать грань тела. - \en Deform a face of a solid. \~ - \details \ru Деформировать грань тела путём подстановки присланных контрольных точек NURBS-поверхности грани. \n - \en Deform a face of a solid by substitution the control points of NURBS-surface of the face with the given control points. \n \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] sameShell - \ru Режим копирования исходного тела. - \en The mode of copying of the initial solid. \~ - \param[in] face - \ru Изменяемая грань тела. - \en A face of a solid to be modified. \~ - \param[in] faceSurface - \ru Новая NURBS-поверхность для грани. - \en The new NURBS-surface of the face. \~ - \param[in] fixedPoints - \ru Неподвижные узлы. - \en The fixed points. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Модифицированное тело. - \en The modified solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Direct_Modeling -*/ -// --- -MATH_FUNC (MbResultType) NurbsModification( MbSolid & solid, - MbeCopyMode sameShell, - MbFace * face, - MbSurface & faceSurface, - Array2 & fixedPoints, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Деформировать грань тела. - \en Deform a face of a solid. \~ - \details \ru Деформировать грань тела путём подстановки присланных контрольных точек NURBS-поверхности грани. \n - \en Deform a face of a solid by substitution the control points of NURBS-surface of the face with the given control points. \n \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] sameShell - \ru Режим копирования исходного тела. - \en The mode of copying of the initial solid. \~ - \param[in] face - \ru Изменяемая грань тела. - \en A face of a solid to be modified. \~ - \param[in] controlPoints - \ru Контрольные точки NURBS-поверхности грани. - \en The control points of the NURBS-surface of the face. \~ - \param[in] weights - \ru Веса контрольных точек. - \en The weights of the control points. \~ - \param[in] fixedPoints - \ru Неподвижные узлы. - \en The fixed points. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Модифицированное тело. - \en The modified solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Direct_Modeling -*/ -// --- -MATH_FUNC (MbResultType) NurbsModification( MbSolid & solid, - MbeCopyMode sameShell, - MbFace * face, - const Array2 & controlPoints, - const Array2 & weights, - Array2 * fixedPoints, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить деформируемую призму. - \en Create a deformable prism. \~ - \details \ru Построить тело в форме прямого параллелепипеда с деформируемыми гранями. \n - \en Create a solid as a right parallelepiped with deformable faces. \n \~ - \param[in] place - \ru Локальная система координат. - \en A local coordinate system. \~ - \param[in] ax - \ru Размер по X. - \en The size in X-direction. \~ - \param[in] ay - \ru Размер по Y. - \en The size in Y-direction. \~ - \param[in] az - \ru Размер по Z. - \en The size in Z-direction. \~ - \param[in] outDir - \ru Ориентация нормалей наружу. - \en An outer orientation of the normals. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] name - \ru Главное имя. - \en The main name. \~ - \param[in] param - \ru Параметры NURBS-поверхностей граней параллелепипеда. - \en The parameters of NURBS-surfaces of the parallelepiped faces. \~ - \param[out] result - \ru Тело из NURBS-поверхностей. - \en The solid constructed from the NU|RBS-surfaces. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Direct_Modeling -*/ -// --- -MATH_FUNC (MbResultType) NurbsBlockSolid( const MbPlacement3D & place, - double ax, - double ay, - double az, - bool outDir, - const MbSNameMaker & names, - SimpleName name, - NurbsBlockValues & param, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить согласованную поверхность. - \en Create a matched surface. \~ - \details \ru Для исходной поверхности выполнить построение изменённой поверхности - путем выставления сопряжения вдоль кривой. \n - \en Create a modified surface for the initial surface - by specifying the conjugation along the curve. \n \~ - \param[in] curve - \ru Кривая пересечения поверхностей ребра. - \en The intersection curve of the edge surfaces. \~ - \param[in] sences - \ru Ориентация кривой ребра в цикле. - \en The edge curve sense in the loop. \~ - \param[in] faceSences - \ru Ориентация нормали на смежной грани. - \en The adjacent face normal orientation. \~ - \param[in] surface - \ru Исходная сплайновая поверхность для изменяемой грани. - \en The initial spline surface of the face to be modified. \~ - \param[in] tension - \ru Натяжение. - \en The tension. \~ - \param[in] conType - \ru Тип сопряжения. - \en The conjugation type. \~ - \param[in] insertNum - \ru Вставляемый ряд. - \en The row number. \~ - \param[out] result - \ru NURBS-поверхность, полученная в результате преобразований. - \en The NURBS-surface obtained as a result of the modifications. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Direct_Modeling -*/ -// --- -MATH_FUNC (MbResultType) NurbsFaceConjugation( const MbSurfaceIntersectionCurve & curve, - bool sences, - bool faceSences, - const MbSplineSurface & surface, - double tension, - MbeConjugationType conType, - size_t insertNum, - MbSplineSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить подобную поверхность. - \en Create a similar surface. \~ - \details \ru Для исходной поверхности выполнить построение подобной поверхности - по указанной поверхности-образцу. \n - \en Create a surface similar to the initial one - given the pattern surface. \n \~ - \param[in] originSurface - \ru Поверхность-образец. - \en A pattern surface. \~ - \param[in] surface - \ru Исходная сплайновая поверхность для изменяемой грани. - \en The initial spline surface of the face to be modified. \~ - \param[in] uToU - \ru Флаг сохранения параметрического направления как у поверхности-образца. - \en Whether to keep the parametric direction of the pattern surface. \~ - \param[in] normSence - \ru Флаг сохранения направления нормали поверхности-образца. - \en Whether to keep the normal direction of the pattern surface. \~ - \param[out] result - \ru NURBS-поверхность, полученная в результате преобразований. - \en The NURBS-surface obtained as a result of the modifications. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Direct_Modeling -*/ -// --- -MATH_FUNC (MbResultType) NurbsFaceSimilarity( const MbSurface & originSurface, - const MbSplineSurface & surface, - bool uToU, - bool normSence, - MbSplineSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить сглаженную поверхность. - \en Create a smoothed surface. \~ - \details \ru Выполнить сглаживание копии исходной поверхности не изменяя ее порядок и количество контрольных точек. \n - \en Perform smoothing of a copy of the initial surface without changing its order and the number of control points. \n \~ - \param[in] surface - \ru Исходная сплайновая поверхность для изменяемой грани. - \en The initial spline surface of the face to be modified. \~ - \param[in] udegree - \ru Параметр сглаживания по первому параметру поверхности. - \en The smoothing surface degree for direction of first parameter of surface. \~ - \param[in] vdegree - \ru Параметр сглаживания по второму параметру поверхности. - \en The smoothing surface degree for direction of second parameter of surface. \~ - \param[out] result - \ru NURBS-поверхность, полученная в результате преобразований. - \en The NURBS-surface obtained as a result of the modifications. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Direct_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SplineSurfaceSmoothing( const MbSplineSurface & surface, - size_t udegree, - size_t vdegree, - MbSplineSurface *& result ); - - -#endif // __ACTION_DIRECT_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Методы прямого редактирования тел. + \en Functions for direct editing of solids. \~ + \details \ru Прямое моделирование позволяет редактировать и создавать подобные тела + путём непосредственной модификации элементов уже построенных тел. \n + Представленные ниже функции пока не доведены до коммерческого состояния + и позволяют лишь познакомиться с будущими возможностями геометрического ядра. + \en The direct modeling allows to edit and to create similar solids + by direct modification of elements of already constructed solids. \n + The following functions do not conform to the state of a commercial product yet + and allows just to acquaint oneself with the future features of the geometrical kernel. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ACTION_DIRECT_H +#define __ACTION_DIRECT_H + + +#include +#include +#include +#include +#include +#include + + +class MATH_CLASS MbCurve; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbSurface; +class MATH_CLASS MbSplineSurface; +class MATH_CLASS MbCurveEdge; +class MATH_CLASS MbFace; +class MATH_CLASS MbSolid; +class MATH_CLASS MbSNameMaker; + + +//------------------------------------------------------------------------------ +/** \brief \ru Модифицировать тело по матрице. + \en Modify a solid by the matrix. \~ + \details \ru Выполнить трансформацию копии исходного тела по матрице, рассчитанной по габаритному кубу. \n + \en Transform a copy of the solid using the matrix calculated by bounding box of solid. \n \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] sameShell - \ru Режим копирования исходного тела. + \en The mode of copying of the initial solid. \~ + \param[in] p - \ru Параметры трансформации. + \en The transformation parameters. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Модифицированное тело. + \en The modified solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Direct_Modeling +*/ +// --- +MATH_FUNC (MbResultType) TransformedSolid( MbSolid & solid, + MbeCopyMode sameShell, + const TransformValues & p, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Собрать грани оболочки для методов прямого моделирования. + \en Modify a shell by the methods of direct modeling. \~ + \details \ru Функция собирает грани оболочки для методов прямого моделирования: \n + удаление из тела выбранных граней с окружением (way==dmt_Remove), \n + удаление выбранных граней скругления тела (way==dmt_Purify). \n + Для удаления граней собираются замкнутые цилиндрические, конические, тороидальные, сферические грани тела, + а также грани вращения, радиус которых не превосходит указанный радиус. + Для удаления граней скругления собираются незамкнутые цилиндрические, тороидальные, сферические грани, + а также грани скругления, радиус которых не превосходит указанный радиус. \n + \en The method collects the faces of the shell for direct modeling methods: \n + removal of the faces from a shell (way==dmt_Remove), \n + removal of the fillet faces from a shell (way==dmt_Purify). \n + The cylindrical, conical, toroidal, spherical, and revolution periodic faces are collect to remove way. + The cylindrical, toroidal, spherical non-periodic, and fillet faces are collect to purify way. \n \~ + \param[in] shell - \ru Исходная оболочка тела. + \en The initial faces set. \~ + \param[in] way - \ru Способ модификации. + \en Way of the modification. \~ + \param[in] radius - \ru Радиус собираемых граней. + \en Radius of collected faces. \~ + \param[in] faces - \ru Найденные грани для дальнейшей модификации. + \en Found faces to be modified. \~ + \return \ru Возвращает код результата действий. + \en Returns action result code. \~ + \ingroup Direct_Modeling +*/ +// --- +MATH_FUNC (MbResultType) CollectFacesForModification( MbFaceShell * shell, + MbeModifyingType way, + double radius, + RPArray & faces ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Модифицировать или построить тело методами прямого моделирования. + \en Modify a solid by the methods of direct modeling. \~ + \details \ru В зависимости от параметров модификации метод выполняет одно из следующих действий: \n + 1. Удаление из тела выбранных граней с окружением (param.way==dmt_Remove). \n + 2. Создание тела из выбранных граней с окружением (param.way==dmt_Create). \n + 3. Перемещение выбранных граней с окружением относительно оставшихся граней тела (param.way==dmt_Action). \n + 4. Замена выбранных граней тела эквидистантными гранями (param.way==dmt_Offset). \n + 5. Изменение радиуса выбранных граней скругления (param.way==dmt_Fillet). \n + 6. Замена выбранных граней тела деформируемыми гранями для редактирования (param.way==dmt_Supple). \n + 7. Удаление выбранных граней скругления тела (param.way==dmt_Purify). + \en The method is for one of listed actions below depends of parameters: \n + 1. Removal of the specified faces with the neighborhood from a solid (param.way==dmt_Remove). \n + 2. Creation of a solid from the specified faces with the neighborhood (param.way==dmt_Create). \n + 3. Translation of the specified faces with neighborhood relative to the other faces of the solid (param.way==dmt_Action). \n + 4. Replacement of the specified faces of a solid with the offset faces (param.way==dmt_Offset). \n + 5. Changing of the radius of the specified fillet faces (param.way==dmt_Fillet). \n + 6. Replacement of the specified faces of a solid with a deformable faces for editing (param.way==dmt_Supple). \n + 7. Removal of the specified fillet faces from a solid (param.way==dmt_Purify). \n \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] sameShell - \ru Режим копирования исходного тела. + \en The mode of copying of the initial solid. \~ + \param[in] params - \ru Параметры модификации. + \en Parameters of the modification. \~ + \param[in] faces - \ru Изменяемые грани тела. + \en Faces to be modified. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Модифицированное тело. + \en The modified solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Direct_Modeling +*/ +// --- +MATH_FUNC (MbResultType) FaceModifiedSolid( MbSolid & solid, + MbeCopyMode sameShell, + const ModifyValues & params, + const RPArray & faces, + const MbSNameMaker & names, + MbSolid *& result ); + +//------------------------------------------------------------------------------ +/** \brief \ru Модифицировать или построить тело методами прямого моделирования. + \en Modify a solid by the methods of direct modeling. \~ + \details \ru Метод выполняет удаление указанных рёбер, слияние их вершин и модификацию окружающих граней (param.way==dmt_Merger). + По направлению вектора "param.direction" определяется: начальная ли вершина ребра будет слита с конечной вершиной, или конечная вершина ребра будет слита с начальной вершиной. \n + \en The method performs the deletion of selectsd edges, merging their vertices and modification of surrounding faces (param.way==dmt_Merger). + The direction of the vector "params.direction" determines whether the start vertex of an edge is merged with the end vertex, or whether the end vertex of an edge is merged with the start vertex. \n + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] sameShell - \ru Режим копирования исходного тела. + \en The mode of copying of the initial solid. \~ + \param[in] params - \ru Параметры модификации, способ должен быть равен param.way==dmt_Merger. + \en Parameters of the modification, the way must be equal to param.way==dmt_Merger. \~ + \param[in] edges - \ru Удаляемые рёьра тела. + \en Edges to be removed. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Модифицированное тело. + \en The modified solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Direct_Modeling +*/ +// --- + +MATH_FUNC (MbResultType) EdgeModifiedSolid( MbSolid & solid, + MbeCopyMode sameShell, + const ModifyValues & params, + const RPArray & edges, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Заменить выбранные грани тела деформируемыми гранями. + \en Replace the specified faces of solid with deformable faces. \~ + \details \ru Заменить выбранные грани тела деформируемыми гранями (превращение в NURBS для редактирования). \n + \en Replace the specified faces of the solid with deformable faces (conversion to NURBS for editing). \n \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] sameShell - \ru Режим копирования исходного тела. + \en The mode of copying of the initial solid. \~ + \param[in] p - \ru Параметры преобразования. + \en The transformation parameters. \~ + \param[in] faces - \ru Заменяемые грани тела. + \en Faces to replace. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Модифицированное тело. + \en The modified solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Direct_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ModifiedNurbsItem( MbSolid & solid, + MbeCopyMode sameShell, + const NurbsValues & p, + const RPArray & faces, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Заменить выбранную грань тела деформируемой гранью. + \en Replace the specified face of the solid by a deformable face. \~ + \details \ru Заменить выбранную грань тела деформируемой гранью (превращение в NURBS для редактирования). \n + \en Replace the specified face of the solid by a deformable face (conversion to NURBS for editing). \n \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] sameShell - \ru Режим копирования исходного тела. + \en The mode of copying of the initial solid. \~ + \param[in] p - \ru Параметры преобразования. + \en The transformation parameters. \~ + \param[in] face - \ru Заменяемая грань тела. + \en A face to replace. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Модифицированное тело. + \en The modified solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Direct_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ModifiedNurbsItem( MbSolid & solid, + MbeCopyMode sameShell, + const NurbsValues & p, + const MbFace & face, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Получить NURBS-поверхности грани. + \en Get the NURBS-surfaces of a face. \~ + \details \ru Выполнить построение деформируемой поверхности для исходной грани. \n + \en Create a deformable surface for the initial face. \n \~ + \param[in] face - \ru Исходная грань. + \en The initial face. \~ + \return \ru Возвращает NURBS-поверхности грани. + \en Returns NURBS-surfaces of the face. \~ + \ingroup Direct_Modeling +*/ +// --- +MATH_FUNC (MbSurface *) GetControlSurface( const MbFace & face ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Получить контрольные точки NURBS-поверхности грани. + \en Get the control points of the NURBS-surface of a face. \~ + \details \ru Получить множество контрольных точек NURBS-поверхности грани и множества их весов. \n + \en Get a set of the control points of a NURBS-surface of a face and a set of their weights. \n \~ + \param[in] face - \ru Исходная грань. + \en The initial face. \~ + \param[out] controlPoints - \ru Контрольные точки NURBS-поверхности грани. + \en The control points of the NURBS-surface of the face. \~ + \param[out] result - \ru Веса контрольных точек. + \en The weights of the control points. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Direct_Modeling +*/ +// --- +MATH_FUNC (MbResultType) FaceControlPoints( const MbFace & face, + Array2 & controlPoints, + Array2 & result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Деформировать грань тела. + \en Deform a face of a solid. \~ + \details \ru Деформировать грань тела путём подстановки присланных контрольных точек NURBS-поверхности грани. \n + \en Deform a face of a solid by substitution the control points of NURBS-surface of the face with the given control points. \n \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] sameShell - \ru Режим копирования исходного тела. + \en The mode of copying of the initial solid. \~ + \param[in] face - \ru Изменяемая грань тела. + \en A face of a solid to be modified. \~ + \param[in] faceSurface - \ru Новая NURBS-поверхность для грани. + \en The new NURBS-surface of the face. \~ + \param[in] fixedPoints - \ru Неподвижные узлы. + \en The fixed points. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Модифицированное тело. + \en The modified solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Direct_Modeling +*/ +// --- +MATH_FUNC (MbResultType) NurbsModification( MbSolid & solid, + MbeCopyMode sameShell, + MbFace * face, + MbSurface & faceSurface, + Array2 & fixedPoints, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Деформировать грань тела. + \en Deform a face of a solid. \~ + \details \ru Деформировать грань тела путём подстановки присланных контрольных точек NURBS-поверхности грани. \n + \en Deform a face of a solid by substitution the control points of NURBS-surface of the face with the given control points. \n \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] sameShell - \ru Режим копирования исходного тела. + \en The mode of copying of the initial solid. \~ + \param[in] face - \ru Изменяемая грань тела. + \en A face of a solid to be modified. \~ + \param[in] controlPoints - \ru Контрольные точки NURBS-поверхности грани. + \en The control points of the NURBS-surface of the face. \~ + \param[in] weights - \ru Веса контрольных точек. + \en The weights of the control points. \~ + \param[in] fixedPoints - \ru Неподвижные узлы. + \en The fixed points. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Модифицированное тело. + \en The modified solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Direct_Modeling +*/ +// --- +MATH_FUNC (MbResultType) NurbsModification( MbSolid & solid, + MbeCopyMode sameShell, + MbFace * face, + const Array2 & controlPoints, + const Array2 & weights, + Array2 * fixedPoints, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить деформируемую призму. + \en Create a deformable prism. \~ + \details \ru Построить тело в форме прямого параллелепипеда с деформируемыми гранями. \n + \en Create a solid as a right parallelepiped with deformable faces. \n \~ + \param[in] place - \ru Локальная система координат. + \en A local coordinate system. \~ + \param[in] ax - \ru Размер по X. + \en The size in X-direction. \~ + \param[in] ay - \ru Размер по Y. + \en The size in Y-direction. \~ + \param[in] az - \ru Размер по Z. + \en The size in Z-direction. \~ + \param[in] outDir - \ru Ориентация нормалей наружу. + \en An outer orientation of the normals. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] name - \ru Главное имя. + \en The main name. \~ + \param[in] param - \ru Параметры NURBS-поверхностей граней параллелепипеда. + \en The parameters of NURBS-surfaces of the parallelepiped faces. \~ + \param[out] result - \ru Тело из NURBS-поверхностей. + \en The solid constructed from the NU|RBS-surfaces. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Direct_Modeling +*/ +// --- +MATH_FUNC (MbResultType) NurbsBlockSolid( const MbPlacement3D & place, + double ax, + double ay, + double az, + bool outDir, + const MbSNameMaker & names, + SimpleName name, + NurbsBlockValues & param, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить согласованную поверхность. + \en Create a matched surface. \~ + \details \ru Для исходной поверхности выполнить построение изменённой поверхности + путем выставления сопряжения вдоль кривой. \n + \en Create a modified surface for the initial surface + by specifying the conjugation along the curve. \n \~ + \param[in] curve - \ru Кривая пересечения поверхностей ребра. + \en The intersection curve of the edge surfaces. \~ + \param[in] sences - \ru Ориентация кривой ребра в цикле. + \en The edge curve sense in the loop. \~ + \param[in] faceSences - \ru Ориентация нормали на смежной грани. + \en The adjacent face normal orientation. \~ + \param[in] surface - \ru Исходная сплайновая поверхность для изменяемой грани. + \en The initial spline surface of the face to be modified. \~ + \param[in] tension - \ru Натяжение. + \en The tension. \~ + \param[in] conType - \ru Тип сопряжения. + \en The conjugation type. \~ + \param[in] insertNum - \ru Вставляемый ряд. + \en The row number. \~ + \param[out] result - \ru NURBS-поверхность, полученная в результате преобразований. + \en The NURBS-surface obtained as a result of the modifications. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Direct_Modeling +*/ +// --- +MATH_FUNC (MbResultType) NurbsFaceConjugation( const MbSurfaceIntersectionCurve & curve, + bool sences, + bool faceSences, + const MbSplineSurface & surface, + double tension, + MbeConjugationType conType, + size_t insertNum, + MbSplineSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить подобную поверхность. + \en Create a similar surface. \~ + \details \ru Для исходной поверхности выполнить построение подобной поверхности + по указанной поверхности-образцу. \n + \en Create a surface similar to the initial one + given the pattern surface. \n \~ + \param[in] originSurface - \ru Поверхность-образец. + \en A pattern surface. \~ + \param[in] surface - \ru Исходная сплайновая поверхность для изменяемой грани. + \en The initial spline surface of the face to be modified. \~ + \param[in] uToU - \ru Флаг сохранения параметрического направления как у поверхности-образца. + \en Whether to keep the parametric direction of the pattern surface. \~ + \param[in] normSence - \ru Флаг сохранения направления нормали поверхности-образца. + \en Whether to keep the normal direction of the pattern surface. \~ + \param[out] result - \ru NURBS-поверхность, полученная в результате преобразований. + \en The NURBS-surface obtained as a result of the modifications. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Direct_Modeling +*/ +// --- +MATH_FUNC (MbResultType) NurbsFaceSimilarity( const MbSurface & originSurface, + const MbSplineSurface & surface, + bool uToU, + bool normSence, + MbSplineSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить сглаженную поверхность. + \en Create a smoothed surface. \~ + \details \ru Выполнить сглаживание копии исходной поверхности не изменяя ее порядок и количество контрольных точек. \n + \en Perform smoothing of a copy of the initial surface without changing its order and the number of control points. \n \~ + \param[in] surface - \ru Исходная сплайновая поверхность для изменяемой грани. + \en The initial spline surface of the face to be modified. \~ + \param[in] udegree - \ru Параметр сглаживания по первому параметру поверхности. + \en The smoothing surface degree for direction of first parameter of surface. \~ + \param[in] vdegree - \ru Параметр сглаживания по второму параметру поверхности. + \en The smoothing surface degree for direction of second parameter of surface. \~ + \param[out] result - \ru NURBS-поверхность, полученная в результате преобразований. + \en The NURBS-surface obtained as a result of the modifications. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Direct_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SplineSurfaceSmoothing( const MbSplineSurface & surface, + size_t udegree, + size_t vdegree, + MbSplineSurface *& result ); + + +#endif // __ACTION_DIRECT_H diff --git a/C3d/Include/action_mesh.h b/C3d/Include/action_mesh.h index 5d03edb..e4b16d6 100644 --- a/C3d/Include/action_mesh.h +++ b/C3d/Include/action_mesh.h @@ -1,279 +1,318 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Методы построения полигональных геометрических объектов. - \en Functions for construction of the polygonal geometric object. \~ - \details \ru Полигональные геометрические объекты могут быть построены по набору точек или на базе других объектов. - \en Polygonal geometric objects can be constructed using a set of point or on the basis of other objects. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ACTION_MESH_H -#define __ACTION_MESH_H - - -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbPlacement3D; -class MATH_CLASS MbMesh; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbSurface; -class MATH_CLASS MbSolid; -class MATH_CLASS MbPlaneItem; -class MATH_CLASS MbSNameMaker; -class MATH_CLASS MbFace; -class MATH_CLASS MbCollection; - - -//------------------------------------------------------------------------------ - /** \brief \ru Расчет полигона кривой. - \en Calculation of polygon of curve. \~ - \details \ru Расчет трехмерного полигона двумерной кривой в плоскости XOY локальная системы координат. - \en Calculation of three-dimensional polygon of two-dimensional curve located in the XOY-plane of a local coordinate system. \~ - \param[in] curve - \ru Двумерная кривая. - \en A two-dimensional curve. \~ - \param[in] plane - \ru Локальная система координат. - \en Local coordinate system. \~ - \param[in] sag - \ru Максимальное допустимое отклонение полигона от оригинала по прогибу или по углу между соседними элементами. - \en Maximum allowable deviation of polygon from the original by sag or by angle between neighboring elements. \~ - \param[out] polygon - \ru Рассчитанный полигон. - \en Calculated polygon. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (void) CalculatePolygon( const MbCurve & curve, - const MbPlacement3D & plane, - double sag, - MbPolygon3D & polygon ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить полигональный двухмерный объект. - \en Create a polygonal two-dimensional object. \~ - \details \ru Построить полигональный объект для двумерного объекта в плоскости XOY - локальной системы координат. - \en Create a polygonal object for two-dimensional object in the XOY-plane - of the local coordinate system. \~ - \param[in] obj - \ru Двумерный объект (если NULL, то объект не создаётся). - \en Two-dimensional object (if NULL, object isn't created). \~ - \param[in] plane - \ru Локальная система координат. - \en A local coordinate system. \~ - \param[in] sag - \ru Максимальное отклонение полигонального объекта от оригинала по прогибу. - \en The maximum deviation of polygonal object from the original object by sag. \~ - \param[out] mesh - \ru Полигональный объект. - \en Polygonal object. \~ - \ingroup Polygonal_Objects -*/ -// --- -MATH_FUNC (void) CalculateWire( const MbPlaneItem & obj, - const MbPlacement3D & plane, - double sag, - MbMesh & mesh ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить икосаэдр в виде полигональной модели. - \en Construct an icosahedron mesh. \~ - \details \ru Построить икосаэдр в виде полигональной модели. \n - \en Construct an icosahedron mesh. \n \~ - \param[in] place - \ru Местная система координат. - \en Local placement. \~ - \param[in] radius - \ru Радиус описанной сферы. - \en The radius of the sphere. \~ - \param[in] fn - \ru Способ построения полигонального объекта. - \en Way for polygonal object constructing. \~ - \param[out] result - \ru Результат построения. - \en The resulting mesh. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Polygonal_Objects -*/ -// --- -MATH_FUNC (MbResultType) CreateIcosahedron( const MbPlacement3D & place, - double radius, - const MbFormNote & fn, - MbMesh *& result ); - - -//------------------------------------------------------------------------------ -// . -/** \brief \ru Построить полигональную сферу. - \en Construct an spherical mesh. \~ - \details \ru Построить аппроксимацию сферы выпуклым многогранником. \n - \en Construct an approximation of the sphere by a convex polyhedron. \n \~ - \param[in] place - \ru Местная система координат. - \en Local placement. \~ - \param[in] radius - \ru Радиус сферы. - \en The radius of the sphere. \~ - \param[in] epsilon - \ru Параметр аппроксимации сферы. - \en The approximation parameter. \~ - \param[out] result - \ru Результат построения. - \en The resulting mesh. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Polygonal_Objects -*/ -// --- -MATH_FUNC (MbResultType) CreateSpherePolyhedron( const MbPlacement3D & place, - double radius, - double & epsilon, - MbMesh *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Вычислить выпуклую оболочку для множества точек. - \en Calculate a convex hull of a point set. \~ - \details \ru Вычислить сетку, представляющую выпуклой оболочку для множества точек. - \en Calculate mesh being a convex hull of a point set. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Polygonal_Objects -*/ -// --- -MATH_FUNC (MbResultType) CreateConvexPolyhedron( const SArray & points, - MbMesh *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить выпуклую оболочку для триангуляционной сетки. - \en Construct the convex hull of triangulation grid. \~ - \details \ru Построить сетку, представляющую собой выпуклую оболочку для тела, - заданного его триангуляционной сеткой. По заданному объекту MbMesh - строится охватывающая его вершины выпуклая триангуляционная сетка. - Расстояние offset задает смещение точек результирующей сетки относительно - заданной вдоль нормалей к её граням. Если offset = 0, то результирующая сетка - будет в точности охватывать все вершины заданной. Смещение по нормали может - быть как положительным, так и отрицательным (внутрь сетки). Используется для - определения пересечения с некоторым допуском (offset). \n - \en Construct the convex hull of triangulation grid. \n \~ - \param[in] mesh - \ru Исходная триангуляционная сетка. - \en Initial triangulated mesh. \~ - \param[in] offset - \ru Отступ по нормали для результирующей сетки. - \en The offset along a normal for the resulting grid. \~ - \param[out] resMesh - \ru Результирующая выпуклая триангуляционная сетка. - \en The resulting triangulation convex grid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Polygonal_Objects -*/ -// --- -MATH_FUNC (MbResultType) CreateConvexPolyhedron( const MbMesh & mesh, - double offset, - MbMesh *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Определить, пересекаются ли данные выпуклые сетки. - \en Whether there is intersection of convex grids. \~ - \details \ru Определить, пересекаются ли данные выпуклые оболочки, заданные - триангуляционными сетками. Пересечение определяется по алгоритму - Гильберта-Джонсона-Керти (Gilbert-Johnson-Keerthi). Заданные сетки - равноправны, их последовательность в алгоритме не важна. Сложность - алгоритма линейная, зависит от количества вершин сеток. \n - \en Whether there is intersection of convex grids. \n \~ - \param[in] mesh1 - \ru Первая выпуклая триангуляционная сетка. - \en The first convex grid. \~ - \param[in] mesh2 - \ru Вторая выпуклая триангуляционная сетка. - \en The second convex grid. \~ - \return \ru true - Выпуклые триангуляционные сетки пересекаются. - false - Выпуклые триангуляционные сетки не пересекаются. - \en true - true - there is an intersection, - false - there are no intersections. \~ - \ingroup Polygonal_Objects -*/ -// --- -MATH_FUNC (bool) AreIntersectConvexPolyhedrons( const MbMesh & mesh1, - const MbMesh & mesh2 ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Отрезать часть полигонального объекта плоскостью. - \en Cut a part of a polygonal object by a plane. \~ - \details \ru Отрезать часть полигонального объекта плоскостью XY локальной системы координат. \n - part = 1 - оставляем часть объекта, расположенную сверху плоскости XY локальной системы координат, \n - part = -1 - оставляем часть объекта, расположенную снизу плоскости XY локальной системы координат. \n - \en Cut a part of a polygonal object off by a plane XY of local coordinate system. \n - part = 1 - a part of polygonal object above the XY plane is to be retained. \n - part = -1 - a part of polygonal object below the XY plane is to be retained. \n \~ - \param[in] mesh - \ru Исходный полигональный объект. - \en The source polygonal object. \~ - \param[in] sameShell - \ru Режим копирования исходного объекта. - \en The mode of copying of the source polygonal object. \~ - \param[in] place - \ru Секущая плоскость. - \en A cutting plane. \~ - \param[in] part - \ru Направление отсечения. - \en The direction of cutting off. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] onlySection - \ru Флаг режима отсечения: false - сечем как тело, true - сечем как оболочку. - \en The flag of the cutting off mode: false - cut as a solid, true - cut as a shell. \~ - \param[out] result - \ru Построенный полигональный объект. - \en The resultant polygonal object. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Polygonal_Objects -*/ -// --- -MATH_FUNC (MbResultType) MeshCutting( MbMesh & mesh, - MbeCopyMode sameShell, - const MbPlacement3D & place, - int part, - const MbSNameMaker & names, - bool onlySection, - MbMesh *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить контур сечения полигонального объекта плоскостью. - \en Create a section contour of a polygon figure. \~ - \details \ru Построить контур сечения присланного объекта плоскостью XY локальной системы координат. \n - \en Construct curves of the section of the mesh object lying on the XY plane of the local coordinate system. \n - \param[in] mesh - \ru Исходный полигональный объект. - \en The source polygonal object. \~ - \param[in] place - \ru Секущая плоскость. - \en A cutting plane. \~ - \param[out] polylines - \ru Построенные ломагные контура сечения объекта. - \en The resultant contours. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Polygonal_Objects -*/ -// --- -MATH_FUNC (MbResultType) MeshSection( const MbMesh & mesh, - const MbPlacement3D & place, - RPArray & polylines ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить триангуляцию по облаку точек на основе алгоритма поворотного шара. - \en Build a triangulation by point cloud with Ball Pivoting algorithm. \~ - \param[in] collection - \ru Коллекция трехмерных элементов. - \en Collection of 3d elements. \~ - \param[in] radius - \ru Радиус поворотного шара, если radius==0 будет предпринята попытка его автоопределения. - \en Radius of the pivoting ball, if radius==0 an autoguess for the ball pivoting radius is attempted \~ - \param[in] radiusMin - \ru Радиус кластеризации ( в % от радиуса поворотного шара ). - \en Clusterization radius ( % from radius value). \~ - \param[in] angle - \ru Максимальный угол между двумя соседними элементами сетки. - \en Max angle between two mesh faces \~ - \param[out] result - \ru Построенный полигональный объект. - \en The resultant polygonal object. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Polygonal_Objects -*/ -// --- -MATH_FUNC (MbResultType) CalculateBallPivotingGrid( const MbCollection & collection, - double radius, - double radiusMin, - double angle, - MbMesh *& result ); - - -#endif // __ACTION_MESH_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Методы построения полигональных геометрических объектов. + \en Functions for construction of the polygonal geometric object. \~ + \details \ru Полигональные геометрические объекты могут быть построены по набору точек или на базе других объектов. + \en Polygonal geometric objects can be constructed using a set of point or on the basis of other objects. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ACTION_MESH_H +#define __ACTION_MESH_H + + +#include +#include +#include +#include +#include +#include + + +class MATH_CLASS MbPlacement3D; +class MATH_CLASS MbMesh; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbSurface; +class MATH_CLASS MbSolid; +class MATH_CLASS MbPlaneItem; +class MATH_CLASS MbSNameMaker; +class MATH_CLASS MbFace; +class MATH_CLASS MbCollection; + + +//------------------------------------------------------------------------------ + /** \brief \ru Расчет полигона кривой. + \en Calculation of polygon of curve. \~ + \details \ru Расчет трехмерного полигона двумерной кривой в плоскости XOY локальная системы координат. + \en Calculation of three-dimensional polygon of two-dimensional curve located in the XOY-plane of a local coordinate system. \~ + \param[in] curve - \ru Двумерная кривая. + \en A two-dimensional curve. \~ + \param[in] plane - \ru Локальная система координат. + \en Local coordinate system. \~ + \param[in] sag - \ru Максимальное допустимое отклонение полигона от оригинала по прогибу или по углу между соседними элементами. + \en Maximum allowable deviation of polygon from the original by sag or by angle between neighboring elements. \~ + \param[out] polygon - \ru Рассчитанный полигон. + \en Calculated polygon. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (void) CalculatePolygon( const MbCurve & curve, + const MbPlacement3D & plane, + double sag, + MbPolygon3D & polygon ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить полигональный двухмерный объект. + \en Create a polygonal two-dimensional object. \~ + \details \ru Построить полигональный объект для двумерного объекта в плоскости XOY + локальной системы координат. + \en Create a polygonal object for two-dimensional object in the XOY-plane + of the local coordinate system. \~ + \param[in] obj - \ru Двумерный объект (если NULL, то объект не создаётся). + \en Two-dimensional object (if NULL, object isn't created). \~ + \param[in] plane - \ru Локальная система координат. + \en A local coordinate system. \~ + \param[in] sag - \ru Максимальное отклонение полигонального объекта от оригинала по прогибу. + \en The maximum deviation of polygonal object from the original object by sag. \~ + \param[out] mesh - \ru Полигональный объект. + \en Polygonal object. \~ + \ingroup Polygonal_Objects +*/ +// --- +MATH_FUNC (void) CalculateWire( const MbPlaneItem & obj, + const MbPlacement3D & plane, + double sag, + MbMesh & mesh ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить икосаэдр в виде полигональной модели. + \en Construct an icosahedron mesh. \~ + \details \ru Построить икосаэдр в виде полигональной модели. \n + \en Construct an icosahedron mesh. \n \~ + \param[in] place - \ru Местная система координат. + \en Local placement. \~ + \param[in] radius - \ru Радиус описанной сферы. + \en The radius of the sphere. \~ + \param[in] fn - \ru Способ построения полигонального объекта. + \en Way for polygonal object constructing. \~ + \param[out] result - \ru Результат построения. + \en The resulting mesh. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Polygonal_Objects +*/ +// --- +MATH_FUNC (MbResultType) CreateIcosahedron( const MbPlacement3D & place, + double radius, + const MbFormNote & fn, + MbMesh *& result ); + +//------------------------------------------------------------------------------ +/** \brief \ru Построить параллелепипед в виде полигональной модели. + \en Construct a parallepiped mesh. \~ + \details \ru Построить ориентированный параллелепипед в виде полигональной модели. + Если матрица 'trans' единичная, то результатом вызова будет кубик + единичного объема. В общем случае матрица содержит смещение и ротацию, + позиционирующую куб относительно начала координат. Размеры параллелепипеда + определяются коэффициентами масштабирования по собственным осям ЛСК + параллелепипеда, содержащимися в матрице. + \en Construct an oriented box in the mesh representation. If the matrix 'trans' + is identity then the result of the call will be an unit cube. In general, + the matrix contains a translation and rotation that positions the cube + relative to the origin. The sizes of the parallelepiped are specified + by the scaling factors of the matrix along the eigen axes of the LCS of + the parallelepiped. \~ + \note \ru У матрицы должна быть ротационная часть ортогональной и не вырожденной. + Масштабирующая часть определяет размеры параллелепипеда. + \en The rotational componet of the matrix should be nondegenerate and ortogonal. + The scaling component specifies the box sizes. + \param[in] trans - \ru Матрица, задающая позицию, ориентацию и размеры сторон параллелепипеда. + \en The matrix specifying a postion, orientation and sizes of the box sides. \~ + \param[out] result - \ru Результат построения. + \en The resulting mesh. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Polygonal_Objects +*/ +// --- +MATH_FUNC (MbResultType) CreateBoxMesh( const MbMatrix3D & trans, SPtr & result ); + +//------------------------------------------------------------------------------ +/** \brief \ru Построить параллелепипед в виде проволочной модели. + \en Construct an parallepiped wireframe. + \details \ru Функция конструирует проволочный каркас параллелепипеда по тем же правилам, что и #CreateBoxMesh. + \en The function makes the wireframe of the oriented box by the same rules as #CreateBoxMesh. + \ingroup Polygonal_Objects +*/ +//--- +MATH_FUNC (MbResultType) CreateBoxWire( const MbMatrix3D & trans, SPtr & result ); + +//------------------------------------------------------------------------------ +// . +/** \brief \ru Построить полигональную сферу. + \en Construct an spherical mesh. \~ + \details \ru Построить аппроксимацию сферы выпуклым многогранником. \n + \en Construct an approximation of the sphere by a convex polyhedron. \n \~ + \param[in] place - \ru Местная система координат. + \en Local placement. \~ + \param[in] radius - \ru Радиус сферы. + \en The radius of the sphere. \~ + \param[in] epsilon - \ru Параметр аппроксимации сферы. + \en The approximation parameter. \~ + \param[out] result - \ru Результат построения. + \en The resulting mesh. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Polygonal_Objects +*/ +// --- +MATH_FUNC (MbResultType) CreateSpherePolyhedron( const MbPlacement3D & place, + double radius, + double & epsilon, + MbMesh *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Вычислить выпуклую оболочку для множества точек. + \en Calculate a convex hull of a point set. \~ + \details \ru Вычислить сетку, представляющую выпуклой оболочку для множества точек. + \en Calculate mesh being a convex hull of a point set. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Polygonal_Objects +*/ +// --- +MATH_FUNC (MbResultType) CreateConvexPolyhedron( const SArray & points, + MbMesh *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить выпуклую оболочку для триангуляционной сетки. + \en Construct the convex hull of triangulation grid. \~ + \details \ru Построить сетку, представляющую собой выпуклую оболочку для тела, + заданного его триангуляционной сеткой. По заданному объекту MbMesh + строится охватывающая его вершины выпуклая триангуляционная сетка. + Расстояние offset задает смещение точек результирующей сетки относительно + заданной вдоль нормалей к её граням. Если offset = 0, то результирующая сетка + будет в точности охватывать все вершины заданной. Смещение по нормали может + быть как положительным, так и отрицательным (внутрь сетки). Используется для + определения пересечения с некоторым допуском (offset). \n + \en Construct the convex hull of triangulation grid. \n \~ + \param[in] mesh - \ru Исходная триангуляционная сетка. + \en Initial triangulated mesh. \~ + \param[in] offset - \ru Отступ по нормали для результирующей сетки. + \en The offset along a normal for the resulting grid. \~ + \param[out] resMesh - \ru Результирующая выпуклая триангуляционная сетка. + \en The resulting triangulation convex grid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Polygonal_Objects +*/ +// --- +MATH_FUNC (MbResultType) CreateConvexPolyhedron( const MbMesh & mesh, + double offset, + MbMesh *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Определить, пересекаются ли данные выпуклые сетки. + \en Whether there is intersection of convex grids. \~ + \details \ru Определить, пересекаются ли данные выпуклые оболочки, заданные + триангуляционными сетками. Пересечение определяется по алгоритму + Гильберта-Джонсона-Керти (Gilbert-Johnson-Keerthi). Заданные сетки + равноправны, их последовательность в алгоритме не важна. Сложность + алгоритма линейная, зависит от количества вершин сеток. \n + \en Whether there is intersection of convex grids. \n \~ + \param[in] mesh1 - \ru Первая выпуклая триангуляционная сетка. + \en The first convex grid. \~ + \param[in] mesh2 - \ru Вторая выпуклая триангуляционная сетка. + \en The second convex grid. \~ + \return \ru true - Выпуклые триангуляционные сетки пересекаются. + false - Выпуклые триангуляционные сетки не пересекаются. + \en true - true - there is an intersection, + false - there are no intersections. \~ + \ingroup Polygonal_Objects +*/ +// --- +MATH_FUNC (bool) AreIntersectConvexPolyhedrons( const MbMesh & mesh1, + const MbMesh & mesh2 ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Отрезать часть полигонального объекта плоскостью. + \en Cut a part of a polygonal object by a plane. \~ + \details \ru Отрезать часть полигонального объекта плоскостью XY локальной системы координат. \n + part = 1 - оставляем часть объекта, расположенную сверху плоскости XY локальной системы координат, \n + part = -1 - оставляем часть объекта, расположенную снизу плоскости XY локальной системы координат. \n + \en Cut a part of a polygonal object off by a plane XY of local coordinate system. \n + part = 1 - a part of polygonal object above the XY plane is to be retained. \n + part = -1 - a part of polygonal object below the XY plane is to be retained. \n \~ + \param[in] mesh - \ru Исходный полигональный объект. + \en The source polygonal object. \~ + \param[in] sameShell - \ru Режим копирования исходного объекта. + \en The mode of copying of the source polygonal object. \~ + \param[in] place - \ru Секущая плоскость. + \en A cutting plane. \~ + \param[in] part - \ru Направление отсечения. + \en The direction of cutting off. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] onlySection - \ru Флаг режима отсечения: false - сечем как тело, true - сечем как оболочку. + \en The flag of the cutting off mode: false - cut as a solid, true - cut as a shell. \~ + \param[out] result - \ru Построенный полигональный объект. + \en The resultant polygonal object. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Polygonal_Objects +*/ +// --- +MATH_FUNC (MbResultType) MeshCutting( MbMesh & mesh, + MbeCopyMode sameShell, + const MbPlacement3D & place, + int part, + const MbSNameMaker & names, + bool onlySection, + MbMesh *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить контур сечения полигонального объекта плоскостью. + \en Create a section contour of a polygon figure. \~ + \details \ru Построить контур сечения присланного объекта плоскостью XY локальной системы координат. \n + \en Construct curves of the section of the mesh object lying on the XY plane of the local coordinate system. \n + \param[in] mesh - \ru Исходный полигональный объект. + \en The source polygonal object. \~ + \param[in] place - \ru Секущая плоскость. + \en A cutting plane. \~ + \param[out] polylines - \ru Построенные ломагные контура сечения объекта. + \en The resultant contours. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Polygonal_Objects +*/ +// --- +MATH_FUNC (MbResultType) MeshSection( const MbMesh & mesh, + const MbPlacement3D & place, + RPArray & polylines ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить триангуляцию по облаку точек на основе алгоритма поворотного шара. + \en Build a triangulation by point cloud with Ball Pivoting algorithm. \~ + \param[in] collection - \ru Коллекция трехмерных элементов. + \en Collection of 3d elements. \~ + \param[in] radius - \ru Радиус поворотного шара, если radius==0 будет предпринята попытка его автоопределения. + \en Radius of the pivoting ball, if radius==0 an autoguess for the ball pivoting radius is attempted \~ + \param[in] radiusMin - \ru Радиус кластеризации ( в % от радиуса поворотного шара ). + \en Clusterization radius ( % from radius value). \~ + \param[in] angle - \ru Максимальный угол между двумя соседними элементами сетки. + \en Max angle between two mesh faces \~ + \param[out] result - \ru Построенный полигональный объект. + \en The resultant polygonal object. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Polygonal_Objects +*/ +// --- +MATH_FUNC (MbResultType) CalculateBallPivotingGrid( const MbCollection & collection, + double radius, + double radiusMin, + double angle, + MbMesh *& result ); + + +#endif // __ACTION_MESH_H diff --git a/C3d/Include/action_phantom.h b/C3d/Include/action_phantom.h index bd26ea5..c26309b 100644 --- a/C3d/Include/action_phantom.h +++ b/C3d/Include/action_phantom.h @@ -1,299 +1,317 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Построение фантомов операций. - \en Creation of phantom operations. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ACTION_PHANTOM_H -#define __ACTION_PHANTOM_H - - -#include -#include -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbCurve; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbSurface; -class MATH_CLASS MbCurveEdge; -class MATH_CLASS MbFace; -class MATH_CLASS MbSolid; -class MATH_CLASS MbSNameMaker; - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить фантомные поверхности скругления/фаски. - \en Create phantom surfaces of fillet/chamfer. \~ - \details \ru Построить фантомные поверхности скругления/фаски и сложить в контейнер surfaces. \n - По окончании работ поверхности можно и нужно удалить. \n - \en Create phantom surfaces of fillet/chamfer and store them in the container 'surfaces'. \n - After finish working with the surfaces they should be deleted. \n \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] edges - \ru Множество выбранных ребер для скругления/фаски. - \en An array of edges for fillet/chamfer. \~ - \param[in] params - \ru Параметры операции скругления/фаски. - \en Parameters of the fillet/chamfer operation. \~ - \param[out] result - \ru Поверхности скругления/фаски. - \en The fillet/chamfer surfaces. \~ - \return \ru Возвращает код результата построения. - \en Returns the creation result code. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (MbResultType) SmoothPhantom( const MbSolid & solid, - RPArray & edges, - const SmoothValues & params, - RPArray & result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить фантомные поверхности скругления/фаски. - \en Create phantom surfaces of fillet/chamfer.\~ - \details \ru Построить фантомные поверхности скругления/фаски и сложить в контейнер surfaces. \n - По окончании работ поверхности можно и нужно удалить. - \en Create phantom surfaces of fillet/chamfer and store them in the container 'surfaces'. \n - After finish working with the surfaces they should be deleted. \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] edges - \ru Множество выбранных ребер и функций изменения радиуса для скругления/фаски. - \en An array of edges and radius laws for fillet/chamfer. \~ - \param[in] params - \ru Параметры операции скругления/фаски. - \en Parameters of the fillet/chamfer operation. \~ - \param[out] result - \ru Поверхности скругления/фаски. - \en The fillet/chamfer surfaces. \~ - \return \ru Возвращает код результата построения. - \en Returns the creation result code. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (MbResultType) SmoothPhantom( const MbSolid & solid, - SArray & edges, - const SmoothValues & params, - RPArray & result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить последовательности гладко стыкующихся рёбер. - \en \~ - \details \ru Построить последовательности гладко стыкующихся рёбер, скругляемых одновременно, - а также поверхности скругления/фаски (массив surfaces). \n - По окончании работ поверхности можно и нужно удалить. - \en \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] edges - \ru Множество выбранных ребер для скругления/фаски. - \en An array of edges for fillet/chamfer. \~ - \param[in] params - \ru Параметры операции скругления/фаски. - \en Parameters of the fillet/chamfer operation. \~ - \param[in] createSurfaces - \ru Создавать ли поверхности скругления/фаски для фантома? - \en Create a fillet/chamfer surfaces for phantom. \~ - \param[out] sequences - \ru Последовательность гладко стыкующихся рёбер. - \en Sequence of smooth mating edges. \~ - \param[out] result - \ru Поверхности скругления/фаски. - \en The fillet/chamfer surfaces. \~ - \return \ru Возвращает код результата построения. - \en \~ - \ingroup Algorithms_3D -*/ - -// --- -MATH_FUNC (MbResultType) SmoothSequence( const MbSolid & solid, - RPArray & edges, - const SmoothValues & params, - bool createSurfaces, - RPArray & sequences, - RPArray & result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить последовательности гладко стыкующихся рёбер. - \en \~ - \details \ru Построить последовательности гладко стыкующихся рёбер, скругляемых одновременно, - а также поверхности скругления/фаски (массив surfaces). \n - По окончании работ поверхности можно и нужно удалить. - \en \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] edges - \ru Множество выбранных ребер и функций изменения радиуса для скругления/фаски. - \en An array of edges and radius laws for fillet/chamfer. \~ - \param[in] params - \ru Параметры операции скругления/фаски. - \en Parameters of the fillet/chamfer operation. \~ - \param[in] createSurfaces - \ru Создавать ли поверхности скругления/фаски для фантома? - \en Create a fillet/chamfer surfaces for phantom. \~ - \param[out] sequences - \ru Последовательность гладко стыкующихся рёбер. - \en Sequence of smooth mating edges. \~ - \param[out] result - \ru Поверхности скругления/фаски. - \en The fillet/chamfer surfaces. \~ - \return \ru Возвращает код результата построения. - \en \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (MbResultType) SmoothSequence( const MbSolid & solid, - SArray & edges, - const SmoothValues & params, - bool createSurfaces, - RPArray & sequences, - RPArray & result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить фантомные эквидистантные поверхности для граней оболочки. - \en Create phantom offset surfaces for faces of a shell. \~ - \details \ru Построить фантомные эквидистантные поверхности для граней оболочки, \n - кроме имеющих перечислены кроме имеющих перечисленные индексы, и сложить в массив surfaces. \n - По окончании работ поверхности можно и нужно удалить. - \en Create phantom offset surfaces for faces of a shell, \n - except the faces with specified indices and store them in array 'surfaces'. \n - After finish working with the surfaces they should be deleted. \n \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] outFaces - \ru Множество вскрываемых граней тела. - \en An array of shelling faces of the solid. \~ - \param[in] offFaces - \ru Множество граней, для которых заданы индивидуальные значения толщин. - \en An array of faces for which the individual values of thickness are specified. \~ - \param[in] offDists - \ru Множество индивидуальных значений толщин (должен быть синхронизирован с массивом offFaces). - \en An array of individual values of thickness (must be synchronized with the array 'offFaces'). \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] operNames - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Результат операции. - \en The operation result. \~ - \param[out] hpShellFaceInd - \ru Номер грани в исходной оболочки для построения хот-точки. - \en The face number in the initial shell for a hot-point creation. \~ - \return \ru Возвращает код результата построения. - \en Returns the creation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) OffsetPhantom( const MbSolid & solid, - RPArray & outFaces, - RPArray & offFaces, - SArray & offDists, - const SweptValues & params, - const MbSNameMaker & operNames, - MbFaceShell *& result, - size_t * hpShellFaceInd = NULL ); // \ru Номер грани в исходной оболочки для построения хот-точки); \en The face number in the initial shell for a hot-point creation); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить фантом габаритного куба в локальной системе координат. - \en Create a phantom of a bounding box in local coordinate system. \~ - \details \ru Построить фантом габаритного куба в локальной системе координат. \n - \en Create a phantom of a bounding box in local coordinate system. \n \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] place - \ru Локальная система координат (ЛСК). - \en A local coordinate system (LCS). \~ - \param[in] bScale - \ru Является ли ЛСК масштабирующей. - \en Whether the LCS is scaling. \~ - \param[in] operNames - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Фантом локального куба. - \en The phantom of the local bounding box. \~ - \return \ru Возвращает код результата построения. - \en Returns the creation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) LocalCubePhantom( const MbSolid & solid, - const MbPlacement3D & place, - bool bScale, - const MbSNameMaker & operNames, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Определить фантомное направление усечения. - \en Determine a phantom direction of truncation. \~ - \details \ru Определить фантомное направление усечения по усеченной грани исходного тела. \n - \en Determine a phantom direction of truncation given the truncated face of the initial solid. \n \~ - \param[in] truncatingEdge - \ru Ребро усеченной грани исходного тела. - \en An edge of truncated face of the initial solid. \~ - \param[in] dirPlace - \ru Система координат направления усечения (Ось Z - направление усечения). - \en A coordinate system of truncation direction (Z-axis is a truncation direction). \~ - \return \ru Возвращает true, если получилось определить направление. - \en Returns true if the direction has been successfully determined. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) TruncatDirection( const MbCurveEdge & truncatingEdge, - MbPlacement3D & dirPlace ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить опорные точки размеров операции скругления/фаски. - \en Create support points of fillet/chamfer operation sizes. \~ - \details \ru Построить опорные точки размеров операции скругления/фаски и сложить в контейнер data. \n - Первые две точки лежат на краях поверхности скругления/фаски. - \en Create support points of fillet/chamfer operation sizes and store them in container 'data'. \n - The first two points lie on the fillet/chamfer surface boundary. \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] edges - \ru Множество выбранных ребер для скругления/фаски. - \en An array of edges for fillet/chamfer. \~ - \param[in] params - \ru Параметры операции скругления/фаски. - \en Parameters of the fillet/chamfer operation. \~ - \param[out] result - \ru Опорные точки размеров операции скругления/фаски. - \en Support points of the fillet/chamfer operation sizes. \~ - \param[in] edgeParam - \ru Параметр точки на ребре (0 <= edgeParam <= 1). - \en The parameter of a point on the edge (0 <= edgeParam <= 1). \~ - \param[in] dimensionEdge - \ru Ребро, на котором дать опорные точки. - \en The edge on which the support points are to be created. \~ - \return \ru Возвращает код результата построения. - \en Returns the creation result code. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (MbResultType) SmoothPositionData( const MbSolid & solid, - RPArray & edges, - const SmoothValues & params, - RPArray & result, - double edgeParam = 0.5, - const MbCurveEdge * dimensionEdge = NULL ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить опорные точки размеров операции скругления/фаски. - \en Create support points of fillet/chamfer operation sizes. \~ - \details \ru Построить опорные точки размеров операции скругления/фаски и сложить в контейнер data. \n - Первые две точки лежат на краях поверхности скругления/фаски. - \en Create support points of fillet/chamfer operation sizes and store them in container 'data'. \n - The first two points lie on the fillet/chamfer surface boundary. \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] edges - \ru Множество выбранных ребер для скругления/фаски и функций изменения радиуса для скругления/фаски. - \en The array of specified edges for fillet/chamfer and radius laws for fillet/chamfer. \~ - \param[in] params - \ru Параметры операции скругления/фаски. - \en Parameters of the fillet/chamfer operation. \~ - \param[out] result - \ru Опорные точки размеров операции скругления/фаски. - \en Support points of the fillet/chamfer operation sizes. \~ - \param[in] edgeParam - \ru Параметр точки на ребре (0 <= edgeParam <= 1). - \en The parameter of a point on the edge (0 <= edgeParam <= 1). \~ - \param[in] dimensionEdge - \ru Ребро, на котором дать опорные точки. - \en The edge on which the support points are to be created. \~ - \return \ru Возвращает код результата построения. - \en Returns the creation result code. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (MbResultType) SmoothPositionData( const MbSolid & solid, - SArray & edges, - const SmoothValues & params, - RPArray & result, - double edgeParam = 0.5, - const MbCurveEdge * dimensionEdge = NULL ); - - -#endif // __ACTION_PHANTOM_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Построение фантомов операций. + \en Creation of phantom operations. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ACTION_PHANTOM_H +#define __ACTION_PHANTOM_H + + +#include +#include +#include +#include +#include +#include +#include +#include + + +class MATH_CLASS MbCurve; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbSurface; +class MATH_CLASS MbCurveEdge; +class MATH_CLASS MbFace; +class MATH_CLASS MbSolid; +class MATH_CLASS MbSNameMaker; + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить фантомные поверхности скругления/фаски. + \en Create phantom surfaces of fillet/chamfer. \~ + \details \ru Построить фантомные поверхности скругления/фаски и сложить в контейнер surfaces. \n + По окончании работ поверхности можно и нужно удалить. \n + \en Create phantom surfaces of fillet/chamfer and store them in the container 'surfaces'. \n + After finish working with the surfaces they should be deleted. \n \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] edges - \ru Множество выбранных ребер для скругления/фаски. + \en An array of edges for fillet/chamfer. \~ + \param[in] params - \ru Параметры операции скругления/фаски. + \en Parameters of the fillet/chamfer operation. \~ + \param[out] result - \ru Поверхности скругления/фаски. + \en The fillet/chamfer surfaces. \~ + \return \ru Возвращает код результата построения. + \en Returns the creation result code. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (MbResultType) SmoothPhantom( const MbSolid & solid, + RPArray & edges, + const SmoothValues & params, + RPArray & result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить фантомные поверхности скругления/фаски. + \en Create phantom surfaces of fillet/chamfer.\~ + \details \ru Построить фантомные поверхности скругления/фаски и сложить в контейнер surfaces. \n + По окончании работ поверхности можно и нужно удалить. + \en Create phantom surfaces of fillet/chamfer and store them in the container 'surfaces'. \n + After finish working with the surfaces they should be deleted. \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] edges - \ru Множество выбранных ребер и функций изменения радиуса для скругления/фаски. + \en An array of edges and radius laws for fillet/chamfer. \~ + \param[in] params - \ru Параметры операции скругления/фаски. + \en Parameters of the fillet/chamfer operation. \~ + \param[out] result - \ru Поверхности скругления/фаски. + \en The fillet/chamfer surfaces. \~ + \return \ru Возвращает код результата построения. + \en Returns the creation result code. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (MbResultType) SmoothPhantom( const MbSolid & solid, + SArray & edges, + const SmoothValues & params, + RPArray & result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить последовательности гладко стыкующихся рёбер. + \en \~ + \details \ru Построить последовательности гладко стыкующихся рёбер, скругляемых одновременно, + а также поверхности скругления/фаски (массив surfaces). \n + По окончании работ поверхности можно и нужно удалить. + \en \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] edges - \ru Множество выбранных ребер для скругления/фаски. + \en An array of edges for fillet/chamfer. \~ + \param[in] params - \ru Параметры операции скругления/фаски. + \en Parameters of the fillet/chamfer operation. \~ + \param[in] createSurfaces - \ru Создавать ли поверхности скругления/фаски для фантома? + \en Create a fillet/chamfer surfaces for phantom. \~ + \param[out] sequences - \ru Последовательность гладко стыкующихся рёбер. + \en Sequence of smooth mating edges. \~ + \param[out] result - \ru Поверхности скругления/фаски. + \en The fillet/chamfer surfaces. \~ + \return \ru Возвращает код результата построения. + \en \~ + \ingroup Algorithms_3D +*/ + +// --- +MATH_FUNC (MbResultType) SmoothSequence( const MbSolid & solid, + RPArray & edges, + const SmoothValues & params, + bool createSurfaces, + RPArray & sequences, + RPArray & result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить последовательности гладко стыкующихся рёбер. + \en \~ + \details \ru Построить последовательности гладко стыкующихся рёбер, скругляемых одновременно, + а также поверхности скругления/фаски (массив surfaces). \n + По окончании работ поверхности можно и нужно удалить. + \en \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] edges - \ru Множество выбранных ребер и функций изменения радиуса для скругления/фаски. + \en An array of edges and radius laws for fillet/chamfer. \~ + \param[in] params - \ru Параметры операции скругления/фаски. + \en Parameters of the fillet/chamfer operation. \~ + \param[in] createSurfaces - \ru Создавать ли поверхности скругления/фаски для фантома? + \en Create a fillet/chamfer surfaces for phantom. \~ + \param[out] sequences - \ru Последовательность гладко стыкующихся рёбер. + \en Sequence of smooth mating edges. \~ + \param[out] result - \ru Поверхности скругления/фаски. + \en The fillet/chamfer surfaces. \~ + \return \ru Возвращает код результата построения. + \en \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (MbResultType) SmoothSequence( const MbSolid & solid, + SArray & edges, + const SmoothValues & params, + bool createSurfaces, + RPArray & sequences, + RPArray & result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить фантомные эквидистантные поверхности для граней оболочки. + \en Create phantom offset surfaces for faces of a shell. \~ + \details \ru Построить фантомные эквидистантные поверхности для граней оболочки, \n + кроме имеющих перечислены кроме имеющих перечисленные индексы, и сложить в массив surfaces. \n + По окончании работ поверхности можно и нужно удалить. + \en Create phantom offset surfaces for faces of a shell, \n + except the faces with specified indices and store them in array 'surfaces'. \n + After finish working with the surfaces they should be deleted. \n \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] outFaces - \ru Множество вскрываемых граней тела. + \en An array of shelling faces of the solid. \~ + \param[in] offFaces - \ru Множество граней, для которых заданы индивидуальные значения толщин. + \en An array of faces for which the individual values of thickness are specified. \~ + \param[in] offDists - \ru Множество индивидуальных значений толщин (должен быть синхронизирован с массивом offFaces). + \en An array of individual values of thickness (must be synchronized with the array 'offFaces'). \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] operNames - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Результат операции. + \en The operation result. \~ + \param[out] hpShellFaceInd - \ru Номер грани в исходной оболочки для построения хот-точки. + \en The face number in the initial shell for a hot-point creation. \~ + \return \ru Возвращает код результата построения. + \en Returns the creation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) OffsetPhantom( const MbSolid & solid, + RPArray & outFaces, + RPArray & offFaces, + SArray & offDists, + const SweptValues & params, + const MbSNameMaker & operNames, + MbFaceShell *& result, + size_t * hpShellFaceInd = NULL ); // \ru Номер грани в исходной оболочки для построения хот-точки); \en The face number in the initial shell for a hot-point creation); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить фантом габаритного куба в локальной системе координат. + \en Create a phantom of a bounding box in local coordinate system. \~ + \details \ru Построить фантом габаритного куба в локальной системе координат. \n + \en Create a phantom of a bounding box in local coordinate system. \n \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] place - \ru Локальная система координат (ЛСК). + \en A local coordinate system (LCS). \~ + \param[in] bScale - \ru Является ли ЛСК масштабирующей. + \en Whether the LCS is scaling. \~ + \param[in] operNames - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Фантом локального куба. + \en The phantom of the local bounding box. \~ + \return \ru Возвращает код результата построения. + \en Returns the creation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) LocalCubePhantom( const MbSolid & solid, + const MbPlacement3D & place, + bool bScale, + const MbSNameMaker & operNames, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Определить фантомное направление усечения. + \en Determine a phantom direction of truncation. \~ + \details \ru Определить фантомное направление усечения по усеченной грани исходного тела. \n + \en Determine a phantom direction of truncation given the truncated face of the initial solid. \n \~ + \param[in] truncatingEdge - \ru Ребро усеченной грани исходного тела. + \en An edge of truncated face of the initial solid. \~ + \param[in] dirPlace - \ru Система координат направления усечения (Ось Z - направление усечения). + \en A coordinate system of truncation direction (Z-axis is a truncation direction). \~ + \return \ru Возвращает true, если получилось определить направление. + \en Returns true if the direction has been successfully determined. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) TruncatDirection( const MbCurveEdge & truncatingEdge, + MbPlacement3D & dirPlace ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить опорные точки размеров операции скругления/фаски. + \en Create support points of fillet/chamfer operation sizes. \~ + \details \ru Построить опорные точки размеров операции скругления/фаски и сложить в контейнер data. \n + Первые две точки лежат на краях поверхности скругления/фаски. + \en Create support points of fillet/chamfer operation sizes and store them in container 'data'. \n + The first two points lie on the fillet/chamfer surface boundary. \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] edges - \ru Множество выбранных ребер для скругления/фаски. + \en An array of edges for fillet/chamfer. \~ + \param[in] params - \ru Параметры операции скругления/фаски. + \en Parameters of the fillet/chamfer operation. \~ + \param[out] result - \ru Опорные точки размеров операции скругления/фаски. + \en Support points of the fillet/chamfer operation sizes. \~ + \param[in] edgeParam - \ru Параметр точки на ребре (0 <= edgeParam <= 1). + \en The parameter of a point on the edge (0 <= edgeParam <= 1). \~ + \param[in] dimensionEdge - \ru Ребро, на котором дать опорные точки. + \en The edge on which the support points are to be created. \~ + \return \ru Возвращает код результата построения. + \en Returns the creation result code. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (MbResultType) SmoothPositionData( const MbSolid & solid, + RPArray & edges, + const SmoothValues & params, + RPArray & result, + double edgeParam = 0.5, + const MbCurveEdge * dimensionEdge = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить опорные точки размеров операции скругления/фаски. + \en Create support points of fillet/chamfer operation sizes. \~ + \details \ru Построить опорные точки размеров операции скругления/фаски и сложить в контейнер data. \n + Первые две точки лежат на краях поверхности скругления/фаски. + \en Create support points of fillet/chamfer operation sizes and store them in container 'data'. \n + The first two points lie on the fillet/chamfer surface boundary. \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] edges - \ru Множество выбранных ребер для скругления/фаски и функций изменения радиуса для скругления/фаски. + \en The array of specified edges for fillet/chamfer and radius laws for fillet/chamfer. \~ + \param[in] params - \ru Параметры операции скругления/фаски. + \en Parameters of the fillet/chamfer operation. \~ + \param[out] result - \ru Опорные точки размеров операции скругления/фаски. + \en Support points of the fillet/chamfer operation sizes. \~ + \param[in] edgeParam - \ru Параметр точки на ребре (0 <= edgeParam <= 1). + \en The parameter of a point on the edge (0 <= edgeParam <= 1). \~ + \param[in] dimensionEdge - \ru Ребро, на котором дать опорные точки. + \en The edge on which the support points are to be created. \~ + \return \ru Возвращает код результата построения. + \en Returns the creation result code. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (MbResultType) SmoothPositionData( const MbSolid & solid, + SArray & edges, + const SmoothValues & params, + RPArray & result, + double edgeParam = 0.5, + const MbCurveEdge * dimensionEdge = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построение функции изменения указанной координаты кривой. + \en Create a function by one of three coordinates of curve. \~ + \details \ru Для указанной координаты кривой построить склярную функцию её изменения, зависящую от параметра кривой. \n + \en A function creation for behavior of selected curve coordinate with curve parameter. \n + \param[in] curve - \ru Кривая. + \en The curve. \~ + \param[in] coordinate - \ru Номер координаты пространства. + \en The number of curve coordinate. \~ + \return \ru Возвращает построенную функцию. + \en Returns the created function. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (MbFunction *) CreateFunction( const MbCurve3D & curve, + size_t coordinate ); + + +#endif // __ACTION_PHANTOM_H diff --git a/C3d/Include/action_point.h b/C3d/Include/action_point.h index 304621b..706e36d 100644 --- a/C3d/Include/action_point.h +++ b/C3d/Include/action_point.h @@ -1,820 +1,852 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Функции создания точек. - \en Functions for points creation. \~ - \details \ru Функции, использующие в качестве выходных параметров точки или массивы точек. - \en Functions that take points or arrays of points as input parameters. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ACTION_POINT_H -#define __ACTION_POINT_H - - -#include -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbCurve; -class MATH_CLASS MbLineSegment; -class MATH_CLASS MbLine3D; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbSurface; - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать массив. - \en Create an array. \~ - \details \ru Создать массив с контролем выделения памяти. \n - \en Create an array with memory allocation control. \n \~ - \param[in] cnt - \ru Количество элементов массива. - \en Number of elements in the array. \~ - \param[out] res - \ru Результат операции. - \en The operation result. \~ - \return \ru Возвращает массив элементов, если он создан, или NULL в противном случае. - \en Returns an array of elements if it has been created, otherwise returns NULL. \~ - \ingroup Algorithms_3D -*/ -// --- -template -inline SArray * CreateArray( size_t cnt, MbResultType & res ) -{ - SArray * arr = new SArray ( cnt, 1 ); - if ( arr != NULL && arr->GetAddr() == NULL ) { - delete arr; - arr = NULL; - } - if ( arr == NULL ) - res = rt_TooManyPoints; - - return arr; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Выделить в массиве память под n элементов. - \en Allocate memory in the array for n elements. \~ - \details \ru Выделить в массиве память под n элементов с контролем выделения памяти. \n - \en Allocate memory in the array for n elements with memory allocation control. \n \~ - \param[in, out] arr - \ru Массив. - \en An array. \~ - \param[in] n - \ru Количество элементов, под которые нужно выделить память. - \en Number of elements for allocation. \~ - \param[out] res - \ru Результат операции. - \en The operation result. \~ - \return \ru Возвращает true в случае успешного выделения памяти. - \en Returns true if the memory has been successfully allocated. \~ - \ingroup Algorithms_3D -*/ -// --- -template -inline bool ReserveArray( SArray & arr, size_t n, MbResultType & res ) -{ - arr.Reserve( n ); - if ( arr.GetAddr() == NULL ) { - res = rt_TooManyPoints; - return false; - } - return true; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Добавить в массив элемент. - \en Add an element to the array. \~ - \details \ru Добавить в массив элемент с контролем выделения памяти. \n - \en Add an element to the array with memory allocation control. \n \~ - \param[in, out] arr - \ru Массив. - \en An array. \~ - \param[in] item - \ru Элемент, который нужно добавить. - \en The element to add. \~ - \param[out] res - \ru Результат операции. - \en The operation result. \~ - \return \ru Возвращает true в случае успешного добавления. - \en Returns true if the element has been successfully added. \~ - \ingroup Algorithms_3D -*/ -// --- -template -inline bool AddItem( SArray & arr, const Type & item, MbResultType & res ) -{ - arr.Add( item ); - if ( arr.GetAddr() == NULL ) { - res = rt_TooManyPoints; - return false; - } - return true; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Пространственно-параметрическая точка. - \en A space-parametric point. \~ - \details \ru Пространственно-параметрическая точка. \n - Содержит в себе трехмерную и двумерную точки. - \en A space-parametric point. \n - Contains a three-dimensional point and a two-dimensional point. \~ - \ingroup Point_Modeling -*/ -// --- -class MATH_CLASS MbSpaceParamPnt { -protected: - MbCartPoint3D spacePnt; ///< \ru Пространственная точка. \en A spatial point. - MbCartPoint paramPnt; ///< \ru Параметрическая точка. \en A parametric point. - -public: // \ru Конструкторы \en Constructors - /// \ru Конструктор по пространственной точке. \en A constructor that takes a space point. - explicit MbSpaceParamPnt( const MbCartPoint3D & sp ) : spacePnt( sp ), paramPnt( UNDEFINED_DBL, 0.0 ) {} - /// \ru Конструктор по пространственной и параметрической точкам. \en A constructor that takes a space point and a parametric point. - explicit MbSpaceParamPnt( const MbCartPoint3D & sp, const MbCartPoint & pp ) : spacePnt( sp ), paramPnt( pp ) {} - /// \ru Конструктор по пространственно-параметрической точке. \en A constructor that takes a space-parametric point. - explicit MbSpaceParamPnt( const MbSpaceParamPnt & cp ) : spacePnt( cp.spacePnt ), paramPnt( cp.paramPnt ) {} - ~MbSpaceParamPnt() {} - -public: // \ru Инициализация \en The initialization - /// \ru Инициализация по пространственно-параметрической точке. \en Initialization with a space-parametric point. - void Init( const MbSpaceParamPnt & cp ) { spacePnt = cp.spacePnt; paramPnt = cp.paramPnt; } - /// \ru Инициализация по пространственной и параметрической точкам. \en Initialization with a space point and a parametric point. - void Init( const MbCartPoint3D & sp, const MbCartPoint & pp ) { spacePnt = sp; paramPnt = pp; } -public: // \ru Функции \en Functions - /// \ru Установлена ли параметрическая точка? \en Whether the parametric point is speified. - bool IsParamPnt() const { return (paramPnt.x != UNDEFINED_DBL); } //-V550 - /// \ru Перевести параметрическую точку в неустановленное состояние. \en Reset a parametric point. - void ResetParamPnt() { paramPnt.x = UNDEFINED_DBL; } - /// \ru Проверка на равенство параметрических точек по X с заданной погрешностью. \en Check if parametric points are equal by X component with the specified tolerance. - bool IsParamEqualX( const MbSpaceParamPnt & cp, double eps ) const { return (::fabs(paramPnt.x - cp.paramPnt.x) < eps); } - /// \ru Проверка на равенство параметрических точек по Y с заданной погрешностью. \en Check if parametric points are equal by Y component with the specified tolerance. - bool IsParamEqualY( const MbSpaceParamPnt & cp, double eps ) const { return (::fabs(paramPnt.y - cp.paramPnt.y) < eps); } - - /// \ru Получить ссылку на пространственную точку. \en Get a reference to the space point. - const MbCartPoint3D & GetSpacePnt() const { return spacePnt; } - /// \ru Получить ссылку на параметрическую точку. \en Get a reference to the parametric point. - const MbCartPoint & GetParamPnt() const { return paramPnt; } - -private: // \ru Нереализованные \en Not implemented - MbSpaceParamPnt(); - MbSpaceParamPnt( const MbCartPoint & ); - void operator = ( const MbCartPoint3D & ); - void operator = ( const MbCartPoint & ); - void operator = ( const MbSpaceParamPnt & ); - bool operator == ( const MbSpaceParamPnt & ) const; -}; - - -typedef std::pair MbLocPnt; ///< \ru Пространственно-параметрическая точка с индексированным положением. \en A space-parametric point with indexed position. - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать точки на поверхности. - \en Create points on a surface. \~ - \details \ru Создать группу точек на поверхности. \n - \en Create a group of points on a surface. \n \~ - \param[in] surface - \ru Поверхность-источник. - \en The source surface. \~ - \param[in] stepType - \ru Тип шага по поверхности. - \en Type of spacing on a surface. \~ - \param[in] uValue - \ru Величина шага по u или количество точек по u при шаге по параметру - \en U-spacing value or number of points in u-direction while sampling by parameter \~ - \param[in] vValue - \ru Величина шага по v или количество точек по v при шаге по параметру. - \en V-spacing value or number of points in v-direction while sampling by parameter. \~ - \param[in] truncateByBounds - \ru Усечь границами поверхности. - \en Whether to truncate by surface boundary. \~ - \param[out] result - \ru Индексированные пространственно-параметрические точки. - \en Indexed space-parametric points. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (MbResultType) PointsOnSurface( const MbSurface & surface, - MbeStepType stepType, - double uValue, - double vValue, - bool truncateByBounds, - RPArray< SArray > & result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать точки на поверхности. - \en Create points on a surface. \~ - \details \ru Создать группу точек на поверхности. \n - \en Create a group of points on a surface. \n \~ - \param[in] surface - \ru Поверхность-источник. - \en The source surface. \~ - \param[in] gridType - \ru Тип cетки на поверхности. - \en A type of a grid on a surface. \~ - \param[in] uv0 - \ru Центральная точка сетки - \en The central point of the grid. \~ - \param[in] angle - \ru Угол поворота сетки относительно направления U (в радианах) - \en Rotaion angle of the grid relative to U direction (in radians). \~ - \param[in] stepType - \ru Тип шага по поверхности. - \en Type of spacing on a surface. \~ - \param[in] step1 - \ru Величина шага по первому направлению - \en A spacing value in the first direction \~ - \param[in] step2 - \ru Величина шага по второму направлению - \en A spacing value in the second direction \~ - \param[in] truncateByBounds - \ru Усечь границами поверхности. - \en Whether to truncate by surface boundary. \~ - \param[out] result - \ru Индексированные пространственно-параметрические точки. - \en Indexed space-parametric points. \~ - \param[in] maxPntsCnt - \ru Максимально допустимое количество точек. - \en The maximal acceptable number of points. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (MbResultType) PointsOnSurface( const MbSurface & surface, - MbeItemGridType & gridType, - const MbCartPoint & uv0, - double angle, - MbeStepType stepType, - double step1, - double step2, - bool truncateByBounds, - RPArray< SArray > & result, - size_t maxPntsCnt = c3d::ARRAY_MAX_COUNT ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Определить умолчательную разбивку поверхности. - \en Define the default sampling of a surface. \~ - \details \ru Определить умолчательную разбивку поверхности \n - (вспомогательная функция для функции PointsOnSurface). - \en Define the default sampling of a surface \n - (an auxillary function for function PointsOnSurface). \~ - \param[in] surface - \ru Исходная поверхность. - \en The initial surface. \~ - \param[out] uPntsCnt - \ru Количество разбиений по u. - \en The points number in U direction. \~ - \param[out] vPntsCnt - \ru Количество разбиений по v. - \en The points number in V direction. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (void) DefinePointsOnSurfaceCounts( const MbSurface & surface, - size_t & uPntsCnt, - size_t & vPntsCnt ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точку пересечения трех поверхностей. - \en Calculate the intersection point of three surfaces. \~ - \details \ru Найти точку пересечения трех поверхностей по начальным приближениям. \n - \en Calculate the intersection point of three surfaces given the initial estimates. \n \~ - \param[in] surf0 - \ru Первая поверхность. - \en The first surface. \~ - \param[in] ext0 - \ru Флаг поиска на продолжении первой поверхности. - \en Whether to use the extension of the first surface. \~ - \param[in] surf1 - \ru Вторая поверхность. - \en The second surface. \~ - \param[in] ext1 - \ru Флаг поиска на продолжении второй поверхности. - \en Whether to use the extension of the second surface. \~ - \param[in] surf2 - \ru Третья поверхность. - \en The third surface. \~ - \param[in] ext2 - \ru Флаг поиска на продолжении третьей поверхности. - \en Whether to use the extension of the third surface. \~ - \param[in,out] uv0 - \ru Началальное приближение и результат на поверхности surf0. - \en The initial approximation and the result on surface surf0. \~ - \param[in,out] uv1 - \ru Началальное приближение и результат на поверхности surf1. - \en The initial approximation and the result on surface surf1. \~ - \param[in,out] uv2 - \ru Началальное приближение и результат на поверхности surf2. - \en The initial approximation and the result on surface surf2. \~ - \return \ru Возвращает код результата итерационного поиска точки пересечения. - \en Returns the result code of the intersection point iterative search. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (MbeNewtonResult) IntersectionPoint( const MbSurface & surf0, bool ext0, - const MbSurface & surf1, bool ext1, - const MbSurface & surf2, bool ext2, - MbCartPoint & uv0, - MbCartPoint & uv1, - MbCartPoint & uv2 ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти все точки пересечения поверхности и кривой. - \en Calculate all the points of intersection of a surface and a curve. \~ - \details \ru Найти все точки пересечения поверхности и кривой. \n - \en Calculate all the points of intersection of a surface and a curve. \n \~ - \param[in] surf - \ru Поверхность. - \en A surface. \~ - \param[in] surfExt - \ru Искать на продолжении поверхности. - \en Use the surface extension. \~ - \param[in] curv - \ru Кривая. - \en The curve. \~ - \param[in] curveExt - \ru Искать на продолжении кривой. - \en Use the curve extension. \~ - \param[out] uv - \ru Параметры точек пересечения на поверхности. - \en Parameters of the intersection points on the surface. \~ - \param[out] tt - \ru Параметры точек пересечения на кривой. - \en Parameters of the intersection points on the curve. \~ - \param[in] touchInclude - \ru Считать касания пересечениями. - \en Consider tangencies as intersections. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (void) IntersectionPoints( const MbSurface & surf, bool surfExt, - const MbCurve3D & curv, bool curveExt, - SArray & uv, - SArray & tt, - bool touchInclude = false ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Определить параметры ближайших точек прямых. - \en Determine the parameters of the nearest points of lines. \~ - \details \ru Определить параметры ближайших точек прямых, заданных точкой и вектором направления. - \en Determine the parameters of the nearest points of lines which are defined by the given point and direction vector. \~ - \param[in] origin1, direction1 - \ru Точка и направление первой прямой. - \en A point and direction of the first line. \~ - \param[in] origin2, direction2 - \ru Точка и направление второй прямой. - \en A point and direction of the second line. \~ - \param[out] t1 - \ru Параметр на первой прямой. - \en Parameter on the first line. \~ - \param[out] t2 - \ru Параметр на второй прямой. - \en Parameter on the second line. \~ - \return \ru Возвращает true, если есть прямые не параллельны. \n - \en Returns true, if lines are not parallel. \n \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) LineLineNearestParams( const MbCartPoint3D & origin1, const MbVector3D & direction1, - const MbCartPoint3D & origin2, const MbVector3D & direction2, - double & t1, double & t2 ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Определение расстояния между ближайшими точками p1 и p2 прямых line1 и line2 - \en Determination of the distance between the nearest points p1 and p2 of lines line1 and line2 \~ - \details \ru Определение расстояния между ближайшими точками p1 и p2 прямых line1 и line2 - \en Determination of the distance between the nearest points p1 and p2 of lines line1 and line2 \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (double) LineLineNearestPoints( const MbLine3D & line1, const MbLine3D & line2, - MbCartPoint3D & p1, MbCartPoint3D & p2 ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Определить параметры ближайших точек прямых. - \en Determine the parameters of the nearest points of lines. \~ - \details \ru Определить параметры ближайших точек прямых, заданных точкой и вектором направления. - \en Determine the parameters of the nearest points of lines which are defined by the given point and direction vector. \~ - \param[in] origin1, direction1 - \ru Точка и направление первой прямой. - \en A point and direction of the first line. \~ - \param[in] origin2, direction2 - \ru Точка и направление второй прямой. - \en A point and direction of the second line. \~ - \param[out] t1 - \ru Параметр на первой прямой. - \en Parameter on the first line. \~ - \param[out] t2 - \ru Параметр на второй прямой. - \en Parameter on the second line. \~ - \return \ru Возвращает true, если есть прямые не параллельны. \n - \en Returns true, if lines are not parallel. \n \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) LineLineNearestParams( const MbCartPoint & origin1, const MbVector & direction1, - const MbCartPoint & origin2, const MbVector & direction2, - double & t1, double & t2 ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точку пересечения двух прямых. - \en Calculate the point of two lines intersection. \~ - \details \ru Найти точку пересечения двух точно пересекающихся прямых без проверки параллельности. \n - \en Calculate the intersection point of two exactly intersecting lines without check. \n \~ - \param[in] line1 - \ru Первая прямая. - \en The first line. \~ - \param[in] line2 - \ru Вторая прямая. - \en The second line. \~ - \param[out] result - \ru Точка пересечения. - \en The intersection point. \~ - \ingroup Point_Modeling -*/ -// --- -inline void FastLineLine( const MbLine & line1, - const MbLine & line2, - MbCartPoint & result ) -{ - const MbDirection & dir1 = line1.GetDirection(); - const MbDirection & dir2 = line2.GetDirection(); - const MbCartPoint & pnt1 = line1.GetOrigin(); - const MbCartPoint & pnt2 = line2.GetOrigin(); - - double t = ( dir1.ax * (pnt2.y - pnt1.y) - dir1.ay * (pnt2.x - pnt1.x )) / - ( dir1.ay * dir2.ax - dir1.ax * dir2.ay ); - - result.x = pnt2.x + dir2.ax * t; - result.y = pnt2.y + dir2.ay * t; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точку пересечения двух прямых. - \en Calculate the point of two lines intersection. \~ - \details \ru Найти точку пересечения двух прямых. \n - Прямые могут быть параллельны или совпадать. \n - \en Calculate the point of two lines intersection. \n - The curves can be parallel or coincident. \n \~ - \param[in] line1 - \ru Первая прямая. - \en The first line. \~ - \param[in] line2 - \ru Вторая прямая. - \en The second line. \~ - \param[out] result - \ru Точка пересечения. - \en The intersection point. \~ - \return \ru Возвращает результат пересечения: \n - 1 - Прямые пересекаются. \n - 0 - Прямые параллельны или совпадают. - \en Returns the result of intersection: \n - 1 - The lines intersect at a point. \n - 0 - The lines are parallel or coincident. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (int) LineLine( const MbLine & line1, - const MbLine & line2, - MbCartPoint & result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точку пересечения двух прямых. - \en Calculate the point of two lines intersection. \~ - \details \ru Найти точку пересечения двух прямых. \n - Прямые могут быть параллельны или совпадать. \n - \en Calculate the point of two lines intersection. \n - The curves can be parallel or coincident. \n \~ - \param[in] line1 - \ru Первая прямая. - \en The first line. \~ - \param[in] line2 - \ru Вторая прямая. - \en The second line. \~ - \param[out] result - \ru Точка пересечения. - \en The intersection point. \~ - \return \ru Возвращает результат пересечения: \n - 1 : прямые пересекаются; \n - 0 : прямые параллельны; \n - 1 : прямые совпадают - касательная точка пересечения. - \en Returns the result of intersection: \n 1 : the curves intersect at a point; \n 0 : the curves are parallel; \n 1 : the curves are coincident - the tangent intersection point. \~\ingroup Point_Modeling -*/ -// --- -MATH_FUNC (int) LineLine( const MbLine & line1, - const MbLine & line2, - MbCrossPoint & result ); - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точку пересечения прямой и отрезка. - \en Calculate the intersection point of a line and a line segment. \~ - \details \ru Найти точку пересечения прямой и отрезка. \n - Отрезок может быть параллелен прямой или лежать на ней. \n - \en Calculate the intersection point of a line and a line segment. \n - The line segment can be parallel to the curve or lie on it. \n \~ - \param[in] line - \ru Прямая. - \en The line. \~ - \param[in] lseg - \ru Отрезок. - \en The segment. \~ - \param[out] result - \ru Точка пересечения. - \en The intersection point. \~ - \return \ru Возвращает результат пересечения: \n - 1 : прямая и отрезок пересекаются; \n - 0 : прямая и отрезок параллельны; \n - 1 : отрезок лежит на прямой - касательная точка пересечения. - \en Returns the result of intersection: \n 1 : the line and the line segment intersect at a point; \n 0 : the line and a line segment are parallel; \n 1 : the segment lies on the curve - a tangent intersection point. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (int) LineLineSeg( const MbLine & line, - const MbLineSegment & lseg, - MbCrossPoint & result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точки пересечения прямой и окружности. - \en Calculate intersection points of a line and a circle. \~ - \details \ru Найти параметры точек пересечения прямой и окружности. \n - \en Calculate the parameters of intersection points of a line and a circle. \n \~ - \param[in] line - \ru Прямая. - \en The line. \~ - \param[in] centre - \ru Центр окружности. - \en The circle center. \~ - \param[in] radius - \ru Радиус окружности. - \en The circle radius. \~ - \param[out] result - \ru Точки пересечения (указатель на массив из двух(!) элементов). - \en The intersection points (a pointer to the array of two (!) elements). \~ - \return \ru Возвращает количество найденных пересечений. - \en Returns the number of intersections. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (int) LineCircle( const MbLine & line, - const MbCartPoint & centre, - double radius, - MbCrossPoint * result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точки пересечения двух кривых. - \en Calculate intersection points of two curves. \~ - \details \ru Найти параметры точек пересечения двух произвольных кривых. \n - Общий метод вызывается, если нет частной функции пересечения. \n - \en Calculate the parameters of intersection points of two arbitrary curves. \n - The general method is used if there is no special function for intersection. \n \~ - \param[in] pCurve1 - \ru Первая кривая. - \en The first curve. \~ - \param[in] pCurve2 - \ru Вторая кривая. - \en The second curve. \~ - \param[out] result - \ru Множество точек пересечения. - \en The array of intersection points. \~ - \param[in] touchInclude - \ru Считать касания пересечениями. - \en Consider tangencies as intersections. \~ - \param[in] epsilon - \ru Точность совпадения точек пересечения кривых. - \en The accuracy of coincidence points of intersection. \~ - \param[in] allowInaccuracy - \ru Разрешить понижать входную точность. - \en Allow lowering input accuracy. \~ - \return \ru Количество найденных пересечений. - \en The number of intersections. \~ - \warning \ru Применяется для двумерных построений, аналог CurveCurveIntersection. - \en Used for two-dimensional constructions, the analogue of CurveCurveIntersection. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (ptrdiff_t) IntersectTwoCurves( const MbCurve & pCurve1, - const MbCurve & pCurve2, - SArray & result, - bool touchInclude = true, - double epsilon = Math::LengthEps*c3d::METRIC_DELTA, - bool allowInaccuracy = true ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точки пересечения двух кривых. - \en Calculate intersection points of two curves. \~ - \details \ru Найти параметры точек пересечения двух произвольных кривых. \n - Общий метод вызывается, если нет частной функции пересечения. \n - \en Calculate the parameters of intersection points of two arbitrary curves. \n - The general method is used if there is no special function for intersection. \n \~ - \param[in] curve1 - \ru Первая кривая. - \en The first curve. \~ - \param[in] curve2 - \ru Вторая кривая. - \en The second curve. \~ - \param[out] result1 - \ru Параметры пересечений первой кривой. - \en The parameters of intersections for the first curve. \~ - \param[out] result2 - \ru Параметры пересечений второй кривой. - \en The parameters of intersections for the second curve. \~ - \param[in] xEpsilon - \ru Точность по x. - \en Tolerance in x direction. \~ - \param[in] yEpsilon - \ru Точность по y. - \en Tolerance in y direction. \~ - \param[in] touchInclude - \ru Считать касания пересечениями. - \en Consider tangencies as intersections. \~ - \param[in] allowInaccuracy - \ru Разрешить нахождение решения с меньшей точностью при невозможности удовлетворить указанной. - \en Allow to find a solution with less precision when we can't get a solution with given precision. \~ - \return \ru Количество найденных пересечений. - \en The number of intersections. \~ - \warning \ru Применяется для трехмерных построений, аналог IntersectTwoCurves. - \en Used for three-dimensional constructions, the analogue of IntersectTwoCurves. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (ptrdiff_t) CurveCurveIntersection( const MbCurve & curve1, - const MbCurve & curve2, - SArray & result1, - SArray & result2, - double xEpsilon, - double yEpsilon, - bool touchInclude, bool allowInaccuracy = true ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точки самопересечения кривой. - \en Calculate the points of curve self-intersection. \~ - \details \ru Найти параметры точек самопересечения кривой с заданной точностью. \n - \en Calculate the self-intersection points parameters with the given tolerance. \n \~ - \param[in] curve - \ru Кривая. - \en The curve. \~ - \param[in] xEpsilon - \ru Точность по x. - \en Tolerance in x direction. \~ - \param[in] yEpsilon - \ru Точность по y. - \en Tolerance in y direction. \~ - \param[out] result1 - \ru Множество параметров самопересечения. - \en The self-intersection parameters array. \~ - \param[out] result2 - \ru Множество параметров самопересечения. - \en The self-intersection parameters array. \~ - \param[in] version - \ru Версия операции. - \en The version of the operation. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (void) CurveSelfIntersect( const MbCurve & curve, - double xEpsilon, - double yEpsilon, - SArray & result1, - SArray & result2, - VERSION version = Math::DefaultMathVersion() ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Удалить точки касания. - \en Remove touch points. \~ - \details \ru Удалить все точки касания кривых вне зависимости от положения параметра на кривой - (внутри области определения или на границах кривой). - \en Remove all curves touch points regardless the position on the curve - (in the domain or on the borders). \~ - \param[in] curve1 - \ru Первая кривая. - \en The first curve. \~ - \param[in] curve2 - \ru Вторая кривая. - \en The second curve. \~ - \param[in, out] result - \ru Множество точек пересечения. - \en The array of intersection points. \~ - \param[in] eps - \ru Погрешность для функции проверки параллельности касательных RoundColinear. - \en Accuracy for the function RoundColinear of testing the parallelism of tangents. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (void) RemoveAllTouchParams( const MbCurve & curve1, - const MbCurve & curve2, - SArray & result, - double eps = PARAM_NEAR ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точки пересечения двух кривых. - \en Calculate intersection points of two curves. \~ - \details \ru Найти параметры точек пересечения двух произвольных кривых. \n - \en Calculate the parameters of intersection points of two arbitrary curves. \n \~ - \param[in] curve1 - \ru Первая кривая. - \en The first curve. \~ - \param[in] curve2 - \ru Вторая кривая. - \en The second curve. \~ - \param[out] result1 - \ru Параметры точек пересечения для первой кривой. - \en The intersection points parameters for the first curve. \~ - \param[out] result2 - \ru Параметры точек пересечения для второй кривой. - \en The intersection points parameters for the second curve. \~ - \param[in] mEps - \ru Возможная максимальная погрешность найденных пересечений. - \en The intersection tolerance. \~ - \return \ru Количество найденных пересечений. - \en The number of intersections. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (ptrdiff_t) CurveCurveIntersection( const MbCurve3D & curve1, - const MbCurve3D & curve2, - SArray & result1, - SArray & result2, - double mEps = Math::metricRegion ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить кривую на самопересечение. - \en Determine if the curve has self-intersections. \~ - \details \ru Проверить заданную кривую на самопересечение. \n - \en Determine if the given curve has self-intersections. \n \~ - \param[in] curve - \ru Кривая. - \en The curve. \~ - \param[in] mEps - \ru Возможная максимальная погрешность найденных самопересечений. - \en The tolerance of self-intersections. \~ - \return \ru Возвращает true, если кривая самопересекается. - \en Returns true if the curve has self-intersections. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (bool) IsSelfIntersect( const MbCurve3D & curve, - double mEps = Math::metricRegion ); - - - -//------------------------------------------------------------------------------ -/** \brief \ru Убрать касательные точки пересечения. - \en Remove the tangent intersection points. \~ - \details \ru Убрать параметры касательных точек пересечения внутри областей определения кривых. \n - \en Remove the tangent intersection points parameters inside the domains of curves. \n \~ - \param[in] curve1 - \ru Первая кривая. - \en The first curve. \~ - \param[in] curve2 - \ru Вторая кривая. - \en The second curve. \~ - \param[out] result1 - \ru Параметры точек пересечения для первой кривой. - \en The intersection points parameters for the first curve. \~ - \param[out] result2 - \ru Параметры точек пересечения для второй кривой. - \en The intersection points parameters for the second curve. \~ - \param[in] mEps - \ru Возможная максимальная погрешность найденных пересечений. - \en The intersection tolerance. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (void) FilterTouchParams( const MbCurve3D & curve1, - const MbCurve3D & curve2, - SArray & result1, - SArray & result2, - double mEps = Math::metricRegion ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точки скрещения двух кривых. - \en Calculate the points of two curves crossing. \~ - \details \ru Найти параметры точек скрещения двух кривых. \n - \en Calculate parameters of the points of two curves crossing. \n \~ - \param[in] curve1 - \ru Первая кривая. - \en The first curve. \~ - \param[in] curve2 - \ru Вторая кривая. - \en The second curve. \~ - \param[out] result1 - \ru Параметры точек скрещения для первой кривой. - \en Parameters of the points of crossing for the first curve. \~ - \param[out] result2 - \ru Параметры точек скрещения для второй кривой. - \en Parameters of the points of crossing for the second curve. \~ - \return \ru Количество найденных скрещений. - \en The points of crossing number. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (ptrdiff_t) CurveCurveCrossing( const MbCurve3D & curve1, - const MbCurve3D & curve2, - SArray & result1, - SArray & result2, - double epsilon = Math::metricRegion ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти проекцию точки на поверхность относительно внешнего контура поверхности. - \en Find the projection of a point on a surface relative to the outer contour of the surface. \~ - \details \ru Найти проекцию пространственной точки на поверхность в виде двумерной точки на поверхности - относительно внешнего контура поверхности. \n - \en Calculate the projection of a space point on a surface as a two-dimensional point on the surface. - relative to the outer contour of the surface. \n \~ - \param[in] surface - \ru Поверхность. - \en A surface. \~ - \param[in] pnt - \ru Пространственная точка. - \en A space point. \~ - \param[in] byOuterRectOnly - \ru Классифицировать проекцию только относительно внешнего габаритного прямоугольника. - \en Whether to classify the projection relative to the outer bounding box only. \~ - \param[out] result - \ru Двумерная параметрическая точка на поверхности. - \en A two-dimensional parametric point on the surface. \~ - \return \ru Возвращает true, если найдена нормальная проекция точки на поверхность. - \en Returns true if a normal projection of the point on the surface has been calculated. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (bool) PointProjectionRelativeOuterLoop( const MbSurface & surface, - const MbCartPoint3D & pnt, - bool byOuterRectOnly, - MbCartPoint & result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Является ли проекция точки точно неоднозначной. - \en Determine whether the point projection is multiple-valued. \~ - \details \ru Является ли проекция точки неоднозначной при проецировании - в области определения поверхности. \n - \en Determine whether the point projection is multiple-valued while projecting - inside the surface domain. \n \~ - \param[in] surface - \ru Поверхность. - \en A surface. \~ - \param[in] result - \ru Пространственная точка. - \en A space point. \~ - \return \ru Возвращает true, если проекция точки является неоднозначной. - \en Returns true if the point projection is multiple-valued \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC (bool) IsMultipleProjection( const MbSurface & surface, - const MbCartPoint3D & result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти точки касания двух поверхностей. - \en Calculate the touch points of two surfaces. \~ - \details \ru Найти параметры точек касания двух поверхностей. \n - \en Calculate the parameters of touch points of two surfaces. \n \~ - \param[in] surf1 - \ru Первая поверхность. - \en A first surface. \~ - \param[in] ext1 - \ru Искать на продолжении первой поверхности. - \en Use the first surface extension. \~ - \param[in] surf2 - \ru Вторая поверхность. - \en A second surface. \~ - \param[in] ext2 - \ru Искать на продолжении второй поверхности. - \en Use the second surface extension. \~ - \param[in] uv1arr - \ru Параметры точек касания первой поверхности. - \en Parameters of touch points of first surface. \~ - \param[in] uv2arr - \ru Параметры точек касания второй поверхности. - \en Parameters of touch points of second surface. \~ - \return \ru Возвращает true, если найдены точки касания. - \en Returns true if the touch points has be calculate. \~ - \warning \ru В разработке. - \en Under development. \~ - \ingroup Point_Modeling -*/ -// --- -MATH_FUNC ( bool) TouchIntersectionPoints( const MbSurface & surf1, bool ext1, - const MbSurface & surf2, bool ext2, - std::vector & uv1arr, - std::vector & uv2arr ); - - -#endif // __ACTION_POINT_H - +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Функции создания точек. + \en Functions for points creation. \~ + \details \ru Функции, использующие в качестве выходных параметров точки или массивы точек. + \en Functions that take points or arrays of points as input parameters. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ACTION_POINT_H +#define __ACTION_POINT_H + + +#include +#include +#include +#include +#include +#include +#include + + +class MATH_CLASS MbCurve; +class MATH_CLASS MbLineSegment; +class MATH_CLASS MbLine3D; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbSurface; +class MATH_CLASS MbFaceShell; + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать массив. + \en Create an array. \~ + \details \ru Создать массив с контролем выделения памяти. \n + \en Create an array with memory allocation control. \n \~ + \param[in] cnt - \ru Количество элементов массива. + \en Number of elements in the array. \~ + \param[out] res - \ru Результат операции. + \en The operation result. \~ + \return \ru Возвращает массив элементов, если он создан, или NULL в противном случае. + \en Returns an array of elements if it has been created, otherwise returns NULL. \~ + \ingroup Algorithms_3D +*/ +// --- +template +inline SArray * CreateArray( size_t cnt, MbResultType & res ) +{ + SArray * arr = new SArray ( cnt, 1 ); + if ( arr != NULL && arr->GetAddr() == NULL ) { + delete arr; + arr = NULL; + } + if ( arr == NULL ) + res = rt_TooManyPoints; + + return arr; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Выделить в массиве память под n элементов. + \en Allocate memory in the array for n elements. \~ + \details \ru Выделить в массиве память под n элементов с контролем выделения памяти. \n + \en Allocate memory in the array for n elements with memory allocation control. \n \~ + \param[in, out] arr - \ru Массив. + \en An array. \~ + \param[in] n - \ru Количество элементов, под которые нужно выделить память. + \en Number of elements for allocation. \~ + \param[out] res - \ru Результат операции. + \en The operation result. \~ + \return \ru Возвращает true в случае успешного выделения памяти. + \en Returns true if the memory has been successfully allocated. \~ + \ingroup Algorithms_3D +*/ +// --- +template +inline bool ReserveArray( SArray & arr, size_t n, MbResultType & res ) +{ + arr.Reserve( n ); + if ( arr.GetAddr() == NULL ) { + res = rt_TooManyPoints; + return false; + } + return true; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Добавить в массив элемент. + \en Add an element to the array. \~ + \details \ru Добавить в массив элемент с контролем выделения памяти. \n + \en Add an element to the array with memory allocation control. \n \~ + \param[in, out] arr - \ru Массив. + \en An array. \~ + \param[in] item - \ru Элемент, который нужно добавить. + \en The element to add. \~ + \param[out] res - \ru Результат операции. + \en The operation result. \~ + \return \ru Возвращает true в случае успешного добавления. + \en Returns true if the element has been successfully added. \~ + \ingroup Algorithms_3D +*/ +// --- +template +inline bool AddItem( SArray & arr, const Type & item, MbResultType & res ) +{ + arr.Add( item ); + if ( arr.GetAddr() == NULL ) { + res = rt_TooManyPoints; + return false; + } + return true; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Пространственно-параметрическая точка. + \en A space-parametric point. \~ + \details \ru Пространственно-параметрическая точка. \n + Содержит в себе трехмерную и двумерную точки. + \en A space-parametric point. \n + Contains a three-dimensional point and a two-dimensional point. \~ + \ingroup Point_Modeling +*/ +// --- +class MATH_CLASS MbSpaceParamPnt { +protected: + MbCartPoint3D spacePnt; ///< \ru Пространственная точка. \en A spatial point. + MbCartPoint paramPnt; ///< \ru Параметрическая точка. \en A parametric point. + +public: // \ru Конструкторы \en Constructors + /// \ru Конструктор по пространственной точке. \en A constructor that takes a space point. + explicit MbSpaceParamPnt( const MbCartPoint3D & sp ) : spacePnt( sp ), paramPnt( UNDEFINED_DBL, 0.0 ) {} + /// \ru Конструктор по пространственной и параметрической точкам. \en A constructor that takes a space point and a parametric point. + explicit MbSpaceParamPnt( const MbCartPoint3D & sp, const MbCartPoint & pp ) : spacePnt( sp ), paramPnt( pp ) {} + /// \ru Конструктор по пространственно-параметрической точке. \en A constructor that takes a space-parametric point. + explicit MbSpaceParamPnt( const MbSpaceParamPnt & cp ) : spacePnt( cp.spacePnt ), paramPnt( cp.paramPnt ) {} + ~MbSpaceParamPnt() {} + +public: // \ru Инициализация \en The initialization + /// \ru Инициализация по пространственно-параметрической точке. \en Initialization with a space-parametric point. + void Init( const MbSpaceParamPnt & cp ) { spacePnt = cp.spacePnt; paramPnt = cp.paramPnt; } + /// \ru Инициализация по пространственной и параметрической точкам. \en Initialization with a space point and a parametric point. + void Init( const MbCartPoint3D & sp, const MbCartPoint & pp ) { spacePnt = sp; paramPnt = pp; } +public: // \ru Функции \en Functions + /// \ru Установлена ли параметрическая точка? \en Whether the parametric point is specified. + bool IsParamPnt() const { return (paramPnt.x != UNDEFINED_DBL); } //-V550 + /// \ru Перевести параметрическую точку в неустановленное состояние. \en Reset a parametric point. + void ResetParamPnt() { paramPnt.x = UNDEFINED_DBL; } + /// \ru Проверка на равенство параметрических точек по X с заданной погрешностью. \en Check if parametric points are equal by X component with the specified tolerance. + bool IsParamEqualX( const MbSpaceParamPnt & cp, double eps ) const { return (::fabs(paramPnt.x - cp.paramPnt.x) < eps); } + /// \ru Проверка на равенство параметрических точек по Y с заданной погрешностью. \en Check if parametric points are equal by Y component with the specified tolerance. + bool IsParamEqualY( const MbSpaceParamPnt & cp, double eps ) const { return (::fabs(paramPnt.y - cp.paramPnt.y) < eps); } + + /// \ru Получить ссылку на пространственную точку. \en Get a reference to the space point. + const MbCartPoint3D & GetSpacePnt() const { return spacePnt; } + /// \ru Получить ссылку на параметрическую точку. \en Get a reference to the parametric point. + const MbCartPoint & GetParamPnt() const { return paramPnt; } + +private: // \ru Нереализованные \en Not implemented + MbSpaceParamPnt(); + MbSpaceParamPnt( const MbCartPoint & ); + void operator = ( const MbCartPoint3D & ); + void operator = ( const MbCartPoint & ); + void operator = ( const MbSpaceParamPnt & ); + bool operator == ( const MbSpaceParamPnt & ) const; +}; + + +typedef std::pair MbLocPnt; ///< \ru Пространственно-параметрическая точка с индексированным положением. \en A space-parametric point with indexed position. + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать точки на поверхности. + \en Create points on a surface. \~ + \details \ru Создать группу точек на поверхности. \n + \en Create a group of points on a surface. \n \~ + \param[in] surface - \ru Поверхность-источник. + \en The source surface. \~ + \param[in] stepType - \ru Тип шага по поверхности. + \en Type of spacing on a surface. \~ + \param[in] uValue - \ru Величина шага по u или количество точек по u при шаге по параметру + \en U-spacing value or number of points in u-direction while sampling by parameter \~ + \param[in] vValue - \ru Величина шага по v или количество точек по v при шаге по параметру. + \en V-spacing value or number of points in v-direction while sampling by parameter. \~ + \param[in] truncateByBounds - \ru Усечь границами поверхности. + \en Whether to truncate by surface boundary. \~ + \param[out] result - \ru Индексированные пространственно-параметрические точки. + \en Indexed space-parametric points. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (MbResultType) PointsOnSurface( const MbSurface & surface, + MbeStepType stepType, + double uValue, + double vValue, + bool truncateByBounds, + RPArray< SArray > & result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать точки на поверхности. + \en Create points on a surface. \~ + \details \ru Создать группу точек на поверхности. \n + \en Create a group of points on a surface. \n \~ + \param[in] surface - \ru Поверхность-источник. + \en The source surface. \~ + \param[in] gridType - \ru Тип сетки на поверхности. + \en A type of a grid on a surface. \~ + \param[in] uv0 - \ru Центральная точка сетки + \en The central point of the grid. \~ + \param[in] angle - \ru Угол поворота сетки относительно направления U (в радианах) + \en Rotation angle of the grid relative to U direction (in radians). \~ + \param[in] stepType - \ru Тип шага по поверхности. + \en Type of spacing on a surface. \~ + \param[in] step1 - \ru Величина шага по первому направлению + \en A spacing value in the first direction \~ + \param[in] step2 - \ru Величина шага по второму направлению + \en A spacing value in the second direction \~ + \param[in] truncateByBounds - \ru Усечь границами поверхности. + \en Whether to truncate by surface boundary. \~ + \param[out] result - \ru Индексированные пространственно-параметрические точки. + \en Indexed space-parametric points. \~ + \param[in] maxPntsCnt - \ru Максимально допустимое количество точек. + \en The maximal acceptable number of points. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (MbResultType) PointsOnSurface( const MbSurface & surface, + MbeItemGridType & gridType, + const MbCartPoint & uv0, + double angle, + MbeStepType stepType, + double step1, + double step2, + bool truncateByBounds, + RPArray< SArray > & result, + size_t maxPntsCnt = c3d::ARRAY_MAX_COUNT ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Определить разбивку поверхности по умолчанию. + \en Define the default sampling of a surface. \~ + \details \ru Определить разбивку поверхности по умолчанию \n + (вспомогательная функция для функции PointsOnSurface). + \en Define the default sampling of a surface \n + (an auxiliary function for function PointsOnSurface). \~ + \param[in] surface - \ru Исходная поверхность. + \en The initial surface. \~ + \param[out] uPntsCnt - \ru Количество разбиений по u. + \en The points number in U direction. \~ + \param[out] vPntsCnt - \ru Количество разбиений по v. + \en The points number in V direction. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (void) DefinePointsOnSurfaceCounts( const MbSurface & surface, + size_t & uPntsCnt, + size_t & vPntsCnt ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точку пересечения трех поверхностей. + \en Calculate the intersection point of three surfaces. \~ + \details \ru Найти точку пересечения трех поверхностей по начальным приближениям. \n + \en Calculate the intersection point of three surfaces given the initial estimates. \n \~ + \param[in] surf0 - \ru Первая поверхность. + \en The first surface. \~ + \param[in] ext0 - \ru Флаг поиска на продолжении первой поверхности. + \en Whether to use the extension of the first surface. \~ + \param[in] surf1 - \ru Вторая поверхность. + \en The second surface. \~ + \param[in] ext1 - \ru Флаг поиска на продолжении второй поверхности. + \en Whether to use the extension of the second surface. \~ + \param[in] surf2 - \ru Третья поверхность. + \en The third surface. \~ + \param[in] ext2 - \ru Флаг поиска на продолжении третьей поверхности. + \en Whether to use the extension of the third surface. \~ + \param[in,out] uv0 - \ru Начальное приближение и результат на поверхности surf0. + \en The initial approximation and the result on surface surf0. \~ + \param[in,out] uv1 - \ru Начальное приближение и результат на поверхности surf1. + \en The initial approximation and the result on surface surf1. \~ + \param[in,out] uv2 - \ru Начальное приближение и результат на поверхности surf2. + \en The initial approximation and the result on surface surf2. \~ + \return \ru Возвращает код результата итерационного поиска точки пересечения. + \en Returns the result code of the intersection point iterative search. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (MbeNewtonResult) IntersectionPoint( const MbSurface & surf0, bool ext0, + const MbSurface & surf1, bool ext1, + const MbSurface & surf2, bool ext2, + MbCartPoint & uv0, + MbCartPoint & uv1, + MbCartPoint & uv2 ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти все точки пересечения поверхности и кривой. + \en Calculate all the points of intersection of a surface and a curve. \~ + \details \ru Найти все точки пересечения поверхности и кривой. \n + \en Calculate all the points of intersection of a surface and a curve. \n \~ + \param[in] surf - \ru Поверхность. + \en A surface. \~ + \param[in] surfExt - \ru Искать на продолжении поверхности. + \en Use the surface extension. \~ + \param[in] curv - \ru Кривая. + \en The curve. \~ + \param[in] curveExt - \ru Искать на продолжении кривой. + \en Use the curve extension. \~ + \param[out] uv - \ru Параметры точек пересечения на поверхности. + \en Parameters of the intersection points on the surface. \~ + \param[out] tt - \ru Параметры точек пересечения на кривой. + \en Parameters of the intersection points on the curve. \~ + \param[in] touchInclude - \ru Считать касания пересечениями. + \en Consider tangencies as intersections. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (void) IntersectionPoints( const MbSurface & surf, + bool surfExt, + const MbCurve3D & curv, + bool curveExt, + SArray & uv, + SArray & tt, + bool touchInclude = false ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Определить параметры ближайших точек прямых. + \en Determine the parameters of the nearest points of lines. \~ + \details \ru Определить параметры ближайших точек прямых, заданных точкой и вектором направления. + \en Determine the parameters of the nearest points of lines which are defined by the given point and direction vector. \~ + \param[in] origin1, direction1 - \ru Точка и направление первой прямой. + \en A point and direction of the first line. \~ + \param[in] origin2, direction2 - \ru Точка и направление второй прямой. + \en A point and direction of the second line. \~ + \param[out] t1 - \ru Параметр на первой прямой. + \en Parameter on the first line. \~ + \param[out] t2 - \ru Параметр на второй прямой. + \en Parameter on the second line. \~ + \return \ru Возвращает true, если есть прямые не параллельны. \n + \en Returns true, if lines are not parallel. \n \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) LineLineNearestParams( const MbCartPoint3D & origin1, + const MbVector3D & direction1, + const MbCartPoint3D & origin2, + const MbVector3D & direction2, + double & t1, + double & t2 ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Определение расстояния между ближайшими точками p1 и p2 прямых line1 и line2 + \en Determination of the distance between the nearest points p1 and p2 of lines line1 and line2 \~ + \details \ru Определение расстояния между ближайшими точками p1 и p2 прямых line1 и line2 + \en Determination of the distance between the nearest points p1 and p2 of lines line1 and line2 \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (double) LineLineNearestPoints( const MbLine3D & line1, + const MbLine3D & line2, + MbCartPoint3D & p1, + MbCartPoint3D & p2 ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Определить параметры ближайших точек прямых. + \en Determine the parameters of the nearest points of lines. \~ + \details \ru Определить параметры ближайших точек прямых, заданных точкой и вектором направления. + \en Determine the parameters of the nearest points of lines which are defined by the given point and direction vector. \~ + \param[in] origin1, direction1 - \ru Точка и направление первой прямой. + \en A point and direction of the first line. \~ + \param[in] origin2, direction2 - \ru Точка и направление второй прямой. + \en A point and direction of the second line. \~ + \param[out] t1 - \ru Параметр на первой прямой. + \en Parameter on the first line. \~ + \param[out] t2 - \ru Параметр на второй прямой. + \en Parameter on the second line. \~ + \return \ru Возвращает true, если есть прямые не параллельны. \n + \en Returns true, if lines are not parallel. \n \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) LineLineNearestParams( const MbCartPoint & origin1, const MbVector & direction1, + const MbCartPoint & origin2, const MbVector & direction2, + double & t1, double & t2 ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точку пересечения двух прямых. + \en Calculate the point of two lines intersection. \~ + \details \ru Найти точку пересечения двух точно пересекающихся прямых без проверки параллельности. \n + \en Calculate the intersection point of two exactly intersecting lines without check. \n \~ + \param[in] line1 - \ru Первая прямая. + \en The first line. \~ + \param[in] line2 - \ru Вторая прямая. + \en The second line. \~ + \param[out] result - \ru Точка пересечения. + \en The intersection point. \~ + \ingroup Point_Modeling +*/ +// --- +inline +void FastLineLine( const MbLine & line1, + const MbLine & line2, + MbCartPoint & result ) +{ + const MbDirection & dir1 = line1.GetDirection(); + const MbDirection & dir2 = line2.GetDirection(); + const MbCartPoint & pnt1 = line1.GetOrigin(); + const MbCartPoint & pnt2 = line2.GetOrigin(); + + double t = ( dir1.ax * (pnt2.y - pnt1.y) - dir1.ay * (pnt2.x - pnt1.x )) / + ( dir1.ay * dir2.ax - dir1.ax * dir2.ay ); + + result.x = pnt2.x + dir2.ax * t; + result.y = pnt2.y + dir2.ay * t; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точку пересечения двух прямых. + \en Calculate the point of two lines intersection. \~ + \details \ru Найти точку пересечения двух прямых. \n + Прямые могут быть параллельны или совпадать. \n + \en Calculate the point of two lines intersection. \n + The curves can be parallel or coincident. \n \~ + \param[in] line1 - \ru Первая прямая. + \en The first line. \~ + \param[in] line2 - \ru Вторая прямая. + \en The second line. \~ + \param[out] result - \ru Точка пересечения. + \en The intersection point. \~ + \return \ru Возвращает результат пересечения: \n + 1 - Прямые пересекаются. \n + 0 - Прямые параллельны или совпадают. + \en Returns the result of intersection: \n + 1 - The lines intersect at a point. \n + 0 - The lines are parallel or coincident. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (int) LineLine( const MbLine & line1, + const MbLine & line2, + MbCartPoint & result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точку пересечения двух прямых. + \en Calculate the point of two lines intersection. \~ + \details \ru Найти точку пересечения двух прямых. \n + Прямые могут быть параллельны или совпадать. \n + \en Calculate the point of two lines intersection. \n + The curves can be parallel or coincident. \n \~ + \param[in] line1 - \ru Первая прямая. + \en The first line. \~ + \param[in] line2 - \ru Вторая прямая. + \en The second line. \~ + \param[out] result - \ru Точка пересечения. + \en The intersection point. \~ + \return \ru Возвращает результат пересечения: \n + 1 : прямые пересекаются; \n + 0 : прямые параллельны; \n + 1 : прямые совпадают - касательная точка пересечения. + \en Returns the result of intersection: \n 1 : the curves intersect at a point; \n 0 : the curves are parallel; \n 1 : the curves are coincident - the tangent intersection point. \~\ingroup Point_Modeling +*/ +// --- +MATH_FUNC (int) LineLine( const MbLine & line1, + const MbLine & line2, + MbCrossPoint & result ); + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точку пересечения прямой и отрезка. + \en Calculate the intersection point of a line and a line segment. \~ + \details \ru Найти точку пересечения прямой и отрезка. \n + Отрезок может быть параллелен прямой или лежать на ней. \n + \en Calculate the intersection point of a line and a line segment. \n + The line segment can be parallel to the curve or lie on it. \n \~ + \param[in] line - \ru Прямая. + \en The line. \~ + \param[in] lseg - \ru Отрезок. + \en The segment. \~ + \param[out] result - \ru Точка пересечения. + \en The intersection point. \~ + \return \ru Возвращает результат пересечения: \n + 1 : прямая и отрезок пересекаются; \n + 0 : прямая и отрезок параллельны; \n + 1 : отрезок лежит на прямой - касательная точка пересечения. + \en Returns the result of intersection: \n 1 : the line and the line segment intersect at a point; \n 0 : the line and a line segment are parallel; \n 1 : the segment lies on the curve - a tangent intersection point. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (int) LineLineSeg( const MbLine & line, + const MbLineSegment & lseg, + MbCrossPoint & result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точки пересечения прямой и окружности. + \en Calculate intersection points of a line and a circle. \~ + \details \ru Найти параметры точек пересечения прямой и окружности. \n + \en Calculate the parameters of intersection points of a line and a circle. \n \~ + \param[in] line - \ru Прямая. + \en The line. \~ + \param[in] centre - \ru Центр окружности. + \en The circle center. \~ + \param[in] radius - \ru Радиус окружности. + \en The circle radius. \~ + \param[out] result - \ru Точки пересечения (указатель на массив из двух(!) элементов). + \en The intersection points (a pointer to the array of two (!) elements). \~ + \return \ru Возвращает количество найденных пересечений. + \en Returns the number of intersections. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (int) LineCircle( const MbLine & line, + const MbCartPoint & centre, + double radius, + MbCrossPoint * result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точки пересечения двух кривых. + \en Calculate intersection points of two curves. \~ + \details \ru Найти параметры точек пересечения двух произвольных кривых. \n + Общий метод вызывается, если нет частной функции пересечения. \n + \en Calculate the parameters of intersection points of two arbitrary curves. \n + The general method is used if there is no special function for intersection. \n \~ + \param[in] pCurve1 - \ru Первая кривая. + \en The first curve. \~ + \param[in] pCurve2 - \ru Вторая кривая. + \en The second curve. \~ + \param[out] result - \ru Множество точек пересечения. + \en The array of intersection points. \~ + \param[in] touchInclude - \ru Считать касания пересечениями. + \en Consider tangencies as intersections. \~ + \param[in] epsilon - \ru Точность совпадения точек пересечения кривых. + \en The accuracy of coincidence points of intersection. \~ + \param[in] allowInaccuracy - \ru Разрешить понижать входную точность. + \en Allow lowering input accuracy. \~ + \return \ru Количество найденных пересечений. + \en The number of intersections. \~ + \warning \ru Применяется для двумерных построений, аналог CurveCurveIntersection. + \en Used for two-dimensional constructions, the analogue of CurveCurveIntersection. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (ptrdiff_t) IntersectTwoCurves( const MbCurve & pCurve1, + const MbCurve & pCurve2, + SArray & result, + bool touchInclude, // = true, + double epsilon = Math::LengthEps*c3d::METRIC_DELTA, + bool allowInaccuracy = true ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точки пересечения двух кривых. + \en Calculate intersection points of two curves. \~ + \details \ru Найти параметры точек пересечения двух произвольных кривых. \n + Общий метод вызывается, если нет частной функции пересечения. \n + \en Calculate the parameters of intersection points of two arbitrary curves. \n + The general method is used if there is no special function for intersection. \n \~ + \param[in] curve1 - \ru Первая кривая. + \en The first curve. \~ + \param[in] curve2 - \ru Вторая кривая. + \en The second curve. \~ + \param[out] result1 - \ru Параметры пересечений первой кривой. + \en The parameters of intersections for the first curve. \~ + \param[out] result2 - \ru Параметры пересечений второй кривой. + \en The parameters of intersections for the second curve. \~ + \param[in] xEpsilon - \ru Точность по x. + \en Tolerance in x direction. \~ + \param[in] yEpsilon - \ru Точность по y. + \en Tolerance in y direction. \~ + \param[in] touchInclude - \ru Считать касания пересечениями. + \en Consider tangencies as intersections. \~ + \param[in] allowInaccuracy - \ru Разрешить нахождение решения с меньшей точностью при невозможности удовлетворить указанной. + \en Allow to find a solution with less precision when we can't get a solution with given precision. \~ + \return \ru Количество найденных пересечений. + \en The number of intersections. \~ + \warning \ru Применяется для трехмерных построений, аналог IntersectTwoCurves. + \en Used for three-dimensional constructions, the analogue of IntersectTwoCurves. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (ptrdiff_t) CurveCurveIntersection( const MbCurve & curve1, + const MbCurve & curve2, + SArray & result1, + SArray & result2, + double xEpsilon, + double yEpsilon, + bool touchInclude, + bool allowInaccuracy = true ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точки самопересечения кривой. + \en Calculate the points of curve self-intersection. \~ + \details \ru Найти параметры точек самопересечения кривой с заданной точностью. \n + \en Calculate the self-intersection points parameters with the given tolerance. \n \~ + \param[in] curve - \ru Кривая. + \en The curve. \~ + \param[in] xEpsilon - \ru Точность по x. + \en Tolerance in x direction. \~ + \param[in] yEpsilon - \ru Точность по y. + \en Tolerance in y direction. \~ + \param[out] result1 - \ru Множество параметров самопересечения. + \en The self-intersection parameters array. \~ + \param[out] result2 - \ru Множество параметров самопересечения. + \en The self-intersection parameters array. \~ + \param[in] version - \ru Версия операции. + \en The version of the operation. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (void) CurveSelfIntersect( const MbCurve & curve, + double xEpsilon, + double yEpsilon, + SArray & result1, + SArray & result2, + VERSION version = Math::DefaultMathVersion() ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Удалить точки касания. + \en Remove touch points. \~ + \details \ru Удалить все точки касания кривых вне зависимости от положения параметра на кривой + (внутри области определения или на границах кривой). + \en Remove all curves touch points regardless the position on the curve + (in the domain or on the borders). \~ + \param[in] curve1 - \ru Первая кривая. + \en The first curve. \~ + \param[in] curve2 - \ru Вторая кривая. + \en The second curve. \~ + \param[in, out] result - \ru Множество точек пересечения. + \en The array of intersection points. \~ + \param[in] eps - \ru Погрешность для функции проверки параллельности касательных RoundColinear. + \en Accuracy for the function RoundColinear of testing the parallelism of tangents. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (void) RemoveAllTouchParams( const MbCurve & curve1, + const MbCurve & curve2, + SArray & result, + double eps = PARAM_NEAR ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точки пересечения двух кривых. + \en Calculate intersection points of two curves. \~ + \details \ru Найти параметры точек пересечения двух произвольных кривых. \n + \en Calculate the parameters of intersection points of two arbitrary curves. \n \~ + \param[in] curve1 - \ru Первая кривая. + \en The first curve. \~ + \param[in] curve2 - \ru Вторая кривая. + \en The second curve. \~ + \param[out] result1 - \ru Параметры точек пересечения для первой кривой. + \en The intersection points parameters for the first curve. \~ + \param[out] result2 - \ru Параметры точек пересечения для второй кривой. + \en The intersection points parameters for the second curve. \~ + \param[in] mEps - \ru Возможная максимальная погрешность найденных пересечений. + \en The intersection tolerance. \~ + \return \ru Количество найденных пересечений. + \en The number of intersections. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (ptrdiff_t) CurveCurveIntersection( const MbCurve3D & curve1, + const MbCurve3D & curve2, + SArray & result1, + SArray & result2, + double mEps, // = Math::metricRegion ); + VERSION version ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить кривую на самопересечение. + \en Determine if the curve has self-intersections. \~ + \details \ru Проверить заданную кривую на самопересечение. \n + \en Determine if the given curve has self-intersections. \n \~ + \param[in] curve - \ru Кривая. + \en The curve. \~ + \param[in] mEps - \ru Возможная максимальная погрешность найденных самопересечений. + \en The tolerance of self-intersections. \~ + \return \ru Возвращает true, если кривая самопересекается. + \en Returns true if the curve has self-intersections. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (bool) IsSelfIntersect( const MbCurve3D & curve, double mEps = Math::metricRegion ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Убрать касательные точки пересечения. + \en Remove the tangent intersection points. \~ + \details \ru Убрать параметры касательных точек пересечения внутри областей определения кривых. \n + \en Remove the tangent intersection points parameters inside the domains of curves. \n \~ + \param[in] curve1 - \ru Первая кривая. + \en The first curve. \~ + \param[in] curve2 - \ru Вторая кривая. + \en The second curve. \~ + \param[out] result1 - \ru Параметры точек пересечения для первой кривой. + \en The intersection points parameters for the first curve. \~ + \param[out] result2 - \ru Параметры точек пересечения для второй кривой. + \en The intersection points parameters for the second curve. \~ + \param[in] mEps - \ru Возможная максимальная погрешность найденных пересечений. + \en The intersection tolerance. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (void) FilterTouchParams( const MbCurve3D & curve1, + const MbCurve3D & curve2, + SArray & result1, + SArray & result2, + double mEps = Math::metricRegion ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точки скрещения двух кривых. + \en Calculate the points of two curves crossing. \~ + \details \ru Найти параметры точек скрещения двух кривых. \n + \en Calculate parameters of the points of two curves crossing. \n \~ + \param[in] curve1 - \ru Первая кривая. + \en The first curve. \~ + \param[in] curve2 - \ru Вторая кривая. + \en The second curve. \~ + \param[out] result1 - \ru Параметры точек скрещения для первой кривой. + \en Parameters of the points of crossing for the first curve. \~ + \param[out] result2 - \ru Параметры точек скрещения для второй кривой. + \en Parameters of the points of crossing for the second curve. \~ + \return \ru Количество найденных скрещений. + \en The points of crossing number. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (ptrdiff_t) CurveCurveCrossing( const MbCurve3D & curve1, + const MbCurve3D & curve2, + SArray & result1, + SArray & result2, + double epsilon = Math::metricRegion ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти проекцию точки на поверхность относительно внешнего контура поверхности. + \en Find the projection of a point on a surface relative to the outer contour of the surface. \~ + \details \ru Найти проекцию пространственной точки на поверхность в виде двумерной точки на поверхности + относительно внешнего контура поверхности. \n + \en Calculate the projection of a space point on a surface as a two-dimensional point on the surface. + relative to the outer contour of the surface. \n \~ + \param[in] surface - \ru Поверхность. + \en A surface. \~ + \param[in] pnt - \ru Пространственная точка. + \en A space point. \~ + \param[in] byOuterRectOnly - \ru Классифицировать проекцию только относительно внешнего габаритного прямоугольника. + \en Whether to classify the projection relative to the outer bounding box only. \~ + \param[out] result - \ru Двумерная параметрическая точка на поверхности. + \en A two-dimensional parametric point on the surface. \~ + \return \ru Возвращает true, если найдена нормальная проекция точки на поверхность. + \en Returns true if a normal projection of the point on the surface has been calculated. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (bool) PointProjectionRelativeOuterLoop( const MbSurface & surface, + const MbCartPoint3D & pnt, + bool byOuterRectOnly, + MbCartPoint & result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Является ли проекция точки точно неоднозначной. + \en Determine whether the point projection is multiple-valued. \~ + \details \ru Является ли проекция точки неоднозначной при проецировании + в области определения поверхности. \n + \en Determine whether the point projection is multiple-valued while projecting + inside the surface domain. \n \~ + \param[in] surface - \ru Поверхность. + \en A surface. \~ + \param[in] result - \ru Пространственная точка. + \en A space point. \~ + \return \ru Возвращает true, если проекция точки является неоднозначной. + \en Returns true if the point projection is multiple-valued \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (bool) IsMultipleProjection( const MbSurface & surface, + const MbCartPoint3D & result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти точки касания двух поверхностей. + \en Calculate the touch points of two surfaces. \~ + \details \ru Найти параметры точек касания двух поверхностей. \n + \en Calculate the parameters of touch points of two surfaces. \n \~ + \param[in] surf1 - \ru Первая поверхность. + \en A first surface. \~ + \param[in] ext1 - \ru Искать на продолжении первой поверхности. + \en Use the first surface extension. \~ + \param[in] surf2 - \ru Вторая поверхность. + \en A second surface. \~ + \param[in] ext2 - \ru Искать на продолжении второй поверхности. + \en Use the second surface extension. \~ + \param[in] uv1arr - \ru Параметры точек касания первой поверхности. + \en Parameters of touch points of first surface. \~ + \param[in] uv2arr - \ru Параметры точек касания второй поверхности. + \en Parameters of touch points of second surface. \~ + \return \ru Возвращает true, если найдены точки касания. + \en Returns true if the touch points has be calculate. \~ + \warning \ru В разработке. + \en Under development. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC ( bool) TouchIntersectionPoints( const MbSurface & surf1, + bool ext1, + const MbSurface & surf2, + bool ext2, + std::vector & uv1arr, + std::vector & uv2arr ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Определить положение точек относительно оболочки. + \en Determine the position of points relative to the shell. \~ + \details \ru Определить положение точек относительно оболочки. \n + \en Determine the position of points relative to the shell. \n \~ + \param[in] points - \ru Точки. + \en Points. \~ + \param[in] shell - \ru Оболочка. + \en Shell. \~ + \param[out] pLocs - \ru Положение точек относительно оболочки. + \en The position of points relative to the shell. \~ + \return \ru Возвращает false, если хотя бы одно положение точки не определено, иначе true. + \en Returns false if at least one point position is not defined, otherwise true. \~ + \ingroup Point_Modeling +*/ +// --- +MATH_FUNC (bool) PointsRelativeShell( const std::vector & points, + const MbFaceShell & shell, + std::vector & pLocs ); + + +#endif // __ACTION_POINT_H + diff --git a/C3d/Include/action_sheet.h b/C3d/Include/action_sheet.h index 1c59799..8b5ba24 100644 --- a/C3d/Include/action_sheet.h +++ b/C3d/Include/action_sheet.h @@ -876,7 +876,7 @@ MATH_FUNC (MbResultType) CloseCorner( MbSolid & solid, //------------------------------------------------------------------------------ /** \brief \ru Подрезка массива тела solidArray контурами плоских листовых граней тела sheetSolid. \en Cutting the solidArray with contours of plane sheet faces of sheetSolid. \~ - \details \ru Для подрезания используются только грани, компланарые хотя бы одной ЛСК из массива placements. + \details \ru Для подрезания используются только грани, компланарные хотя бы одной ЛСК из массива placements. Контурные тела граней строятся выдавливанием ограничивающих контуров в обе стороны на величину depth.\n \en For cutting are used only those faces whose placements are complanar to the ones from placements array. The faces contours solids is built by extrusion of bounding contours of the faces in both directions on "depth" value.\n \~ @@ -1002,11 +1002,11 @@ MATH_FUNC (MbResultType) Stamp( MbSolid & solid, //------------------------------------------------------------------------------ /** \brief \ru Штамповка телом-инструментом (пуансоном или матрицей). - \en Stamping by tool solid (punch or die). \~ + \en Stamping with a tool solid (punch or die). \~ \details \ru Штамповка строится на основе произвольного тела-инструмента и заданной плоской листовой грани. - Штамповка подрезается границами листовой грани, которую перескает тело.\n - \en The stamping is created based on a tool body and a flat sheet face. - The stamping is trimmed by the boundary of the sheet face which contains the sketch.\n \~ + Штамповка подрезается границами листовой грани, которую пересекает тело.\n + \en The stamping is created based on a tool body and a flat sheet face. + The stamping is trimmed by the boundary of the sheet face which contains the sketch.\n \~ \param[in] solid - \ru Исходное листовое тело. \en The source sheet solid. \~ \param[in] sameShell - \ru Флаг удаления оболочки исходного тела. @@ -1032,16 +1032,16 @@ MATH_FUNC (MbResultType) Stamp( MbSolid & solid, \ingroup Sheet_Metal_Modeling */ // --- -MATH_FUNC( MbResultType ) StampBySolid( MbSolid & solid, - MbeCopyMode sameShell, - const MbFace & targetFace, - MbSolid & toolSolid, - MbeCopyMode sameShellTool, - bool punch, - const RPArray & openingFaces, - const MbUserStampingValues & params, - const MbSNameMaker & names, - MbSolid *& result ); +MATH_FUNC( MbResultType ) StampWithToolSolid( MbSolid & solid, + MbeCopyMode sameShell, + const MbFace & targetFace, + MbSolid & toolSolid, + MbeCopyMode sameShellTool, + bool punch, + const RPArray & pierceFaces, + const MbToolStampingValues & params, + const MbSNameMaker & nameMaker, + MbSolid *& result ); //------------------------------------------------------------------------------ @@ -1892,16 +1892,24 @@ MATH_FUNC (MbResultType) SplitContourIntoSegments( const MbCurve & curve, \en Split a part of a contours (an arc) into segments. \~ \details \ru Аппроксимировать участки контуров (дуги) ломаной.\n \en Split a part of a contours (an arc) into segments.\n \~ - \param[in] contour1 - \ru Первый контур. - \en First contour.\~ - \param[in] breaks1 - \ru Массив параметров разбиения первого контура. - \en Parameters of a partition of the first contour. \~ - \param[in] contour2 - \ru Второй контур. - \en Second contour.\~ - \param[in] breaks2 - \ru Массив параметров разбиения второго контура. - \en Parameters of a partition of the second contour. \~ - \param[in] segmNumber - \ru Количество сегментов аппроксимации. - \en Number of segments after splitting.\~ + \param[in,out] contour1 - \ru Первый контур. + \en First contour.\~ + \param[in,out] breaks1 - \ru Массив параметров разбиения первого контура. + \en Parameters of a partition of the first contour. \~ + \param[in,out] contour2 - \ru Второй контур. + \en Second contour.\~ + \param[in,out] breaks2 - \ru Массив параметров разбиения второго контура. + \en Parameters of a partition of the second contour. \~ + \param[in,out] names - \ru Именователь. + \en Name maker. + \param[in] segmNumbers1 - \ru Количество сегментов аппроксимации для каждого сегмента обечайки после применения разбиения по breaks1. + \en Number of the linear segments for each segment of the lofted bend after splitting by breaks1. + \param[in] segmNumbers2 - \ru Количество сегментов аппроксимации для каждого сегмента обечайки после применения разбиения по breaks2. + \en Number of the linear segments for each segment of the lofted bend after splitting by breaks2. + \param[in] defSegmNumb - \ru Количество сегментов аппроксимации, если не задано в segmNumbers1 и segmNumbers2. + \en Number of segments after splitting, if not defined in segmNumbers1 и segmNumbers2.\~ + \param[in] gapValue - \ru Ширина зазора. + \en The gap width. \result \ru - Код результата операции. \en - The operation result code. \~ \ingroup Sheet_Metal_Modeling @@ -2347,6 +2355,42 @@ MATH_FUNC (MbResultType) RemoveOperationResult( MbSolid & solid, MbSolid *& result ); + +//------------------------------------------------------------------------------ +/** \brief \ru Преобразовать тело в листовой металл. + \en Construct sheet metal solid based on an arbitary solid. \~ + \details \ru Операция строит листовое тело на базе произвольного тела.\n + \en The operation builds a sheet metal solid based on an arbitrary solid.\n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Флаг удаления оболочки исходного тела. + \en Whether to delete the shell of the source solid. \~ + \param[in] initFace - \ru Исходная грань для построения листового тела. + \en The initial face for sheet metall solid building. \~ + \param[in] sense - \ru Направление придания толщины относительно нормали исходной грани. + \en Direction of sheet metal building relative to initial face normal. \~ + \param[in] parameters - \ru Параметры операции. + \en Operation parameters. \~ + \param[in] nameMaker - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Результирующее тело. + \en The resultant solid. \~ + \result \ru - Код результата операции. + \en - The operation result code. \~ + \warning \ru В разработке. + \en Under development. \~ + \ingroup Sheet_Metal_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ConvertSolidToSheetMetal( MbSolid & solid, + const MbeCopyMode sameShell, + const MbFace & initFace, + bool sence, + const MbSolidToSheetMetalValues & parameters, + MbSNameMaker & nameMaker, + MbSolid *& result ); + + #endif // __ACTION_SHEET_H diff --git a/C3d/Include/action_shell.h b/C3d/Include/action_shell.h index 6633933..9c6487a 100644 --- a/C3d/Include/action_shell.h +++ b/C3d/Include/action_shell.h @@ -1,842 +1,906 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Методы построения незамкнутых тел. - \en Functions for open solids construction. \~ - \details \ru Геометрическое ядро C3D поддерживает поверхностное моделирование. - Результатом поверхностного моделирования являются элементы геометрической модели, - которые будем называть незамкнутыми телами. Незамкнутые тела характерны тем, - что они описывают не всю поверхность моделируемого объекта, а только часть её. - Часто незамкнутое тело состоит из одной грани. В незамкнутом теле всегда присутствуют - краевые рёбра. Незамкнутое тело описывает множество точек, принадлежащих только граням - этого тела, тогда как замкнутое тело описывает множество точек, располагающихся - на поверхности моделируемого объекта и внутри него. - \en The geometric kernel C3D supports the surface modeling. - The result of surface modeling are elements of geometric model - which are called open solids here. Open solids - describe not the whole surface of an object of modeling but only a part of it. - An open solid often consists of one face. An open solid always contains - boundary edges. An open solid describes a point set that belong to faces of the solid only, - whereas a closed solid describes a point set - on the surface of the modeled object and inside it. \~ -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ACTION_SHELL_H -#define __ACTION_SHELL_H - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbCurve; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbSurface; -class MATH_CLASS MbCurveEdge; -class MATH_CLASS MbFace; -class MATH_CLASS MbSolid; -class MATH_CLASS MbSNameMaker; -class MATH_CLASS MbPatchCurve; -class IProgressIndicator; - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить заплатку. - \en Create a patch. \~ - \details \ru Построить заплатку по выбранным ребрам. \n - \en Create a patch from the specified edges. \n \~ - \param[in] initEdges - \ru Набор ребер. - \en A set of edges. \~ - \param[in] p - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] n - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенная заплатка. - \en The required patch. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) PatchShell( const RPArray & initEdges, - const PatchValues & p, - const MbSNameMaker & n, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить заплатку. - \en Create a patch. \~ - \details \ru Построить заплатку по выбранным кривым. \n - \en Create a patch from the specified curves. \n \~ - \param[in] initCurves - \ru Набор кривых. - \en A set of curves. \~ - \param[in] p - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] n - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенная заплатка. - \en The required patch. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) PatchShell( const RPArray & initCurves, - const PatchValues & p, - const MbSNameMaker & n, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить незамкнутое тело по множеству групп точек. - \en Create an open solid given a set of point groups. \~ - \details \ru Построить незамкнутое тело по сечениям, образованным сплайнами, построенными по группе контрольных точек. \n - \en Create an open lofted solid whose profiles are defined by splines created from the specified groups of points. \n \~ - \param[in] points - \ru Набор точек. - \en A point set. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] name - \ru Идентификатор. - \en An identifier. \~ - \param[out] result - \ru Результирующая оболочка. - \en The required shell. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) LoftedShell( const RPArray< SArray > & points, - const MbSNameMaker & names, - SimpleName name, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить незамкнутое тело по множеству кривых. - \en Create an open solid from a set of curves. \~ - \details \ru Построить незамкнутое тело по сечениям, образованным кривыми. \n - \en Create an open lofted solids whose profiles are defined by the curves. \n \~ - \param[in] curves - \ru Набор кривых. - \en A set of curves. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] name - \ru Идентификатор. - \en An identifier. \~ - \param[out] result - \ru Результирующая оболочка. - \en The required shell. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) LoftedShell( const RPArray & curves, - const MbSNameMaker & names, - SimpleName name, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить незамкнутое эквидистантное тело. - \en Create an open offset solid. \~ - \details \ru Построить незамкнутое эквидистантное тело на базе указанных в initFaces граней. \n - \en Create an open offset solid on the basis of the faces 'initFaces'. \n \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] sameShell - \ru Режим копирования тела. - \en Whether to copy the solid. \~ - \param[in] initFaces - \ru Грани исходного тела для построения. - \en Faces of the initial solid for construction. \~ - \param[in] checkFacesConnection - \ru Необходимость проверки связности выбранных граней. - \en Whether to check connectivity of the specified faces. \~ - \param[in] p - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] operNames - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] copyFaceAttrs - \ru Копировать атрибуты из исходных граней в эквидистантные. - \en Copy attributes of initial faces to offset faces. \~ - \param[out] result - \ru Эквидистантная оболочка. - \en The offset shell. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) OffsetShell( MbSolid & solid, - MbeCopyMode sameShell, - RPArray & initFaces, - bool checkFacesConnection, - SweptValues & p, - const MbSNameMaker & operNames, - bool copyFaceAttrs, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить незамкнутое тело по множеству точек. - \en Create an open solid from a point set. \~ - \details \ru Построить незамкнутое тело по множеству точек, заданных в параметрах построения. \n - \en Create an open solid from a point set specified in parameters. \n \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] operNames - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] isPhantom - \ru Режим создания фантома. - \en Create in the phantom mode. \~ - \param[out] result - \ru Результирующая оболочка. - \en The required shell. \~ - \param[in,out] progBar - \ru Индикатор прогресса выполнения операции. - \en A progress indicator of the operation. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) NurbsSurfacesShell( NurbsSurfaceValues & params, - const MbSNameMaker & operNames, - bool isPhantom, - MbSolid *& result, - IProgressIndicator * progBar ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить незамкнутое тело по сети кривых. - \en Create an open solid from a set of curves. \~ - \details \ru Построить незамкнутое тело по сети кривых, заданных в параметрах построения. \n - \en Create an open solid from a set of curves specified in the parameters. \n \~ - \param[in] pars - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] operNames - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] isPhantom - \ru Режим создания фантома. - \en Create in the phantom mode. \~ - \param[out] result - \ru Результирующая оболочка. - \en The required shell. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) MeshShell( MeshSurfaceValues & pars, - const MbSNameMaker & operNames, - bool isPhantom, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Усечь (обрезать) незамкнутое тело. - \en Truncate an open solid. \~ - \details \ru Выполнить построение незамкнутого тела путём усечения исходного тела. \n - \en Create an open solid by truncation the initial solid. \n \~ - \param[in] initSolid - \ru Исходная оболочка. - \en The initial shell. \~ - \param[in] selIndices - \ru Номера выбранных граней (если массив пуст, то вся оболочка). - \en The numbers of selected faces (if the array is empty, the whole shell is selected). \~ - \param[in] initCopyMode - \ru Режим копирования исходных оболочек. - \en Whether to copy the initial shells. \~ - \param[in] operNames - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] truncatingItems - \ru Усекающие объекты. - \en Truncating objects. \~ - \param[in] truncatingOrients - \ru Ориентация усекающих объектов. - \en The truncating objects orientation. \~ - \param[in] truncatingSplitMode - \ru Кривые используются как линии разъема. - \en The curves are used as parting lines. \~ - \param[in] truncatingCopyMode - \ru Режим копирования усекающих оболочек. - \en Whether to copy the truncating shells. \~ - \param[in] mergeFlags - \ru Флаги слияния элементов оболочки. - \en Control flags of shell items merging. \~ - \param[out] result - \ru Усеченная оболочка. - \en The truncated shell. \~ - \param[out] resultPlace - \ru Фантомное направление усечения. - \en A phantom direction of truncation. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) TruncateShell( MbSolid & initSolid, - SArray & selIndices, - MbeCopyMode initCopyMode, - const MbSNameMaker & operNames, - RPArray & truncatingItems, - SArray & truncatingOrients, - bool truncatingSplitMode, - MbeCopyMode truncatingCopyMode, - const MbMergingFlags & mergeFlags, - MbSolid *& result, - MbPlacement3D *& resultPlace ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить линейчатое незамкнутое тело. - \en Create an open ruled solid. \~ - \details \ru Построить линейчатое незамкнутое тело по двум кривым, заданным в параметрах. \n - \en Create an open ruled solid from two curves specified in parameters. \n \~ - \param[in] pars - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] operNames - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] isPhantom - \ru Режим создания фантома. - \en Create in the phantom mode. \~ - \param[out] result - \ru Результирующая оболочка. - \en The required shell. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) RuledShell( RuledSurfaceValues & pars, - const MbSNameMaker & operNames, - bool isPhantom, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить кривую для построения линейчатого тела. - \en Check the curve for a ruled solid creation. \~ - \details \ru Проверить вторую кривую на согласованность с первой кривой для построения - линейчатого незамкнутого тела и выполнить необходимую модификацию второй кривой. \n - \en Check the second curve for consistency with the first curve for creation - of the open ruled solid and make the necessary modification of the second curve. \n \~ - \param[in] curve0 - \ru Первая кривая. - \en The first curve. \~ - \param[in] curve1 - \ru Вторая кривая. - \en The second curve. \~ - \param[out] isInverted1 - \ru Была ли вторая кривая инвертирована. - \en Whether the second curve was inverted. \~ - \param[out] isShifted1 - \ru Было ли смещено начало второй кривой. - \en Whether the beginning of the first curve was shifted. \~ - \param[in] version - \ru Версия операции. - \en The version of the operation. \~ - \warning \ru Вспомогательная функция операции RuledShell. - \en An auxiliary function of operation 'RuledShell'. \~ - \ingroup Shell_Modeling -*/ -//--- -MATH_FUNC (void) CheckRuledCurve( const MbCurve3D & curve0, - const MbCurve3D & curve1, - bool & isInverted1, - bool & isShifted1, - VERSION version ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить параметры кривой для построения линейчатого тела. - \en Check the curve parameters for creation of a ruled solid. \~ - \details \ru Проверить параметры кривой и выполнить нормализацию параметров замкнутой кривой. \n - \en Check the curve parameters and perform the normalization of a closed curve parameters. \n \~ - \param[in] curve - \ru Кривая. - \en The curve. \~ - \param[in,out] params - \ru Множество параметров кривой. - \en An array of the curve parameters. \~ - \param[in] isAscending - \ru Будет ли порядок параметров возрастающим. - \en Whether the parameters are specified in the ascending order. \~ - \return \ru Возвращает true, если удалось нормализовать массив параметров. - \en Returns true if the parameter array has been successfully normalized. \~ - \warning \ru Вспомогательная функция операции RuledShell. - \en An auxiliary function of operation 'RuledShell'. \~ - \ingroup Shell_Modeling -*/ -//--- -MATH_FUNC (bool) CheckRuledParams( const MbCurve3D & curve, - SArray & params, - bool isAscending ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить продолжение незамкнутого тела выдавливанием. - \en Create an extension of an open solid by extrusion. \~ - \details \ru Построить продолжение незамкнутого тела путём выдавливания указанных краевых рёбер заданной грани тела. \n - \en Create an extension of an open solid by extrusion of specified boundary edges of the given face of the solid. \n \~ - \param[in] solid - \ru Исходная оболочка. - \en The initial shell. \~ - \param[in] sameShell - \ru Режим копирования оболочки. - \en Whether to copy the shell. \~ - \param[in] face - \ru Продляемая грань в исходной оболочке. - \en A face of the initial shell to be extended. \~ - \param[in] edges - \ru Множество ребер продляемой грани, через которые выполняется продление. - \en An array of edges through which to extend the face. \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] operNames - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Результирующая оболочка. - \en The required shell. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ExtensionShell( MbSolid & solid, - MbeCopyMode sameShell, - MbFace & face, - const RPArray & edges, - const ExtensionValues & params, - const MbSNameMaker & operNames, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить тело соединения по двум кривым. - \en Create a joint solid from two curves. \~ - \details \ru Построить незамкнутое тело соединения по двум кривым на поверхности. \n - \en Create an open joint solid from two curves on a surface. \n \~ - \param[in] curve1 - \ru Первая поверхностная кривая. - \en The first curve on a surface. \~ - \param[in] curve2 - \ru Вторая поверхностная кривая. - \en The second curve on a surface. \~ - \param[in] parameters - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] operNames - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Результирующая оболочка. - \en The required shell. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -//--- -MATH_FUNC (MbResultType) JoinShell( MbSurfaceCurve & curve1, - MbSurfaceCurve & curve2, - JoinSurfaceValues & parameters, - const MbSNameMaker & operNames, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить тело соединения по двум множествам рёбер. - \en Create a joint solid from two sets of edges. \~ - \details \ru Построить незамкнутое тело соединения по двум множествам ребер. \n - \en Create an open joint solid from two sets of edges. \n \~ - \param[in] edges1 - \ru Первая группа ребер. - \en The first group of edges. \~ - \param[in] orients1 - \ru Ориентации ребер в первой группе. - \en The edges senses in the first group. \~ - \param[in] edges2 - \ru Вторая группа ребер. - \en The second group of edges. \~ - \param[in] orients2 - \ru Ориентация ребер во второй группе. - \en The edges senses in the second group. \~ - \param[in] matr1 - \ru Матрица преобразования первой группы ребер в единую систему координат. - \en The matrix of transformation of the first group of edges to the common coordinate system. \~ - \param[in] matr2 - \ru Матрица преобразования второй группы ребер в единую систему координат. - \en The matrix of transformation of the second group of edges to the common coordinate system. \~ - \param[in] parameters - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] operNames - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Результирующая оболочка. - \en The required shell. \~ - \param[in] isPhantom - \ru Режим фантома операции. - \en The operation phantom mode. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -//--- -MATH_FUNC (MbResultType) JoinShell( const RPArray & edges1, - const SArray & orients1, - const RPArray & edges2, - const SArray & orients2, - const MbMatrix3D & matr1, - const MbMatrix3D & matr2, - JoinSurfaceValues & parameters, - const MbSNameMaker & operNames, - MbSolid *& result, - bool isPhantom = false ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Разделить оболочку на части по заданному набору ребер. - \en Divide a shell into parts using a given set of edges. \~ - \details \ru Разделить оболочку на части по заданному набору ребер. \n - \en Divide shell into parts using a given set of edges. \n \~ - \param[in] solid - \ru Оболочка. - \en A shell. \~ - \param[in] sameShell - \ru Режим копирования оболочки. - \en Whether to copy the shell. \~ - \param[in] edges - \ru Набор ребер. - \en Set of edges. \~ - \param[in] operNames - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Результирующая оболочка. - \en The required shell. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ -\ingroup Shell_Modeling -*/ -//--- -MATH_FUNC (MbResultType) DivideShell( MbSolid & solid, - MbeCopyMode sameShell, - const RPArray & edges, - const MbSNameMaker & operNames, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить кривую для построения тела соединения. - \en Check a curve for creation a joint solid. \~ - \details \ru Проверить вторую кривую на согласованность с первой кривой для построения - незамкнутого тела соединения и выполнить необходимую модификацию второй кривой. \n - \en Check the second curve for consistency with the first curve for creation - of the open joint solid and make the necessary modification of the second curve. \n \~ - \param[in] curve1 - \ru Первая кривая. - \en The first curve. \~ - \param[in] curve2 - \ru Вторая кривая. - \en The second curve. \~ - \param[out] isInverted1 - \ru Была ли вторая кривая инвертирована. - \en Whether the second curve was inverted. \~ - \param[out] isShifted1 - \ru Было ли смещено начало второй кривой. - \en Whether the beginning of the first curve was shifted. \~ - \param[in] version - \ru Версия операции. - \en The version of the operation. \~ - \warning \ru Вспомогательная функция операции JoinShell. - \en An auxiliary function of operation JoinShell. \~ - \ingroup Shell_Modeling -*/ -//--- -MATH_FUNC (void) CheckJoinedCurve( const MbCurve3D & curve1, - const MbCurve3D & curve2, - bool & isInverted1, - bool & isShifted1, - VERSION version ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить параметры кривой для построения тела соединения. - \en Check the curve parameters for creation of a joint solid. \~ - \details \ru Проверить параметры кривой и нормализовать параметры замкнутой кривой. \n - \en Check the curve parameters and normalize a closed curve parameters. \n \~ - \param[in] curve - \ru Кривая. - \en The curve. \~ - \param[in,out] params - \ru Множество параметров кривой. - \en An array of the curve parameters. \~ - \param[in] isAscending - \ru Будет ли порядок параметров возрастающим. - \en Whether the parameters are specified in the ascending order. \~ - \return \ru Возвращает true, если удалось нормализовать массив параметров. - \en Returns true if the parameter array has been successfully normalized. \~ - \warning \ru Вспомогательная функция операции JoinShell. - \en An auxiliary function of operation JoinShell. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (bool) CheckJoinedParams( const MbCurve3D & curve, - SArray & params, - bool isAscending ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить кривую по множеству рёбер. - \en Create a curve from a set of edges. \~ - \details \ru Создать кривую для поверхности соединения по списку ребер. \n - \en Create a curve for a surface of the joint from a list of edges. \n \~ - \param[in] edges - \ru Набор ребер. - \en A set of edges. \~ - \param[in] orients - \ru Ориентации ребер. - \en Edges senses. \~ - \param[in] matr - \ru Матрица преобразования ребер. - \en Edges transformation matrix. \~ - \param[out] res - \ru Результат операции. - \en The operation result. \~ - \return \ru Возвращает указатель на кривую, если ее получилось создать, - иначе возвращает ноль. - \en Returns a pointer to the curve if it has been successfully created, - otherwise it returns null. \~ - \warning \ru Вспомогательная функция операции JoinShell. - \en An auxiliary function of operation JoinShell. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbCurve3D *) CreateJoinedCurve( const RPArray & edges, - const SArray & orients, - const MbMatrix3D & matr, - MbResultType & res ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить тело сопряжения несвязанных граней. - \en Create a solid of two non-connected faces. \~ - \details \ru Построить незамкнутое тело, состоящее из грани скругления между двумя несвязанными гранями. \n - \en Create an open solid that consists of a fillet face between two non-connected faces. \n \~ - \param[in] solid1 - \ru Первое тело. - \en The first solid. \~ - \param[in] face1 - \ru Сопрягаемая грань первого тела. - \en The first solid face to fillet. \~ - \param[in] solid2 - \ru Второе тело. - \en The second solid. \~ - \param[in] face2 - \ru Сопрягаемая грань второго тела. - \en The second solid face to fillet. \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) FacesFillet( const MbSolid & solid1, - const MbFace & face1, - const MbSolid & solid2, - const MbFace & face2, - const SmoothValues & params, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить тело на базе элементарной поверхности. - \en Create a solid given an elementary surface. \~ - \details \ru Построить тело, состоящее из одной грани, на базе исходной элементарной поверхности. \n - \en Create a solid which consists of a face with the specified underlying elementary surface. \n \~ - \param[in] surface - \ru Поверхность. - \en The surface. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенная оболочка. - \en The resultant shell. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ElementaryShell( const MbSurface & surface, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить тело на базе поверхности. - \en Create a solid given a surface. \~ - \details \ru Построить тело, состоящее из одной грани, на базе исходной поверхности. - Поверхность должна быть без самопересечений, с корректной ориентацией - ограничивающих кривых в случае поверхности MbCurveBoundedSurface. \n - \en Create a solid which consists of a face with the specified underlying surface. - The surface should have no self-intersections, - the bounding curves should be correctly oriented in case of surface MbCurveBoundedSurface. \n \~ - \param[in] surface - \ru Поверхность. - \en The surface. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенная оболочка. - \en The resultant shell. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SurfaceShell( const MbSurface & surface, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Разрезать тело силуэтным контуром. - \en Cut a solid by a silhouette contour. \~ - \details \ru Построить оболочки, полученные в результате разрезания тела его силуэтным контуром. \n - \en Create solids as a result of cutting a solids by its silhouette contour.\n\~ - \param[in] shell - \ru Исходное тело. - \en The solid\~ - \param[in] sameShell - \ru Способ передачи данных при копировании оболочек. - \en Methods of transferring data while copying shells \~ - \param[in] eye - \ru Направление взгляда. - \en Eye's direction. \~ - \param[out] outlineCurves - \ru Кривые, входящие в силуэтный контур. - - \en Curves of the silhouette contour. \~ - \param[out] result - \ru Тела, полученные в результате применения операции. - - \en The resultant solids.\~ - \return \ru Возвращает код результата операции.\~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) CutShellSilhouetteContour( MbSolid & solid, - MbeCopyMode sameShell, - const MbVector3D & eye, - const VERSION version, - RPArray & outlineCurves, - RPArray & result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Сшить грани нескольких тел в одно тело. - \en Stitch faces of several solids into single solid. \~ - \details \ru Сшить стыкующиеся друг с другом грани нескольких тел в одно тело. Ориентация граней может быть изменена. \n - \en Stitch faces of several solids with coincident edges into single solid. The faces orientation can be changed. \n \~ - \param[in] initialSolids - \ru Множество тел для сшивки. - \en An array of solids for stitching. \~ - \param[in] operNames - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[in] formSolidBody - \ru Флаг формирования твердого тела из результирующей оболочки. - \en Whether to form a solid solid from the resultant shell. \~ - \param[in] stitchPrecision - \ru Точность сшивки. - \en Stitching accuracy. \~ - \param[out] resultSolid - \ru Результирующая оболочка или тело (в зависимости от флага). - \en The resultant shell or solid (depends on the flag). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbeStitchResType) StitchToOneSheetSolid( const RPArray & initialSolids, - const MbSNameMaker & operNames, - bool formSolidBody, - double stitchPrecision, - MbSolid *& resultSolid ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Определение оси токарного сечения и построение кривых сечения для тела. - \en Search for lathe axis and construction of lathe elements for the solid. \~ - \details \ru Функция выполняет поиск токарной оси граней вращения и строит токарное сечение в некоторой плоскости. \n - \en The function searches for lathe axis of rotation faces and builds the curves of lathe-section in a plane. \n \~ - \param[in] solid - \ru Тело. \en Solid. \~ - \param[in] axis - \ru Ось токарного сечения может быть нуль). \en Lathe axis, may be null. \~ - \param[in] angle - \ru Угол, управляющий построением перпендикулярных оси сечения отрезками, рекомендуется M_PI_4-M_PI. \en The angle, managing the construction of segments which perpendicular to the axis, recomended M_PI_4-M_PI. \~ - \param[out] position - \ru Плоскость, в плоскости XY которой лежат кривые сечения, а ось X является осью токарного сечения. \en Plane position of section, axis X is a axis of section. \~ - \param[out] curves - \ru Кривые токарного сечения располагаются в плоскости XY position. \en The curves of section located on plane XY of position. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) LatheCurves( const MbSolid & solid, - const MbAxis3D * axis, - double angle, - MbPlacement3D & position, - RPArray & curves ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построение следа кривой при её вращении вокруг оси токарного сечения. - \en Building of curves for lathe section for given curve. \~ - \details \ru Функция выполняет построение следа ребра в плоскости XY локальной системы координат при его вращении вокруг оси X. \n - \en The function builds the generatrix track in the XY plane of the local coordinate system as it rotates around the axis X. \n \~ - \param[in] generatrix - \ru Кривая. \en Curve \~ - \param[in] position - \ru Плоскость, ось X которой является осью токарного сечения. \en Plane position of section, axis X is a axis of section. \~ - \param[out] curves - \ru Контейр кривых, в который будет добавлен след в плоскости XY position от вращения кривой generatrix вокруг оси X. \en The curve on plane XY of position will be added to contaner curves. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) LatheCurve( const MbCurve3D & generatrix, - const MbPlacement3D & position, - RPArray & curves ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить срединную оболочку по граням тела, основанным на - эквидистантных поверхностях. - \en Create a median shell by solid faces, based on equidistant - surfaces. \~ - \details \ru Построить срединную оболочку по парам граней тела, основанным на - эквидистантных поверхностях. Пары граней либо выбираются пользователем, - либо находятся автоматически по заданному расстоянию между гранями. - Грани должны принадлежать одному и тому же телу.\n - \en Construct a median shell between pair of faces, based on equidistant - surfaces. Pair of faces are selected by user or are found by given distance - between faces. The faces must belong to the same body. \n \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] sameShell - \ru Режим копирования тела. - \en Whether to copy the solid. \~ - \param[in] faceIndexes - \ru Выбранные пары граней. - \en Selected face pairs. \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] operNames - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Результирующая оболочка. - \en The required shell. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC( MbResultType ) MedianShell( MbSolid & solid, - MbeCopyMode sameShell, - const c3d::IndicesPairsVector & faceIndexes, - const MedianShellValues & params, - const MbSNameMaker & operNames, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построение развёртки грани на плоскость. - \en Construction of a face sweep on a plane. \~ - \details \ru Построение развёртки грани на плоскость.\n - \en Construction of a face sweep on a plane.\n \~ - \param[in] face - \ru Исходная грань. - \en The initial face. \~ - \param[in] values - \ru Параметры построения: локальная система координат развернутой поверхности грани, данные для вычисления шага при триангуляции, коэффициент Пуассона материала грани. - \en The parameters: Local coordinate system for result surface, Data for step calculation during triangulation, the Poisson's ratio of face material. \~ - \param[out] result - \ru Тело - плоская развертка исходной грани. - \en The built solid unbend face on plane. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \warning \ru В разработке. - \en Under development. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) RectifyFace( const MbFace & face, - const RectifyValues values, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать решетчатую оболочку. - \en Create a lattice shell. \~ - \details \ru Создать решетчатую оболочку по трем управляющим точкам, параметрам решетки и количеству элементов. \n - \en Create a lattice shell on the three control points of the lattice parameters and the number of elements. \~ - \param[in] point0 - \ru Точка, определяющая начало локальной системы координат поверхности. - \en The origin of the surface local coordinate system. \~ - \param[in] point1 - \ru Точка, определяющая направление оси X локальной системы и размер элемента. - \en A point specifying the direction of X-axis of the local system and the size of element. \~ - \param[in] point2 - \ru Точка, определяющая направление оси Y локальной системы. - \en A point specifying the direction of Y-axis of the local system. \~ - \param[in] xRadius - \ru Шаг вдоль первой оси локальной системы координат. - \en The step along the first axis of the local coordinate system. \~ - \param[in] yRadius - \ru Шаг вдоль второй оси локальной системы координат. - \en The step along the second axis of the local coordinate system. \~ - \param[in] zRadius - \ru Шаг вдоль третьей оси локальной системы координат. - \en The step along the third axis of the local coordinate system. \~ - \param[in] xCount - \ru Количество ячеек вдоль первой оси локальной системы координат. - \en The number of cells along a first axis of the local coordinate system. \~ - \param[in] yCount - \ru Количество ячеек вдоль второй оси локальной системы координат. - \en The number of cells along a second axis of the local coordinate system. \~ - \param[in] zCount - \ru Количество ячеек вдоль третьей оси локальной системы координат. - \en The number of cells along a third axis of the local coordinate system. \~ - \param[out] result - \ru Построенная тело. - \en The constructed solid. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) OctaLattice( const MbCartPoint3D & point_0, - const MbCartPoint3D & point_1, - const MbCartPoint3D & point_2, - double xRadius, - double yRadius, - double zRadius, - size_t xCount, - size_t yCount, - size_t zCount, - const MbSNameMaker & names, - MbSolid *& result ); - - -#endif // __ACTION_SHELL_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Методы построения незамкнутых тел. + \en Functions for open solids construction. \~ + \details \ru Геометрическое ядро C3D поддерживает поверхностное моделирование. + Результатом поверхностного моделирования являются элементы геометрической модели, + которые будем называть незамкнутыми телами. Незамкнутые тела характерны тем, + что они описывают не всю поверхность моделируемого объекта, а только часть её. + Часто незамкнутое тело состоит из одной грани. В незамкнутом теле всегда присутствуют + краевые рёбра. Незамкнутое тело описывает множество точек, принадлежащих только граням + этого тела, тогда как замкнутое тело описывает множество точек, располагающихся + на поверхности моделируемого объекта и внутри него. + \en The geometric kernel C3D supports the surface modeling. + The result of surface modeling are elements of geometric model + which are called open solids here. Open solids + describe not the whole surface of an object of modeling but only a part of it. + An open solid often consists of one face. An open solid always contains + boundary edges. An open solid describes a point set that belong to faces of the solid only, + whereas a closed solid describes a point set + on the surface of the modeled object and inside it. \~ +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ACTION_SHELL_H +#define __ACTION_SHELL_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class MATH_CLASS MbCurve; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbSurface; +class MATH_CLASS MbCurveEdge; +class MATH_CLASS MbFace; +class MATH_CLASS MbSolid; +class MATH_CLASS MbSNameMaker; +class MATH_CLASS MbPatchCurve; +class IProgressIndicator; + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить заплатку. + \en Create a patch. \~ + \details \ru Построить заплатку по выбранным ребрам. \n + \en Create a patch from the specified edges. \n \~ + \param[in] initEdges - \ru Набор ребер. + \en A set of edges. \~ + \param[in] p - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] n - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенная заплатка. + \en The required patch. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) PatchShell( const RPArray & initEdges, + const PatchValues & p, + const MbSNameMaker & n, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить заплатку. + \en Create a patch. \~ + \details \ru Построить заплатку по выбранным кривым. \n + \en Create a patch from the specified curves. \n \~ + \param[in] initCurves - \ru Набор кривых. + \en A set of curves. \~ + \param[in] p - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] n - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенная заплатка. + \en The required patch. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) PatchShell( const RPArray & initCurves, + const PatchValues & p, + const MbSNameMaker & n, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить незамкнутое тело по множеству групп точек. + \en Create an open solid given a set of point groups. \~ + \details \ru Построить незамкнутое тело по сечениям, образованным сплайнами, построенными по группе контрольных точек. \n + \en Create an open lofted solid whose profiles are defined by splines created from the specified groups of points. \n \~ + \param[in] points - \ru Набор точек. + \en A point set. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] name - \ru Идентификатор. + \en An identifier. \~ + \param[out] result - \ru Результирующая оболочка. + \en The required shell. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) LoftedShell( const RPArray< SArray > & points, + const MbSNameMaker & names, + SimpleName name, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить незамкнутое тело по множеству кривых. + \en Create an open solid from a set of curves. \~ + \details \ru Построить незамкнутое тело по сечениям, образованным кривыми. \n + \en Create an open lofted solids whose profiles are defined by the curves. \n \~ + \param[in] curves - \ru Набор кривых. + \en A set of curves. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] name - \ru Идентификатор. + \en An identifier. \~ + \param[out] result - \ru Результирующая оболочка. + \en The required shell. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) LoftedShell( const RPArray & curves, + const MbSNameMaker & names, + SimpleName name, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить незамкнутое эквидистантное тело. + \en Create an open offset solid. \~ + \details \ru Построить незамкнутое эквидистантное тело на базе указанных в initFaces граней. \n + \en Create an open offset solid on the basis of the faces 'initFaces'. \n \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] sameShell - \ru Режим копирования тела. + \en Whether to copy the solid. \~ + \param[in] initFaces - \ru Грани исходного тела для построения. + \en Faces of the initial solid for construction. \~ + \param[in] checkFacesConnection - \ru Необходимость проверки связности выбранных граней. + \en Whether to check connectivity of the specified faces. \~ + \param[in] p - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] operNames - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] copyFaceAttrs - \ru Копировать атрибуты из исходных граней в эквидистантные. + \en Copy attributes of initial faces to offset faces. \~ + \param[out] result - \ru Эквидистантная оболочка. + \en The offset shell. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) OffsetShell( MbSolid & solid, + MbeCopyMode sameShell, + RPArray & initFaces, + bool checkFacesConnection, + SweptValues & p, + const MbSNameMaker & operNames, + bool copyFaceAttrs, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить незамкнутое тело по множеству точек. + \en Create an open solid from a point set. \~ + \details \ru Построить незамкнутое тело по множеству точек, заданных в параметрах построения. \n + \en Create an open solid from a point set specified in parameters. \n \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] operNames - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] isPhantom - \ru Режим создания фантома. + \en Create in the phantom mode. \~ + \param[out] result - \ru Результирующая оболочка. + \en The required shell. \~ + \param[in,out] progBar - \ru Индикатор прогресса выполнения операции. + \en A progress indicator of the operation. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) NurbsSurfacesShell( NurbsSurfaceValues & params, + const MbSNameMaker & operNames, + bool isPhantom, + MbSolid *& result, + IProgressIndicator * progBar ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить незамкнутое тело по сети кривых. + \en Create an open solid from a set of curves. \~ + \details \ru Построить незамкнутое тело по сети кривых, заданных в параметрах построения. \n + \en Create an open solid from a set of curves specified in the parameters. \n \~ + \param[in] pars - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] operNames - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] isPhantom - \ru Режим создания фантома. + \en Create in the phantom mode. \~ + \param[out] result - \ru Результирующая оболочка. + \en The required shell. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) MeshShell( MeshSurfaceValues & pars, + const MbSNameMaker & operNames, + bool isPhantom, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Усечь (обрезать) незамкнутое тело. + \en Truncate an open solid. \~ + \details \ru Выполнить построение незамкнутого тела путём усечения исходного тела. \n + \en Create an open solid by truncation the initial solid. \n \~ + \param[in] initSolid - \ru Исходная оболочка. + \en The initial shell. \~ + \param[in] selIndices - \ru Номера выбранных граней (если массив пуст, то вся оболочка). + \en The numbers of selected faces (if the array is empty, the whole shell is selected). \~ + \param[in] initCopyMode - \ru Режим копирования исходных оболочек. + \en Whether to copy the initial shells. \~ + \param[in] operNames - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] truncatingItems - \ru Усекающие объекты. + \en Truncating objects. \~ + \param[in] truncatingOrients - \ru Ориентация усекающих объектов. + \en The truncating objects orientation. \~ + \param[in] truncatingSplitMode - \ru Кривые используются как линии разъема. + \en The curves are used as parting lines. \~ + \param[in] truncatingCopyMode - \ru Режим копирования усекающих оболочек. + \en Whether to copy the truncating shells. \~ + \param[in] mergeFlags - \ru Флаги слияния элементов оболочки. + \en Control flags of shell items merging. \~ + \param[out] result - \ru Усеченная оболочка. + \en The truncated shell. \~ + \param[out] resultPlace - \ru Фантомное направление усечения. + \en A phantom direction of truncation. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) TruncateShell( MbSolid & initSolid, + SArray & selIndices, + MbeCopyMode initCopyMode, + const MbSNameMaker & operNames, + RPArray & truncatingItems, + SArray & truncatingOrients, + bool truncatingSplitMode, + MbeCopyMode truncatingCopyMode, + const MbMergingFlags & mergeFlags, + MbSolid *& result, + MbPlacement3D *& resultPlace ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить линейчатое незамкнутое тело. + \en Create an open ruled solid. \~ + \details \ru Построить линейчатое незамкнутое тело по двум кривым, заданным в параметрах. \n + \en Create an open ruled solid from two curves specified in parameters. \n \~ + \param[in] pars - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] operNames - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] isPhantom - \ru Режим создания фантома. + \en Create in the phantom mode. \~ + \param[out] result - \ru Результирующая оболочка. + \en The required shell. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) RuledShell( RuledSurfaceValues & pars, + const MbSNameMaker & operNames, + bool isPhantom, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить кривую для построения линейчатого тела. + \en Check the curve for a ruled solid creation. \~ + \details \ru Проверить вторую кривую на согласованность с первой кривой для построения + линейчатого незамкнутого тела и выполнить необходимую модификацию второй кривой. \n + \en Check the second curve for consistency with the first curve for creation + of the open ruled solid and make the necessary modification of the second curve. \n \~ + \param[in] curve0 - \ru Первая кривая. + \en The first curve. \~ + \param[in] curve1 - \ru Вторая кривая. + \en The second curve. \~ + \param[out] isInverted1 - \ru Была ли вторая кривая инвертирована. + \en Whether the second curve was inverted. \~ + \param[out] isShifted1 - \ru Было ли смещено начало второй кривой. + \en Whether the beginning of the first curve was shifted. \~ + \param[in] version - \ru Версия операции. + \en The version of the operation. \~ + \warning \ru Вспомогательная функция операции RuledShell. + \en An auxiliary function of operation 'RuledShell'. \~ + \ingroup Shell_Modeling +*/ +//--- +MATH_FUNC (void) CheckRuledCurve( const MbCurve3D & curve0, + const MbCurve3D & curve1, + bool & isInverted1, + bool & isShifted1, + VERSION version ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить параметры кривой для построения линейчатого тела. + \en Check the curve parameters for creation of a ruled solid. \~ + \details \ru Проверить параметры кривой и выполнить нормализацию параметров замкнутой кривой. \n + \en Check the curve parameters and perform the normalization of a closed curve parameters. \n \~ + \param[in] curve - \ru Кривая. + \en The curve. \~ + \param[in,out] params - \ru Множество параметров кривой. + \en An array of the curve parameters. \~ + \param[in] isAscending - \ru Будет ли порядок параметров возрастающим. + \en Whether the parameters are specified in the ascending order. \~ + \return \ru Возвращает true, если удалось нормализовать массив параметров. + \en Returns true if the parameter array has been successfully normalized. \~ + \warning \ru Вспомогательная функция операции RuledShell. + \en An auxiliary function of operation 'RuledShell'. \~ + \ingroup Shell_Modeling +*/ +//--- +MATH_FUNC (bool) CheckRuledParams( const MbCurve3D & curve, + SArray & params, + bool isAscending ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить продолжение незамкнутого тела выдавливанием. + \en Create an extension of an open solid by extrusion. \~ + \details \ru Построить продолжение незамкнутого тела путём продления указанных краевых рёбер тела. \n + Продление может быть выполнено следующими способами. + Может быть удлинена на заданное расстояние указанная грань. + К указанной грани может быть добавлена гладко стыкующаяся с ней грань. + К указанной грани может быть добавлена грань, полученная выдавливанием крайнего ребра в заданном направлении. + \en Create an extension of an open solid by extension of specified boundary edges of the solid. \n \~ + An extension can be performed in the following ways: + The specified faces can be extended on the given distance. + A smoothly connected face can be added to the given face. + A face obtained by extrusion of a boundary edge in the given direction can be added to the specified face. + \param[in] solid - \ru Исходная оболочка. + \en The initial shell. \~ + \param[in] sameShell - \ru Режим копирования оболочки. + \en Whether to copy the shell. \~ + \param[in] face - \ru Одна из продляемых граней в исходной оболочке. + \en One of the face of the initial shell to be extended. \~ + \param[in] edges - \ru Множество краевых ребер, через которые выполняется продление. + \en An array of boundary edges through which to extend the face. \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] operNames - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Результирующая оболочка. + \en The required shell. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ExtensionShell( MbSolid & solid, + MbeCopyMode sameShell, + MbFace & face, + const RPArray & edges, + const ExtensionValues & params, + const MbSNameMaker & operNames, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить тело соединения по двум кривым. + \en Create a joint solid from two curves. \~ + \details \ru Построить незамкнутое тело соединения по двум кривым на поверхности. \n + \en Create an open joint solid from two curves on a surface. \n \~ + \param[in] curve1 - \ru Первая поверхностная кривая. + \en The first curve on a surface. \~ + \param[in] curve2 - \ru Вторая поверхностная кривая. + \en The second curve on a surface. \~ + \param[in] parameters - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] operNames - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Результирующая оболочка. + \en The required shell. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +//--- +MATH_FUNC (MbResultType) JoinShell( MbSurfaceCurve & curve1, + MbSurfaceCurve & curve2, + JoinSurfaceValues & parameters, + const MbSNameMaker & operNames, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить тело соединения по двум множествам рёбер. + \en Create a joint solid from two sets of edges. \~ + \details \ru Построить незамкнутое тело соединения по двум множествам ребер. \n + \en Create an open joint solid from two sets of edges. \n \~ + \param[in] edges1 - \ru Первая группа ребер. + \en The first group of edges. \~ + \param[in] orients1 - \ru Ориентации ребер в первой группе. + \en The edges senses in the first group. \~ + \param[in] edges2 - \ru Вторая группа ребер. + \en The second group of edges. \~ + \param[in] orients2 - \ru Ориентация ребер во второй группе. + \en The edges senses in the second group. \~ + \param[in] matr1 - \ru Матрица преобразования первой группы ребер в единую систему координат. + \en The matrix of transformation of the first group of edges to the common coordinate system. \~ + \param[in] matr2 - \ru Матрица преобразования второй группы ребер в единую систему координат. + \en The matrix of transformation of the second group of edges to the common coordinate system. \~ + \param[in] parameters - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] operNames - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Результирующая оболочка. + \en The required shell. \~ + \param[in] isPhantom - \ru Режим фантома операции. + \en The operation phantom mode. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +//--- +MATH_FUNC (MbResultType) JoinShell( const RPArray & edges1, + const SArray & orients1, + const RPArray & edges2, + const SArray & orients2, + const MbMatrix3D & matr1, + const MbMatrix3D & matr2, + JoinSurfaceValues & parameters, + const MbSNameMaker & operNames, + MbSolid *& result, + bool isPhantom = false ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Разделить оболочку на части по заданному набору ребер. + \en Divide a shell into parts using a given set of edges. \~ + \details \ru Разделить оболочку на части по заданному набору ребер. \n + \en Divide shell into parts using a given set of edges. \n \~ + \param[in] solid - \ru Оболочка. + \en A shell. \~ + \param[in] sameShell - \ru Режим копирования оболочки. + \en Whether to copy the shell. \~ + \param[in] edges - \ru Набор ребер. + \en Set of edges. \~ + \param[in] operNames - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Результирующая оболочка. + \en The required shell. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ +\ingroup Shell_Modeling +*/ +//--- +MATH_FUNC (MbResultType) DivideShell( MbSolid & solid, + MbeCopyMode sameShell, + const RPArray & edges, + const MbSNameMaker & operNames, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить кривую для построения тела соединения. + \en Check a curve for creation a joint solid. \~ + \details \ru Проверить вторую кривую на согласованность с первой кривой для построения + незамкнутого тела соединения и выполнить необходимую модификацию второй кривой. \n + \en Check the second curve for consistency with the first curve for creation + of the open joint solid and make the necessary modification of the second curve. \n \~ + \param[in] curve1 - \ru Первая кривая. + \en The first curve. \~ + \param[in] curve2 - \ru Вторая кривая. + \en The second curve. \~ + \param[out] isInverted1 - \ru Была ли вторая кривая инвертирована. + \en Whether the second curve was inverted. \~ + \param[out] isShifted1 - \ru Было ли смещено начало второй кривой. + \en Whether the beginning of the first curve was shifted. \~ + \param[in] version - \ru Версия операции. + \en The version of the operation. \~ + \warning \ru Вспомогательная функция операции JoinShell. + \en An auxiliary function of operation JoinShell. \~ + \ingroup Shell_Modeling +*/ +//--- +MATH_FUNC (void) CheckJoinedCurve( const MbCurve3D & curve1, + const MbCurve3D & curve2, + bool & isInverted1, + bool & isShifted1, + VERSION version ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить параметры кривой для построения тела соединения. + \en Check the curve parameters for creation of a joint solid. \~ + \details \ru Проверить параметры кривой и нормализовать параметры замкнутой кривой. \n + \en Check the curve parameters and normalize a closed curve parameters. \n \~ + \param[in] curve - \ru Кривая. + \en The curve. \~ + \param[in,out] params - \ru Множество параметров кривой. + \en An array of the curve parameters. \~ + \param[in] isAscending - \ru Будет ли порядок параметров возрастающим. + \en Whether the parameters are specified in the ascending order. \~ + \return \ru Возвращает true, если удалось нормализовать массив параметров. + \en Returns true if the parameter array has been successfully normalized. \~ + \warning \ru Вспомогательная функция операции JoinShell. + \en An auxiliary function of operation JoinShell. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (bool) CheckJoinedParams( const MbCurve3D & curve, + SArray & params, + bool isAscending ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить кривую по множеству рёбер. + \en Create a curve from a set of edges. \~ + \details \ru Создать кривую для поверхности соединения по списку ребер. \n + \en Create a curve for a surface of the joint from a list of edges. \n \~ + \param[in] edges - \ru Набор ребер. + \en A set of edges. \~ + \param[in] orients - \ru Ориентации ребер. + \en Edges senses. \~ + \param[in] matr - \ru Матрица преобразования ребер. + \en Edges transformation matrix. \~ + \param[out] res - \ru Результат операции. + \en The operation result. \~ + \return \ru Возвращает указатель на кривую, если ее получилось создать, + иначе возвращает ноль. + \en Returns a pointer to the curve if it has been successfully created, + otherwise it returns null. \~ + \warning \ru Вспомогательная функция операции JoinShell. + \en An auxiliary function of operation JoinShell. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbCurve3D *) CreateJoinedCurve( const RPArray & edges, + const SArray & orients, + const MbMatrix3D & matr, + MbResultType & res ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить тело сопряжения несвязанных граней. + \en Create a solid of two non-connected faces. \~ + \details \ru Построить незамкнутое тело, состоящее из грани скругления между двумя несвязанными гранями. \n + \en Create an open solid that consists of a fillet face between two non-connected faces. \n \~ + \param[in] solid1 - \ru Первое тело. + \en The first solid. \~ + \param[in] face1 - \ru Сопрягаемая грань первого тела. + \en The first solid face to fillet. \~ + \param[in] solid2 - \ru Второе тело. + \en The second solid. \~ + \param[in] face2 - \ru Сопрягаемая грань второго тела. + \en The second solid face to fillet. \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) FacesFillet( const MbSolid & solid1, + const MbFace & face1, + const MbSolid & solid2, + const MbFace & face2, + const SmoothValues & params, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить тело на базе элементарной поверхности. + \en Create a solid given an elementary surface. \~ + \details \ru Построить тело, состоящее из одной грани, на базе исходной элементарной поверхности. \n + \en Create a solid which consists of a face with the specified underlying elementary surface. \n \~ + \param[in] surface - \ru Поверхность. + \en The surface. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенная оболочка. + \en The resultant shell. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ElementaryShell( const MbSurface & surface, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить тело на базе поверхности. + \en Create a solid given a surface. \~ + \details \ru Построить тело, состоящее из одной грани, на базе исходной поверхности. + Поверхность должна быть без самопересечений, с корректной ориентацией + ограничивающих кривых в случае поверхности MbCurveBoundedSurface. \n + \en Create a solid which consists of a face with the specified underlying surface. + The surface should have no self-intersections, + the bounding curves should be correctly oriented in case of surface MbCurveBoundedSurface. \n \~ + \param[in] surface - \ru Поверхность. + \en The surface. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенная оболочка. + \en The resultant shell. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SurfaceShell( const MbSurface & surface, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Разрезать тело силуэтным контуром. + \en Cut a solid by a silhouette contour. \~ + \details \ru Построить оболочки, полученные в результате разрезания тела его силуэтным контуром. \n + \en Create solids as a result of cutting a solids by its silhouette contour.\n\~ + \param[in] shell - \ru Исходное тело. + \en The solid\~ + \param[in] sameShell - \ru Способ передачи данных при копировании оболочек. + \en Methods of transferring data while copying shells \~ + \param[in] eye - \ru Направление взгляда. + \en Eye's direction. \~ + \param[in] operNames - \ru Именователь с версией. + \en An object defining the names with the version. \~ + \param[out] outlineCurves - \ru Кривые, входящие в силуэтный контур. + - \en Curves of the silhouette contour. \~ + \param[out] cutSolids - \ru Тела, полученные в результате применения операции. + - \en The resultant solids.\~ + \return \ru Возвращает код результата операции.\~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) CutShellSilhouetteContour( MbSolid & solid, + MbeCopyMode sameShell, + const MbVector3D & eye, + const MbSNameMaker & operNames, + c3d::SpaceCurvesSPtrVector & outlineCurves, + RPArray & cutSolids ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Сшить грани нескольких тел в одно тело. + \en Stitch faces of several solids into single solid. \~ + \details \ru Сшить стыкующиеся друг с другом грани нескольких тел в одно тело. Ориентация граней может быть изменена. \n + \en Stitch faces of several solids with coincident edges into single solid. The faces orientation can be changed. \n \~ + \param[in] initialSolids - \ru Множество тел для сшивки. + \en An array of solids for stitching. \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] formSolidBody - \ru Флаг формирования твердого тела из результирующей оболочки. + \en Whether to form a solid solid from the resultant shell. \~ + \param[in] stitchPrecision - \ru Точность сшивки. + \en Stitching accuracy. \~ + \param[out] resultSolid - \ru Результирующая оболочка или тело (в зависимости от флага). + \en The resultant shell or solid (depends on the flag). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling + \warning \ru Операция устарела! + \en The operation is deprecated! \~ +*/ +// --- +DEPRECATE_DECLARE +MATH_FUNC (MbeStitchResType) StitchToOneSheetSolid( const RPArray & initialSolids, + const MbSNameMaker & operNames, + bool formSolidBody, + double stitchPrecision, + MbSolid *& resultSolid ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Сшить грани нескольких тел в одно тело. + \en Stitch faces of several solids into single solid. \~ + \details \ru Сшить стыкующиеся друг с другом грани нескольких тел в одно тело. Ориентация граней может быть изменена. \n + \en Stitch faces of several solids with coincident edges into single solid. The faces orientation can be changed. \n \~ + \param[in] initialSolids - \ru Множество тел для сшивки. + \en An array of solids for stitching. \~ + \param[in] stitchParams - \ru Параметры сшивки оболочек. + \en Shells stitch parameters. \~ + \param[out] resultSolid - \ru Результирующая оболочка или тело (в зависимости от флага). + \en The resultant shell or solid (depends on the flag). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbeStitchResType) StitchShells( const c3d::SolidsSPtrVector & initialSolids, + const MbShellStitchParams & stitchParams, + MbSolid *& resultSolid ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Определение оси токарного сечения и построение кривых сечения для тела. + \en Search for lathe axis and construction of lathe elements for the solid. \~ + \details \ru Функция выполняет поиск токарной оси граней вращения и строит токарное сечение в некоторой плоскости. \n + \en The function searches for lathe axis of rotation faces and builds the curves of lathe-section in a plane. \n \~ + \param[in] solid - \ru Тело. \en Solid. \~ + \param[in] axis - \ru Ось токарного сечения может быть нуль). \en Lathe axis, may be null. \~ + \param[in] angle - \ru Угол, управляющий построением перпендикулярных оси сечения отрезками, рекомендуется M_PI_4-M_PI. \en The angle, managing the construction of segments which perpendicular to the axis, recomended M_PI_4-M_PI. \~ + \param[out] position - \ru Плоскость, в плоскости XY которой лежат кривые сечения, а ось X является осью токарного сечения. \en Plane position of section, axis X is a axis of section. \~ + \param[out] curves - \ru Кривые токарного сечения располагаются в плоскости XY position. \en The curves of section located on plane XY of position. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) LatheCurves( const MbSolid & solid, + const MbAxis3D * axis, + double angle, + MbPlacement3D & position, + RPArray & curves ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построение следа кривой при её вращении вокруг оси токарного сечения. + \en Building of curves for lathe section for given curve. \~ + \details \ru Функция выполняет построение следа ребра в плоскости XY локальной системы координат при его вращении вокруг оси X. \n + \en The function builds the generatrix track in the XY plane of the local coordinate system as it rotates around the axis X. \n \~ + \param[in] generatrix - \ru Кривая. \en Curve \~ + \param[in] position - \ru Плоскость, ось X которой является осью токарного сечения. \en Plane position of section, axis X is a axis of section. \~ + \param[out] curves - \ru Контейр кривых, в который будет добавлен след в плоскости XY position от вращения кривой generatrix вокруг оси X. \en The curve on plane XY of position will be added to contaner curves. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) LatheCurve( const MbCurve3D & generatrix, + const MbPlacement3D & position, + RPArray & curves ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить срединную оболочку по граням тела, основанным на эквидистантных поверхностях. + \en Create a median shell by solid faces, based on equidistant surfaces. \~ + \details \ru Построить срединную оболочку по парам граней тела, основанным на + эквидистантных поверхностях. Пары граней либо выбираются пользователем, + либо находятся автоматически по заданному расстоянию между гранями. + Грани должны принадлежать одному и тому же телу.\n + \en Construct a median shell between pair of faces, based on equidistant + surfaces. Pair of faces are selected by user or are found by given distance + between faces. The faces must belong to the same body. \n \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] sameShell - \ru Режим копирования тела. + \en Whether to copy the solid. \~ + \param[in] faceIndexes - \ru Выбранные пары граней. + \en Selected face pairs. \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] operNames - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] medianFaces - \ru Множество граней для создания срединной оболочки. + \en Set of faces for build a median shell. \~ + \param[out] result - \ru Результирующая оболочка. + \en The required shell. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC( MbResultType ) MedianShell( MbSolid & solid, + MbeCopyMode sameShell, + const c3d::IndicesPairsVector & faceIndexes, + const MedianShellValues & params, + const MbSNameMaker & operNames, + MedianShellFaces & medianFaces, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построение развёртки грани на плоскость. + \en Construction of a face sweep on a plane. \~ + \details \ru Построение развёртки грани на плоскость.\n + \en Construction of a face sweep on a plane.\n \~ + \param[in] face - \ru Исходная грань. + \en The initial face. \~ + \param[in] values - \ru Параметры построения: локальная система координат развернутой поверхности грани, данные для вычисления шага при триангуляции, коэффициент Пуассона материала грани. + \en The parameters: Local coordinate system for result surface, Data for step calculation during triangulation, the Poisson's ratio of face material. \~ + \param[out] result - \ru Тело - плоская развертка исходной грани. + \en The built solid unbend face on plane. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \warning \ru В разработке. + \en Under development. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) RectifyFace( const MbFace & face, + const RectifyValues values, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать решетчатую оболочку. + \en Create a lattice shell. \~ + \details \ru Создать решетчатую оболочку по трем управляющим точкам, параметрам решетки и количеству элементов. \n + \en Create a lattice shell on the three control points of the lattice parameters and the number of elements. \~ + \param[in] point0 - \ru Точка, определяющая начало локальной системы координат поверхности. + \en The origin of the surface local coordinate system. \~ + \param[in] point1 - \ru Точка, определяющая направление оси X локальной системы и размер элемента. + \en A point specifying the direction of X-axis of the local system and the size of element. \~ + \param[in] point2 - \ru Точка, определяющая направление оси Y локальной системы. + \en A point specifying the direction of Y-axis of the local system. \~ + \param[in] xRadius - \ru Шаг вдоль первой оси локальной системы координат. + \en The step along the first axis of the local coordinate system. \~ + \param[in] yRadius - \ru Шаг вдоль второй оси локальной системы координат. + \en The step along the second axis of the local coordinate system. \~ + \param[in] zRadius - \ru Шаг вдоль третьей оси локальной системы координат. + \en The step along the third axis of the local coordinate system. \~ + \param[in] xCount - \ru Количество ячеек вдоль первой оси локальной системы координат. + \en The number of cells along a first axis of the local coordinate system. \~ + \param[in] yCount - \ru Количество ячеек вдоль второй оси локальной системы координат. + \en The number of cells along a second axis of the local coordinate system. \~ + \param[in] zCount - \ru Количество ячеек вдоль третьей оси локальной системы координат. + \en The number of cells along a third axis of the local coordinate system. \~ + \param[out] result - \ru Построенное тело. + \en The constructed solid. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) OctaLattice( const MbCartPoint3D & point_0, + const MbCartPoint3D & point_1, + const MbCartPoint3D & point_2, + double xRadius, + double yRadius, + double zRadius, + size_t xCount, + size_t yCount, + size_t zCount, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить обологчку на поверхности переменного сечения. + \en Create a shell on swept mutable section surface. \~ + \details \ru Построить грань тела путём движения образующей кривой по направляющей кривой + и выполнить булеву операцию с оболочкой, если последняя задана. \n + Одновременно с построением оболочки функция создаёт её строитель.\n + \en Create a face of shell by moving the generating curve along the spine curve + and perform the Boolean operation with the shell if it is specified. \n + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] solid - \ru Обологчка, к которой дополняется построение. + \en The shell the construction is complemented with respect to. \~ + \param[in] sameShell - \ru Способ копирования граней обологчки. + \en The method of copying faces of shell. \~ + \param[in] data - \ru Данные о поверхности переменного сечения. + \en Data about swept mutable section surface. \~ + \param[in] operNames - \ru Именователь новой грани оболочки. + \en Generating face names. \~ + \param[out] result - \ru Построенная обологчка. + \en The constructed shell. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC( MbResultType ) SectionShell( MbSolid * solid, + MbeCopyMode sameShell, + const MbSectionData & data, + const MbSNameMaker & operNames, + MbSolid *& result ); + + +#endif // __ACTION_SHELL_H diff --git a/C3d/Include/action_solid.h b/C3d/Include/action_solid.h index 4394a2c..b915c26 100644 --- a/C3d/Include/action_solid.h +++ b/C3d/Include/action_solid.h @@ -1,2239 +1,2237 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Функции создания тел, операции с телами. - \en Functions for creation of solids, operations on solids. \~ - \details \ru Процесс построения тел в геометрическом моделировании похож на процесс - изготовления моделируемого объекта. Сначала создаются тела простой формы, а далее - выполняется набор действий, позволяющих из тел простой формы получить более сложные тела. - При необходимости создаются вспомогательные объекты. Редактировать и создавать подобные - тела можно путём изменения параметров с последующим повторением процесса построения тел.\n - Все функции создания тел содержат в качестве входного параметр MbSNameMaker, - обеспечивающий именование граней, рёбер и вершин. - Первым параметром конструктора генератора имён MbSNameMaker служит главное имя операции. - По главному имени можно определить, в какой функции рождена та или иная грань, ребро, вершина. - Главное имя выдаёт метод GetMainName().\n - \en The process of solids creation in geometric modeling is similar to the process - of the modeled object manufacturing. Firstly solids of a simple form are created, and then - a set of operations are performed to obtain a more complex solids from solids of a simple form. - Auxiliary objects are created if necessary. The similar solids can be edited and created - by modifying of the parameters and further repeating the process of the solids creation.\n - All of the function contain input parameter MbSNameMaker, - providing the naming of faces, edges and vertices. - The first parameter to the constructor MbSNameMaker is the main name of the function. - You can determine which function is born one or the other face, edge, vertex by main name. - GetMainName() gives the main name of the function (face.GetMainName(), edge.GetMainName(), vertex.GetMainName()).\~ -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ACTION_SOLID_H -#define __ACTION_SOLID_H - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbCurve; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbSurface; -class MATH_CLASS MbCurveEdge; -class MATH_CLASS MbFace; -class MATH_CLASS MbSolid; -class MATH_CLASS MbItem; -class MATH_CLASS MbSNameMaker; -class MATH_CLASS MbPartSolidIndices; -class MATH_CLASS MbSpine; -class MATH_CLASS MbMesh; -class MATH_CLASS MbGrid; -class MATH_CLASS MbCollection; -class MATH_CLASS IProgressIndicator; - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать элементарное тело. - \en Create an elementary solid. \~ - \details \ru Создать одно из элементарных тел по заданным точкам и типу: \n - solidType = et_Sphere - шар (3 точки), \n - solidType = et_Torus - тор (3 точки), \n - solidType = et_Cylinder - цилиндр (3 точки), \n - solidType = et_Cone - конус (3 точки), \n - solidType = et_Block - блок (4 точки), \n - solidType = et_Wedge - клин (4 точки), \n - solidType = et_Prism - призма (количество вершин основания+1 точка), \n - solidType = et_Pyramid - пирамида (количество вершин основания+1 точка), \n - solidType = et_Plate - плита (4 точки), - solidType = et_Icosahedron - икосаэдр (3 точки), \n - solidType = et_Polyhedron - многогранник (3 точки), \n - solidType = et_Tetrapipe - тетра-труба (3 точки), \n - solidType = et_Octapipe - окта-труба (3 точки). \n - \en Create one of elementary solids from the specified points and type: \n - solidType = et_Sphere - a sphere (3 points), \n - solidType = et_Torus - a torus (3 points), \n - solidType = et_Cylinder - a cylinder (3 points), \n - solidType = et_Cone - a cone (3 points), \n - solidType = et_Block - a block (4 points), \n - solidType = et_Wedge - a wedge (4 points), \n - solidType = et_Prism - a prism (points count is equal to the base vertices count + 1), \n - solidType = et_Pyramid - a pyramid (points count is equal to the base vertices count + 1), \n - solidType = et_Plate - a plate (4 points), \n - solidType = et_Icosahedron - an icosahedron (3 points), \n - solidType = et_Polyhedron - a polyhedron (3 points), \n - solidType = et_Tetrapipe - a tetra-pipe (3 points), \n - solidType = et_Octapipe - an octa-pipe (3 points). \n \~ - \param[in] points - \ru Набор точек. \n - points[0] определяет начало локальной системы координат. \n - Для сферы, тора, цилиндра и конуса: \n - points[1] определяет направление оси Z локальной системы. \n - points[2] определяет направление оси X локальной системы. \n - Для блока, клина и плиты: \n - points[1] определяет направление оси X локальной системы. \n - points[2] определяет направление оси Y локальной системы. \n - Кроме того, \n - points[1] определяет высоту цилиндра, высоту конуса, - большой радиус тора, длину блока, длину клина. \n - points[2] определяет радиус цилиндра, угол конуса как угол между векторами v1(points[0],points[1]) и v2(points[0],points[2]), - радиус сферы, малый радиус тора, ширину блока, ширину клина. \n - В случае конуса вектора v1(points[0],points[1]) и v2(points[0],points[2]) не должны быть параллельны или перпендикулярны друг другу. \n - Последняя точка определяет высоту блока, клина, плиты, вершину пирамиды. - \en A point set. \n - points[0] determines a local coordinate system origin. \n - For a sphere, a torus, a cylinder or a cone: \n - points[1] determines the direction of Z-axis of a local coordinate system. \n - points[2] determines the direction of X-axis of a local coordinate system. \n - For a block, a plate or a wedge: \n - points[1] determines the direction of X-axis of a local coordinate system. \n - points[2] determines the direction of Y-axis of a local coordinate system. \n - Also, \n - points[1] determines the height of a cylinder or a cone, - the major radius of a torus, the length of a block or a wedge. \n - points[2] determines the radius of a cylinder, cone angle as angle between vectors v1(points[0],points[1]) and v2(points[0],points[2]), - radius of a sphere, the minor radius of a torus, the width of a block or a wedge. \n - In the case of the cone of the vector v1(points [0], points [1]) and v2(points [0], points [2]) must not be parallel or perpendicular to each other. \n - The last point determines the height of a block, a wedge or a plate, the vertex of a pyramid. \~ - \param[in] solidType - \ru Тип создаваемого тела. - \en The solid type. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ElementarySolid( const SArray & points, - ElementaryShellType solidType, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело по поверхности. - \en Create a solid from a surface. \~ - \details \ru Создать тело по элементарной поверхности. \n - Допускается только тип поверхности - цилиндр, конус, сфера, тор. - \en Create a solid from an elementary surface. \n - The only acceptable surface types are cylinder, cone, sphere, torus. \~ - \param[in] surface - \ru Поверхность. - \en The surface. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ElementarySolid( const MbSurface & surface, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело на основе полигональной модели. - \en Create a solid on the basis of a polygonal geometric object. \~ - \details \ru Создать тело #MbSolid на основе полигональной модели #MbMesh. \n - \en Create a solid #MbSolid on the basis of a polygonal geometric object #MbMesh. \n \~ - \param[in] mesh - \ru Полигональная модель. - \en The polygonal geometric object. \~ - \param[in] params - \ru Параметры операции. - \en Operation parameters. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) MeshSolid( const MbMesh & mesh, - const GridsToShellValues & params, - const MbSNameMaker & names, - MbSolid *& result, - IProgressIndicator * prog = NULL ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело на основе триангуляции. - \en Create a solid on the basis of a triangulation. \~ - \details \ru Создать тело #MbSolid на основе триангуляции #MbGrid. \n - \en Create a solid #MbSolid on the basis of a triangulation #MbGrid. \n \~ - \param[in] grid - \ru Полигональная модель. - \en The polygonal geometric object. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) GridSolid( const MbGrid & grid, - const MbSNameMaker & names, - MbSolid *& result, - IProgressIndicator * prog = NULL ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело на основе коллекции элементов. - \en Create a solid on the basis of elements. \~ - \details \ru Создать тело #MbSolid на основе коллекции элементов #MbCollection. \n - \en Create a solid #MbSolid on the basis of elements #MbCollection. \n \~ - \param[in] grid - \ru Коллекция элементов. - \en The elements collection. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) CollectionSolid( const MbCollection & grid, - const MbSNameMaker & names, - MbSolid *& result, - IProgressIndicator * progBar = NULL ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело c заданной оболочкой. - \en Create a solid with a given shell. \~ - \details \ru Создать тело без истории построения с заданной оболочкой. \n - \en Create a solid with a given shell without a history. \n \~ - \param[in] shell - \ru Оболочка. - \en A shell. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \return \ru Возвращает тело без истории. - \en Returns a solid without the history. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbSolid *) CreateSolid( MbFaceShell & shell, - const MbSNameMaker & names ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Рассчитать глубину выдавливания или угол вращения. - \en Compute the extrusion depth or the rotation angle. \~ - \details \ru Рассчитать value - глубину выдавливания или угол вращения (0.0 : M_PI2) - для последующего построения тела путем выдавливания или вращения образующей кривой. \n - \en Compute 'value' - the extrusion depth or the rotation angle (0.0 : M_PI2) - for the further construction of a solid by extrusion or revolution of the generating curve. \n \~ - \param[in] sweptData - \ru Данные об образующей. - \en The generating curve data. \~ - \param[in] axis - \ru Ось вращения. - \en Rotation axis. \~ - \param[in] direction - \ru Направление выдавливания. - \en An extrusion direction. \~ - \param[in] rotation - \ru Вращение или выдавливание. - \en Rotation or extrusion. \~ - \param[in] operationDirection - \ru Вперед\назад. - \en Forward or backward direction. \~ - \param[in] point - \ru Точка, до которой требуется вращать или выдавливать поверхность. - \en The point to rotate or extrude the surface up to. \~ - \param[out] value - \ru Глубина выдавливания или угол вращения. - \en The extrusion depth or the rotation angle. \~ - \return \ru Возвращает true, если расчет выполнен успешно. - \en Returns true if the value has been successfully calculated. \~ - \warning \ru Вспомогательная функция операций ExtrusionSolid, RevolutionSolid, ExtrusionResult и RevolutionResult. - \en An auxiliary function of operations ExtrusionSolid, RevolutionSolid, ExtrusionResult and RevolutionResult. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (bool) GetSweptValue( const MbSweptData & sweptData, - const MbAxis3D & axis, - const MbVector3D & direction, - const bool rotation, - const bool operationDirection, - const MbCartPoint3D & point, - double & value ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Получить начальное приближение для нахождения образа при вращении/выдавливании. - \en Get the initial approximation for image calculation while rotating/extruding. \~ - \details \ru Вычислить положение образа точки образующей кривой на поверхности для последующего - построения тела путем выдавливания или вращения образующей кривой до заданной поверхности. \n - \en Compute the position of a generating curve point image on a surface for further - solid construction by extrusion or revolution of the generating curve up to the specified surface. \n \~ - \param[in] generatrix - \ru Кривая. - \en The curve. \~ - \param[in] surface - \ru Поверхность, до которой строим операцию. - \en The surface to construct up to. \~ - \param[in] direction - \ru Направление выдавливания. - \en An extrusion direction. \~ - \param[in] axis - \ru Ось вращения. - \en Rotation axis. \~ - \param[in] rotation - \ru Вращение (true) или выдавливание (false) - \en Rotation (true) or extrusion (false) \~ - \param[out] imagePosition - \ru Точка образа на поверхности. - \en The image point on the surface. \~ - \param[out] resType - \ru Код результата операции. - \en Operation result code. \~ - \warning \ru Вспомогательная функция операций ExtrusionSolid, RevolutionSolid, ExtrusionResult и RevolutionResult. - \en An auxiliary function of operations ExtrusionSolid, RevolutionSolid, ExtrusionResult and RevolutionResult. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (void) GetSweptImagePosition( const MbCurve3D & generatrix, - const MbSurface & surface, - const MbVector3D & direction, - const MbAxis3D & axis, - const bool rotation, - MbCartPoint & imagePosition, - MbResultType & resType ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти расстояния/углы от образующей до поверхности при вращении/выдавливании. - \en Calculate the distances/angles from generating curve to the surface while rotating/extruding. \~ - \details \ru Вычислить глубины выдавливания в прямом и обратном направлениях или углы вращения - в прямом и обратном направлениях для последующего построения тела путем выдавливания или вращения - образующей кривой до заданной поверхности, а также и габарит образа кривой. \n - \en Calculate the extrusion depths in forward and backward directions or the rotating angles - in forward and backward directions for further solid construction by extrusion or revolution - of the generating curve up to the specified surface; and also calculate the bounding box of the curve image. \n \~ - \param[in] surface - \ru Поверхность, до которой строим операцию. - \en The surface to construct up to. \~ - \param[in] curve - \ru Образующая кривая. - \en The generating curve. \~ - \param[in] direction - \ru Направление выдавливания. - \en An extrusion direction. \~ - \param[in] axis - \ru Ось вращения. - \en Rotation axis. \~ - \param[in] rotation - \ru Вращение (true) или выдавливание (false). - \en Revolution (true) or extrusion (false). \~ - \param[in] operationDirection - \ru Направление движения: вперед (true) или назад (false). - \en The motion direction: forward (true) or backward (false). \~ - \param[out] imagePosition - \ru Точка на части поверхности, в которой лежит образ. - \en A point on a surface part that contains the image. \~ - \param[out] range - \ru Расстояния до поверхности в обратном и прямом направлениях. - \en The distance to surface in the backward and the forward directions. \~ - \param[out] rectOnSurface - \ru Габарит образа на поверхности. - \en The bounding box of image on the surface. \~ - \param[out] resType - \ru Код результата операции. - \en Operation result code. \~ - \warning \ru Вспомогательная функция операций ExtrusionSolid, RevolutionSolid, ExtrusionResult и RevolutionResult. - \en An auxiliary function of operations ExtrusionSolid, RevolutionSolid, ExtrusionResult and RevolutionResult. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (void) GetRangeToSurface( const MbSurface & surface, - const MbCurve3D & curve, - const MbVector3D & direction, - const MbAxis3D & axis, - const bool rotation, - const bool operationDirection, - const MbCartPoint & imagePosition, - double range[2], - MbRect & rectOnSurface, - MbResultType & resType ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Вычислить площадь проекции кривой на виртуальную координатную плоскость. - \en Compute the area of a curve projection onto a virtual coordinate plane. \~ - \details \ru Вычислить площадь проекции кривой на виртуальную координатную плоскость \n - для определения ориентации образующей кривой в оболочке выдавливания и вращения. \n - Параметры direction и axis определяют направление выдавливания и ось вращения, - а параметр rotation определяет тип операции: \n - - выдавливание, вычисляется площадь проекции на плоскость XOY, \n - - вращение, вычисляется площадь "проекции" на "плоскость" ROZ. - Для незамкнутой кривой в расчет добавляется "замыкание отрезком". - \en Compute the area of a curve projection onto a virtual coordinate plane \n - to determine the generating curve orientation in the shell of extrusion and revolution. \n - Parameters 'direction' and 'axis' determine the extrusion direction and the rotation axis, - and parameter 'rotation' determines the operation type: \n - - extrusion, the area of projection onto the plane XOY is computed, \n - - revolution, the area of "projection" to the "plane" ROZ is computed. - For an open curve "enclosure by a segment" is considered. \~ - \param[in] curve - \ru Кривая. - \en The curve. \~ - \param[in] axis - \ru Ось вращения. - \en Rotation axis. \~ - \param[in] direction - \ru Направление выдавливания. - \en An extrusion direction. \~ - \param[in] rotation - \ru Вращение (true) или выдавливание (false). - \en Revolution (true) or extrusion (false). \~ - \return \ru Возвращает площадь проекции. - \en Returns the projection area. \~ - \warning \ru Вспомогательная функция операций ExtrusionSolid, RevolutionSolid, ExtrusionResult и RevolutionResult. - \en An auxiliary function of operations ExtrusionSolid, RevolutionSolid, ExtrusionResult and RevolutionResult. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (double) AreaSign( const MbCurve3D & curve, - const MbAxis3D & axis, - const MbVector3D & direction, - bool rotation ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Определить ориентацию секущей грани относительно тела выдавливания/вращения. - \en Determine the orientation of a cutting surface relative to the extrusion/revolution solid. \~ - \details \ru Определить ориентацию секущей поверхности относительно тела, которое будет строиться - путём выдавливания или вращения образующей кривой до заданной поверхности. \n - \en Determine the orientation of a cutting surface relative to the solid which is to be constructed - by extrusion or revolution the generation curve up to the specified surface. \n \~ - \param[in] cuttingSurface - \ru Поверхность для анализа. - \en A surface to analyze. \~ - \param[in] imagePosition - \ru Место образа на поверхности. - \en An image location on the surface. \~ - \param[in] curve - \ru Образующая кривая. - \en The generating curve. \~ - \param[in] direction - \ru Направление выдавливания. - \en An extrusion direction. \~ - \param[in] axis - \ru Ось вращения. - \en Rotation axis. \~ - \param[in] rotation - \ru Вращение (true) или выдавливание (false). - \en Revolution (true) or extrusion (false). \~ - \param[in] operationDirection - \ru Направление движения: вперед (true) или назад (false). - \en The motion direction: forward (true) or backward (false). \~ - \param[out] relativeSense - \ru Ориентация поверхности по отношению к операции. - \en The surface orientation relative to the operation. \~ - \param[out] resType - \ru Код результата операции. - \en Operation result code. \~ - \warning \ru Вспомогательная функция операций ExtrusionSolid, RevolutionSolid, ExtrusionResult и RevolutionResult. - \en An auxiliary function of operations ExtrusionSolid, RevolutionSolid, ExtrusionResult and RevolutionResult. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (void) AnalyzeSurfaceRelationToSweptOperation( const MbSurface & cuttingSurface, - const MbCartPoint & imagePosition, - const MbCurve3D & curve, - const MbVector3D & direction, - const MbAxis3D & axis, - const bool rotation, - bool operationDirection, - bool & relativeSense, - MbResultType& resType ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти сегменты контура на поверхности, соответствующие швам и полюсам. - \en Find contour segments corresponding to the seams and poles. \~ - \details \ru Найти сегменты контура на поверхности, соответствующие швам и полюсам. \n - \en Find contour segments corresponding to the seams and poles. \n \~ - \param[in] surface - \ru Поверхность. - \en Surface. \~ - \param[in] contour - \ru Контур. - \en Contour. \~ - \param[out] seamsAndPoles - \ru Номера сегментов, соответствующих швам и полюсам. - \en Segment numbers corresponding to the seams and poles. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (void) FindPolesAndSeamsInContour( const MbSurface & surface, - const MbContour & contour, - c3d::IndicesVector & seamsAndPoles ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело выдавливания. - \en Create an extrusion solid. \~ - \details \ru Создать тело выдавливания. \n - solid1 и solid2 используются с опцией "До ближайших граней" этих тел. \n - \en Create an extrusion solid. \n - solid1 and solid2 are used with option "Up to the closest faces" of these solids. \n \~ - \param[in] sweptData - \ru Данные об образующей кривой. - \en The generating curve data. \~ - \param[in] direction - \ru Направление выдавливания. - \en An extrusion direction. \~ - \param[in] solid1 - \ru До ближайших граней этого тела в прямом направлении. - \en Up to the closest faces of this solid in the forward direction. \~ - \param[in] solid2 - \ru До ближайших граней этого тела в обратном направлении. - \en Up to the closest faces of this solid in the backward direction. \~ - \param[in] checkIntersection - \ru Объединять тела solid1 и solid2 с проверкой пересечения. - \en Whether to union the solids solid1 and solid2 with the check for intersections. \~ - \param[in, out] params - \ru Параметры выдавливания. - Возвращают информацию для построения элементов массива операций до поверхности. - \en The extrusion parameters. - Returns the information for construction of the up-to-surface operation array elements. \~ - \param[in] operNames - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[in] contoursNames - \ru Именователи сегментов образующего контура. - \en An objects defining a names of the generating contour segments. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ExtrusionSolid( const MbSweptData & sweptData, - const MbVector3D & direction, - const MbSolid * solid1, - const MbSolid * solid2, - bool checkIntersection, - const ExtrusionValues & params, - const MbSNameMaker & operNames, - const RPArray & contoursNames, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело вращения. - \en Create a solid of revolution. \~ - \details \ru Создать тело вращения по данным об образующей. \n - \en Create a solid of revolution by the generating curve data. \n \~ - \param[in] sweptData - \ru Данные об образующей кривой. - \en The generating curve data. \~ - \param[in] axis - \ru Ось вращения. - \en Rotation axis. \~ - \param[in, out] params - \ru Параметры вращения. - Возвращают информацию для построения элементов массива операций до поверхности. - \en The revolution parameters. - Returns the information for construction of the up-to-surface operation array elements. \~ - \param[in] operNames - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[in] contoursNames - \ru Именователи сегментов образующего контура. - \en An objects defining a names of the generating contour segments. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) RevolutionSolid( const MbSweptData & sweptData, - const MbAxis3D & axis, - const RevolutionValues & params, - const MbSNameMaker & operNames, - const RPArray & contoursNames, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кинематическое тело. - \en Create a sweeping solid. \~ - \details \ru Создать кинематическое тело путем движения образующей кривой вдоль направляющей кривой. \n - \en Create a sweeping solid by moving the generating curve along the guide curve. \n \~ - \param[in] sweptData - \ru Данные об образующей. - \en The generating curve data. \~ - \param[in] spine - \ru Направляющая кривая. - \en The spine curve. \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] operNames - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[in] contoursNames - \ru Именователь контуров образующей. - \en An object defining the names of generating curve contours. \~ - \param[in] spineNames - \ru Именователь направляющей. - \en An object defining the name of a guide curve. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) EvolutionSolid( const MbSweptData & sweptData, - const MbCurve3D & spine, - const EvolutionValues & params, - const MbSNameMaker & operNames, - const RPArray & contoursNames, - const MbSNameMaker & spineNames, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кинематическое тело. - \en Create a sweeping solid. \~ - \details \ru Создать кинематическое тело путем движения образующей кривой вдоль направляющей кривой c дополнительной информацией. \n - \en Create a sweeping solid by moving the generating curve along the guide curve with additional data. \n \~ - \param[in] sweptData - \ru Данные об образующей. - \en The generating curve data. \~ - \param[in] spine - \ru Направляющая кривая c дополнительной информацией. - \en The spine curve with additional data. \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] operNames - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[in] contoursNames - \ru Именователь контуров образующей. - \en An object defining the names of generating curve contours. \~ - \param[in] spineNames - \ru Именователь направляющей. - \en An object defining the name of a guide curve. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) EvolutionSolid( const MbSweptData & sweptData, - const MbSpine & spine, - const EvolutionValues & params, - const MbSNameMaker & operNames, - const RPArray & contoursNames, - const MbSNameMaker & spineNames, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело по плоским сечениям. - \en Create a solid from a planar sections. \~ - \details \ru Создать тело по плоским сечениям c направляющей линией. \n - \en Create a solid from a planar sections with a guide curve. \n \~ - \param[in] pl - \ru Множество систем координат образующих контуров. - \en An array of generating contours coordinate systems. \~ - \param[in] c - \ru Множество образующих контуров. - \en An array of generating contours. \~ - \param[in] spine - \ru Направляющая кривая (может быть NULL). - \en A guide curve (can be NULL). \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] ps - \ru Множество точек на образующих контурах, задающий их начальные точки. - \en A point array on the generating contours which determines the start points of the contours. \~ - \param[in] names - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[in] ns - \ru Именователи образующих контуров. - \en The objects defining the names of generating contours. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) LoftedSolid( SArray & pl, - RPArray & c, - const MbCurve3D * spine, - const LoftedValues & params, - SArray * ps, - const MbSNameMaker & names, - RPArray & ns, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело по пространственным сечениям. - \en Create a solid from a space sections. \~ - \details \ru Создать тело по пространственным сечениям c направляющей линией. \n - \en Create a solid from a space sections with a guide curve. \n \~ - \param[in] pl - \ru Множество систем координат образующих контуров. - \en An array of generating contours coordinate systems. \~ - \param[in] c - \ru Множество образующих контуров. - \en An array of generating contours. \~ - \param[in] spine - \ru Осевая кривая (может быть NULL). - \en A guide curve (can be NULL). \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] guideCurves - \ru Множество направляющих кривых, задающих траектории соответствующих точек контуров. - \en An array of the guide curves that determines the trajectories of the corresponding points of the contours. \~ - \param[in] ps - \ru Множество точек на образующих контурах, задающее соответствующие точки (цепочки точек). - \en A point array on the generating contours which determines the corresponding points of the contours (chains of points). \~ - \param[in] names - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[in] ns - \ru Именователи образующих контуров. - \en The objects defining the names of generating contours. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) LoftedSolid( SArray & pl, - RPArray & c, - const MbCurve3D * spine, // осевая линия может быть NULL - const LoftedValues & params, - RPArray * guideCurves, - SArray * ps, - const MbSNameMaker & names, - RPArray & ns, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело по пространственным сечениям. - \en Create a solid from a space sections. \~ - \details \ru Создать тело по пространственным сечениям c направляющей линией. \n - \en Create a solid from a space sections with a guide curve. \n \~ - \param[in] surfs - \ru Множество поверхностей образующих контуров. - \en An array of surfaces of generating contours. \~ - \param[in] c - \ru Множество образующих контуров. - \en An array of generating contours. \~ - \param[in] spine - \ru Осевая кривая (может быть NULL). - \en A guide curve (can be NULL). \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] guideCurves - \ru Множество направляющих кривых, задающих траектории соответствующих точек контуров. - \en An array of the guide curves that determines the trajectories of the corresponding points of the contours. \~ - \param[in] ps - \ru Множество точек на образующих контурах, задающее соответствующие точки (цепочки точек). - \en A point array on the generating contours which determines the corresponding points of the contours (chains of points). \~ - \param[in] names - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[in] ns - \ru Именователи образующих контуров. - \en The objects defining the names of generating contours. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) LoftedSolid( RPArray & surfs, - RPArray & c, - const MbCurve3D * spine, // осевая линия может быть NULL - const LoftedValues & params, - RPArray * guideCurves, - SArray * ps, - const MbSNameMaker & names, - RPArray & ns, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело выдавливания и выполнить булеву операцию. - \en Create an extrusion solid and perform a boolean operation. \~ - \details \ru Создать тело выдавливания и выполнить булеву операцию типа oType с телом solid. - Принимаемые значения OperationType для тел: \n - bo_Union - объединение, \n - bo_Intersect - пересечение, \n - bo_Difference - вычитание. - \en Create an extrusion solid and perform a boolean operation of type 'oType' with solid 'solid'. - The possible values of 'OperationType' for solids: \n - bo_Union - union, \n - bo_Intersect - intersection, \n - bo_Difference - subtraction. \~ - \param[in] solid - \ru Первое тело для булевой операции. - \en The first solid for a boolean operation. \~ - \param[in] sameShell - \ru Режим копирования тела. - \en Whether to copy the solid. \~ - \param[in] sweptData - \ru Данные об образующих кривых. - \en The generating curve data. \~ - \param[in] direction - \ru Направление выдавливания. - \en An extrusion direction. \~ - \param[in, out] params - \ru Параметры выдавливания. - Возвращают информацию для построения элементов массива операций до поверхности. - \en The extrusion parameters. - Returns the information for construction of the up-to-surface operation array elements. \~ - \param[in] oType - \ru Тип булевой операции. - \en A boolean operation type. \~ - \param[in] operNames - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[in] contoursNames - \ru Именователи образующих кривых. - \en The objects defining the names of generating lines. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ExtrusionResult( MbSolid & solid, - MbeCopyMode sameShell, - const MbSweptData & sweptData, - const MbVector3D & direction, - const ExtrusionValues & params, - OperationType oType, - const MbSNameMaker & operNames, - const RPArray & contoursNames, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело вращения и выполнить булеву операцию. - \en Create a revolution solid and perform a boolean operation. \~ - \details \ru Создать тело вращения и выполнить булеву операцию типа oType с телом solid. - Принимаемые значения OperationType для тел: \n - bo_Union - объединение, \n - bo_Intersect - пересечение, \n - bo_Difference - вычитание. - \en Create a revolution solid and perform a boolean operation of type oType with solid 'solid'. - The possible values of 'OperationType' for solids: \n - bo_Union - union, \n - bo_Intersect - intersection, \n - bo_Difference - subtraction. \~ - \param[in] solid - \ru Первое тело для булевой операции. - \en The first solid for a boolean operation. \~ - \param[in] sameShell - \ru Режим копирования тела. - \en Whether to copy the solid. \~ - \param[in] sweptData - \ru Данные об образующих кривых. - \en The generating curve data. \~ - \param[in] axis - \ru Ось вращения. - \en Rotation axis. \~ - \param[in, out] params - \ru Параметры вращения. - Возвращают информацию для построения элементов массива операций до поверхности. - \en The revolution parameters. - Returns the information for construction of the up-to-surface operation array elements. \~ - \param[in] oType - \ru Тип булевой операции. - \en A boolean operation type. \~ - \param[in] operNames - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[in] contoursNames - \ru Именователи образующих кривых. - \en The objects defining the names of generating lines. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) RevolutionResult( MbSolid & solid, - MbeCopyMode sameShell, - const MbSweptData & sweptData, - const MbAxis3D & axis, - const RevolutionValues & params, - OperationType oType, - const MbSNameMaker & operNames, - const RPArray & contoursNames, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Сориентировать образующий контур и направляющую кинематики. - \en Determine the orientation for a generating contour and for a guide curve of kinematics (evolution). \~ - \details \ru Выполнить ориентацию образующего контура и направляющей кривой для построения кинематического тела. \n - \en Orientate the generating contour and the guide curve for a sweeping solid construction. \n \~ - \param[in] surface - \ru Поверхность. - \en The surface. \~ - \param[in] contours - \ru Образующие контуры. - \en Generating contours. \~ - \param[in] guide - \ru Направляющая кривая. - \en The spine curve. \~ - \param[in] parameters - \ru Параметры операции. - \en The operation parameters. \~ - \param[out] axis - \ru Ось доворота образующей. - \en The axis for the generating curve additional turn. \~ - \param[out] angle - \ru Угол доворота образующей. - \en The additional turn for generating line. \~ - \param[in] version - \ru Версия операции. - \en The version of the operation. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \warning \ru Вспомогательная функция операций EvolutionSolid и EvolutionResult. - \en An auxiliary function of operations EvolutionSolid and EvolutionResult. \~ - \ingroup Shell_Modeling -*/ -// --- -MATH_FUNC (MbResultType) EvolutionNormalize( const MbSurface & surface, - const RPArray & contours, - const MbCurve3D & guide, - const EvolutionValues & parameters, - MbAxis3D & axis, - double & angle, - VERSION version ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать усеченную замкнутую кривую на копии кривой. - \en Create a trimmed closed curve on a curve copy. \~ - \details \ru Выполнить построение копии замкнутой кривой с началом в точке, определяемой параметром t. \n - \en Create a copy of a closed curve starting at a point with parameter t. \n \~ - \param[in] curve - \ru Направляющая кривая. - \en The spine curve. \~ - \param[in] t - \ru Параметр кривой. - \en A curve parameter. \~ - \return \ru При удачной работе функция возвращает построенную копию кривой - с началом в заданной точке, в противном случае функция возвращает ноль. - \en Returns a constructed curve copy starting at the specified point if it has been successfully created, - otherwise it returns null. \~ - \warning \ru Вспомогательная функция операций EvolutionSolid и EvolutionResult. - \en An auxiliary function of operations EvolutionSolid and EvolutionResult. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbCurve3D *) TrimClosedSpine( const MbCurve3D & curve, - double t ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кинематическое тело и выполнить булеву операцию. - \en Create an evolution solid and perform a boolean operation. \~ - \details \ru Создать кинематическое тело и выполнить булеву операцию типа oType с телом solid. - Принимаемые значения OperationType для тел: \n - bo_Union - объединение, \n - bo_Intersect - пересечение, \n - bo_Difference - вычитание. - \en Create an evolution solid and perform a boolean operation of type oType with solid 'solid'. - The possible values of 'OperationType' for solids: \n - bo_Union - union, \n - bo_Intersect - intersection, \n - bo_Difference - subtraction. \~ - \param[in] solid - \ru Первое тело для булевой операции. - \en The first solid for a boolean operation. \~ - \param[in] sameShell - \ru Режим копирования тела. - \en Whether to copy the solid. \~ - \param[in] sweptData - \ru Данные об образующей. - \en The generating curve data. \~ - \param[in] spine - \ru Направляющая кривая. - \en The spine curve. \~ - \param[in] params - \ru Параметры кинематической операции. - \en Parameters of the sweeping operation. \~ - \param[in] oType - \ru Тип булевой операции. - \en A boolean operation type. \~ - \param[in] operNames - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] contoursNames - \ru Именователь контуров образующей. - \en An object defining the names of generating curve contours. \~ - \param[in] spineNames - \ru Именователь направляющей. - \en An object defining the name of a guide curve. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC(MbResultType) EvolutionResult( MbSolid & solid, - MbeCopyMode sameShell, - const MbSweptData & sweptData, - const MbCurve3D & spine, - const EvolutionValues & params, - OperationType oType, - const MbSNameMaker & operNames, - const RPArray & contoursNames, - const MbSNameMaker & spineNames, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело по плоским сечениям и выполнить булеву операцию. - \en Create a solid from the planar sections and perform a boolean operation. \~ - \details \ru Создать тело по плоским сечениям и выполнить булеву операцию типа oType с телом solid. - Принимаемые значения OperationType для тел: \n - bo_Union - объединение, \n - bo_Intersect - пересечение, \n - bo_Difference - вычитание. - \en Create a solid from a planar sections and perform a boolean operation of type oType with solid 'solid'. - The possible values of 'OperationType' for solids: \n - bo_Union - union, \n - bo_Intersect - intersection, \n - bo_Difference - subtraction. \~ - \param[in] solid - \ru Первое тело для булевой операции. - \en The first solid for a boolean operation. \~ - \param[in] sameShell - \ru Режим копирования тела. - \en Whether to copy the solid. \~ - \param[in] pl - \ru Множество систем координат образующих контуров. - \en An array of generating contours coordinate systems. \~ - \param[in] c - \ru Множество образующих контуров. - \en An array of generating contours. \~ - \param[in] spine - \ru Направляющая кривая (может быть NULL). - \en A guide curve (can be NULL). \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] oType - \ru Тип булевой операции. - \en A boolean operation type. \~ - \param[in] ps - \ru Множество точек на образующих контурах, задающий их начальные точки. - \en A point array on the generating contours which determines the start points of the contours. \~ - \param[in] names - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[in] ns - \ru Именователи образующих контуров. - \en The objects defining the names of generating contours. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC(MbResultType) LoftedResult( MbSolid & solid, - MbeCopyMode sameShell, - SArray & pl, - RPArray & c, - const MbCurve3D * spine, - const LoftedValues & params, - OperationType oType, - SArray * ps, - const MbSNameMaker & names, - RPArray & ns, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело по пространственным сечениям и выполнить булеву операцию. - \en Create a solid from the space sections and perform a boolean operation. \~ - \details \ru Создать тело по пространственным сечениям и выполнить булеву операцию типа oType с телом solid. - Принимаемые значения OperationType для тел: \n - bo_Union - объединение, \n - bo_Intersect - пересечение, \n - bo_Difference - вычитание. - \en Create a solid from a space sections and perform a boolean operation of type oType with solid 'solid'. - The possible values of 'OperationType' for solids: \n - bo_Union - union, \n - bo_Intersect - intersection, \n - bo_Difference - subtraction. \~ - \param[in] solid - \ru Первое тело для булевой операции. - \en The first solid for a boolean operation. \~ - \param[in] sameShell - \ru Режим копирования тела. - \en Whether to copy the solid. \~ - \param[in] surfs - \ru Множество поверхностей контуров. - \en An array of generating contours surfaces. \~ - \param[in] c - \ru Множество образующих контуров. - \en An array of generating contours. \~ - \param[in] spine - \ru Осевая кривая (может быть NULL). - \en A guide curve (can be NULL). \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] oType - \ru Тип булевой операции. - \en A boolean operation type. \~ - \param[in] guideCurves - \ru Массив направляющих кривых. - \en An array of the guide curves. \~ - \param[in] ps - \ru Множество точек на образующих контурах, задающий их начальные точки. - \en A point array on the generating contours which determines the start points of the contours. \~ - \param[in] names - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[in] ns - \ru Именователи образующих контуров. - \en The objects defining the names of generating contours. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC(MbResultType) LoftedResult( MbSolid & solid, - MbeCopyMode sameShell, - RPArray & surfs, - RPArray & c, - const MbCurve3D * spine, - const LoftedValues & params, - OperationType oType, - RPArray * guideCurves, - SArray * ps, - const MbSNameMaker & names, - RPArray & ns, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Выполнить булеву операцию. - \en Perform a Boolean operation. \~ - \details \ru Функция выполняет указанную булеву операцию над двумя телами с возможностью управления слиянием граней и рёбер.\n - \en The function performs the specified Boolean operation on two solids with faces and edges merging control.\n \~ - \param[in] solid1 - \ru Набор граней первого тела. - \en The set of faces of the first solid. \~ - \param[in] sameShell1 - \ru Способ копирования граней первого тела. - \en Method of copying the faces of the first solid. \~ - \param[in] solid2 - \ru Набор граней второго тела. - \en The second solid face set. \~ - \param[in] sameShell2 - \ru Способ копирования граней второго тела. - \en Method of copying the faces of the second solid. \~ - \param[in] oType - \ru Тип булевой операции. - \en A Boolean operation type. \~ - \param[in] flags - \ru Управляющие флаги булевой операции. - \en Control flags of the Boolean operation. \~ - \param[in] operNames - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[out] result - \ru Построенный набор граней. - \en Constructed set of faces. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) BooleanResult( MbSolid & solid1, - MbeCopyMode sameShell1, - MbSolid & solid2, - MbeCopyMode sameShell2, - OperationType oType, - const MbBooleanFlags & flags, - const MbSNameMaker & operNames, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело путем булевой операции. - \en Create a solid using a boolean operation. \~ - \details \ru Создать тело путем булевой операции типа oType для тел solid1 и solid2. - Принимаемые значения OperationType для тел: \n - bo_Union - объединение, \n - bo_Intersect - пересечение, \n - bo_Difference - вычитание. \n - Функция работает только с замкнутыми телами, сливает подобные грани и рёбра. - Функция выполняет одноимённую булеву операцию над множествами точек, - расположенными внутри и на поверхности тел. - \en Create a solid by applying a boolean operation of type oType to solids 'solid1' and 'solid2'. - The possible values of 'OperationType' for solids: \n - bo_Union - union, \n - bo_Intersect - intersection, \n - bo_Difference - subtraction. \n - The function accepts only closed solids, similar faces and similar edges will be merged. - The function performs a boolean operation of the same name with a point sets - located inside the solids and on their boundary. \~ - \param[in] solid1 - \ru Первое тело. - \en The first solid. \~ - \param[in] sameShell1 - \ru Режим копирования первого тела. - \en Whether to copy the first solid. \~ - \param[in] solid2 - \ru Второе тело. - \en The second solid. \~ - \param[in] sameShell2 - \ru Режим копирования второго тела. - \en Whether to copy the second solid. \~ - \param[in] oType - \ru Тип булевой операции. - \en A boolean operation type. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) BooleanSolid( MbSolid & solid1, - MbeCopyMode sameShell1, - MbSolid & solid2, - MbeCopyMode sameShell2, - OperationType oType, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело путем булевой операции. - \en Create a solid using a boolean operation. \~ - \details \ru Создать тело путем булевой операции типа oType для оболочек solid1 и solid2. - Один из операндов solid1 или solid2 - незамкнутая оболочка. - Принимаемые значения OperationType для оболочек: \n - bo_Variety - объединение, \n - bo_Internal - пересечение, \n - bo_External - вычитание. - \en Create a solid applying a boolean operation of type oType to shells 'solid1' and solid2'. - One of the operands 'solid1' and 'solid2' should be an open shell. - Possible values of 'OperationType' for a shells are: \n - bo_Variety - a union, \n - bo_Internal - an intersection, \n - bo_External - a subtraction. \~ - \param[in] solid1 - \ru Первое тело. - \en The first solid. \~ - \param[in] sameShell1 - \ru Режим копирования первого тела. - \en Whether to copy the first solid. \~ - \param[in] solid2 - \ru Второе тело. - \en The second solid. \~ - \param[in] sameShell2 - \ru Режим копирования второго тела. - \en Whether to copy the second solid. \~ - \param[in] oType - \ru Тип булевой операции. - \en A boolean operation type. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) BooleanShell( MbSolid & solid1, - MbeCopyMode sameShell1, - MbSolid & solid2, - MbeCopyMode sameShell2, - OperationType oType, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Отрезать часть тела поверхностью. - \en Cut a part of a solid off by a surface. \~ - \details \ru Отрезать часть тела пересекающей его поверхностью. \n - part = 1 - оставляем часть тела, расположенную сверху поверхности. \n - part = -1 - оставляем часть тела, расположенную снизу поверхности. \n - \en Cut a part of a solid off by a surface that intersects the solid. \n - part = 1 - a part of solid above the surface is to be retained. \n - part = -1 - a part of solid below the surface is to be retained. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования исходного тела. - \en The mode of copying of the source solid. \~ - \param[in] surface - \ru Секущая поверхность. - \en A cutting plane. \~ - \param[in] retainedPart - \ru Направление отсечения. - \en The direction of cutting off. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] closed - \ru Флаг режима отсечения: true - сечем как тело, false - сечем как оболочку. - \en The flag of the cutting off mode: true - cut as a solid, false - cut as a shell. \~ - \param[in] flags - \ru Флаги слияния элементов оболочки. - \en Control flags of shell items merging. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SolidCutting( MbSolid & solid, - MbeCopyMode sameShell, - const MbSurface & surface, - int retainedPart, - const MbSNameMaker & names, - bool closed, - const MbMergingFlags & flags, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Отрезать часть тела выдавленным плоским контуром. - \en Cut a part of a solid off with an extruded planar contour. \~ - \details \ru Отрезать часть тела оболочкой, полученной выдавливанием плоского контура. \n - part = 1 - оставляем часть тела, расположенную сверху поверхности выдавливания. \n - part = -1 - оставляем часть тела, расположенную снизу поверхности выдавливания. \n - \en Cut a part of a solid by a shell of planar contour extrusion. \n - part = 1 - a part of solid above the extrusion surface is to be retained. \n - part = -1 - a part of solid below the extrusion surface is to be retained. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования входного тела. - \en Whether to copy the input solid. \~ - \param[in] place - \ru Система координат образующего контура. - \en The generating contour coordinate system. \~ - \param[in] contour - \ru Образующий контур. - \en The generating contour. \~ - \param[in] direction - \ru Направление выдавливания образующего контура. - \en An extrusion direction of the generating contour. \~ - \param[in] retainedPart - \ru Направление отсечения. - \en The direction of cutting off. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] closed - \ru Флаг режим отсечения: true - сечем как тело, false - сечем как оболочку. - \en The cutting off mode flag: true - cut as a solid, false - cut as a shell. \~ - \param[in] flags - \ru Флаги слияния элементов оболочки. - \en Control flags of shell items merging. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SolidCutting( MbSolid & solid, - MbeCopyMode sameShell, - const MbPlacement3D & place, - const MbContour & contour, - const MbVector3D & direction, - int retainedPart, - const MbSNameMaker & names, - bool closed, - const MbMergingFlags & flags, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Разрезать тело поверхностью. - \en Cut a solid off by a surface. \~ - \details \ru Разрезать тело поверхностью с построением всех отрезанных частей. \n - \en Cut a solid off by a surface, keep all parts of the solid. \n - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования исходного тела. При sameShell != cm_Copy построенные тела нельзя перемещать относительно друг друга. - \en Whether to copy the source solid. Built bodies can not move relative to each other when sameShell != Vm_Copy. \~ - \param[in] surface - \ru Секущая поверхность. - \en A cutting plane. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] closed - \ru Флаг режима отсечения: true - сечем как тело, false - сечем как оболочку. - \en The flag of the cutting off mode: true - cut as a solid, false - cut as a shell. \~ - \param[in] flags - \ru Флаги слияния элементов оболочки. - \en Control flags of shell items merging. \~ - \param[out] result - \ru Построенные тела. - \en The resultant solids. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -DEPRECATE_DECLARE -MATH_FUNC (MbResultType) SolidCutting( MbSolid & solid, - MbeCopyMode sameShell, - const MbSurface & surface, - const MbSNameMaker & names, - bool closed, - const MbMergingFlags & flags, - RPArray & result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Разрезать тело выдавленным плоским контуром. - \en Cut a solid off with an extruded planar contour. \~ - \details \ru Разрезать тело оболочкой, полученной выдавливанием плоского контура, с построением всех отрезанных частей. \n - \en Cut a solid by a shell of planar contour extrusion, keep all parts of the solid. \n - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования исходного тела. При sameShell != cm_Copy построенные тела нельзя перемещать относительно друг друга. - \en Whether to copy the source solid. Built bodies can not move relative to each other when sameShell != Vm_Copy. \~ - \param[in] place - \ru Система координат образующего контура. - \en The generating contour coordinate system. \~ - \param[in] contour - \ru Образующий контур. - \en The generating contour. \~ - \param[in] direction - \ru Направление выдавливания образующего контура. - \en An extrusion direction of the generating contour. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] closed - \ru Флаг режим отсечения: true - сечем как тело, false - сечем как оболочку. - \en The cutting off mode flag: true - cut as a solid, false - cut as a shell. \~ - \param[in] flags - \ru Флаги слияния элементов оболочки. - \en Control flags of shell items merging. \~ - \param[out] result - \ru Построенные тела. - \en The resultant solids. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -DEPRECATE_DECLARE -MATH_FUNC (MbResultType) SolidCutting( MbSolid & solid, - MbeCopyMode sameShell, - const MbPlacement3D & place, - const MbContour & contour, - const MbVector3D & direction, - const MbSNameMaker & names, - bool closed, - const MbMergingFlags & flags, - RPArray & result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Разрезать тело на части. - \en Cut a solid into parts. \~ - \details \ru Разрезать тело на части. \n - \en Cut a solid into parts. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования исходного тела. - \en The mode of copying of the source solid. \~ - \param[in] cuttingParams - \ru Параметры операции. - \en Operation parameters. \~ - \param[out] results - \ru Построенные тела. - \en The resultant solids. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SolidCutting( MbSolid & solid, - MbeCopyMode sameShell, - const MbShellCuttingParams & cuttingParams, - RPArray & results ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать симметричное тело относительно плоскости. - \en Create a symmetric solid relative to a plane. \~ - \details \ru Создать симметричное тело относительно плоскости XY локальной системы координат. \n - Функция создаёт симметричное тело с заданной плоскостью симметрии следующим образом. - Исходное тело режется плоскостью XY локальной системы координат, берётся часть исходного тела, - расположенная сверху режущей плоскости, строится зеркальная копия выбранной части исходного тела - и объединяется с выбранной частью исходного тела. \n - \en Crate a symmetric solid relative to XY-plane of a local coordinate system. \n - The function creates a symmetric solid with the specified plane of symmetry in the following way. - The source solid is cut off by the plane XY of the local coordinate system; a part of the source solid above the cutting plane - is retained. A mirror copy of the chosen part is created - and then is united with the chosen part of the source solid. \n \~ - \param[in] solid - \ru Исходная оболочка. - \en The source shell. \~ - \param[in] sameShell - \ru Режим копирования оболочки. - \en Whether to copy the shell. \~ - \param[in] place - \ru Система координат плоскости симметрии. - \en The symmetry plane coordinate system. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SymmetrySolid( MbSolid & solid, - MbeCopyMode sameShell, - const MbPlacement3D & place, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать зеркальную копию тела относительно плоскости. - \en Create a mirror copy of a solid relative to a plane. \~ - \details \ru Создать зеркальную копию тела относительно плоскости XY локальной системы координат. \n - \en Create a mirror copy of a solid relative to the XY-plane of a local coordinate system. \n \~ - \param[in] solid - \ru Исходная оболочка. - \en The source shell. \~ - \param[in] place - \ru Система координат плоскости симметрии. - \en The symmetry plane coordinate system. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) MirrorSolid( const MbSolid & solid, - const MbPlacement3D & place, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тело с ребром жёсткости. - \en Create a solid with a rib. \~ - \details \ru Создать тело с ребром жёсткости. \n - По заданному контуру функция строит ребро жёсткости и объединяет его с исходным телом. - Сегмент контура с указанным номером устанавливает вектор уклона. \n - \en Create a solid with a rib. \n - The function creates a rib from a given contour and unites it with the source solid. - The segment of the contour with the given number determines the slope vector. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования входного тела. - \en Whether to copy the input solid. \~ - \param[in] place - \ru Система координат образующего контура. - \en The generating contour coordinate system. \~ - \param[in] contour - \ru Формообразующий контур на плоскости XY системы координат place. - \en The generating contour on XY-plane of coordinate system 'place'. \~ - \param[in] index - \ru Номер сегмента в контуре. - \en The segment number in the contour. \~ - \param[in] pars - \ru Параметры ребра жёсткости. - \en Parameters of a rib. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) RibSolid( MbSolid & solid, - MbeCopyMode sameShell, - const MbPlacement3D & place, - const MbContour & contour, - size_t index, - RibValues & pars, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать отдельное ребро жёсткости. - \en Create a separate rib. \~ - \details \ru Создать отдельное ребро жёсткости для исходного тела без приклеивания. \n - \en Create a separate rib for source solid without gluing. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] place - \ru Система координат образующего контура. - \en The generating contour coordinate system. \~ - \param[in] contour - \ru Образующий контур. - \en The generating contour. \~ - \param[in] index - \ru Номер сегмента в контуре. - \en The segment number in the contour. \~ - \param[in] pars - \ru Параметры ребра жёсткости. - \en Parameters of a rib. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) RibElement( const MbSolid & solid, - const MbPlacement3D & place, - MbContour & contour, - size_t index, - RibValues & pars, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Скруглить ребра постоянным радиусом. - \en Fillet edges with a constant radius. \~ - \details \ru Скруглить указанные рёбра тела постоянным радиусом. \n - Функция выполняет замену указанных рёбер исходного тела гранями, - гладко сопрягающими смежные грани указанных рёбер. В поперечном сечении - сопрягающие грани могут иметь форму дуги окружности, эллипса, гиперболы, параболы. \n - \en Fillet the specified edges of the solid with a constant radius. \n - The function performs the replacement of the specified edges of the source solid by faces - smoothly connecting the adjacent faces of the specified edges. The cross-section - of the connecting faces can be of the form of a circle, an ellipse, a hyperbola or a parabola. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования входного тела. - \en Whether to copy the input solid. \~ - \param[in] initCurves - \ru Множество скругляемых ребер тела. - \en A set of edges of the solid to fillet. \~ - \param[in] initBounds - \ru Множество граней для обрезки торцов. - \en A set of faces for trimming of the butt-ends. \~ - \param[in] params - \ru Параметры скругления рёбер. - \en Parameters of edges fillet. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) FilletSolid( MbSolid & solid, - MbeCopyMode sameShell, - RPArray & initCurves, - RPArray & initBounds, - const SmoothValues & params, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Скруглить ребра переменным радиусом. - \en Fillet edges with a variable radius. \~ - \details \ru Скруглить указанные ребра тела переменным радиусом, задаваемым MbEdgeFunction.function. \n - Функция выполняет замену указанных рёбер исходного тела гранями, - гладко сопрягающими смежные грани указанных рёбер. В поперечном сечении - сопрягающие грани могут иметь форму дуги окружности, эллипса, гиперболы, параболы. - Параметры поперечного сечения могут изменяться по заданному закону. \n - \en Fillet the given edges of the solid with a variable radius specified by MbEdgeFunction.function. \n - The function performs the replacement of the specified edges of the source solid by faces - smoothly connecting the adjacent faces of the specified edges. The cross-section - of the connecting faces can be of the form of a circle, an ellipse, a hyperbola or a parabola. - The parameters of the cross-section can vary by the specified law. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования входного тела. - \en Whether to copy the input solid. \~ - \param[in] initCurves - \ru Множество скругляемых ребер тела с функциями изменения радиуса. - \en An array of edges of the solid to fillet together with the radius laws. \~ - \param[in] initBounds - \ru Множество граней для обрезки торцов. - \en A set of faces for trimming of the butt-ends. \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) FilletSolid( MbSolid & solid, - MbeCopyMode sameShell, - SArray & initCurves, - RPArray & initBounds, - const SmoothValues & params, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Скруглить вершины и примыкающие к ней рёбра постоянным радиусом. - \en Create fillets on vertices and the edges adjacent to these vertices with a constant radius. \~ - \details \ru Скруглить вершины и примыкающие к ней рёбра тела постоянным радиусом. \n - В вершинах должно стыковаться три ребра. - Функция выполняет замену указанных вершин и рёбер исходного тела гранями, - гладко сопрягающими смежные грани указанных вершин и рёбер. В поперечном сечении - сопрягающие грани могут иметь форму дуги окружности, эллипса, гиперболы, параболы. \n - \en Create fillets on vertices and the edges of the solid adjacent to these vertices with a constant radius. \n - Three edges must be incident to each vertex. - The functions performs replacement of the specified vertices and edges of the source solid by faces of the solid - smoothly connecting the faces adjacent to the specified vertices and edges. The cross-section - of the connecting faces can be of the form of a circle, an ellipse, a hyperbola or a parabola. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования входного тела. - \en Whether to copy the input solid. \~ - \param[in] initCurves - \ru Множество скругляемых ребер тела. - \en A set of edges of the solid to fillet. \~ - \param[in] initBounds - \ru Множество граней для обрезки торцев. - \en A set of faces for trimming of the butt-ends. \~ - \param[in] initVertices - \ru Множество скругляемых вершин. - \en A set of vertices to fillet. \~ - \param[in] params - \ru Параметры скругления рёбер. - \en Parameters of edges fillet. \~ - \param[in] cornerData - \ru Параметры скругления вершин. - \en Parameters of vertices fillet. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) FilletSolid( MbSolid & solid, - MbeCopyMode sameShell, - RPArray & initCurves, - RPArray & initBounds, - RPArray & initVertices, - const SmoothValues & params, - const CornerValues & cornerData, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Скруглить цепочку граней тела. - \en Create a fillet of faces of the solid. \~ - \details \ru Скруглить указанные грани тела. \n - Функция выполняет замену указанных граней исходного тела гранями, - гладко сопрягающими грани, связанные с указанными гранью. - В поперечном сечении сопрягающие грани имеют форму - дуги окружности, касающейся трёх граней исходного тела. \n - \en Create a fillet on the specified faces of the solid. \n - The function performs replacement of the specified faces of the source solid with faces - smoothly connecting the faces adjacent to the specified faces. - The cross-section of the connecting faces has a form of - a circle arc tangent to three faces of the source solid. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования входного тела. - \en Whether to copy the input solid. \~ - \param[in] initFaces - \ru Набор граней для скругления. - \en A set of faces to fillet. \~ - \param[in] initFacesLeft - \ru Набор ограничивающих слева граней. - \en A set of left bounding faces. \~ - \param[in] initFacesRight - \ru Набор ограничивающих справа граней. - \en A set of right bounding faces. \~ - \param[in] params - \ru Параметры скругления. - \en Parameters of fillet. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) FullFilletSolid( MbSolid & solid, - MbeCopyMode sameShell, - const RPArray & initFaces, - const RPArray & initFacesLeft, - const RPArray & initFacesRight, - const FullFilletValues & params, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить фаски ребер тела. - \en Create chamfers for edges of the solid. \~ - \details \ru Построить фаски указанных ребер тела. \n - Функция выполняет замену указанных рёбер исходного тела гранями фасок. \n - \en Create chamfers for the specified edges of the solid. \n - The function performs replacement of the specified edges of the source solid with faces of chamfers. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования входного тела. - \en Whether to copy the input solid. \~ - \param[in] initCurves - \ru Множество скругляемых ребер тела. - \en An array of edges to create chamfer on. \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ChamferSolid( MbSolid & solid, - MbeCopyMode sameShell, - RPArray & initCurves, - const SmoothValues & params, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать эквидистантное тело. - \en Create а equidistant solid. \~ - \details \ru Создать эквидистантное тело или оболочку. \n - \en Create an offset solid by equidistant faces or create an equidistant shell. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования входного тела. - \en Whether to copy the input solid. \~ - \param[in] offset - \ru Расстояние смещения граней. - \en The equidistant parameter. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) OffsetSolid( MbSolid & solid, - MbeCopyMode sameShell, - double offset, - const MbSNameMaker & names, - MbSolid *& result ); - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тонкостенное тело исключением граней. - \en Create a thin-walled solid by exclusion of faces. \~ - \details \ru Создать тонкостенное тело исключением граней outFaces \n - и приданием одинаковой толщины оставшимся граням \n - или создание незамкнутой оболочки. \n - \en Create a thin-walled solid by exclusion of faces outFaces \n - and supplying the rest of faces with the same thickness \n - or create an open shell. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования входного тела. - \en Whether to copy the input solid. \~ - \param[in] outFaces - \ru Вскрываемые грани тела. - \en Faces of the solid to open. \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] copyFaceAttrs - \ru Копировать атрибуты из исходных граней в эквидистантные. - \en Copy attributes of initial faces to offset faces. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ThinSolid( MbSolid & solid, - MbeCopyMode sameShell, - RPArray & outFaces, - SweptValues & params, - const MbSNameMaker & names, - bool copyFaceAttrs, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать тонкостенное тело исключением граней. - \en Create a thin-walled solid by exclusion of faces. \~ - \details \ru Создать тонкостенное тело исключением граней outFaces \n - и приданием различной толщины оставшимся граням \n - или создание незамкнутой оболочки. \n - \en Create a thin-walled solid by exclusion of faces outFaces \n - and supplying the rest of faces with different thickness \n - or create an open shell. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования входного тела. - \en Whether to copy the input solid. \~ - \param[in] outFaces - \ru Вскрываемые грани тела. - \en Faces of the solid to open. \~ - \param[in] offFaces - \ru Множество граней, для которых заданы индивидуальные значения толщин. - \en An array of faces for which the individual values of thickness are specified. \~ - \param[in] offDists - \ru Множество индивидуальных значений толщин (должен быть синхронизирован с массивом offFaces). - \en An array of individual values of thickness (must be synchronized with the array 'offFaces'). \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] copyFaceAttrs - \ru Копировать атрибуты из исходных граней в эквидистантные. - \en Copy attributes of initial faces to offset faces. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling - \warning \ru Операция ПОЛНОЦЕННО НЕ РЕАЛИЗОВАНА! - \en The operation is NOT COMPLETELY IMPLEMENTED! \~ -*/ -// --- -MATH_FUNC (MbResultType) ThinSolid( MbSolid & solid, - MbeCopyMode sameShell, - RPArray & outFaces, - RPArray & offFaces, - SArray & offDists, - SweptValues & params, - const MbSNameMaker & names, - bool copyFaceAttrs, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Выполнить разбиение граней оболочки. - \en Perform splitting of a shell faces. \~ - \details \ru Выполнить разбиение граней оболочки поверхностями выдавливания контуров. \n - Функция создаёт копию тела и разбивает указанные грани поверхностями выдавливания контуров. \n - \en Perform splitting of a shell faces with surfaces of the contours extrusion. \n - The function creates a copy of a solid and splits the specified faces with surfaces of the contours extrusion. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования входного тела. - \en Whether to copy the input solid. \~ - \param[in] spPlace - \ru Система координат контуров. - \en The coordinate system of the contours. \~ - \param[in] spType - \ru Направление вытягивания. - \en The extrusion direction. \~ - \param[in] spContours - \ru Контура разбиения. - \en The contours of splitting. \~ - \param[in] spSame - \ru Использовать оригиналы или копии кривых. - \en Whether to use the originals or copies of curves. \~ - \param[in] selFaces - \ru Выбранные грани входного тела. - \en The chosen faces of the input solid. \~ - \param[in] flags - \ru Флаги слияния элементов оболочки. - \en Control flags of shell items merging. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SplitSolid( MbSolid & solid, - MbeCopyMode sameShell, - const MbPlacement3D & spPlace, - MbeSenseValue spType, - const RPArray & spContours, - bool spSame, - RPArray & selFaces, - const MbMergingFlags & flags, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Выполнить разбиение граней оболочки. - \en Perform splitting of a shell faces. \~ - \details \ru Выполнить разбиение граней оболочки пространственными кривыми, поверхностями и оболочками. \n - Функция создаёт копию тела и разбивает указанные грани пространственными кривыми, поверхностями и оболочками. \n - Пространственные элементы разбиения не должны иметь полных или частичных наложений, а также - самопересечений. \n - \en Perform splitting of the shell faces with space curves, surfaces and shells. \n - The function creates a copy of the solid and splits the specified faces with space curves, surfaces and shells. \n - The spatial elements of a splitting must not have complete or partial overlaps, and also self-intersections. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования входного тела. - \en Whether to copy the input solid. \~ - \param[in] spItems - \ru Пространственные элементы разбиения. - \en A spatial elements of splitting. \~ - \param[in] spSame - \ru Использовать оригиналы или копии кривых. - \en Whether to use the originals or copies of curves. \~ - \param[in] selFaces - \ru Выбранные грани входного тела. - \en The chosen faces of the input solid. \~ - \param[in] flags - \ru Флаги слияния элементов оболочки. - \en Control flags of shell items merging. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SplitSolid( MbSolid & solid, - MbeCopyMode sameShell, - const RPArray & spItems, - bool spSame, - RPArray & selFaces, - const MbMergingFlags & flags, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Уклонить указанные грани тела. - \en Slope the specified faces of the solid. \~ - \details \ru Уклонить указанные грани тела от нейтральной изоплоскости на заданный угол. \n - \en Slope the specified faces of the solid at the specified angle relative to the neutral isoplane. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования входного тела. - \en Whether to copy the input solid. \~ - \param[in] neutralPlace - \ru Нейтральная плоскость. - \en The neutral plane. \~ - \param[in] angle - \ru Угол уклона. - \en The slope angle. \~ - \param[in] faces - \ru Уклоняемые грани во входном теле. - \en The faces of input solid to be sloped. \~ - \param[in] fp - \ru Признак захвата граней, гладко стыкующихся с уклоняемыми гранями. - \en Whether to capture the faces smoothly connected with the faces being sloped. \~ - \param[in] reverse - \ru Флаг обратного направления уклона. - \en Whether to slope in the reverse direction. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) DraftSolid( MbSolid & solid, - MbeCopyMode sameShell, - const MbPlacement3D & neutralPlace, - double angle, - const RPArray & faces, - MbeFacePropagation fp, - bool reverse, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Выполнить объединение пересекающихся тел. - \en Perform the union of intersecting solids. \~ - \details \ru Выполнить объединение пересекающихся тел и булеву операцию oType с телом solid, если оно не нулевое: \n - bo_Union - объединение, \n - bo_Intersect - пересечение, \n - bo_Difference - вычитание. \n - Если флаг проверки пересечения checkIntersect == true, то выполняется проверка на пересечение тел - и булева операция объединения пересекающихся тел заданного множества в одно тело. В противном случае - объединение тел заданного множества выполняется простым перекладыванием граней всех тел в одно новое тело. \n - Если флаг регулярности множества тел isArray == true, то тела множества расположены в узлах - прямоугольной или круговой сетки и позиции тел заданы в именах граней. \n - \en Perform the union of intersecting solids and a boolean operation oType with solid 'solid' if it is not null: \n - bo_Union - union, \n - bo_Intersect - intersection, \n - bo_Difference - subtraction. \n - If the flag of intersection check checkIntersect == true, check for solids intersection is performed - and the boolean operation of union the intersection solids of the specified set into one solid is performed. Otherwise - the union of solids from the given set is performed by simple moving the faces of all the solids into a new solid. \n - If the flag of solid set regularity isArray == true, the solids are located at the nodes - of rectangular or circular grid and positions of solids are specified in the names of faces. \n \~ - \param[in] solid - \ru Тело. - \en A solid. \~ - \param[in] sameShell - \ru Режим копирования тела. - \en Whether to copy the solid. \~ - \param[in] solids - \ru Множество тел. - \en An array of solids. \~ - \param[in] sameShells - \ru Режим копирования тел. - \en Whether to copy the solids. \~ - \param[in] oType - \ru Тип булевой операции между телом и массивом тел. - \en The type of the boolean operation for the solid and the set of solids. \~ - \param[in] checkIntersect - \ru Проверять пересечение тел. - \en Whether to check the solids intersection. \~ - \param[in] mergeFaces - \ru Сливать подобные грани. - \en \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] isArray - \ru Флаг регулярности множества тел. - \en A flag of solid set regularity. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \param[out] notGluedSolids - \ru Множество тел, которые не получилось приклеить. - \en An array of solids which was not glued. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) UnionResult( MbSolid * solid, - MbeCopyMode sameShell, - RPArray & solids, - MbeCopyMode sameShells, - OperationType oType, - bool checkIntersect, - bool mergeFaces, - const MbSNameMaker & names, - bool isArray, - MbSolid *& result, - RPArray * notGluedSolids = NULL ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать одно тело из присланных тел. - \en Create a solid from the specified solids. \~ - \details \ru Создать тело с объединением или без объединения пересекающихся тел. \n - Если флаг проверки пересечения checkIntersect == true, то выполняется проверка на пересечение тел - и булева операция объединения пересекающихся тел заданного множества в одно тело. В противном случае - объединение тел заданного множества выполняется простым перекладыванием граней всех тел в одно новое тело. \n - Если флаг регулярности множества тел isArray == true, то тела множества расположены в узлах - прямоугольной или круговой сетки и позиции тел заданы в именах граней. \n - \en Create a solid with or without union of the intersecting solids. \n - If the flag of intersection check checkIntersect == true, check for solids intersection is performed - and the boolean operation of union the intersection solids of the specified set into one solid is performed. Otherwise - the union of solids from the given set is performed by simple moving the faces of all the solids into a new solid. \n - If the flag of solid set regularity isArray == true, the solids are located at the nodes - of rectangular or circular grid and positions of solids are specified in the names of faces. \n \~ - \param[in] solids - \ru Множество тел. - \en An array of solids. \~ - \param[in] sameShells - \ru Режим копирования тел. - \en Whether to copy the solids. \~ - \param[in] checkIntersect - \ru Проверять пересечение тел. - \en Whether to check the solids intersection. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] isArray - \ru Флаг регулярности множества тел. - \en A flag of solid set regularity. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \param[out] notGluedSolids - \ru Множество тел, которые не получилось приклеить. - \en An array of solids which was not glued. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) UnionSolid( RPArray & solids, - MbeCopyMode sameShells, - bool checkIntersect, - const MbSNameMaker & names, - bool isArray, - MbSolid *& result, - RPArray * notGluedSolids = NULL ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать одно тело из присланных тел. - \en Create a solid from the specified solids. \~ - \details \ru Создать одно тело из присланных тел, не меняя их. \n - Объединение тел заданного множества выполняется простым перекладыванием - граней всех тел в одно новое тело. \n - \en Create a solid from the specified solids without the modification of the given solids. \n - The union of solids from the specified set is performed by simple moving - the faces of all the solids into a new solid. \n \~ - \param[in] solids - \ru Множество тел. - \en An array of solids. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) UnionSolid( const RPArray & solids, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Разделить тело на отдельные части. - \en Split the solid into separate parts. \~ - \details \ru Если исходное тело распадается на части, то наибольшая часть остаётся в исходном теле, - а остальные части части будут сложены в присланный контейнер тел. \n - Если флаг сортировки sort == true, то в исходном теле останется часть с наибольшим габаритом, - а отделённые части будут сортированы по убыванию габарита. В противном случае в исходном теле - останется часть, топологически связанная с первой гранью, а отделённые части будут сортированы - по номеру начальной грани в исходном теле. \n - \en If the source solid is decomposed, the greatest part remains in the source solid, - and the other parts are put into the given array of solids. \n - If 'sort' == 'true', the part with the greatest bounding box will remain in the source solid, - and separated parts will be sorted by bounding box size in descending order. Otherwise a part topologically connected with the first face will remain in the source solid - and the separated parts will be sorted - by the number of the initial face in the source solid. \n \~ - \param[in,out] solid - \ru Исходное модифицируемое тело. - \en The source solid to be modified. \~ - \param[out] parts - \ru Отделённые части тела. - \en The separated parts of the solid. \~ - \param[in] sort - \ru Сортировать по убыванию габарита. - \en Whether to sort by the bounding box size in descending order. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \return \ru Возвращает количество отделенных частей. - \en Returns the number of the separated parts. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (size_t) DetachParts( MbSolid & solid, - RPArray & parts, - bool sort, - const MbSNameMaker & names ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Разделить тело на отдельные части. - \en Split the solid into separate parts. \~ - \details \ru Если исходное тело распадается на части, то все его части будут сложены в присланный контейнер тел. \n - Исходное тело остаётся неизменённым. \n - \en If the source solid is decomposed, all the parts of the solid will be put into the given array of solids. \n - The source solid remains unchanged. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[out] parts - \ru Части тела. - \en The parts of the solid. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \return \ru Возвращает количество созданных частей. - \en Returns the number of created parts. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (size_t) CreateParts( const MbSolid & solid, - RPArray & parts, - const MbSNameMaker & names ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить оболочку тела по поверхности и толщине. - \en Create a shell of the solid from a surface and a thickness. \~ - \details \ru Выполнить построение тела путём придания толщины заданной поверхности. \n - \en Create a solid by supplying the surface with a thickness. \n \~ - \param[in] surface - \ru Поверхность. - \en The surface. \~ - \param[in] faceSense - \ru Ориентация нормали поверхности. - \en The surface normal orientation. \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] name - \ru Основное простое имя. - \en The main simple name. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ThinSolid( const MbSurface & surface, - bool faceSense, - SweptValues & params, - const MbSNameMaker & names, - SimpleName name, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Cоздать отверстие, карман, фигурный паз в теле. - \en Create a hole, a pocket, a groove in the solid. \~ - \details \ru Cоздать отверстие, карман, фигурный паз в теле или создать cверло, бобышку, если solid==NULL. \n - \en Create a hole, a pocket, a groove in the solid or create a drill, a boss if 'solid' == NULL. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Режим копирования тела. - \en Whether to copy the solid. \~ - \param[in] place - \ru Местная система координат для операции. - \en A local coordinate system for the operation. \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) HoleSolid( MbSolid * solid, - MbeCopyMode sameShell, - const MbPlacement3D & place, - const HoleValues & params, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Выделить в отдельное тело указанную часть распадающегося на части тела. - \en Extract the specified part of decomposing solid to a separate solid. \~ - \details \ru Создать тело, из указанной части тела, распадающегося на части. - Исходное тело должно состоять из отдельных частей. \n - \en Create a solid from the specified part of decomposing solid. - The source solid should consist of separate parts. \n \~ - \param[in] solid - \ru Разделяемое на части тело. - \en A decomposing solid. \~ - \param[in] id - \ru Номер выбранной части тела - \en The number of selected part of the solid. \~ - \param[in] path - \ru Идентификатор для выбранной части. - \en An identifier for the selected part. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in,out] partIndices - \ru Индексы частей тела. - \en Indices of the parts of the solid. \~ - \param[out] result - \ru Построенная оболочка (тело). - \en The resultant shell (solid). \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ShellPart( const MbSolid & solid, - size_t id, - const MbPath & path, - const MbSNameMaker & names, - MbPartSolidIndices & partIndices, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Размножить тело. - \en Duplicate the solid. \~ - \details \ru Размножить тело согласно параметрам и объединить копии в одно тело.\n - \en Duplicate the solid by the parameters and unite copies in one solid. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] params - \ru Параметры размножения. - \en The parameters of duplication. \~ - \param[in] names - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[out] result - \ru Результирующее тело. - \en The result solid. \~ - \return \ru Возвращает код результата операции. - \en . \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) DuplicationSolid( const MbSolid & solid, - const DuplicationValues & params, - const MbSNameMaker & names, - MbSolid *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Cоздать одно тело слиток из присланных объектов. - \en Create an ingot solid from the specified objects. \~ - \details \ru Cоздать одно тело слиток из присланных объектов. \n - Среди присланных объектов используются тела, вставки тел и сборки тел, из которых строится одно тело, - которое по внешности совпадает с присланными телами и служит их упрощенным заменителем по внешним параметрам. \n - \en Create an ingot solid from the specified solids without the modification of the given solids. \n - Among the objects sent using the body, insert bodies and assembling bodies of which is built the same body, - which in appearance coincides with the bodies had been sent and serves as a substitute for their simplistic external parameters. \n \~ - \param[in] solids - \ru Множество тел. - \en An array of solids. \~ - \param[in] names - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] makeCopy - \ru Флаг копирования тел перед использованием: true - копировать, false - не копировать. - \en The flag of the copying solid before using: true - copy solid, false - not copy. \~ - \param[out] result - \ru Построенное тело. - \en The resultant solid. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Solid_Modeling -*/ -// --- -MATH_FUNC (MbResultType) IngotSolid( RPArray & solids, - bool makeCopy, - const MbSNameMaker & names, - MbSolid *& result ); - - -#endif // __ACTION_SOLID_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Функции создания тел, операции с телами. + \en Functions for creation of solids, operations on solids. \~ + \details \ru Процесс построения тел в геометрическом моделировании похож на процесс + изготовления моделируемого объекта. Сначала создаются тела простой формы, а далее + выполняется набор действий, позволяющих из тел простой формы получить более сложные тела. + При необходимости создаются вспомогательные объекты. Редактировать и создавать подобные + тела можно путём изменения параметров с последующим повторением процесса построения тел.\n + Все функции создания тел содержат в качестве входного параметр MbSNameMaker, + обеспечивающий именование граней, рёбер и вершин. + Первым параметром конструктора генератора имён MbSNameMaker служит главное имя операции. + По главному имени можно определить, в какой функции рождена та или иная грань, ребро, вершина. + Главное имя выдаёт метод GetMainName().\n + \en The process of solids creation in geometric modeling is similar to the process + of the modeled object manufacturing. Firstly solids of a simple form are created, and then + a set of operations are performed to obtain a more complex solids from solids of a simple form. + Auxiliary objects are created if necessary. The similar solids can be edited and created + by modifying of the parameters and further repeating the process of the solids creation.\n + All of the function contain input parameter MbSNameMaker, + providing the naming of faces, edges and vertices. + The first parameter to the constructor MbSNameMaker is the main name of the function. + You can determine which function is born one or the other face, edge, vertex by main name. + GetMainName() gives the main name of the function (face.GetMainName(), edge.GetMainName(), vertex.GetMainName()).\~ +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ACTION_SOLID_H +#define __ACTION_SOLID_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class MATH_CLASS MbCurve; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbSurface; +class MATH_CLASS MbCurveEdge; +class MATH_CLASS MbFace; +class MATH_CLASS MbSolid; +class MATH_CLASS MbItem; +class MATH_CLASS MbSNameMaker; +class MATH_CLASS MbPartSolidIndices; +class MATH_CLASS MbSpine; +class MATH_CLASS MbMesh; +class MATH_CLASS MbGrid; +class MATH_CLASS MbCollection; +class MATH_CLASS IProgressIndicator; + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать элементарное тело. + \en Create an elementary solid. \~ + \details \ru Создать одно из элементарных тел по заданным точкам и типу: \n + solidType = et_Sphere - шар (3 точки), \n + solidType = et_Torus - тор (3 точки), \n + solidType = et_Cylinder - цилиндр (3 точки), \n + solidType = et_Cone - конус (3 точки), \n + solidType = et_Block - блок (4 точки), \n + solidType = et_Wedge - клин (4 точки), \n + solidType = et_Prism - призма (количество вершин основания+1 точка), \n + solidType = et_Pyramid - пирамида (количество вершин основания+1 точка), \n + solidType = et_Plate - плита (4 точки), + solidType = et_Icosahedron - икосаэдр (3 точки), \n + solidType = et_Polyhedron - многогранник (3 точки), \n + solidType = et_Tetrapipe - тетра-труба (3 точки), \n + solidType = et_Octapipe - окта-труба (3 точки). \n + \en Create one of elementary solids from the specified points and type: \n + solidType = et_Sphere - a sphere (3 points), \n + solidType = et_Torus - a torus (3 points), \n + solidType = et_Cylinder - a cylinder (3 points), \n + solidType = et_Cone - a cone (3 points), \n + solidType = et_Block - a block (4 points), \n + solidType = et_Wedge - a wedge (4 points), \n + solidType = et_Prism - a prism (points count is equal to the base vertices count + 1), \n + solidType = et_Pyramid - a pyramid (points count is equal to the base vertices count + 1), \n + solidType = et_Plate - a plate (4 points), \n + solidType = et_Icosahedron - an icosahedron (3 points), \n + solidType = et_Polyhedron - a polyhedron (3 points), \n + solidType = et_Tetrapipe - a tetra-pipe (3 points), \n + solidType = et_Octapipe - an octa-pipe (3 points). \n \~ + \param[in] points - \ru Набор точек. \n + points[0] определяет начало локальной системы координат. \n + Для сферы, тора, цилиндра и конуса: \n + points[1] определяет направление оси Z локальной системы. \n + points[2] определяет направление оси X локальной системы. \n + Для блока, клина и плиты: \n + points[1] определяет направление оси X локальной системы. \n + points[2] определяет направление оси Y локальной системы. \n + Кроме того, \n + points[1] определяет высоту цилиндра, высоту конуса, + большой радиус тора, длину блока, длину клина. \n + points[2] определяет радиус цилиндра, угол конуса как угол между векторами v1(points[0],points[1]) и v2(points[0],points[2]), + радиус сферы, малый радиус тора, ширину блока, ширину клина. \n + В случае конуса вектора v1(points[0],points[1]) и v2(points[0],points[2]) не должны быть параллельны или перпендикулярны друг другу. \n + Последняя точка определяет высоту блока, клина, плиты, вершину пирамиды. + \en A point set. \n + points[0] determines a local coordinate system origin. \n + For a sphere, a torus, a cylinder or a cone: \n + points[1] determines the direction of Z-axis of a local coordinate system. \n + points[2] determines the direction of X-axis of a local coordinate system. \n + For a block, a plate or a wedge: \n + points[1] determines the direction of X-axis of a local coordinate system. \n + points[2] determines the direction of Y-axis of a local coordinate system. \n + Also, \n + points[1] determines the height of a cylinder or a cone, + the major radius of a torus, the length of a block or a wedge. \n + points[2] determines the radius of a cylinder, cone angle as angle between vectors v1(points[0],points[1]) and v2(points[0],points[2]), + radius of a sphere, the minor radius of a torus, the width of a block or a wedge. \n + In the case of the cone of the vector v1(points [0], points [1]) and v2(points [0], points [2]) must not be parallel or perpendicular to each other. \n + The last point determines the height of a block, a wedge or a plate, the vertex of a pyramid. \~ + \param[in] solidType - \ru Тип создаваемого тела. + \en The solid type. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ElementarySolid( const SArray & points, + ElementaryShellType solidType, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело по поверхности. + \en Create a solid from a surface. \~ + \details \ru Создать тело по элементарной поверхности. \n + Допускается только тип поверхности - цилиндр, конус, сфера, тор. + \en Create a solid from an elementary surface. \n + The only acceptable surface types are cylinder, cone, sphere, torus. \~ + \param[in] surface - \ru Поверхность. + \en The surface. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ElementarySolid( const MbSurface & surface, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело на основе полигональной модели. + \en Create a solid on the basis of a polygonal geometric object. \~ + \details \ru Создать тело #MbSolid на основе полигональной модели #MbMesh. \n + \en Create a solid #MbSolid on the basis of a polygonal geometric object #MbMesh. \n \~ + \param[in] mesh - \ru Полигональная модель. + \en The polygonal geometric object. \~ + \param[in] params - \ru Параметры операции. + \en Operation parameters. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) MeshSolid( const MbMesh & mesh, + const GridsToShellValues & params, + const MbSNameMaker & names, + MbSolid *& result, + IProgressIndicator * prog = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело на основе триангуляции. + \en Create a solid on the basis of a triangulation. \~ + \details \ru Создать тело #MbSolid на основе триангуляции #MbGrid. \n + \en Create a solid #MbSolid on the basis of a triangulation #MbGrid. \n \~ + \param[in] grid - \ru Полигональная модель. + \en The polygonal geometric object. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) GridSolid( const MbGrid & grid, + const MbSNameMaker & names, + MbSolid *& result, + IProgressIndicator * prog = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело на основе коллекции элементов. + \en Create a solid on the basis of elements. \~ + \details \ru Создать тело #MbSolid на основе коллекции элементов #MbCollection. \n + \en Create a solid #MbSolid on the basis of elements #MbCollection. \n \~ + \param[in] grid - \ru Коллекция элементов. + \en The elements collection. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) CollectionSolid( const MbCollection & grid, + const MbSNameMaker & names, + MbSolid *& result, + IProgressIndicator * progBar = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело c заданной оболочкой. + \en Create a solid with a given shell. \~ + \details \ru Создать тело без истории построения с заданной оболочкой. \n + \en Create a solid with a given shell without a history. \n \~ + \param[in] shell - \ru Оболочка. + \en A shell. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \return \ru Возвращает тело без истории. + \en Returns a solid without the history. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbSolid *) CreateSolid( MbFaceShell & shell, + const MbSNameMaker & names ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Рассчитать глубину выдавливания или угол вращения. + \en Compute the extrusion depth or the rotation angle. \~ + \details \ru Рассчитать value - глубину выдавливания или угол вращения (0.0 : M_PI2) + для последующего построения тела путем выдавливания или вращения образующей кривой. \n + \en Compute 'value' - the extrusion depth or the rotation angle (0.0 : M_PI2) + for the further construction of a solid by extrusion or revolution of the generating curve. \n \~ + \param[in] sweptData - \ru Данные об образующей. + \en The generating curve data. \~ + \param[in] axis - \ru Ось вращения. + \en Rotation axis. \~ + \param[in] direction - \ru Направление выдавливания. + \en An extrusion direction. \~ + \param[in] rotation - \ru Вращение или выдавливание. + \en Rotation or extrusion. \~ + \param[in] operationDirection - \ru Вперед\назад. + \en Forward or backward direction. \~ + \param[in] point - \ru Точка, до которой требуется вращать или выдавливать поверхность. + \en The point to rotate or extrude the surface up to. \~ + \param[out] value - \ru Глубина выдавливания или угол вращения. + \en The extrusion depth or the rotation angle. \~ + \return \ru Возвращает true, если расчет выполнен успешно. + \en Returns true if the value has been successfully calculated. \~ + \warning \ru Вспомогательная функция операций ExtrusionSolid, RevolutionSolid, ExtrusionResult и RevolutionResult. + \en An auxiliary function of operations ExtrusionSolid, RevolutionSolid, ExtrusionResult and RevolutionResult. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (bool) GetSweptValue( const MbSweptData & sweptData, + const MbAxis3D & axis, + const MbVector3D & direction, + const bool rotation, + const bool operationDirection, + const MbCartPoint3D & point, + double & value ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Получить начальное приближение для нахождения образа при вращении/выдавливании. + \en Get the initial approximation for image calculation while rotating/extruding. \~ + \details \ru Вычислить положение образа точки образующей кривой на поверхности для последующего + построения тела путем выдавливания или вращения образующей кривой до заданной поверхности. \n + \en Compute the position of a generating curve point image on a surface for further + solid construction by extrusion or revolution of the generating curve up to the specified surface. \n \~ + \param[in] generatrix - \ru Кривая. + \en The curve. \~ + \param[in] surface - \ru Поверхность, до которой строим операцию. + \en The surface to construct up to. \~ + \param[in] direction - \ru Направление выдавливания. + \en An extrusion direction. \~ + \param[in] axis - \ru Ось вращения. + \en Rotation axis. \~ + \param[in] rotation - \ru Вращение (true) или выдавливание (false) + \en Rotation (true) or extrusion (false) \~ + \param[out] imagePosition - \ru Точка образа на поверхности. + \en The image point on the surface. \~ + \param[out] resType - \ru Код результата операции. + \en Operation result code. \~ + \warning \ru Вспомогательная функция операций ExtrusionSolid, RevolutionSolid, ExtrusionResult и RevolutionResult. + \en An auxiliary function of operations ExtrusionSolid, RevolutionSolid, ExtrusionResult and RevolutionResult. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (void) GetSweptImagePosition( const MbCurve3D & generatrix, + const MbSurface & surface, + const MbVector3D & direction, + const MbAxis3D & axis, + const bool rotation, + MbCartPoint & imagePosition, + MbResultType & resType ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти расстояния/углы от образующей до поверхности при вращении/выдавливании. + \en Calculate the distances/angles from generating curve to the surface while rotating/extruding. \~ + \details \ru Вычислить глубины выдавливания в прямом и обратном направлениях или углы вращения + в прямом и обратном направлениях для последующего построения тела путем выдавливания или вращения + образующей кривой до заданной поверхности, а также и габарит образа кривой. \n + \en Calculate the extrusion depths in forward and backward directions or the rotating angles + in forward and backward directions for further solid construction by extrusion or revolution + of the generating curve up to the specified surface; and also calculate the bounding box of the curve image. \n \~ + \param[in] surface - \ru Поверхность, до которой строим операцию. + \en The surface to construct up to. \~ + \param[in] curve - \ru Образующая кривая. + \en The generating curve. \~ + \param[in] direction - \ru Направление выдавливания. + \en An extrusion direction. \~ + \param[in] axis - \ru Ось вращения. + \en Rotation axis. \~ + \param[in] rotation - \ru Вращение (true) или выдавливание (false). + \en Revolution (true) or extrusion (false). \~ + \param[in] operationDirection - \ru Направление движения: вперед (true) или назад (false). + \en The motion direction: forward (true) or backward (false). \~ + \param[out] imagePosition - \ru Точка на части поверхности, в которой лежит образ. + \en A point on a surface part that contains the image. \~ + \param[out] range - \ru Расстояния до поверхности в обратном и прямом направлениях. + \en The distance to surface in the backward and the forward directions. \~ + \param[out] rectOnSurface - \ru Габарит образа на поверхности. + \en The bounding box of image on the surface. \~ + \param[out] resType - \ru Код результата операции. + \en Operation result code. \~ + \warning \ru Вспомогательная функция операций ExtrusionSolid, RevolutionSolid, ExtrusionResult и RevolutionResult. + \en An auxiliary function of operations ExtrusionSolid, RevolutionSolid, ExtrusionResult and RevolutionResult. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (void) GetRangeToSurface( const MbSurface & surface, + const MbCurve3D & curve, + const MbVector3D & direction, + const MbAxis3D & axis, + const bool rotation, + const bool operationDirection, + const MbCartPoint & imagePosition, + double range[2], + MbRect & rectOnSurface, + MbResultType & resType ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Вычислить площадь проекции кривой на виртуальную координатную плоскость. + \en Compute the area of a curve projection onto a virtual coordinate plane. \~ + \details \ru Вычислить площадь проекции кривой на виртуальную координатную плоскость \n + для определения ориентации образующей кривой в оболочке выдавливания и вращения. \n + Параметры direction и axis определяют направление выдавливания и ось вращения, + а параметр rotation определяет тип операции: \n + - выдавливание, вычисляется площадь проекции на плоскость XOY, \n + - вращение, вычисляется площадь "проекции" на "плоскость" ROZ. + Для незамкнутой кривой в расчет добавляется "замыкание отрезком". + \en Compute the area of a curve projection onto a virtual coordinate plane \n + to determine the generating curve orientation in the shell of extrusion and revolution. \n + Parameters 'direction' and 'axis' determine the extrusion direction and the rotation axis, + and parameter 'rotation' determines the operation type: \n + - extrusion, the area of projection onto the plane XOY is computed, \n + - revolution, the area of "projection" to the "plane" ROZ is computed. + For an open curve "enclosure by a segment" is considered. \~ + \param[in] curve - \ru Кривая. + \en The curve. \~ + \param[in] axis - \ru Ось вращения. + \en Rotation axis. \~ + \param[in] direction - \ru Направление выдавливания. + \en An extrusion direction. \~ + \param[in] rotation - \ru Вращение (true) или выдавливание (false). + \en Revolution (true) or extrusion (false). \~ + \return \ru Возвращает площадь проекции. + \en Returns the projection area. \~ + \warning \ru Вспомогательная функция операций ExtrusionSolid, RevolutionSolid, ExtrusionResult и RevolutionResult. + \en An auxiliary function of operations ExtrusionSolid, RevolutionSolid, ExtrusionResult and RevolutionResult. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (double) AreaSign( const MbCurve3D & curve, + const MbAxis3D & axis, + const MbVector3D & direction, + bool rotation ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Определить ориентацию секущей грани относительно тела выдавливания/вращения. + \en Determine the orientation of a cutting surface relative to the extrusion/revolution solid. \~ + \details \ru Определить ориентацию секущей поверхности относительно тела, которое будет строиться + путём выдавливания или вращения образующей кривой до заданной поверхности. \n + \en Determine the orientation of a cutting surface relative to the solid which is to be constructed + by extrusion or revolution the generation curve up to the specified surface. \n \~ + \param[in] cuttingSurface - \ru Поверхность для анализа. + \en A surface to analyze. \~ + \param[in] imagePosition - \ru Место образа на поверхности. + \en An image location on the surface. \~ + \param[in] curve - \ru Образующая кривая. + \en The generating curve. \~ + \param[in] direction - \ru Направление выдавливания. + \en An extrusion direction. \~ + \param[in] axis - \ru Ось вращения. + \en Rotation axis. \~ + \param[in] rotation - \ru Вращение (true) или выдавливание (false). + \en Revolution (true) or extrusion (false). \~ + \param[in] operationDirection - \ru Направление движения: вперед (true) или назад (false). + \en The motion direction: forward (true) or backward (false). \~ + \param[out] relativeSense - \ru Ориентация поверхности по отношению к операции. + \en The surface orientation relative to the operation. \~ + \param[out] resType - \ru Код результата операции. + \en Operation result code. \~ + \warning \ru Вспомогательная функция операций ExtrusionSolid, RevolutionSolid, ExtrusionResult и RevolutionResult. + \en An auxiliary function of operations ExtrusionSolid, RevolutionSolid, ExtrusionResult and RevolutionResult. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (void) AnalyzeSurfaceRelationToSweptOperation( const MbSurface & cuttingSurface, + const MbCartPoint & imagePosition, + const MbCurve3D & curve, + const MbVector3D & direction, + const MbAxis3D & axis, + const bool rotation, + bool operationDirection, + bool & relativeSense, + MbResultType& resType ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти сегменты контура на поверхности, соответствующие швам и полюсам. + \en Find contour segments corresponding to the seams and poles. \~ + \details \ru Найти сегменты контура на поверхности, соответствующие швам и полюсам. \n + \en Find contour segments corresponding to the seams and poles. \n \~ + \param[in] surface - \ru Поверхность. + \en Surface. \~ + \param[in] contour - \ru Контур. + \en Contour. \~ + \param[out] seamsAndPoles - \ru Номера сегментов, соответствующих швам и полюсам. + \en Segment numbers corresponding to the seams and poles. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (void) FindPolesAndSeamsInContour( const MbSurface & surface, + const MbContour & contour, + c3d::IndicesVector & seamsAndPoles ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело выдавливания. + \en Create an extrusion solid. \~ + \details \ru Создать тело выдавливания. \n + solid1 и solid2 используются с опцией "До ближайших граней" этих тел. \n + \en Create an extrusion solid. \n + solid1 and solid2 are used with option "Up to the closest faces" of these solids. \n \~ + \param[in] sweptData - \ru Данные об образующей кривой. + \en The generating curve data. \~ + \param[in] direction - \ru Направление выдавливания. + \en An extrusion direction. \~ + \param[in] solid1 - \ru До ближайших граней этого тела в прямом направлении. + \en Up to the closest faces of this solid in the forward direction. \~ + \param[in] solid2 - \ru До ближайших граней этого тела в обратном направлении. + \en Up to the closest faces of this solid in the backward direction. \~ + \param[in] checkIntersection - \ru Объединять тела solid1 и solid2 с проверкой пересечения. + \en Whether to union the solids solid1 and solid2 with the check for intersections. \~ + \param[in, out] params - \ru Параметры выдавливания. + Возвращают информацию для построения элементов массива операций до поверхности. + \en The extrusion parameters. + Returns the information for construction of the up-to-surface operation array elements. \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] contoursNames - \ru Именователи сегментов образующего контура. + \en An objects defining a names of the generating contour segments. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ExtrusionSolid( const MbSweptData & sweptData, + const MbVector3D & direction, + const MbSolid * solid1, + const MbSolid * solid2, + bool checkIntersection, + const ExtrusionValues & params, + const MbSNameMaker & operNames, + const RPArray & contoursNames, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело вращения. + \en Create a solid of revolution. \~ + \details \ru Создать тело вращения по данным об образующей. \n + \en Create a solid of revolution by the generating curve data. \n \~ + \param[in] sweptData - \ru Данные об образующей кривой. + \en The generating curve data. \~ + \param[in] axis - \ru Ось вращения. + \en Rotation axis. \~ + \param[in, out] params - \ru Параметры вращения. + Возвращают информацию для построения элементов массива операций до поверхности. + \en The revolution parameters. + Returns the information for construction of the up-to-surface operation array elements. \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] contoursNames - \ru Именователи сегментов образующего контура. + \en An objects defining a names of the generating contour segments. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) RevolutionSolid( const MbSweptData & sweptData, + const MbAxis3D & axis, + const RevolutionValues & params, + const MbSNameMaker & operNames, + const RPArray & contoursNames, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кинематическое тело. + \en Create a sweeping solid. \~ + \details \ru Создать кинематическое тело путем движения образующей кривой вдоль направляющей кривой. \n + \en Create a sweeping solid by moving the generating curve along the guide curve. \n \~ + \param[in] sweptData - \ru Данные об образующей. + \en The generating curve data. \~ + \param[in] spine - \ru Направляющая кривая. + \en The spine curve. \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] contoursNames - \ru Именователь контуров образующей. + \en An object defining the names of generating curve contours. \~ + \param[in] spineNames - \ru Именователь направляющей. + \en An object defining the name of a guide curve. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) EvolutionSolid( const MbSweptData & sweptData, + const MbCurve3D & spine, + const EvolutionValues & params, + const MbSNameMaker & operNames, + const RPArray & contoursNames, + const MbSNameMaker & spineNames, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кинематическое тело. + \en Create a sweeping solid. \~ + \details \ru Создать кинематическое тело путем движения образующей кривой вдоль направляющей кривой c дополнительной информацией. \n + \en Create a sweeping solid by moving the generating curve along the guide curve with additional data. \n \~ + \param[in] sweptData - \ru Данные об образующей. + \en The generating curve data. \~ + \param[in] spine - \ru Направляющая кривая c дополнительной информацией. + \en The spine curve with additional data. \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] contoursNames - \ru Именователь контуров образующей. + \en An object defining the names of generating curve contours. \~ + \param[in] spineNames - \ru Именователь направляющей. + \en An object defining the name of a guide curve. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) EvolutionSolid( const MbSweptData & sweptData, + const MbSpine & spine, + const EvolutionValues & params, + const MbSNameMaker & operNames, + const RPArray & contoursNames, + const MbSNameMaker & spineNames, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело по плоским сечениям. + \en Create a solid from a planar sections. \~ + \details \ru Создать тело по плоским сечениям c направляющей линией. \n + \en Create a solid from a planar sections with a guide curve. \n \~ + \param[in] pl - \ru Множество систем координат образующих контуров. + \en An array of generating contours coordinate systems. \~ + \param[in] c - \ru Множество образующих контуров. + \en An array of generating contours. \~ + \param[in] spine - \ru Направляющая кривая (может быть NULL). + \en A guide curve (can be NULL). \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] ps - \ru Множество точек на образующих контурах, задающий их начальные точки. + \en A point array on the generating contours which determines the start points of the contours. \~ + \param[in] names - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] ns - \ru Именователи образующих контуров. + \en The objects defining the names of generating contours. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) LoftedSolid( SArray & pl, + RPArray & c, + const MbCurve3D * spine, + const LoftedValues & params, + SArray * ps, + const MbSNameMaker & names, + RPArray & ns, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело по пространственным сечениям. + \en Create a solid from a space sections. \~ + \details \ru Создать тело по пространственным сечениям c направляющей линией. \n + \en Create a solid from a space sections with a guide curve. \n \~ + \param[in] pl - \ru Множество систем координат образующих контуров. + \en An array of generating contours coordinate systems. \~ + \param[in] c - \ru Множество образующих контуров. + \en An array of generating contours. \~ + \param[in] spine - \ru Осевая кривая (может быть NULL). + \en A guide curve (can be NULL). \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] guideCurves - \ru Множество направляющих кривых, задающих траектории соответствующих точек контуров. + \en An array of the guide curves that determines the trajectories of the corresponding points of the contours. \~ + \param[in] ps - \ru Множество точек на образующих контурах, задающее соответствующие точки (цепочки точек). + \en A point array on the generating contours which determines the corresponding points of the contours (chains of points). \~ + \param[in] names - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] ns - \ru Именователи образующих контуров. + \en The objects defining the names of generating contours. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) LoftedSolid( SArray & pl, + RPArray & c, + const MbCurve3D * spine, // осевая линия может быть NULL + const LoftedValues & params, + RPArray * guideCurves, + SArray * ps, + const MbSNameMaker & names, + RPArray & ns, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело по пространственным сечениям. + \en Create a solid from a space sections. \~ + \details \ru Создать тело по пространственным сечениям c направляющей линией. \n + \en Create a solid from a space sections with a guide curve. \n \~ + \param[in] surfs - \ru Множество поверхностей образующих контуров. + \en An array of surfaces of generating contours. \~ + \param[in] c - \ru Множество образующих контуров. + \en An array of generating contours. \~ + \param[in] spine - \ru Осевая кривая (может быть NULL). + \en A guide curve (can be NULL). \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] guideCurves - \ru Множество направляющих кривых, задающих траектории соответствующих точек контуров. + \en An array of the guide curves that determines the trajectories of the corresponding points of the contours. \~ + \param[in] ps - \ru Множество точек на образующих контурах, задающее соответствующие точки (цепочки точек). + \en A point array on the generating contours which determines the corresponding points of the contours (chains of points). \~ + \param[in] names - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] ns - \ru Именователи образующих контуров. + \en The objects defining the names of generating contours. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) LoftedSolid( RPArray & surfs, + RPArray & c, + const MbCurve3D * spine, // осевая линия может быть NULL + const LoftedValues & params, + RPArray * guideCurves, + SArray * ps, + const MbSNameMaker & names, + RPArray & ns, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело выдавливания и выполнить булеву операцию. + \en Create an extrusion solid and perform a boolean operation. \~ + \details \ru Создать тело выдавливания и выполнить булеву операцию типа oType с телом solid. + Принимаемые значения OperationType для тел: \n + bo_Union - объединение, \n + bo_Intersect - пересечение, \n + bo_Difference - вычитание. + \en Create an extrusion solid and perform a boolean operation of type 'oType' with solid 'solid'. + The possible values of 'OperationType' for solids: \n + bo_Union - union, \n + bo_Intersect - intersection, \n + bo_Difference - subtraction. \~ + \param[in] solid - \ru Первое тело для булевой операции. + \en The first solid for a boolean operation. \~ + \param[in] sameShell - \ru Режим копирования тела. + \en Whether to copy the solid. \~ + \param[in] sweptData - \ru Данные об образующих кривых. + \en The generating curve data. \~ + \param[in] direction - \ru Направление выдавливания. + \en An extrusion direction. \~ + \param[in, out] params - \ru Параметры выдавливания. + Возвращают информацию для построения элементов массива операций до поверхности. + \en The extrusion parameters. + Returns the information for construction of the up-to-surface operation array elements. \~ + \param[in] oType - \ru Тип булевой операции. + \en A boolean operation type. \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] contoursNames - \ru Именователи образующих кривых. + \en The objects defining the names of generating lines. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ExtrusionResult( MbSolid & solid, + MbeCopyMode sameShell, + const MbSweptData & sweptData, + const MbVector3D & direction, + const ExtrusionValues & params, + OperationType oType, + const MbSNameMaker & operNames, + const RPArray & contoursNames, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело вращения и выполнить булеву операцию. + \en Create a revolution solid and perform a boolean operation. \~ + \details \ru Создать тело вращения и выполнить булеву операцию типа oType с телом solid. + Принимаемые значения OperationType для тел: \n + bo_Union - объединение, \n + bo_Intersect - пересечение, \n + bo_Difference - вычитание. + \en Create a revolution solid and perform a boolean operation of type oType with solid 'solid'. + The possible values of 'OperationType' for solids: \n + bo_Union - union, \n + bo_Intersect - intersection, \n + bo_Difference - subtraction. \~ + \param[in] solid - \ru Первое тело для булевой операции. + \en The first solid for a boolean operation. \~ + \param[in] sameShell - \ru Режим копирования тела. + \en Whether to copy the solid. \~ + \param[in] sweptData - \ru Данные об образующих кривых. + \en The generating curve data. \~ + \param[in] axis - \ru Ось вращения. + \en Rotation axis. \~ + \param[in, out] params - \ru Параметры вращения. + Возвращают информацию для построения элементов массива операций до поверхности. + \en The revolution parameters. + Returns the information for construction of the up-to-surface operation array elements. \~ + \param[in] oType - \ru Тип булевой операции. + \en A boolean operation type. \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] contoursNames - \ru Именователи образующих кривых. + \en The objects defining the names of generating lines. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) RevolutionResult( MbSolid & solid, + MbeCopyMode sameShell, + const MbSweptData & sweptData, + const MbAxis3D & axis, + const RevolutionValues & params, + OperationType oType, + const MbSNameMaker & operNames, + const RPArray & contoursNames, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Сориентировать образующий контур и направляющую кинематики. + \en Determine the orientation for a generating contour and for a guide curve of kinematics (evolution). \~ + \details \ru Выполнить ориентацию образующего контура и направляющей кривой для построения кинематического тела. \n + \en Orientate the generating contour and the guide curve for a sweeping solid construction. \n \~ + \param[in] surface - \ru Поверхность. + \en The surface. \~ + \param[in] contours - \ru Образующие контуры. + \en Generating contours. \~ + \param[in] guide - \ru Направляющая кривая. + \en The spine curve. \~ + \param[in] parameters - \ru Параметры операции. + \en The operation parameters. \~ + \param[out] axis - \ru Ось доворота образующей. + \en The axis for the generating curve additional turn. \~ + \param[out] angle - \ru Угол доворота образующей. + \en The additional turn for generating line. \~ + \param[in] version - \ru Версия операции. + \en The version of the operation. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \warning \ru Вспомогательная функция операций EvolutionSolid и EvolutionResult. + \en An auxiliary function of operations EvolutionSolid and EvolutionResult. \~ + \ingroup Shell_Modeling +*/ +// --- +MATH_FUNC (MbResultType) EvolutionNormalize( const MbSurface & surface, + const RPArray & contours, + const MbCurve3D & guide, + const EvolutionValues & parameters, + MbAxis3D & axis, + double & angle, + VERSION version ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать усеченную замкнутую кривую на копии кривой. + \en Create a trimmed closed curve on a curve copy. \~ + \details \ru Выполнить построение копии замкнутой кривой с началом в точке, определяемой параметром t. \n + \en Create a copy of a closed curve starting at a point with parameter t. \n \~ + \param[in] curve - \ru Направляющая кривая. + \en The spine curve. \~ + \param[in] t - \ru Параметр кривой. + \en A curve parameter. \~ + \return \ru При удачной работе функция возвращает построенную копию кривой + с началом в заданной точке, в противном случае функция возвращает ноль. + \en Returns a constructed curve copy starting at the specified point if it has been successfully created, + otherwise it returns null. \~ + \warning \ru Вспомогательная функция операций EvolutionSolid и EvolutionResult. + \en An auxiliary function of operations EvolutionSolid and EvolutionResult. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbCurve3D *) TrimClosedSpine( const MbCurve3D & curve, + double t ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кинематическое тело и выполнить булеву операцию. + \en Create an evolution solid and perform a boolean operation. \~ + \details \ru Создать кинематическое тело и выполнить булеву операцию типа oType с телом solid. + Принимаемые значения OperationType для тел: \n + bo_Union - объединение, \n + bo_Intersect - пересечение, \n + bo_Difference - вычитание. + \en Create an evolution solid and perform a boolean operation of type oType with solid 'solid'. + The possible values of 'OperationType' for solids: \n + bo_Union - union, \n + bo_Intersect - intersection, \n + bo_Difference - subtraction. \~ + \param[in] solid - \ru Первое тело для булевой операции. + \en The first solid for a boolean operation. \~ + \param[in] sameShell - \ru Режим копирования тела. + \en Whether to copy the solid. \~ + \param[in] sweptData - \ru Данные об образующей. + \en The generating curve data. \~ + \param[in] spine - \ru Направляющая кривая. + \en The spine curve. \~ + \param[in] params - \ru Параметры кинематической операции. + \en Parameters of the sweeping operation. \~ + \param[in] oType - \ru Тип булевой операции. + \en A boolean operation type. \~ + \param[in] operNames - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] contoursNames - \ru Именователь контуров образующей. + \en An object defining the names of generating curve contours. \~ + \param[in] spineNames - \ru Именователь направляющей. + \en An object defining the name of a guide curve. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC(MbResultType) EvolutionResult( MbSolid & solid, + MbeCopyMode sameShell, + const MbSweptData & sweptData, + const MbCurve3D & spine, + const EvolutionValues & params, + OperationType oType, + const MbSNameMaker & operNames, + const RPArray & contoursNames, + const MbSNameMaker & spineNames, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело по плоским сечениям и выполнить булеву операцию. + \en Create a solid from the planar sections and perform a boolean operation. \~ + \details \ru Создать тело по плоским сечениям и выполнить булеву операцию типа oType с телом solid. + Принимаемые значения OperationType для тел: \n + bo_Union - объединение, \n + bo_Intersect - пересечение, \n + bo_Difference - вычитание. + \en Create a solid from a planar sections and perform a boolean operation of type oType with solid 'solid'. + The possible values of 'OperationType' for solids: \n + bo_Union - union, \n + bo_Intersect - intersection, \n + bo_Difference - subtraction. \~ + \param[in] solid - \ru Первое тело для булевой операции. + \en The first solid for a boolean operation. \~ + \param[in] sameShell - \ru Режим копирования тела. + \en Whether to copy the solid. \~ + \param[in] pl - \ru Множество систем координат образующих контуров. + \en An array of generating contours coordinate systems. \~ + \param[in] c - \ru Множество образующих контуров. + \en An array of generating contours. \~ + \param[in] spine - \ru Направляющая кривая (может быть NULL). + \en A guide curve (can be NULL). \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] oType - \ru Тип булевой операции. + \en A boolean operation type. \~ + \param[in] ps - \ru Множество точек на образующих контурах, задающий их начальные точки. + \en A point array on the generating contours which determines the start points of the contours. \~ + \param[in] names - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] ns - \ru Именователи образующих контуров. + \en The objects defining the names of generating contours. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC(MbResultType) LoftedResult( MbSolid & solid, + MbeCopyMode sameShell, + SArray & pl, + RPArray & c, + const MbCurve3D * spine, + const LoftedValues & params, + OperationType oType, + SArray * ps, + const MbSNameMaker & names, + RPArray & ns, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело по пространственным сечениям и выполнить булеву операцию. + \en Create a solid from the space sections and perform a boolean operation. \~ + \details \ru Создать тело по пространственным сечениям и выполнить булеву операцию типа oType с телом solid. + Принимаемые значения OperationType для тел: \n + bo_Union - объединение, \n + bo_Intersect - пересечение, \n + bo_Difference - вычитание. + \en Create a solid from a space sections and perform a boolean operation of type oType with solid 'solid'. + The possible values of 'OperationType' for solids: \n + bo_Union - union, \n + bo_Intersect - intersection, \n + bo_Difference - subtraction. \~ + \param[in] solid - \ru Первое тело для булевой операции. + \en The first solid for a boolean operation. \~ + \param[in] sameShell - \ru Режим копирования тела. + \en Whether to copy the solid. \~ + \param[in] surfs - \ru Множество поверхностей контуров. + \en An array of generating contours surfaces. \~ + \param[in] c - \ru Множество образующих контуров. + \en An array of generating contours. \~ + \param[in] spine - \ru Осевая кривая (может быть NULL). + \en A guide curve (can be NULL). \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] oType - \ru Тип булевой операции. + \en A boolean operation type. \~ + \param[in] guideCurves - \ru Массив направляющих кривых. + \en An array of the guide curves. \~ + \param[in] ps - \ru Множество точек на образующих контурах, задающий их начальные точки. + \en A point array on the generating contours which determines the start points of the contours. \~ + \param[in] names - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] ns - \ru Именователи образующих контуров. + \en The objects defining the names of generating contours. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC(MbResultType) LoftedResult( MbSolid & solid, + MbeCopyMode sameShell, + RPArray & surfs, + RPArray & c, + const MbCurve3D * spine, + const LoftedValues & params, + OperationType oType, + RPArray * guideCurves, + SArray * ps, + const MbSNameMaker & names, + RPArray & ns, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Выполнить булеву операцию. + \en Perform a Boolean operation. \~ + \details \ru Функция выполняет указанную булеву операцию над двумя телами с возможностью управления слиянием граней и рёбер.\n + \en The function performs the specified Boolean operation on two solids with faces and edges merging control.\n \~ + \param[in] solid1 - \ru Набор граней первого тела. + \en The set of faces of the first solid. \~ + \param[in] sameShell1 - \ru Способ копирования граней первого тела. + \en Method of copying the faces of the first solid. \~ + \param[in] solid2 - \ru Набор граней второго тела. + \en The second solid face set. \~ + \param[in] sameShell2 - \ru Способ копирования граней второго тела. + \en Method of copying the faces of the second solid. \~ + \param[in] oType - \ru Тип булевой операции. + \en A Boolean operation type. \~ + \param[in] flags - \ru Управляющие флаги булевой операции. + \en Control flags of the Boolean operation. \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[out] result - \ru Построенный набор граней. + \en Constructed set of faces. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) BooleanResult( MbSolid & solid1, + MbeCopyMode sameShell1, + MbSolid & solid2, + MbeCopyMode sameShell2, + OperationType oType, + const MbBooleanFlags & flags, + const MbSNameMaker & operNames, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело путем булевой операции. + \en Create a solid using a boolean operation. \~ + \details \ru Создать тело путем булевой операции типа oType для тел solid1 и solid2. + Принимаемые значения OperationType для тел: \n + bo_Union - объединение, \n + bo_Intersect - пересечение, \n + bo_Difference - вычитание. \n + Функция работает только с замкнутыми телами, сливает подобные грани и рёбра. + Функция выполняет одноимённую булеву операцию над множествами точек, + расположенными внутри и на поверхности тел. + \en Create a solid by applying a boolean operation of type oType to solids 'solid1' and 'solid2'. + The possible values of 'OperationType' for solids: \n + bo_Union - union, \n + bo_Intersect - intersection, \n + bo_Difference - subtraction. \n + The function accepts only closed solids, similar faces and similar edges will be merged. + The function performs a boolean operation of the same name with a point sets + located inside the solids and on their boundary. \~ + \param[in] solid1 - \ru Первое тело. + \en The first solid. \~ + \param[in] sameShell1 - \ru Режим копирования первого тела. + \en Whether to copy the first solid. \~ + \param[in] solid2 - \ru Второе тело. + \en The second solid. \~ + \param[in] sameShell2 - \ru Режим копирования второго тела. + \en Whether to copy the second solid. \~ + \param[in] oType - \ru Тип булевой операции. + \en A boolean operation type. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) BooleanSolid( MbSolid & solid1, + MbeCopyMode sameShell1, + MbSolid & solid2, + MbeCopyMode sameShell2, + OperationType oType, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело путем булевой операции. + \en Create a solid using a boolean operation. \~ + \details \ru Создать тело путем булевой операции типа oType для оболочек solid1 и solid2. + Один из операндов solid1 или solid2 - незамкнутая оболочка. + Принимаемые значения OperationType для оболочек: \n + bo_Variety - объединение, \n + bo_Internal - пересечение, \n + bo_External - вычитание. + \en Create a solid applying a boolean operation of type oType to shells 'solid1' and solid2'. + One of the operands 'solid1' and 'solid2' should be an open shell. + Possible values of 'OperationType' for a shells are: \n + bo_Variety - a union, \n + bo_Internal - an intersection, \n + bo_External - a subtraction. \~ + \param[in] solid1 - \ru Первое тело. + \en The first solid. \~ + \param[in] sameShell1 - \ru Режим копирования первого тела. + \en Whether to copy the first solid. \~ + \param[in] solid2 - \ru Второе тело. + \en The second solid. \~ + \param[in] sameShell2 - \ru Режим копирования второго тела. + \en Whether to copy the second solid. \~ + \param[in] oType - \ru Тип булевой операции. + \en A boolean operation type. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) BooleanShell( MbSolid & solid1, + MbeCopyMode sameShell1, + MbSolid & solid2, + MbeCopyMode sameShell2, + OperationType oType, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Отрезать часть тела поверхностью. + \en Cut a part of a solid off by a surface. \~ + \details \ru Отрезать часть тела пересекающей его поверхностью. \n + retainedPart = 1 - оставляем часть тела, расположенную сверху поверхности. \n + retainedPart = -1 - оставляем часть тела, расположенную снизу поверхности. \n + \en Cut a part of a solid off by a surface that intersects the solid. \n + retainedPart = 1 - a part of solid above the surface is to be retained. \n + retainedPart = -1 - a part of solid below the surface is to be retained. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования исходного тела. + \en The mode of copying of the source solid. \~ + \param[in] surface - \ru Секущая поверхность. + \en A cutting plane. \~ + \param[in] retainedPart - \ru Направление отсечения. + \en The direction of cutting off. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] closed - \ru Флаг режима отсечения: true - сечем как тело, false - сечем как оболочку. + \en The flag of the cutting off mode: true - cut as a solid, false - cut as a shell. \~ + \param[in] flags - \ru Флаги слияния элементов оболочки. + \en Control flags of shell items merging. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SolidCutting( MbSolid & solid, + MbeCopyMode sameShell, + const MbSurface & surface, + int retainedPart, + const MbSNameMaker & names, + bool closed, + const MbMergingFlags & flags, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Отрезать часть тела выдавленным плоским контуром. + \en Cut a part of a solid off with an extruded planar contour. \~ + \details \ru Отрезать часть тела оболочкой, полученной выдавливанием плоского контура. \n + retainedPart = 1 - оставляем часть тела, расположенную сверху поверхности выдавливания. \n + retainedPart = -1 - оставляем часть тела, расположенную снизу поверхности выдавливания. \n + \en Cut a part of a solid by a shell of planar contour extrusion. \n + retainedPart = 1 - a part of solid above the extrusion surface is to be retained. \n + retainedPart = -1 - a part of solid below the extrusion surface is to be retained. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования входного тела. + \en Whether to copy the input solid. \~ + \param[in] place - \ru Система координат образующего контура. + \en The generating contour coordinate system. \~ + \param[in] contour - \ru Образующий контур. + \en The generating contour. \~ + \param[in] direction - \ru Направление выдавливания образующего контура. + \en An extrusion direction of the generating contour. \~ + \param[in] retainedPart - \ru Направление отсечения. + \en The direction of cutting off. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] closed - \ru Флаг режим отсечения: true - сечем как тело, false - сечем как оболочку. + \en The cutting off mode flag: true - cut as a solid, false - cut as a shell. \~ + \param[in] flags - \ru Флаги слияния элементов оболочки. + \en Control flags of shell items merging. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SolidCutting( MbSolid & solid, + MbeCopyMode sameShell, + const MbPlacement3D & place, + const MbContour & contour, + const MbVector3D & direction, + int retainedPart, + const MbSNameMaker & names, + bool closed, + const MbMergingFlags & flags, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Разрезать тело поверхностью. + \en Cut a solid off by a surface. \~ + \details \ru Разрезать тело поверхностью с построением всех отрезанных частей. \n + \en Cut a solid off by a surface, keep all parts of the solid. \n + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования исходного тела. При sameShell != cm_Copy построенные тела нельзя перемещать относительно друг друга. + \en Whether to copy the source solid. Built bodies can not move relative to each other when sameShell != Vm_Copy. \~ + \param[in] surface - \ru Секущая поверхность. + \en A cutting plane. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] closed - \ru Флаг режима отсечения: true - сечем как тело, false - сечем как оболочку. + \en The flag of the cutting off mode: true - cut as a solid, false - cut as a shell. \~ + \param[in] flags - \ru Флаги слияния элементов оболочки. + \en Control flags of shell items merging. \~ + \param[out] result - \ru Построенные тела. + \en The resultant solids. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +DEPRECATE_DECLARE +MATH_FUNC (MbResultType) SolidCutting( MbSolid & solid, + MbeCopyMode sameShell, + const MbSurface & surface, + const MbSNameMaker & names, + bool closed, + const MbMergingFlags & flags, + RPArray & result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Разрезать тело выдавленным плоским контуром. + \en Cut a solid off with an extruded planar contour. \~ + \details \ru Разрезать тело оболочкой, полученной выдавливанием плоского контура, с построением всех отрезанных частей. \n + \en Cut a solid by a shell of planar contour extrusion, keep all parts of the solid. \n + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования исходного тела. При sameShell != cm_Copy построенные тела нельзя перемещать относительно друг друга. + \en Whether to copy the source solid. Built bodies can not move relative to each other when sameShell != Vm_Copy. \~ + \param[in] place - \ru Система координат образующего контура. + \en The generating contour coordinate system. \~ + \param[in] contour - \ru Образующий контур. + \en The generating contour. \~ + \param[in] direction - \ru Направление выдавливания образующего контура. + \en An extrusion direction of the generating contour. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] closed - \ru Флаг режим отсечения: true - сечем как тело, false - сечем как оболочку. + \en The cutting off mode flag: true - cut as a solid, false - cut as a shell. \~ + \param[in] flags - \ru Флаги слияния элементов оболочки. + \en Control flags of shell items merging. \~ + \param[out] result - \ru Построенные тела. + \en The resultant solids. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +DEPRECATE_DECLARE +MATH_FUNC (MbResultType) SolidCutting( MbSolid & solid, + MbeCopyMode sameShell, + const MbPlacement3D & place, + const MbContour & contour, + const MbVector3D & direction, + const MbSNameMaker & names, + bool closed, + const MbMergingFlags & flags, + RPArray & result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Разрезать тело на части. + \en Cut a solid into parts. \~ + \details \ru Разрезать тело на части. \n + \en Cut a solid into parts. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования исходного тела. + \en The mode of copying of the source solid. \~ + \param[in] cuttingParams - \ru Параметры операции. + \en Operation parameters. \~ + \param[out] results - \ru Построенные тела. + \en The resultant solids. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SolidCutting( MbSolid & solid, + MbeCopyMode sameShell, + const MbShellCuttingParams & cuttingParams, + RPArray & results ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать симметричное тело относительно плоскости. + \en Create a symmetric solid relative to a plane. \~ + \details \ru Создать симметричное тело относительно плоскости XY локальной системы координат. \n + Функция создаёт симметричное тело с заданной плоскостью симметрии следующим образом. + Исходное тело режется плоскостью XY локальной системы координат, берётся часть исходного тела, + расположенная сверху режущей плоскости, строится зеркальная копия выбранной части исходного тела + и объединяется с выбранной частью исходного тела. \n + \en Crate a symmetric solid relative to XY-plane of a local coordinate system. \n + The function creates a symmetric solid with the specified plane of symmetry in the following way. + The source solid is cut off by the plane XY of the local coordinate system; a part of the source solid above the cutting plane + is retained. A mirror copy of the chosen part is created + and then is united with the chosen part of the source solid. \n \~ + \param[in] solid - \ru Исходная оболочка. + \en The source shell. \~ + \param[in] sameShell - \ru Режим копирования оболочки. + \en Whether to copy the shell. \~ + \param[in] place - \ru Система координат плоскости симметрии. + \en The symmetry plane coordinate system. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SymmetrySolid( MbSolid & solid, + MbeCopyMode sameShell, + const MbPlacement3D & place, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать зеркальную копию тела относительно плоскости. + \en Create a mirror copy of a solid relative to a plane. \~ + \details \ru Создать зеркальную копию тела относительно плоскости XY локальной системы координат. \n + \en Create a mirror copy of a solid relative to the XY-plane of a local coordinate system. \n \~ + \param[in] solid - \ru Исходная оболочка. + \en The source shell. \~ + \param[in] place - \ru Система координат плоскости симметрии. + \en The symmetry plane coordinate system. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) MirrorSolid( const MbSolid & solid, + const MbPlacement3D & place, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тело с ребром жёсткости. + \en Create a solid with a rib. \~ + \details \ru Создать тело с ребром жёсткости. \n + По заданному контуру функция строит ребро жёсткости и объединяет его с исходным телом. + Сегмент контура с указанным номером устанавливает вектор уклона. \n + \en Create a solid with a rib. \n + The function creates a rib from a given contour and unites it with the source solid. + The segment of the contour with the given number determines the slope vector. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования входного тела. + \en Whether to copy the input solid. \~ + \param[in] place - \ru Система координат образующего контура. + \en The generating contour coordinate system. \~ + \param[in] contour - \ru Формообразующий контур на плоскости XY системы координат place. + \en The generating contour on XY-plane of coordinate system 'place'. \~ + \param[in] index - \ru Номер сегмента в контуре. + \en The segment number in the contour. \~ + \param[in] pars - \ru Параметры ребра жёсткости. + \en Parameters of a rib. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) RibSolid( MbSolid & solid, + MbeCopyMode sameShell, + const MbPlacement3D & place, + const MbContour & contour, + size_t index, + RibValues & pars, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать отдельное ребро жёсткости. + \en Create a separate rib. \~ + \details \ru Создать отдельное ребро жёсткости для исходного тела без приклеивания. \n + \en Create a separate rib for source solid without gluing. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] place - \ru Система координат образующего контура. + \en The generating contour coordinate system. \~ + \param[in] contour - \ru Образующий контур. + \en The generating contour. \~ + \param[in] index - \ru Номер сегмента в контуре. + \en The segment number in the contour. \~ + \param[in] pars - \ru Параметры ребра жёсткости. + \en Parameters of a rib. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) RibElement( const MbSolid & solid, + const MbPlacement3D & place, + MbContour & contour, + size_t index, + RibValues & pars, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Скруглить ребра постоянным радиусом. + \en Fillet edges with a constant radius. \~ + \details \ru Скруглить указанные рёбра тела постоянным радиусом. \n + Функция выполняет замену указанных рёбер исходного тела гранями, + гладко сопрягающими смежные грани указанных рёбер. В поперечном сечении + сопрягающие грани могут иметь форму дуги окружности, эллипса, гиперболы, параболы. \n + \en Fillet the specified edges of the solid with a constant radius. \n + The function performs the replacement of the specified edges of the source solid by faces + smoothly connecting the adjacent faces of the specified edges. The cross-section + of the connecting faces can be of the form of a circle, an ellipse, a hyperbola or a parabola. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования входного тела. + \en Whether to copy the input solid. \~ + \param[in] initCurves - \ru Множество скругляемых ребер тела. + \en A set of edges of the solid to fillet. \~ + \param[in] initBounds - \ru Множество граней для обрезки торцов. + \en A set of faces for trimming of the butt-ends. \~ + \param[in] params - \ru Параметры скругления рёбер. + \en Parameters of edges fillet. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) FilletSolid( MbSolid & solid, + MbeCopyMode sameShell, + RPArray & initCurves, + RPArray & initBounds, + const SmoothValues & params, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Скруглить ребра переменным радиусом. + \en Fillet edges with a variable radius. \~ + \details \ru Скруглить указанные ребра тела переменным радиусом, задаваемым MbEdgeFunction.function. \n + Функция выполняет замену указанных рёбер исходного тела гранями, + гладко сопрягающими смежные грани указанных рёбер. В поперечном сечении + сопрягающие грани могут иметь форму дуги окружности, эллипса, гиперболы, параболы. + Параметры поперечного сечения могут изменяться по заданному закону. \n + \en Fillet the given edges of the solid with a variable radius specified by MbEdgeFunction.function. \n + The function performs the replacement of the specified edges of the source solid by faces + smoothly connecting the adjacent faces of the specified edges. The cross-section + of the connecting faces can be of the form of a circle, an ellipse, a hyperbola or a parabola. + The parameters of the cross-section can vary by the specified law. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования входного тела. + \en Whether to copy the input solid. \~ + \param[in] initCurves - \ru Множество скругляемых ребер тела с функциями изменения радиуса. + \en An array of edges of the solid to fillet together with the radius laws. \~ + \param[in] initBounds - \ru Множество граней для обрезки торцов. + \en A set of faces for trimming of the butt-ends. \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) FilletSolid( MbSolid & solid, + MbeCopyMode sameShell, + SArray & initCurves, + RPArray & initBounds, + const SmoothValues & params, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Скруглить вершины и примыкающие к ней рёбра постоянным радиусом. + \en Create fillets on vertices and the edges adjacent to these vertices with a constant radius. \~ + \details \ru Скруглить вершины и примыкающие к ней рёбра тела постоянным радиусом. \n + В вершинах должно стыковаться три ребра. + Функция выполняет замену указанных вершин и рёбер исходного тела гранями, + гладко сопрягающими смежные грани указанных вершин и рёбер. В поперечном сечении + сопрягающие грани могут иметь форму дуги окружности, эллипса, гиперболы, параболы. \n + \en Create fillets on vertices and the edges of the solid adjacent to these vertices with a constant radius. \n + Three edges must be incident to each vertex. + The functions performs replacement of the specified vertices and edges of the source solid by faces of the solid + smoothly connecting the faces adjacent to the specified vertices and edges. The cross-section + of the connecting faces can be of the form of a circle, an ellipse, a hyperbola or a parabola. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования входного тела. + \en Whether to copy the input solid. \~ + \param[in] initCurves - \ru Множество скругляемых ребер тела. + \en A set of edges of the solid to fillet. \~ + \param[in] initBounds - \ru Множество граней для обрезки торцев. + \en A set of faces for trimming of the butt-ends. \~ + \param[in] initVertices - \ru Множество скругляемых вершин. + \en A set of vertices to fillet. \~ + \param[in] params - \ru Параметры скругления рёбер. + \en Parameters of edges fillet. \~ + \param[in] cornerData - \ru Параметры скругления вершин. + \en Parameters of vertices fillet. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) FilletSolid( MbSolid & solid, + MbeCopyMode sameShell, + RPArray & initCurves, + RPArray & initBounds, + RPArray & initVertices, + const SmoothValues & params, + const CornerValues & cornerData, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Скруглить цепочку граней тела. + \en Create a fillet of faces of the solid. \~ + \details \ru Скруглить указанные грани тела. \n + Функция выполняет замену указанных граней исходного тела гранями, + гладко сопрягающими грани, связанные с указанными гранью. + В поперечном сечении сопрягающие грани имеют форму + дуги окружности, касающейся трёх граней исходного тела. \n + \en Create a fillet on the specified faces of the solid. \n + The function performs replacement of the specified faces of the source solid with faces + smoothly connecting the faces adjacent to the specified faces. + The cross-section of the connecting faces has a form of + a circle arc tangent to three faces of the source solid. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования входного тела. + \en Whether to copy the input solid. \~ + \param[in] initFaces - \ru Набор граней для скругления. + \en A set of faces to fillet. \~ + \param[in] initFacesLeft - \ru Набор ограничивающих слева граней. + \en A set of left bounding faces. \~ + \param[in] initFacesRight - \ru Набор ограничивающих справа граней. + \en A set of right bounding faces. \~ + \param[in] params - \ru Параметры скругления. + \en Parameters of fillet. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) FullFilletSolid( MbSolid & solid, + MbeCopyMode sameShell, + const RPArray & initFaces, + const RPArray & initFacesLeft, + const RPArray & initFacesRight, + const FullFilletValues & params, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить фаски ребер тела. + \en Create chamfers for edges of the solid. \~ + \details \ru Построить фаски указанных ребер тела. \n + Функция выполняет замену указанных рёбер исходного тела гранями фасок. \n + \en Create chamfers for the specified edges of the solid. \n + The function performs replacement of the specified edges of the source solid with faces of chamfers. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования входного тела. + \en Whether to copy the input solid. \~ + \param[in] initCurves - \ru Множество скругляемых ребер тела. + \en An array of edges to create chamfer on. \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ChamferSolid( MbSolid & solid, + MbeCopyMode sameShell, + RPArray & initCurves, + const SmoothValues & params, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать эквидистантное тело. + \en Create а equidistant solid. \~ + \details \ru Создать эквидистантное тело или оболочку. \n + \en Create an offset solid by equidistant faces or create an equidistant shell. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования входного тела. + \en Whether to copy the input solid. \~ + \param[in] offset - \ru Расстояние смещения граней. + \en The equidistant parameter. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) OffsetSolid( MbSolid & solid, + MbeCopyMode sameShell, + double offset, + const MbSNameMaker & names, + MbSolid *& result ); + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тонкостенное тело исключением граней. + \en Create a thin-walled solid by exclusion of faces. \~ + \details \ru Создать тонкостенное тело исключением граней outFaces \n + и приданием одинаковой толщины оставшимся граням \n + или создание незамкнутой оболочки. \n + \en Create a thin-walled solid by exclusion of faces outFaces \n + and supplying the rest of faces with the same thickness \n + or create an open shell. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования входного тела. + \en Whether to copy the input solid. \~ + \param[in] outFaces - \ru Вскрываемые грани тела. + \en Faces of the solid to open. \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] copyFaceAttrs - \ru Копировать атрибуты из исходных граней в эквидистантные. + \en Copy attributes of initial faces to offset faces. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ThinSolid( MbSolid & solid, + MbeCopyMode sameShell, + RPArray & outFaces, + SweptValues & params, + const MbSNameMaker & names, + bool copyFaceAttrs, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать тонкостенное тело исключением граней. + \en Create a thin-walled solid by exclusion of faces. \~ + \details \ru Создать тонкостенное тело исключением граней outFaces \n + и приданием различной толщины оставшимся граням \n + или создание незамкнутой оболочки. \n + \en Create a thin-walled solid by exclusion of faces outFaces \n + and supplying the rest of faces with different thickness \n + or create an open shell. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования входного тела. + \en Whether to copy the input solid. \~ + \param[in] outFaces - \ru Вскрываемые грани тела. + \en Faces of the solid to open. \~ + \param[in] offFaces - \ru Множество граней, для которых заданы индивидуальные значения толщин. + \en An array of faces for which the individual values of thickness are specified. \~ + \param[in] offDists - \ru Множество индивидуальных значений толщин (должен быть синхронизирован с массивом offFaces). + \en An array of individual values of thickness (must be synchronized with the array 'offFaces'). \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] copyFaceAttrs - \ru Копировать атрибуты из исходных граней в эквидистантные. + \en Copy attributes of initial faces to offset faces. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ThinSolid( MbSolid & solid, + MbeCopyMode sameShell, + RPArray & outFaces, + RPArray & offFaces, + SArray & offDists, + SweptValues & params, + const MbSNameMaker & names, + bool copyFaceAttrs, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Выполнить разбиение граней оболочки. + \en Perform splitting of a shell faces. \~ + \details \ru Выполнить разбиение граней оболочки поверхностями выдавливания контуров. \n + Функция создаёт копию тела и разбивает указанные грани поверхностями выдавливания контуров. \n + \en Perform splitting of a shell faces with surfaces of the contours extrusion. \n + The function creates a copy of a solid and splits the specified faces with surfaces of the contours extrusion. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования входного тела. + \en Whether to copy the input solid. \~ + \param[in] spPlace - \ru Система координат контуров. + \en The coordinate system of the contours. \~ + \param[in] spType - \ru Направление вытягивания. + \en The extrusion direction. \~ + \param[in] spContours - \ru Контура разбиения. + \en The contours of splitting. \~ + \param[in] spSame - \ru Использовать оригиналы или копии кривых. + \en Whether to use the originals or copies of curves. \~ + \param[in] selFaces - \ru Выбранные грани входного тела. + \en The chosen faces of the input solid. \~ + \param[in] flags - \ru Флаги слияния элементов оболочки. + \en Control flags of shell items merging. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SplitSolid( MbSolid & solid, + MbeCopyMode sameShell, + const MbPlacement3D & spPlace, + MbeSenseValue spType, + const RPArray & spContours, + bool spSame, + RPArray & selFaces, + const MbMergingFlags & flags, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Выполнить разбиение граней оболочки. + \en Perform splitting of a shell faces. \~ + \details \ru Выполнить разбиение граней оболочки пространственными кривыми, поверхностями и оболочками. \n + Функция создаёт копию тела и разбивает указанные грани пространственными кривыми, поверхностями и оболочками. \n + Пространственные элементы разбиения не должны иметь полных или частичных наложений, а также - самопересечений. \n + \en Perform splitting of the shell faces with space curves, surfaces and shells. \n + The function creates a copy of the solid and splits the specified faces with space curves, surfaces and shells. \n + The spatial elements of a splitting must not have complete or partial overlaps, and also self-intersections. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования входного тела. + \en Whether to copy the input solid. \~ + \param[in] spItems - \ru Пространственные элементы разбиения. + \en A spatial elements of splitting. \~ + \param[in] spSame - \ru Использовать оригиналы или копии кривых. + \en Whether to use the originals or copies of curves. \~ + \param[in] selFaces - \ru Выбранные грани входного тела. + \en The chosen faces of the input solid. \~ + \param[in] flags - \ru Флаги слияния элементов оболочки. + \en Control flags of shell items merging. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SplitSolid( MbSolid & solid, + MbeCopyMode sameShell, + const RPArray & spItems, + bool spSame, + RPArray & selFaces, + const MbMergingFlags & flags, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Уклонить указанные грани тела. + \en Slope the specified faces of the solid. \~ + \details \ru Уклонить указанные грани тела от нейтральной изоплоскости на заданный угол. \n + \en Slope the specified faces of the solid at the specified angle relative to the neutral isoplane. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования входного тела. + \en Whether to copy the input solid. \~ + \param[in] neutralPlace - \ru Нейтральная плоскость. + \en The neutral plane. \~ + \param[in] angle - \ru Угол уклона. + \en The slope angle. \~ + \param[in] faces - \ru Уклоняемые грани во входном теле. + \en The faces of input solid to be sloped. \~ + \param[in] fp - \ru Признак захвата граней, гладко стыкующихся с уклоняемыми гранями. + \en Whether to capture the faces smoothly connected with the faces being sloped. \~ + \param[in] reverse - \ru Флаг обратного направления уклона. + \en Whether to slope in the reverse direction. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) DraftSolid( MbSolid & solid, + MbeCopyMode sameShell, + const MbPlacement3D & neutralPlace, + double angle, + const RPArray & faces, + MbeFacePropagation fp, + bool reverse, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Выполнить объединение пересекающихся тел. + \en Perform the union of intersecting solids. \~ + \details \ru Выполнить объединение пересекающихся тел и булеву операцию oType с телом solid, если оно не нулевое: \n + bo_Union - объединение, \n + bo_Intersect - пересечение, \n + bo_Difference - вычитание. \n + Если флаг проверки пересечения checkIntersect == true, то выполняется проверка на пересечение тел + и булева операция объединения пересекающихся тел заданного множества в одно тело. В противном случае + объединение тел заданного множества выполняется простым перекладыванием граней всех тел в одно новое тело. \n + Если флаг регулярности множества тел isArray == true, то тела множества расположены в узлах + прямоугольной или круговой сетки и позиции тел заданы в именах граней. \n + \en Perform the union of intersecting solids and a boolean operation oType with solid 'solid' if it is not null: \n + bo_Union - union, \n + bo_Intersect - intersection, \n + bo_Difference - subtraction. \n + If the flag of intersection check checkIntersect == true, check for solids intersection is performed + and the boolean operation of union the intersection solids of the specified set into one solid is performed. Otherwise + the union of solids from the given set is performed by simple moving the faces of all the solids into a new solid. \n + If the flag of solid set regularity isArray == true, the solids are located at the nodes + of rectangular or circular grid and positions of solids are specified in the names of faces. \n \~ + \param[in] solid - \ru Тело. + \en A solid. \~ + \param[in] sameShell - \ru Режим копирования тела. + \en Whether to copy the solid. \~ + \param[in] solids - \ru Множество тел. + \en An array of solids. \~ + \param[in] sameShells - \ru Режим копирования тел. + \en Whether to copy the solids. \~ + \param[in] oType - \ru Тип булевой операции между телом и массивом тел. + \en The type of the boolean operation for the solid and the set of solids. \~ + \param[in] checkIntersect - \ru Проверять пересечение тел. + \en Whether to check the solids intersection. \~ + \param[in] mergeFaces - \ru Управляющие флаги слияния элементов оболочки. + \en Control flags of shell items merging. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] isArray - \ru Флаг регулярности множества тел. + \en A flag of solid set regularity. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \param[out] notGluedSolids - \ru Множество тел, которые не получилось приклеить. + \en An array of solids which was not glued. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) UnionResult( MbSolid * solid, + MbeCopyMode sameShell, + RPArray & solids, + MbeCopyMode sameShells, + OperationType oType, + bool checkIntersect, + const MbMergingFlags & mergeFlags, + const MbSNameMaker & names, + bool isArray, + MbSolid *& result, + RPArray * notGluedSolids = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать одно тело из присланных тел. + \en Create a solid from the specified solids. \~ + \details \ru Создать тело с объединением или без объединения пересекающихся тел. \n + Если флаг проверки пересечения checkIntersect == true, то выполняется проверка на пересечение тел + и булева операция объединения пересекающихся тел заданного множества в одно тело. В противном случае + объединение тел заданного множества выполняется простым перекладыванием граней всех тел в одно новое тело. \n + Если флаг регулярности множества тел isArray == true, то тела множества расположены в узлах + прямоугольной или круговой сетки и позиции тел заданы в именах граней. \n + \en Create a solid with or without union of the intersecting solids. \n + If the flag of intersection check checkIntersect == true, check for solids intersection is performed + and the boolean operation of union the intersection solids of the specified set into one solid is performed. Otherwise + the union of solids from the given set is performed by simple moving the faces of all the solids into a new solid. \n + If the flag of solid set regularity isArray == true, the solids are located at the nodes + of rectangular or circular grid and positions of solids are specified in the names of faces. \n \~ + \param[in] solids - \ru Множество тел. + \en An array of solids. \~ + \param[in] sameShells - \ru Режим копирования тел. + \en Whether to copy the solids. \~ + \param[in] checkIntersect - \ru Проверять пересечение тел. + \en Whether to check the solids intersection. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] isArray - \ru Флаг регулярности множества тел. + \en A flag of solid set regularity. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \param[out] notGluedSolids - \ru Множество тел, которые не получилось приклеить. + \en An array of solids which was not glued. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) UnionSolid( RPArray & solids, + MbeCopyMode sameShells, + bool checkIntersect, + const MbSNameMaker & names, + bool isArray, + MbSolid *& result, + RPArray * notGluedSolids = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать одно тело из присланных тел. + \en Create a solid from the specified solids. \~ + \details \ru Создать одно тело из присланных тел, не меняя их. \n + Объединение тел заданного множества выполняется простым перекладыванием + граней всех тел в одно новое тело. \n + \en Create a solid from the specified solids without the modification of the given solids. \n + The union of solids from the specified set is performed by simple moving + the faces of all the solids into a new solid. \n \~ + \param[in] solids - \ru Множество тел. + \en An array of solids. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) UnionSolid( const RPArray & solids, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Разделить тело на отдельные части. + \en Split the solid into separate parts. \~ + \details \ru Если исходное тело распадается на части, то наибольшая часть остаётся в исходном теле, + а остальные части части будут сложены в присланный контейнер тел. \n + Если флаг сортировки sort == true, то в исходном теле останется часть с наибольшим габаритом, + а отделённые части будут сортированы по убыванию габарита. В противном случае в исходном теле + останется часть, топологически связанная с первой гранью, а отделённые части будут сортированы + по номеру начальной грани в исходном теле. \n + \en If the source solid is decomposed, the greatest part remains in the source solid, + and the other parts are put into the given array of solids. \n + If 'sort' == 'true', the part with the greatest bounding box will remain in the source solid, + and separated parts will be sorted by bounding box size in descending order. Otherwise a part topologically connected with the first face will remain in the source solid + and the separated parts will be sorted + by the number of the initial face in the source solid. \n \~ + \param[in,out] solid - \ru Исходное модифицируемое тело. + \en The source solid to be modified. \~ + \param[out] parts - \ru Отделённые части тела. + \en The separated parts of the solid. \~ + \param[in] sort - \ru Сортировать по убыванию габарита. + \en Whether to sort by the bounding box size in descending order. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \return \ru Возвращает количество отделенных частей. + \en Returns the number of the separated parts. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (size_t) DetachParts( MbSolid & solid, + RPArray & parts, + bool sort, + const MbSNameMaker & names ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Разделить тело на отдельные части. + \en Split the solid into separate parts. \~ + \details \ru Если исходное тело распадается на части, то все его части будут сложены в присланный контейнер тел. \n + Исходное тело остаётся неизменённым. \n + \en If the source solid is decomposed, all the parts of the solid will be put into the given array of solids. \n + The source solid remains unchanged. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[out] parts - \ru Части тела. + \en The parts of the solid. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \return \ru Возвращает количество созданных частей. + \en Returns the number of created parts. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (size_t) CreateParts( const MbSolid & solid, + RPArray & parts, + const MbSNameMaker & names ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить оболочку тела по поверхности и толщине. + \en Create a shell of the solid from a surface and a thickness. \~ + \details \ru Выполнить построение тела путём придания толщины заданной поверхности. \n + \en Create a solid by supplying the surface with a thickness. \n \~ + \param[in] surface - \ru Поверхность. + \en The surface. \~ + \param[in] faceSense - \ru Ориентация нормали поверхности. + \en The surface normal orientation. \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] name - \ru Основное простое имя. + \en The main simple name. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ThinSolid( const MbSurface & surface, + bool faceSense, + SweptValues & params, + const MbSNameMaker & names, + SimpleName name, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать отверстие, карман, фигурный паз в теле. + \en Create a hole, a pocket, a groove in the solid. \~ + \details \ru Cоздать отверстие, карман, фигурный паз в теле или создать cверло, бобышку, если solid==NULL. \n + \en Create a hole, a pocket, a groove in the solid or create a drill, a boss if 'solid' == NULL. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Режим копирования тела. + \en Whether to copy the solid. \~ + \param[in] place - \ru Местная система координат для операции. + \en A local coordinate system for the operation. \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) HoleSolid( MbSolid * solid, + MbeCopyMode sameShell, + const MbPlacement3D & place, + const HoleValues & params, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Выделить в отдельное тело указанную часть распадающегося на части тела. + \en Extract the specified part of decomposing solid to a separate solid. \~ + \details \ru Создать тело, из указанной части тела, распадающегося на части. + Исходное тело должно состоять из отдельных частей. \n + \en Create a solid from the specified part of decomposing solid. + The source solid should consist of separate parts. \n \~ + \param[in] solid - \ru Разделяемое на части тело. + \en A decomposing solid. \~ + \param[in] id - \ru Номер выбранной части тела + \en The number of selected part of the solid. \~ + \param[in] path - \ru Идентификатор для выбранной части. + \en An identifier for the selected part. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in,out] partIndices - \ru Индексы частей тела. + \en Indices of the parts of the solid. \~ + \param[out] result - \ru Построенная оболочка (тело). + \en The resultant shell (solid). \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ShellPart( const MbSolid & solid, + size_t id, + const MbPath & path, + const MbSNameMaker & names, + MbPartSolidIndices & partIndices, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Размножить тело. + \en Duplicate the solid. \~ + \details \ru Размножить тело согласно параметрам и объединить копии в одно тело.\n + \en Duplicate the solid by the parameters and unite copies in one solid. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] params - \ru Параметры размножения. + \en The parameters of duplication. \~ + \param[in] names - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[out] result - \ru Результирующее тело. + \en The result solid. \~ + \return \ru Возвращает код результата операции. + \en . \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) DuplicationSolid( const MbSolid & solid, + const DuplicationValues & params, + const MbSNameMaker & names, + MbSolid *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать одно тело слиток из присланных объектов. + \en Create an ingot solid from the specified objects. \~ + \details \ru Создать одно тело слиток из присланных объектов. \n + Среди присланных объектов используются тела, вставки тел и сборки тел, из которых строится одно тело, + которое по внешности совпадает с присланными телами и служит их упрощенным заменителем по внешним параметрам. \n + \en Create an ingot solid from the specified solids without the modification of the given solids. \n + Among the objects sent using the body, insert bodies and assembling bodies of which is built the same body, + which in appearance coincides with the bodies had been sent and serves as a substitute for their simplistic external parameters. \n \~ + \param[in] solids - \ru Множество тел. + \en An array of solids. \~ + \param[in] names - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] makeCopy - \ru Флаг копирования тел перед использованием: true - копировать, false - не копировать. + \en The flag of the copying solid before using: true - copy solid, false - not copy. \~ + \param[out] result - \ru Построенное тело. + \en The resultant solid. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Solid_Modeling +*/ +// --- +MATH_FUNC (MbResultType) IngotSolid( RPArray & solids, + bool makeCopy, + const MbSNameMaker & names, + MbSolid *& result ); + + +#endif // __ACTION_SOLID_H diff --git a/C3d/Include/action_surface.h b/C3d/Include/action_surface.h index f8b285c..5cf68da 100644 --- a/C3d/Include/action_surface.h +++ b/C3d/Include/action_surface.h @@ -1,798 +1,865 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Методы построения поверхностей. - \en Functions for surfaces creation. \~ - \details \ru Поверхности являются основным элементом описания формы моделируемых объектов. - На базе поверхностей строятся грани, которые используются в твёрдых телах. - \en Surfaces is a basic element of the modeled objects shape description. - Faces are constructed on the basis of surfaces and then are used in solid solids. \~ -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ACTION_SURFACE_H -#define __ACTION_SURFACE_H - - -#include -#include -#include -#include -#include - - -class MATH_CLASS MbCurve; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbSurface; -class MATH_CLASS MbFace; -class MATH_CLASS MbSolid; -class MATH_CLASS MbSurfaceCurve; -class MATH_CLASS MbCurveEdge; -class MATH_CLASS MbGrid; -class MATH_CLASS MbRegion; - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать элементарную поверхность. - \en Create an elementary surface. \~ - \details \ru Создать одну из элементарных поверхностей по трем управляющим точкам и типу: \n - surfaceType == st_Plane - плоскость \n - surfaceType == st_ConeSurface - коническая поверхность \n - surfaceType == st_CylinderSurface - цилиндрическая поверхность \n - surfaceType == st_SphereSurface - сферическая поверхность \n - surfaceType == st_TorusSurface - поверхность тора \n - \en Create one of elementary surfaces from three points and a type: \n - surfaceType == st_Plane - a plane \n - surfaceType == st_ConeSurface - a conical surface \n - surfaceType == st_CylinderSurface - a cylindrical surface \n - surfaceType == st_SphereSurface - a spherical surface \n - surfaceType == st_TorusSurface - a torus surface \n \~ - \param[in] point0 - \ru Точка, определяющая начало локальной системы координат поверхности. - \en The origin of the surface local coordinate system. \~ - \param[in] point1 - \ru Точка, определяющая направление оси X локальной системы и радиус поверхности. - \en A point specifying the direction of X-axis of the local system and the surface radius. \~ - \param[in] point2 - \ru Точка, определяющая направление оси Y локальной системы. - \en A point specifying the direction of Y-axis of the local system. \~ - \param[in] surfaceType - \ru Тип поверхности. - \en The surface type. \~ - \param[out] result - \ru Построенная поверхность. - \en The constructed surface. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ElementarySurface( const MbCartPoint3D & point0, - const MbCartPoint3D & point1, - const MbCartPoint3D & point2, - MbeSpaceType surfaceType, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать плоскую NURBS - поверхность. - \en Create a planar NURBS - surface. \~ - \details \ru Создать плоскую NURBS - поверхность по угловым точкам. \n - \en Create a planar NURBS - surface given the corner points. \n \~ - \param[in] pUMinVMin - \ru Угловая точка поверхности. - \en A corner point of a surface. \~ - \param[in] pUMaxVMin - \ru Угловая точка поверхности. - \en A corner point of a surface. \~ - \param[in] pUMaxVMax - \ru Угловая точка поверхности. - \en A corner point of a surface. \~ - \param[in] pUMinVMax - \ru Угловая точка поверхности. - \en A corner point of a surface. \~ - \param[in] uCount - \ru Количество точек по U. - \en A number of points by U direction. \~ - \param[in] vCount - \ru Количество точек по V. - \en A number of points by V direction. \~ - \param[in] uDegree - \ru Порядок сплайнов по U. - \en Splines degree by U. \~ - \param[in] vDegree - \ru Порядок сплайнов по V. - \en Splines degree by V. \~ - \param[out] result - \ru Cплайновая поверхность. - \en The spline surface. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SplineSurface( const MbCartPoint3D & pUMinVMin, const MbCartPoint3D & pUMaxVMin, - const MbCartPoint3D & pUMaxVMax, const MbCartPoint3D & pUMinVMax, - size_t uCount, size_t vCount, - size_t uDegree, size_t vDegree, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать NURBS - поверхность. - \en Create a NURBS - surface. \~ - \details \ru Создать NURBS - поверхность по массивам точек и весов. \n - контейнер weightList может быть пустым. \n - контейнер uKnotList может быть пустым. \n - контейнер vKnotList может быть пустым. \n - \en Create a NURBS - surface given arrays of points and weights. \n - container 'weightList' can be empty. \n - container 'uKnotList' can be empty. \n - container 'vKnotList' can be empty. \n \~ - \param[in] pointList - \ru Множество точек. - \en An array of points. \~ - \param[in] weightList - \ru Множество весов - \en An array of weights. \~ - \param[in] uCount - \ru Размерность массива точек по U. - \en The size of point array by U. \~ - \param[in] vCount - \ru Размерность массива точек по V. - \en The size of point array by V. \~ - \param[in] uDegree - \ru Порядок сплайнов по U. - \en Splines degree by U. \~ - \param[in] uKnotList - \ru Узловой вектор по U. - \en A knot vector by U. \~ - \param[in] uClosed - \ru Замкнутость по U. - \en Closedness by U. \~ - \param[in] vDegree - \ru Порядок сплайнов по V. - \en Splines degree by V. \~ - \param[in] vKnotList - \ru Узловой вектор по V. - \en A knot vector by V. \~ - \param[in] vClosed - \ru Замкнутость по V. - \en Closedness by V. \~ - \param[out] result - \ru Cплайновая поверхность. - \en The spline surface. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SplineSurface( const SArray & pointList, - const SArray & weightList, - size_t uCount, size_t vCount, - size_t uDegree, const SArray & uKnotList, bool uClosed, - size_t vDegree, const SArray & vKnotList, bool vClosed, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать поверхность выдавливания. - \en Create an extrusion surface. \~ - \details \ru Создать поверхность выдавливания кривой. \n - \en Create a surface of a curve extrusion. \n \~ - \param[in] curve - \ru Образующая кривая. - \en The generating curve. \~ - \param[in] direction - \ru Вектор выдавливания. - \en An extrusion vector. \~ - \param[in] simplify - \ru Упрощать поверхность, если возможно. - \en Simplify a surface if it's possible. \~ - \param[out] result - \ru Поверхность выдавливания. - \en An extrusion surface. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ExtrusionSurface( MbCurve3D & curve, const MbVector3D & direction, - bool simplify, MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать поверхность вращения. - \en Create a revolution surface. \~ - \details \ru Создать поверхность вращения кривой. \n - \en Create a curve revolution surface. \n \~ - \param[in] curve - \ru Образующая кривая. - \en The generating curve. \~ - \param[in] origin - \ru Точка положения оси вращения. - \en The rotation axis origin. \~ - \param[in] axis - \ru Направление оси вращения. - \en The rotation axis direction. \~ - \param[in] angle - \ru Угол вращения. - \en A rotation angle. \~ - \param[in] simplify - \ru Упрощать поверхность, если возможно. - \en Simplify a surface if it's possible. \~ - \param[out] result - \ru Поверхность вращения. - \en The revolution surface. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) RevolutionSurface( MbCurve3D & curve, const MbCartPoint3D & origin, const MbVector3D & axis, double angle, - bool simplify, MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать поверхность движения. - \en Create an expansion surface. \~ - \details \ru Создать поверхность движения кривой. \n - \en Create a surface of a curve sweeping. \n \~ - \param[in] curve - \ru Образующая кривая. - \en The generating curve. \~ - \param[in] spine - \ru Направляющая кривая. - \en The spine curve. \~ - \param[out] result - \ru Поверхность движения с доворотами. - \en The expansion surface. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ExpansionSurface( MbCurve3D & curve, MbCurve3D & spine, - MbCurve3D * curve1, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кинематическую поверхность. - \en Create an evolution surface. \~ - \details \ru Создать кинематическую поверхность по образующей и направляющей. \n - В случае, если spine имеет тип st_ConeSpiral, результатом построения - является спиральная поверхность. \n - \en Create an evolution surface from the generating curve and the guide curve. \n - If 'spine' has type st_ConeSpiral, the result of the construction - is a spiral surface. \n \~ - \param[in] curve - \ru Образующая кривая. - \en The generating curve. \~ - \param[in] spine - \ru Направляющая кривая. - \en The spine curve. \~ - \param[out] result - \ru Кинематическая поверхность или спиральная поверхность. - \en The evolution surface or a spiral surface. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) EvolutionSurface( MbCurve3D & curve, MbCurve3D & spine, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать спиральную поверхность. - \en Create a spiral surface. \~ - \details \ru Создать спиральную поверхность по образующей и 3 точкам. \n - \en Create a spiral surface from a generating line and three points. \n \~ - \param[in] curve - \ru Образующая кривая спирали. - \en The generating curve of a spiral. \~ - \param[in] p0 - \ru Начало локальной системы координат (ЛСК). - \en The origin of local coordinate system (LCS). \~ - \param[in] p1 - \ru Точка для формирования оси Z ЛСК. - \en A point specifying Z-axis of LCS. \~ - \param[in] p2 - \ru Точка для формирования оси X ЛСК. - \en A point specifying X-axis of LCS. \~ - \param[in] step - \ru Шаг спирали. - \en A pitch. \~ - \param[out] result - \ru Спиральная поверхность. - \en A spiral surface. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SpiralSurface( MbCurve3D & curve, - const MbCartPoint3D & p0, const MbCartPoint3D & p1, const MbCartPoint3D & p2, - double step, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать секториальную поверхность. - \en Create a sectorial surface. \~ - \details \ru Создать секториальную поверхность по кривой и точке. \n - \en Create a sectorial surface from a curve and a point. \n \~ - \param[in] curve - \ru Образующая кривая. - \en The generating curve. \~ - \param[in] point - \ru Точка. - \en A point. \~ - \param[out] result - \ru Линейчатая поверхность в виде сектора. - \en The ruled surface in a form of a sector. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SectorSurface( MbCurve3D & curve, const MbCartPoint3D & point, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать линейчатую поверхность. - \en Create a ruled surface. \~ - \details \ru Создать линейчатую поверхность по двум кривым. \n - \en Create a ruled surface from two curves. \n \~ - \param[in] curve1 - \ru Первая образующая кривая. - \en The first generating curve. \~ - \param[in] curve2 - \ru Вторая образующая кривая. - \en The second generating curve. \~ - \param[in] simplify - \ru Упрощать поверхность, если возможно. - \en Simplify a surface if it's possible. \~ - \param[out] result - \ru Линейчатая поверхность. - \en The ruled surface. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) RuledSurface( MbCurve3D & curve1, MbCurve3D & curve2, - bool simplify, MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать треугольную поверхность. - \en Create a triangular surface. \~ - \details \ru Создать треугольную поверхность по трем кривым. \n - \en Create a triangular surface from three curves. \n \~ - \param[in] curve1 - \ru Первая кривая. - \en The first curve. \~ - \param[in] curve2 - \ru Вторая кривая. - \en The second curve. \~ - \param[in] curve3 - \ru Третья кривая. - \en The third curve. \~ - \param[out] result - \ru Треугольная поверхность по трём кривым. - \en The triangular surface by three curves. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) CornerSurface( MbCurve3D & curve1, - MbCurve3D & curve2, - MbCurve3D & curve3, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать билинейную поверхность. - \en Create a bilinear surface. \~ - \details \ru Создать билинейную поверхность по четырем кривым. \n - \en Create a bilinear surface from four curves. \n \~ - \param[in] curve1 - \ru Первая кривая. - \en The first curve. \~ - \param[in] curve2 - \ru Вторая кривая. - \en The second curve. \~ - \param[in] curve3 - \ru Третья кривая. - \en The third curve. \~ - \param[in] curve4 - \ru Четвертая кривая. - \en The fourth curve. \~ - \param[out] result - \ru Билинейная поверхность по четырём кривым. - \en The bilinear surface from four curves. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) CoverSurface( MbCurve3D & curve1, - MbCurve3D & curve2, - MbCurve3D & curve3, - MbCurve3D & curve4, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать поверхность по семейству кривых. - \en Create a surface by a set of curves. \~ - \details \ru Создать поверхность по семейству кривых. \n - begDirection направление в начале поверхности может быть нулевой длины. \n - endDirection направление в конце поверхности может быть нулевой длины. \n - \en Create a surface by a set of curves. \n - begDirection direction at the begining of the surface can be of zero length. \n - endDirection direction at the end of the surface can be of zero length. \n \~ - \param[in] curveList - \ru Семейство образующих кривых вдоль U-направления. - \en A set of generating curves along U direction. \~ - \param[in] closed - \ru Замкнутость вдоль V-направления. - \en Closedness by V direction. \~ - \param[in] begDirection - \ru Вектор направления в начале поверхности. - \en The vector of direction at the beginning of the surface. \~ - \param[in] endDirection - \ru Вектор направления в конце поверхности. - \en The vector of direction at the end of the surface. \~ - \param[out] result - \ru Поверхность по семейству кривых. - \en The surface from the set of curves. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) LoftedSurface( const RPArray & curveList, bool closed, - const MbVector3D & begDirection, const MbVector3D & endDirection, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать поверхность по семейству кривых и направляющей. - \en Create a surface from a set of curves and a spine curve. \~ - \details \ru Создать поверхность по семейству кривых и направляющей. \n - \en Create a surface from a set of curves and a spine curve. \n \~ - \param[in] curveList - \ru Семейство образующих кривых вдоль U-направления. - \en A set of generating curves along U direction. \~ - \param[in] spine - \ru Направляющая кривая. - \en The spine curve. \~ - \param[out] result - \ru Поверхность по семейству кривых и направляющей. - \en The surface from a set of curves and a spine curve. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) LoftedSurface( const RPArray & curveList, - MbCurve3D & spine, - MbSurface *& result, - bool isSimToEvol = true ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать поверхность на сетке кривых. - \en Create a surface constructed by the grid curves. \~ - \details \ru Создать поверхность на сетке кривых по двум семействам кривых. \n - \en Create a surface constructed by the grid curves given two sets of curves. \n \~ - \param[in] uCurveList - \ru Семейство кривых вдоль U-направления. - \en A curve set along U direction. \~ - \param[in] vCurveList - \ru Семейство кривых вдоль V-направления. - \en A curve set along V direction. \~ - \param[out] result - \ru Поверхность на сетке кривых. - \en The surface constructed by the grid curves. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) MeshSurface( const RPArray & uCurveList, - const RPArray & vCurveList, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать эквидистантную поверхность. - \en Create an offset surface. \~ - \details \ru Создать эквидистантную поверхность к исходной поверхности. \n - \en Create an offset surface to a given surface. \n \~ - \param[in] surface - \ru Исходная поверхность. - \en The initial surface. \~ - \param[in] distance - \ru Величина эквидистанты (знаковая). - \en The offset distance (signed). \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \param[out] result - \ru Эквидистантная поверхность. - \en The offset surface. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) OffsetSurface( MbSurface & surface, - double distance, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать эквидистантную поверхность. - \en Create an offset surface. \~ - \details \ru Создать эквидистантную поверхность по исходной поверхности. \n - \en Create an offset surface from the initial surface. \n \~ - \param[in] surface - \ru Базовая поверхность. - \en The base surface. \~ - \param[in] offsetUminVmin - \ru Смещение в точке Umin Vmin базовой поверхности. - \en Offset distance on point Umin Vmin of base surface. \~ - \param[in] offsetUmaxVmin - \ru Смещение в точке Umax Vmin базовой поверхности. - \en Offset distance on point Umax Vmin of base surface. \~ - \param[in] offsetUminVmax - \ru Смещение в точке Umin Vmax базовой поверхности. - \en Offset distance on point Umin Vmax of base surface. \~ - \param[in] offsetUmaxVmax - \ru Смещение в точке Umax Vmax базовой поверхности. - \en Offset distance on point Umax Vmax of base surface. \~ - \param[in] type - \ru Тип смещения точек: константный, линейный или кубический. - \en The offset type: constant, or linear, or cubic. \~ - \param[out] result - \ru Эквидистантная поверхность. - \en The offset surface. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) OffsetSurface( MbSurface & surface, - double offsetUminVmin, - double offsetUmaxVmin, - double offsetUminVmax, - double offsetUmaxVmax, - MbeOffsetType type, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать продленную поверхность. - \en Create an extended surface. \~ - \details \ru Создать продленную поверхность по исходной поверхности. \n - \en Create an extended surface from the initial surface. \n \~ - \param[in] surface - \ru Исходная поверхность. - \en The initial surface. \~ - \param[in] uMin - \ru Минимальное значение по U. - \en The minimal parameter value by U. \~ - \param[in] uMax - \ru Максимальное значение по U. - \en The maximal parameter value by U. \~ - \param[in] vMin - \ru Минимальное значение по V. - \en The minimal parameter value by V. \~ - \param[in] vMax - \ru Максимальное значение по V. - \en The maximal parameter value by V. \~ - \param[out] result - \ru Продлённая поверхность. - \en The extended surface. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ExtendedSurface( MbSurface & surface, - double uMin, - double uMax, - double vMin, - double vMax, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать деформированную поверхность. - \en Create a deformed surface. \~ - \details \ru Создать деформированную поверхность по исходной поверхности. \n - \en Create a deformed surface from the initial surface. \n \~ - \param[in] surface - \ru Исходная поверхность. - \en The initial surface. \~ - \param[in] uCount - \ru Количество точек по U. - \en A number of points by U direction. \~ - \param[in] vCount - \ru Количество точек по V. - \en A number of points by V direction. \~ - \param[in] uDegree - \ru Порядок сплайнов по U. - \en Splines degree by U. \~ - \param[in] vDegree - \ru Порядок сплайнов по V. - \en Splines degree by V. \~ - \param[in] dist - \ru Величина сдвига вдоль нормали. - \en Shift along the normal. \~ - \param[out] result - \ru Деформированная поверхность. - \en The deformed surface. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) DeformedSurface( MbSurface & surface, - size_t uCount, size_t vCount, - size_t uDegree, size_t vDegree, - double dist, - MbSurface *& result); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать поверхность с заданной границей. - \en Create a surface with the given boundary. \~ - \details \ru Создать поверхность с заданной границей по массиву двумерных кривых. \n - Контейнер boundList может быть пустым. \n - \en Create a surface with the given boundary from an array of two-dimensional curves. \n - Container 'boundList' can be empty. \n \~ - \param[in] surface - \ru Исходная поверхность. - \en The initial surface. \~ - \param[in] boundList - \ru Множество двумерных границ в виде кривых (первая кривая - внешний контур). - \en An array of two-dimensional boundaries in the form of curves (the first curve is an outer contour). \~ - \param[out] result - \ru Поверхность, ограниченная кривыми. - \en The surface bounded by the curves. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) BoundedSurface( MbSurface & surface, - const RPArray & boundList, - MbSurface *& result ); - -//------------------------------------------------------------------------------ -/** \brief \ru Создать поверхность с заданной границей. - \en Create a surface with the given boundary. \~ - \details \ru Создать поверхность с заданной границей по массиву двумерных контуров. \n - \en Create a surface with the given boundary from an array of two-dimensional curves. \n \~ - \param[in] place - \ru Локальная система координат плоскости. - \en The local coordinate system of a plane. \~ - \param[in] region - \ru Множество двумерных границ в виде региона (первая контур - внешний). - \en An array of two-dimensional boundaries in the form of region (the first contour is outer). \~ - \param[out] result - \ru Поверхность, ограниченная кривыми. - \en The surface bounded by the curves. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) BoundedSurface( const MbPlacement3D & place, const MbRegion & region, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать NURBS копию поверхности, ограниченную двумерными границами. - \en Create a NURBS surface copy with two-dimensional boundaries. \~ - \details \ru Создать NURBS копию поверхности, ограниченную двумерными границами проецированием пространственных границ \n - (предполагается, что пространственные граничные кривые лежат на поверхности). \n - \en Create a NURBS surface copy with two-dimensional boundaries by projecting of the spatial boundaries \n - (the boundary space curves are considered to belong to the surface) \n \~ - \param[in] surf - \ru Исходная поверхность. - \en The initial surface. \~ - \param[in] version - \ru Версия исполнения. - \en The version. \~ - \param[out] resSurface - \ru Сплайновая поверхность (ограниченная кривыми). - \en The spline surface (bounded by the curves). \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) NurbsSurface( const MbSurface & surf, VERSION version, MbSurface *& resSurface ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать поверхность симплексного сплайна. - \en Create a simplex spline surface. \~ - \details \ru Создать поверхность симплексного сплайна по массиву вершин. \n - \en Create a simplex spline surface from a point array. \n \~ - \param[in] pList - \ru Множество вершин. - \en An array of points. \~ - \param[out] resSurface - \ru Поверхность симплексного сплайна. - \en The simplex spline surface. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) SimplexSplineSurface( SArray & pList, MbSurface *& resSurface ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать треугольную поверхность Безье. - \en Create a triangular Bezier surface. \~ - \details \ru Создать треугольную поверхность Безье по 3 точкам. \n - \en Create a triangular Bezier surface from three points. \n \~ - \param[in] k - \ru Порядок поверхности. - \en The surface order. \~ - \param[in] p1 - \ru Первая точка. - \en The first point. \~ - \param[in] p2 - \ru Вторая точка. - \en The second point. \~ - \param[in] p3 - \ru Третья точка. - \en The third point. \~ - \param[out] resSurface - \ru Треугольная поверхность Безье. - \en The triangular Bezier surface. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) TriBezierSurface( ptrdiff_t k, MbCartPoint3D & p1, MbCartPoint3D & p2, MbCartPoint3D & p3, - MbSurface *& resSurface ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать треугольную В-сплайн поверхность. - \en Create a triangular B-spline surface. \~ - \details \ru Создать треугольную В-сплайн поверхность по 3 точкам. \n - \en Create a triangular B-spline surface from three points. \n \~ - \param[in] p0 - \ru Первая точка. - \en The first point. \~ - \param[in] p1 - \ru Вторая точка. - \en The second point. \~ - \param[in] p2 - \ru Третья точка. - \en The third point. \~ - \param[in] d - \ru Порядок поверхности. - \en The surface order. \~ - \param[out] resSurface - \ru Треугольная В-сплайн поверхность. - \en The triangular B-spline surface. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) TriSplineSurface( const MbCartPoint3D & p0, - const MbCartPoint3D & p1, - const MbCartPoint3D & p2, - const MbCartPoint3D & p3, - ptrdiff_t d, ptrdiff_t count, - MbSurface *& resSurface ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить характеристическую ломаную сплайновой поверхности. - \en Create a characteristic polyline of a spline surface. \~ - \details \ru Построить характеристическую ломаную сплайновой поверхности. \n - Функция работает с поверхностями типа st_SplineSurface, st_HermitSurface, - st_TriBezierSurface, st_TriSplineSurface. \n - \en Create a characteristic polyline of a spline surface. \n - The function accepts the surfaces of types st_SplineSurface, st_HermitSurface, - st_TriBezierSurface, st_TriSplineSurface. \n \~ - \param[in] surf - \ru Поверхность. - \en The surface. \~ - \param[out] segments - \ru Сегменты характеристической ломаной. - \en The characteristic polyline. \~ - \result \ru Возвращает true - если характеристическая ломаная получена. - \en Returns true - if the characteristic polyline is obtained. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) GetLineSegmentNURBSSurface( MbSurface & surf, RPArray & segments ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создание поверхности на сетке точек. - \en Create a surface from a points grid. \~ - \details \ru Создание поверхности на сетке точек и триангуляции. \n - Множество треугольников должен представлять собой правильную триангуляцию. - \en Create a surface from a points grid and triangulation. \n - The triangles array should form a regular triangulation. \~ - \param[in] grid - \ru Триангуляция. - \en A triangulation. \~ - \param[out] result - \ru Поверхность на сетке точек. - \en The surface on a point set. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) GridSurface( MbGrid & grid, MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать средние плоскости. - \en Create median planes. \~ - \details \ru Создать средние плоскости по двум кривым.\n - \en Create median planes from two curves.\n \~ - \param[in] curve1 - \ru Первая кривая. - \en The first curve. \~ - \param[in] curve2 - \ru Вторая кривая. - \en The second curve. \~ - \param[out] places - \ru Набор систем координат, задающих плоскости. - \en The set of coordinate systems which determine the planes. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \warning \ru В разработке. - \en Under development. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) MiddlePlaces( const MbCurve3D & curve1, - const MbCurve3D & curve2, - std::vector & places ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построение поверхности Кунса. - \en Construction of a Coons surface. \~ - \details \ru Построение бикубической поверхности Кунса на четырех кривых и их поперечных производных, касательной к четырём кривым на прверхностях. \n - \en The construction of Coons surface, which will be tangent to four surfaces and coincide with four curves on this surfaces on it sides.\n \~ - \param[in] surfaceCurve0 - \ru Кривая на поверхности 0. - \en The curve on surface0. \~ - \param[in] surfaceCurve1 - \ru Кривая на поверхности 1. - \en The curve on surface1. \~ - \param[in] surfaceCurve2 - \ru Кривая на поверхности 2. - \en The curve on surface2. \~ - \param[in] surfaceCurve3 - \ru Кривая на поверхности 3. - \en The curve on surface3. \~ - \param[out] result - \ru Построенная поверхность. - \en The constructed surface. \~ - \result \ru Возвращает код результата построения. - \en Returns operation result code. \~ - \warning \ru В разработке. - \en Under development. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) CreateCoonsSurface( const MbSurfaceCurve & surfaceCurve0, - const MbSurfaceCurve & surfaceCurve1, - const MbSurfaceCurve & surfaceCurve2, - const MbSurfaceCurve & surfaceCurve3, - MbSurface *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построение поверхности-заплатки для заданных рёбер. - \en Construction of a surface-patch by the edges. \~ - \details \ru Построение поверхности-заплатки, гладко стыкующейся с поверхностями ребер. \n - \en Construction of a surface-patch, smoothly joining with the surfaces of the edges. \n \~ - \param[in] edges - \ru Ребра, с которыми требуется стыковать новую поверхность. - \en Edges to join the new surface. \~ - \param[out] result - \ru Построенные поверхности. - \en The constructed surfaces. \~ - \result \ru Возвращает код результата построения. - \en Returns operation result code. \~ - \warning \ru В разработке. - \en Under development. \~ - \ingroup Surface_Modeling -*/ -// --- -MATH_FUNC (MbResultType) CreateSplinePatch( const std::vector & edges, - std::vector & result ); - - -#endif // __ACTION_SURFACE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Методы построения поверхностей. + \en Functions for surfaces creation. \~ + \details \ru Поверхности являются основным элементом описания формы моделируемых объектов. + На базе поверхностей строятся грани, которые используются в твёрдых телах. + \en Surfaces is a basic element of the modeled objects shape description. + Faces are constructed on the basis of surfaces and then are used in solid solids. \~ +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ACTION_SURFACE_H +#define __ACTION_SURFACE_H + + +#include +#include +#include +#include +#include + + +class MATH_CLASS MbCurve; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbSurface; +class MATH_CLASS MbFace; +class MATH_CLASS MbSolid; +class MATH_CLASS MbSurfaceCurve; +class MATH_CLASS MbCurveEdge; +class MATH_CLASS MbFunction; +class MATH_CLASS MbGrid; +class MATH_CLASS MbRegion; + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать элементарную поверхность. + \en Create an elementary surface. \~ + \details \ru Создать одну из элементарных поверхностей по трем управляющим точкам и типу: \n + surfaceType == st_Plane - плоскость \n + surfaceType == st_ConeSurface - коническая поверхность \n + surfaceType == st_CylinderSurface - цилиндрическая поверхность \n + surfaceType == st_SphereSurface - сферическая поверхность \n + surfaceType == st_TorusSurface - поверхность тора \n + \en Create one of elementary surfaces from three points and a type: \n + surfaceType == st_Plane - a plane \n + surfaceType == st_ConeSurface - a conical surface \n + surfaceType == st_CylinderSurface - a cylindrical surface \n + surfaceType == st_SphereSurface - a spherical surface \n + surfaceType == st_TorusSurface - a torus surface \n \~ + \param[in] point0 - \ru Точка, определяющая начало локальной системы координат поверхности. + \en The origin of the surface local coordinate system. \~ + \param[in] point1 - \ru Точка, определяющая направление оси X локальной системы и радиус поверхности. + \en A point specifying the direction of X-axis of the local system and the surface radius. \~ + \param[in] point2 - \ru Точка, определяющая направление оси Y локальной системы. + \en A point specifying the direction of Y-axis of the local system. \~ + \param[in] surfaceType - \ru Тип поверхности. + \en The surface type. \~ + \param[out] result - \ru Построенная поверхность. + \en The constructed surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ElementarySurface( const MbCartPoint3D & point0, + const MbCartPoint3D & point1, + const MbCartPoint3D & point2, + MbeSpaceType surfaceType, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать плоскую NURBS - поверхность. + \en Create a planar NURBS - surface. \~ + \details \ru Создать плоскую NURBS - поверхность по угловым точкам. \n + \en Create a planar NURBS - surface given the corner points. \n \~ + \param[in] pUMinVMin - \ru Угловая точка поверхности. + \en A corner point of a surface. \~ + \param[in] pUMaxVMin - \ru Угловая точка поверхности. + \en A corner point of a surface. \~ + \param[in] pUMaxVMax - \ru Угловая точка поверхности. + \en A corner point of a surface. \~ + \param[in] pUMinVMax - \ru Угловая точка поверхности. + \en A corner point of a surface. \~ + \param[in] uCount - \ru Количество точек по U. + \en A number of points by U direction. \~ + \param[in] vCount - \ru Количество точек по V. + \en A number of points by V direction. \~ + \param[in] uDegree - \ru Порядок сплайнов по U. + \en Splines degree by U. \~ + \param[in] vDegree - \ru Порядок сплайнов по V. + \en Splines degree by V. \~ + \param[out] result - \ru Cплайновая поверхность. + \en The spline surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SplineSurface( const MbCartPoint3D & pUMinVMin, const MbCartPoint3D & pUMaxVMin, + const MbCartPoint3D & pUMaxVMax, const MbCartPoint3D & pUMinVMax, + size_t uCount, size_t vCount, + size_t uDegree, size_t vDegree, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать NURBS - поверхность. + \en Create a NURBS - surface. \~ + \details \ru Создать NURBS - поверхность по массивам точек и весов. \n + контейнер weightList может быть пустым. \n + контейнер uKnotList может быть пустым. \n + контейнер vKnotList может быть пустым. \n + \en Create a NURBS - surface given arrays of points and weights. \n + container 'weightList' can be empty. \n + container 'uKnotList' can be empty. \n + container 'vKnotList' can be empty. \n \~ + \param[in] pointList - \ru Множество точек. + \en An array of points. \~ + \param[in] weightList - \ru Множество весов + \en An array of weights. \~ + \param[in] uCount - \ru Размерность массива точек по U. + \en The size of point array by U. \~ + \param[in] vCount - \ru Размерность массива точек по V. + \en The size of point array by V. \~ + \param[in] uDegree - \ru Порядок сплайнов по U. + \en Splines degree by U. \~ + \param[in] uKnotList - \ru Узловой вектор по U. + \en A knot vector by U. \~ + \param[in] uClosed - \ru Замкнутость по U. + \en Closedness by U. \~ + \param[in] vDegree - \ru Порядок сплайнов по V. + \en Splines degree by V. \~ + \param[in] vKnotList - \ru Узловой вектор по V. + \en A knot vector by V. \~ + \param[in] vClosed - \ru Замкнутость по V. + \en Closedness by V. \~ + \param[out] result - \ru Cплайновая поверхность. + \en The spline surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SplineSurface( const SArray & pointList, + const SArray & weightList, + size_t uCount, size_t vCount, + size_t uDegree, const SArray & uKnotList, bool uClosed, + size_t vDegree, const SArray & vKnotList, bool vClosed, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать поверхность выдавливания. + \en Create an extrusion surface. \~ + \details \ru Создать поверхность выдавливания кривой. \n + \en Create a surface of a curve extrusion. \n \~ + \param[in] curve - \ru Образующая кривая. + \en The generating curve. \~ + \param[in] direction - \ru Вектор выдавливания. + \en An extrusion vector. \~ + \param[in] simplify - \ru Упрощать поверхность, если возможно. + \en Simplify a surface if it's possible. \~ + \param[out] result - \ru Поверхность выдавливания. + \en An extrusion surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ExtrusionSurface( MbCurve3D & curve, const MbVector3D & direction, + bool simplify, MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать поверхность вращения. + \en Create a revolution surface. \~ + \details \ru Создать поверхность вращения кривой. \n + \en Create a curve revolution surface. \n \~ + \param[in] curve - \ru Образующая кривая. + \en The generating curve. \~ + \param[in] origin - \ru Точка положения оси вращения. + \en The rotation axis origin. \~ + \param[in] axis - \ru Направление оси вращения. + \en The rotation axis direction. \~ + \param[in] angle - \ru Угол вращения. + \en A rotation angle. \~ + \param[in] simplify - \ru Упрощать поверхность, если возможно. + \en Simplify a surface if it's possible. \~ + \param[out] result - \ru Поверхность вращения. + \en The revolution surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) RevolutionSurface( MbCurve3D & curve, const MbCartPoint3D & origin, const MbVector3D & axis, double angle, + bool simplify, MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать поверхность движения. + \en Create an expansion surface. \~ + \details \ru Создать поверхность движения кривой. \n + \en Create a surface of a curve sweeping. \n \~ + \param[in] curve - \ru Образующая кривая. + \en The generating curve. \~ + \param[in] spine - \ru Направляющая кривая. + \en The spine curve. \~ + \param[out] result - \ru Поверхность движения с доворотами. + \en The expansion surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ExpansionSurface( MbCurve3D & curve, MbCurve3D & spine, + MbCurve3D * curve1, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кинематическую поверхность. + \en Create an evolution surface. \~ + \details \ru Создать кинематическую поверхность по образующей и направляющей. \n + В случае, если spine имеет тип st_ConeSpiral, результатом построения + является спиральная поверхность. \n + \en Create an evolution surface from the generating curve and the guide curve. \n + If 'spine' has type st_ConeSpiral, the result of the construction + is a spiral surface. \n \~ + \param[in] curve - \ru Образующая кривая. + \en The generating curve. \~ + \param[in] spine - \ru Направляющая кривая. + \en The spine curve. \~ + \param[out] result - \ru Кинематическая поверхность или спиральная поверхность. + \en The evolution surface or a spiral surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) EvolutionSurface( const MbCurve3D & curve, + const MbCurve3D & spine, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать спиральную поверхность. + \en Create a spiral surface. \~ + \details \ru Создать спиральную поверхность по образующей и 3 точкам. \n + \en Create a spiral surface from a generating line and three points. \n \~ + \param[in] curve - \ru Образующая кривая спирали. + \en The generating curve of a spiral. \~ + \param[in] sameCurve - \ru Использовать копию образующей кривой, если sameCurve равен false. + \en Use a copy of the generating curve if the flag "sameCurve" is false. \~ + \param[in] p0 - \ru Начало локальной системы координат (ЛСК). + \en The origin of local coordinate system (LCS). \~ + \param[in] p1 - \ru Точка для формирования оси Z ЛСК. + \en A point specifying Z-axis of LCS. \~ + \param[in] p2 - \ru Точка для формирования оси X ЛСК. + \en A point specifying X-axis of LCS. \~ + \param[in] step - \ru Шаг спирали. + \en A pitch. \~ + \param[out] result - \ru Спиральная поверхность. + \en A spiral surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SpiralSurface( const MbCurve3D & curve, + bool sameCurve, + const MbCartPoint3D & p0, + const MbCartPoint3D & p1, + const MbCartPoint3D & p2, + double step, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать секториальную поверхность. + \en Create a sectorial surface. \~ + \details \ru Создать секториальную поверхность по кривой и точке. \n + \en Create a sectorial surface from a curve and a point. \n \~ + \param[in] curve - \ru Образующая кривая. + \en The generating curve. \~ + \param[in] point - \ru Точка. + \en A point. \~ + \param[out] result - \ru Линейчатая поверхность в виде сектора. + \en The ruled surface in a form of a sector. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SectorSurface( const MbCurve3D & curve, + const MbCartPoint3D & point, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать линейчатую поверхность. + \en Create a ruled surface. \~ + \details \ru Создать линейчатую поверхность по двум кривым. \n + \en Create a ruled surface from two curves. \n \~ + \param[in] curve1 - \ru Первая образующая кривая. + \en The first generating curve. \~ + \param[in] curve2 - \ru Вторая образующая кривая. + \en The second generating curve. \~ + \param[in] simplify - \ru Упрощать поверхность, если возможно. + \en Simplify a surface if it's possible. \~ + \param[out] result - \ru Линейчатая поверхность. + \en The ruled surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) RuledSurface( MbCurve3D & curve1, MbCurve3D & curve2, + bool simplify, MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать треугольную поверхность. + \en Create a triangular surface. \~ + \details \ru Создать треугольную поверхность по трем кривым. \n + \en Create a triangular surface from three curves. \n \~ + \param[in] curve1 - \ru Первая кривая. + \en The first curve. \~ + \param[in] curve2 - \ru Вторая кривая. + \en The second curve. \~ + \param[in] curve3 - \ru Третья кривая. + \en The third curve. \~ + \param[out] result - \ru Треугольная поверхность по трём кривым. + \en The triangular surface by three curves. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) CornerSurface( MbCurve3D & curve1, + MbCurve3D & curve2, + MbCurve3D & curve3, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать билинейную поверхность. + \en Create a bilinear surface. \~ + \details \ru Создать билинейную поверхность по четырем кривым. \n + \en Create a bilinear surface from four curves. \n \~ + \param[in] curve1 - \ru Первая кривая. + \en The first curve. \~ + \param[in] curve2 - \ru Вторая кривая. + \en The second curve. \~ + \param[in] curve3 - \ru Третья кривая. + \en The third curve. \~ + \param[in] curve4 - \ru Четвертая кривая. + \en The fourth curve. \~ + \param[out] result - \ru Билинейная поверхность по четырём кривым. + \en The bilinear surface from four curves. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) CoverSurface( MbCurve3D & curve1, + MbCurve3D & curve2, + MbCurve3D & curve3, + MbCurve3D & curve4, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать поверхность по семейству кривых. + \en Create a surface by a set of curves. \~ + \details \ru Создать поверхность по семейству кривых. \n + begDirection направление в начале поверхности может быть нулевой длины. \n + endDirection направление в конце поверхности может быть нулевой длины. \n + \en Create a surface by a set of curves. \n + begDirection direction at the begining of the surface can be of zero length. \n + endDirection direction at the end of the surface can be of zero length. \n \~ + \param[in] curveList - \ru Семейство образующих кривых вдоль U-направления. + \en A set of generating curves along U direction. \~ + \param[in] closed - \ru Замкнутость вдоль V-направления. + \en Closedness by V direction. \~ + \param[in] begDirection - \ru Вектор направления в начале поверхности. + \en The vector of direction at the beginning of the surface. \~ + \param[in] endDirection - \ru Вектор направления в конце поверхности. + \en The vector of direction at the end of the surface. \~ + \param[out] result - \ru Поверхность по семейству кривых. + \en The surface from the set of curves. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) LoftedSurface( const RPArray & curveList, bool closed, + const MbVector3D & begDirection, const MbVector3D & endDirection, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать поверхность по семейству кривых и направляющей. + \en Create a surface from a set of curves and a spine curve. \~ + \details \ru Создать поверхность по семейству кривых и направляющей. \n + \en Create a surface from a set of curves and a spine curve. \n \~ + \param[in] curveList - \ru Семейство образующих кривых вдоль U-направления. + \en A set of generating curves along U direction. \~ + \param[in] spine - \ru Направляющая кривая. + \en The spine curve. \~ + \param[out] result - \ru Поверхность по семейству кривых и направляющей. + \en The surface from a set of curves and a spine curve. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) LoftedSurface( const RPArray & curveList, + MbCurve3D & spine, + MbSurface *& result, + bool isSimToEvol = true ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать поверхность на сетке кривых. + \en Create a surface constructed by the grid curves. \~ + \details \ru Создать поверхность на сетке кривых по двум семействам кривых. \n + \en Create a surface constructed by the grid curves given two sets of curves. \n \~ + \param[in] uCurveList - \ru Семейство кривых вдоль U-направления. + \en A curve set along U direction. \~ + \param[in] vCurveList - \ru Семейство кривых вдоль V-направления. + \en A curve set along V direction. \~ + \param[out] result - \ru Поверхность на сетке кривых. + \en The surface constructed by the grid curves. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) MeshSurface( const RPArray & uCurveList, + const RPArray & vCurveList, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать эквидистантную поверхность. + \en Create an offset surface. \~ + \details \ru Создать эквидистантную поверхность к исходной поверхности. \n + \en Create an offset surface to a given surface. \n \~ + \param[in] surface - \ru Исходная поверхность. + \en The initial surface. \~ + \param[in] distance - \ru Величина эквидистанты (знаковая). + \en The offset distance (signed). \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \param[out] result - \ru Эквидистантная поверхность. + \en The offset surface. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) OffsetSurface( MbSurface & surface, + double distance, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать эквидистантную поверхность. + \en Create an offset surface. \~ + \details \ru Создать эквидистантную поверхность по исходной поверхности. \n + \en Create an offset surface from the initial surface. \n \~ + \param[in] surface - \ru Базовая поверхность. + \en The base surface. \~ + \param[in] offsetUminVmin - \ru Смещение в точке Umin Vmin базовой поверхности. + \en Offset distance on point Umin Vmin of base surface. \~ + \param[in] offsetUmaxVmin - \ru Смещение в точке Umax Vmin базовой поверхности. + \en Offset distance on point Umax Vmin of base surface. \~ + \param[in] offsetUminVmax - \ru Смещение в точке Umin Vmax базовой поверхности. + \en Offset distance on point Umin Vmax of base surface. \~ + \param[in] offsetUmaxVmax - \ru Смещение в точке Umax Vmax базовой поверхности. + \en Offset distance on point Umax Vmax of base surface. \~ + \param[in] type - \ru Тип смещения точек: константный, линейный или кубический. + \en The offset type: constant, or linear, or cubic. \~ + \param[out] result - \ru Эквидистантная поверхность. + \en The offset surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) OffsetSurface( MbSurface & surface, + double offsetUminVmin, + double offsetUmaxVmin, + double offsetUminVmax, + double offsetUmaxVmax, + MbeOffsetType type, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать продленную поверхность. + \en Create an extended surface. \~ + \details \ru Создать продленную поверхность по исходной поверхности. \n + \en Create an extended surface from the initial surface. \n \~ + \param[in] surface - \ru Исходная поверхность. + \en The initial surface. \~ + \param[in] uMin - \ru Минимальное значение по U. + \en The minimal parameter value by U. \~ + \param[in] uMax - \ru Максимальное значение по U. + \en The maximal parameter value by U. \~ + \param[in] vMin - \ru Минимальное значение по V. + \en The minimal parameter value by V. \~ + \param[in] vMax - \ru Максимальное значение по V. + \en The maximal parameter value by V. \~ + \param[out] result - \ru Продлённая поверхность. + \en The extended surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ExtendedSurface( MbSurface & surface, + double uMin, + double uMax, + double vMin, + double vMax, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать деформированную поверхность. + \en Create a deformed surface. \~ + \details \ru Создать деформированную поверхность по исходной поверхности. \n + \en Create a deformed surface from the initial surface. \n \~ + \param[in] surface - \ru Исходная поверхность. + \en The initial surface. \~ + \param[in] uCount - \ru Количество точек по U. + \en A number of points by U direction. \~ + \param[in] vCount - \ru Количество точек по V. + \en A number of points by V direction. \~ + \param[in] uDegree - \ru Порядок сплайнов по U. + \en Splines degree by U. \~ + \param[in] vDegree - \ru Порядок сплайнов по V. + \en Splines degree by V. \~ + \param[in] dist - \ru Величина сдвига вдоль нормали. + \en Shift along the normal. \~ + \param[out] result - \ru Деформированная поверхность. + \en The deformed surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) DeformedSurface( MbSurface & surface, + size_t uCount, size_t vCount, + size_t uDegree, size_t vDegree, + double dist, + MbSurface *& result); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать поверхность с заданной границей. + \en Create a surface with the given boundary. \~ + \details \ru Создать поверхность с заданной границей по массиву двумерных кривых. \n + Контейнер boundList может быть пустым. \n + \en Create a surface with the given boundary from an array of two-dimensional curves. \n + Container 'boundList' can be empty. \n \~ + \param[in] surface - \ru Исходная поверхность. + \en The initial surface. \~ + \param[in] boundList - \ru Множество двумерных границ в виде кривых (первая кривая - внешний контур). + \en An array of two-dimensional boundaries in the form of curves (the first curve is an outer contour). \~ + \param[out] result - \ru Поверхность, ограниченная кривыми. + \en The surface bounded by the curves. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) BoundedSurface( MbSurface & surface, + const RPArray & boundList, + MbSurface *& result ); + +//------------------------------------------------------------------------------ +/** \brief \ru Создать поверхность с заданной границей. + \en Create a surface with the given boundary. \~ + \details \ru Создать поверхность с заданной границей по массиву двумерных контуров. \n + \en Create a surface with the given boundary from an array of two-dimensional curves. \n \~ + \param[in] place - \ru Локальная система координат плоскости. + \en The local coordinate system of a plane. \~ + \param[in] region - \ru Множество двумерных границ в виде региона (первая контур - внешний). + \en An array of two-dimensional boundaries in the form of region (the first contour is outer). \~ + \param[out] result - \ru Поверхность, ограниченная кривыми. + \en The surface bounded by the curves. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) BoundedSurface( const MbPlacement3D & place, const MbRegion & region, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать NURBS копию поверхности, ограниченную двумерными границами. + \en Create a NURBS surface copy with two-dimensional boundaries. \~ + \details \ru Создать NURBS копию поверхности, ограниченную двумерными границами проецированием пространственных границ \n + (предполагается, что пространственные граничные кривые лежат на поверхности). \n + \en Create a NURBS surface copy with two-dimensional boundaries by projecting of the spatial boundaries \n + (the boundary space curves are considered to belong to the surface) \n \~ + \param[in] surf - \ru Исходная поверхность. + \en The initial surface. \~ + \param[in] version - \ru Версия исполнения. + \en The version. \~ + \param[out] resSurface - \ru Сплайновая поверхность (ограниченная кривыми). + \en The spline surface (bounded by the curves). \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) NurbsSurface( const MbSurface & surf, VERSION version, MbSurface *& resSurface ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать поверхность симплексного сплайна. + \en Create a simplex spline surface. \~ + \details \ru Создать поверхность симплексного сплайна по массиву вершин. \n + \en Create a simplex spline surface from a point array. \n \~ + \param[in] pList - \ru Множество вершин. + \en An array of points. \~ + \param[out] resSurface - \ru Поверхность симплексного сплайна. + \en The simplex spline surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SimplexSplineSurface( SArray & pList, MbSurface *& resSurface ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать треугольную поверхность Безье. + \en Create a triangular Bezier surface. \~ + \details \ru Создать треугольную поверхность Безье по 3 точкам. \n + \en Create a triangular Bezier surface from three points. \n \~ + \param[in] k - \ru Порядок поверхности. + \en The surface order. \~ + \param[in] p1 - \ru Первая точка. + \en The first point. \~ + \param[in] p2 - \ru Вторая точка. + \en The second point. \~ + \param[in] p3 - \ru Третья точка. + \en The third point. \~ + \param[out] resSurface - \ru Треугольная поверхность Безье. + \en The triangular Bezier surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) TriBezierSurface( ptrdiff_t k, MbCartPoint3D & p1, MbCartPoint3D & p2, MbCartPoint3D & p3, + MbSurface *& resSurface ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать треугольную В-сплайн поверхность. + \en Create a triangular B-spline surface. \~ + \details \ru Создать треугольную В-сплайн поверхность по 3 точкам. \n + \en Create a triangular B-spline surface from three points. \n \~ + \param[in] p0 - \ru Первая точка. + \en The first point. \~ + \param[in] p1 - \ru Вторая точка. + \en The second point. \~ + \param[in] p2 - \ru Третья точка. + \en The third point. \~ + \param[in] d - \ru Порядок поверхности. + \en The surface order. \~ + \param[out] resSurface - \ru Треугольная В-сплайн поверхность. + \en The triangular B-spline surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) TriSplineSurface( const MbCartPoint3D & p0, + const MbCartPoint3D & p1, + const MbCartPoint3D & p2, + const MbCartPoint3D & p3, + ptrdiff_t d, ptrdiff_t count, + MbSurface *& resSurface ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить характеристическую ломаную сплайновой поверхности. + \en Create a characteristic polyline of a spline surface. \~ + \details \ru Построить характеристическую ломаную сплайновой поверхности. \n + Функция работает с поверхностями типа st_SplineSurface, st_HermitSurface, + st_TriBezierSurface, st_TriSplineSurface. \n + \en Create a characteristic polyline of a spline surface. \n + The function accepts the surfaces of types st_SplineSurface, st_HermitSurface, + st_TriBezierSurface, st_TriSplineSurface. \n \~ + \param[in] surf - \ru Поверхность. + \en The surface. \~ + \param[out] segments - \ru Сегменты характеристической ломаной. + \en The characteristic polyline. \~ + \result \ru Возвращает true - если характеристическая ломаная получена. + \en Returns true - if the characteristic polyline is obtained. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) GetLineSegmentNURBSSurface( MbSurface & surf, RPArray & segments ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создание поверхности на сетке точек. + \en Create a surface from a points grid. \~ + \details \ru Создание поверхности на сетке точек и триангуляции. \n + Множество треугольников должен представлять собой правильную триангуляцию. + \en Create a surface from a points grid and triangulation. \n + The triangles array should form a regular triangulation. \~ + \param[in] grid - \ru Триангуляция. + \en A triangulation. \~ + \param[out] result - \ru Поверхность на сетке точек. + \en The surface on a point set. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) GridSurface( MbGrid & grid, MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать средние плоскости. + \en Create median planes. \~ + \details \ru Создать средние плоскости по двум кривым.\n + \en Create median planes from two curves.\n \~ + \param[in] curve1 - \ru Первая кривая. + \en The first curve. \~ + \param[in] curve2 - \ru Вторая кривая. + \en The second curve. \~ + \param[out] places - \ru Набор систем координат, задающих плоскости. + \en The set of coordinate systems which determine the planes. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \warning \ru В разработке. + \en Under development. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) MiddlePlaces( const MbCurve3D & curve1, + const MbCurve3D & curve2, + std::vector & places ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построение поверхности переменного сечения. + \en Construction of a swept section surface. \~ + \details \ru Поверхность переменного (конического) сечения образуется путем движения плоской кривой, являющейся коническим сечением, вдоль опорной кривой. + В процессе движения форма плоской кривой меняется в соответствии с дискриминантом конического сечения. + Начало плоской кривой располагается на начальной направляющей кривой, а конец - на конечной направляющей кривой. + Плоскость переменного сечения сохраняет ортогональность опорной кривой в процессе движения. + \en The swept (conic) section surface is formed by moving the flat conic section curve along the reference curve. + In the process of movement, the shape of the flat curve changes in accordance with the discriminant of the conic section. + The beginning of the flat curve is located on the first guide curve and the end is located on the second guide curve. + The plane of the swept section preserves the orthogonality to the reference curve during movement. \~ + \param[in] rc - \ru Опорная кривая. + \en The reference curve (spine). \~ + \param[in] g1 - \ru Первая направляющая кривая. + \en The first guide curve. \~ + \param[in] g2 - \ru Вторая направляющая кривая (g1==g2 совпадает с первой при cs_Round). + \en The second guide curve (g1==g2 the same first guide for st_Round). \~ + \param[in] c0 - \ru Дополнительная направляющая кривая (может быть NULL). + \en The additional guide curve (may be NULL). \~ + \param[in] form - \ru Форма сечения поверхности (0, 1, 2, 3). + \en The form of the surface section (0, 1, 2, 3). \~ + \param[in] sense - \ru Направление нормали поверхности направляющей кривой (для guide1==guide2). + \en The normal direction of guide curve surface (for guide1==guide2). \~ + \param[in] uBeg - \ru Минимальное значение первого параметра поверхности. + \en The minimum value of the first surface parameter. \~ + \param[in] uEnd - \ru Максимальное значение первого параметра поверхности. + \en The maximum value of the first surface parameter. \~ + \param[in] func - \ru Функция управления сечением поверхности. + \en Section control function. \~ + \param[in] patt - \ru Образующая кривая при form==cs_Shape. + \en Forming curve for form==cs_Shape. \~ + \param[in] accuracy - \ru Точность построения. + \en The accuracy of building. \~ + \param[in] vers - \ru Версия поверхности. + \en The surface version. \~ + \param[out] result - \ru Построенная поверхность. + \en The constructed surface. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \warning \ru В разработке. + \en Under development. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) SectionSurface( const MbCurve3D & rc, + const MbCurve3D & g1, + const MbCurve3D & g2, + const MbCurve3D * c0, + size_t form, + bool sense, + double uBeg, + double uEnd, + MbFunction * func, + MbCurve * patt, + double accuracy, + VERSION vers, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построение поверхности Кунса. + \en Construction of a Coons surface. \~ + \details \ru Построение бикубической поверхности Кунса на четырех кривых и их поперечных производных, касательной к четырём кривым на прверхностях. \n + \en The construction of Coons surface, which will be tangent to four surfaces and coincide with four curves on this surfaces on it sides.\n \~ + \param[in] surfaceCurve0 - \ru Кривая на поверхности 0. + \en The curve on surface0. \~ + \param[in] surfaceCurve1 - \ru Кривая на поверхности 1. + \en The curve on surface1. \~ + \param[in] surfaceCurve2 - \ru Кривая на поверхности 2. + \en The curve on surface2. \~ + \param[in] surfaceCurve3 - \ru Кривая на поверхности 3. + \en The curve on surface3. \~ + \param[out] result - \ru Построенная поверхность. + \en The constructed surface. \~ + \result \ru Возвращает код результата построения. + \en Returns operation result code. \~ + \warning \ru В разработке. + \en Under development. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) CreateCoonsSurface( const MbSurfaceCurve & surfaceCurve0, + const MbSurfaceCurve & surfaceCurve1, + const MbSurfaceCurve & surfaceCurve2, + const MbSurfaceCurve & surfaceCurve3, + MbSurface *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построение поверхности-заплатки для заданных рёбер. + \en Construction of a surface-patch by the edges. \~ + \details \ru Построение поверхности-заплатки, гладко стыкующейся с поверхностями ребер. \n + \en Construction of a surface-patch, smoothly joining with the surfaces of the edges. \n \~ + \param[in] edges - \ru Ребра, с которыми требуется стыковать новую поверхность. + \en Edges to join the new surface. \~ + \param[out] result - \ru Построенные поверхности. + \en The constructed surfaces. \~ + \result \ru Возвращает код результата построения. + \en Returns operation result code. \~ + \warning \ru В разработке. + \en Under development. \~ + \ingroup Surface_Modeling +*/ +// --- +MATH_FUNC (MbResultType) CreateSplinePatch( const std::vector & edges, + std::vector & result ); + + +#endif // __ACTION_SURFACE_H diff --git a/C3d/Include/action_surface_curve.h b/C3d/Include/action_surface_curve.h index d49cfc1..2d3dc64 100644 --- a/C3d/Include/action_surface_curve.h +++ b/C3d/Include/action_surface_curve.h @@ -1,1083 +1,1082 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Методы построения трехмерных кривых. - \en Functions for three-dimensional curves construction. \~ - \details \ru На базе кривых строятся рёбра. Рёбра используются в твёрдотельной и каркасной модели. - Кроме того, кривые используются для построения поверхностей, а также могут служить - вспомогательными элементами модели. - \en Edges are created on the basis of curves. Edges are used in solid and wireframe model. - In addition curves are used for construction of surfaces as well as can be used - as auxiliary elements of a model. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ACTION_SURFACE_CURVE_H -#define __ACTION_SURFACE_CURVE_H - - -#include -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbAxis3D; -class MATH_CLASS MbCurve; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbContour3D; -class MATH_CLASS MbSurfaceCurve; -class MATH_CLASS MbSurface; -class MATH_CLASS MbElementarySurface; -class MATH_CLASS MbFace; -class MATH_CLASS MbSolid; -class MATH_CLASS MbWireFrame; -class MATH_CLASS MbSNameMaker; - - -//------------------------------------------------------------------------------ -/** \brief \ru Рассчитать вершины ломаной. - \en Compute the vertices of a polyline. \~ - \details \ru Рассчитать вершины ломаной point1 и point2, соединяющей точки origin1 и origin2, - сдвинутые в направлениях direction1 и direction2 на расстояния length1 и length2, - которую можно скруглить радиусами radius1 и radius2. \n - \en Compute the vertices 'point1' and 'point2' of a polyline connecting points 'origin1' and 'origin2' - translated in the directions 'direction1' and 'direction2' by distances 'length1' and 'length2' - which can be rounded with radius 'radius1' and 'radius2'. \n \~ - \param[in] origin1 - \ru Первая точка. - \en The first point. \~ - \param[in] direction1 - \ru Направление сдвига первой точки. - \en The direction of the first point translation. \~ - \param[in] length1 - \ru Величина сдвига первой точки. - \en The distance of the first point translation. \~ - \param[in] radius1 - \ru Радиус скругления для первой точки. - \en The rounding radius for the first point. \~ - \param[in] origin2 - \ru Вторая точка. - \en The second point. \~ - \param[in] direction2 - \ru Направления сдвига для второй точки. - \en The direction of the second point translation. \~ - \param[in] length2 - \ru Величина сдвига для второй точки. - \en The distance of the second point translation. \~ - \param[in] radius2 - \ru Радиус скругления для второй точки. - \en The rounding radius for the second point. \~ - \param[out] result1 - \ru Первая точка ломаной. - \en The first point of the polyline. \~ - \param[out] result2 - \ru Вторая точка ломаной. - \en The second point of the polyline. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) CalculatePipePoints( const MbCartPoint3D & origin1, - const MbVector3D & direction1, - double length1, double radius1, - const MbCartPoint3D & origin2, - const MbVector3D & direction2, - double length2, double radius2, - MbCartPoint3D & result1, MbCartPoint3D & result2 ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать эквидистантную кривую. - \en Create an offset curve. \~ - \details \ru Создать эквидистантную кривую по плоской кривой. \n - \en Create an offset curve from a planar curve. \n \~ - \param[in] curve - \ru Исходная кривая. - \en The initial curve. \~ - \param[in] d - \ru Величина эквидистанты. - \en The offset distance. \~ - \param[out] result - \ru Эквидистантная кривая. - \en The offset curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) OffsetPlaneCurve( const MbCurve3D & curve, - double d, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать эквидистантную кривую. - \en Create an offset curve. \~ - \details \ru Создать эквидистантную кривую по трехмерной кривой и вектору направления. \n - \en Create an offset curve from a three-dimensional curve and a direction vector. \n \~ - \param[in] initCurve - \ru Постранственная кривая, к которой строится эквидистантная. - \en A space curve for which to construct the offset curve. \~ - \param[in] offsetVect - \ru Вектор, задающий смещение в точке кривой. - \en The displacement vector at a point of the curve. \~ - \param[in] useFillet - \ru Если true, то разрывы заполнять скруглением, иначе продолженными кривыми. - \en If 'true', the gaps are to be filled with fillet, otherwise with the extended curves. \~ - \param[in] keepRadius - \ru Если true, то в существующих скруглениях сохранять радиусы. - \en If 'true', the existent fillet radii are to be kept. \~ - \param[in] bluntAngle - \ru Если true, то в притуплять острые углы. - \en If 'true', sharp corners are to be blunt. \~ - \param[in] fromBeg - \ru Вектор смещения привязан к началу. - \en The translation vector is associated with the beginning. \~ - \param[in] snMaker - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] result - \ru Каркас с построенными кривыми. - \en The frame with the constructed curves. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) OffsetCurve( const MbCurve3D & initCurve, - const MbVector3D & offsetVect, - const bool useFillet, - const bool keepRadius, - const bool bluntAngle, - const bool fromBeg, - const MbSNameMaker & snMaker, - MbWireFrame *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать эквидистантную кривую. - \en Create an offset curve. \~ - \details \ru Создать эквидистантную кривую по поверхностной кривой и значению смещения. \n - \en Create an offset curve from a curve on a surface and a shift value. \n \~ - \param[in] curve - \ru Кривая на поверхности грани face. - \en A curve on face 'face' surface. \~ - \param[in] face - \ru Грань, на которой строится эквидистанта. - \en The edge on which to build the offset curve. \~ - \param[in] dirAxis - \ru Направление смещения с точкой приложения. - \en The offset direction with a point of application. \~ - \param[in] dist - \ru Величина смещения. - \en The offset distance. \~ - \param[in] snMaker - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] result - \ru Каркас с построенными кривыми. - \en The frame with the constructed curves. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) OffsetCurve( const MbCurve3D & curve, - const MbFace & face, - const MbAxis3D & dirAxis, - double dist, - const MbSNameMaker & snMaker, - MbWireFrame *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать проекцию кривой на поверхность. - \en Create a curve projection onto the surface. \~ - \details \ru Создать проекцию кривой curve на поверхность surface (направление проецирования direction может быть NULL). \n - \en Create the projection of a curve onto surface 'surface' (the projection direction 'direction' can be NULL). \n \~ - \param[in] surface - \ru Поверхность для проецирования. - \en The surface to project onto. \~ - \param[in] curve - \ru Проецируемая кривая. - \en The curve to project. \~ - \param[in] direction - \ru Направление проецирования (если не указано то проецирование по нормали). - \en The projection direction (if not specified, the projection along the normal). \~ - \param[in] createExact - \ru Создавать проекционную кривую при необходимости. - \en Create a projection curve if necessary. \~ - \param[in] truncateByBounds - \ru Усекать границами поверхности. - \en Truncate by the surface bounds. \~ - \param[in] version - \ru Версия исполнения. - \en The version. \~ - \param[out] result - \ru Множество кривых на поверхности. - \en An array of curves on the surface. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) CurveProjection( const MbSurface & surface, - const MbCurve3D & curve, - MbVector3D * direction, - bool createExact, - bool truncateByBounds, - RPArray & result, - VERSION version = Math::DefaultMathVersion() ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать пространственную кривую по двум плоским проекциям. - \en Create a space curve from two planar projections. \~ - \details \ru Создать пространственную кривую по двум плоским проекциям. \n - \en Create a space curve from two planar projections. \n \~ - \param[in] place1 - \ru Локальная система координат 1. - \en A local coordinate system 1. \~ - \param[in] curve1 - \ru Двумерная кривая 1. - \en A two-dimensional curve 1. \~ - \param[in] place2 - \ru Локальная система координат 2. - \en A local coordinate system 2. \~ - \param[in] curve2 - \ru Двумерная кривая 2. - \en A two-dimensional curve 2. \~ - \param[out] result - \ru Множество трехмерных кривых. - \en The array of three-dimensional curves. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) CurveByTwoProjections( const MbPlacement3D & place1, - const MbCurve & curve1, - const MbPlacement3D & place2, - const MbCurve & curve2, - RPArray & result, - VERSION version = Math::DefaultMathVersion() ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать проекционную кривую по нормали или по направлению. - \en Create a projection curve from a normal or from a direction. \~ - \details \ru Создать проекционную кривую по нормали или по направлению. \n - Если проекция на конструктивную плоскость (плоскость без границ), - то создать на ее основе грань и прислать, а за ее удалением следит приславший. \n - \en Create a projection curve from a normal or from a direction. \n - If the projection is onto the constructive plane (a plane without bounds), - create a face on the basis of this projection. \n \~ - \param[in] curve - \ru Проецируемая кривая. - \en The curve to project. \~ - \param[in] faces - \ru Связный набор граней. - \en A connected set of faces. \~ - \param[in] dir - \ru Вектор направления (если его нет, проекция по нормали). - \en The direction vector (if it is absent, the normal projection). \~ - \param[in] createExact - \ru Создавать проекционную кривую при необходимости. - \en Create a projection curve if necessary. \~ - \param[in] truncateByBounds - \ru Усечь границами. - \en Truncate by bounds. \~ - \param[in] snMaker - \ru Именователь с версией. - \en An object defining the names with the version. \~ - \param[out] result - \ru Проекционные кривые. - \en The projection curves. \~ - \param[out] resultIndices - \ru Индексы соответствия (номера граней в исходном массиве). - \en The indices of faces in the initial array. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) ProjectionCurve( const MbCurve3D & curve, - const RPArray & faces, - const MbVector3D * dir, - const bool createExact, - const bool truncateByBounds, - const MbSNameMaker & snMaker, - RPArray & result, - SArray * resultIndices ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать проекционный проволочный каркас по нормали или по направлению. - \en Create a projection wireframe from a normal or from a direction. \~ - \details \ru Создать проекционный проволочный каркас по нормали или по направлению. \n - Если проекция на конструктивную плоскость (плоскость без границ), - то создать на ее основе грань и прислать, а за ее удалением следит приславший. \n - \en Create a projection curve from a normal or from a direction. \n - If the projection is onto the constructive plane (a plane without bounds), - create a face on the basis of this projection. \n \~ - \param[in] wireFrame - \ru Проецируемый проволочный каркас. - \en The wireframe to project. \~ - \param[in] sameWireFrame - \ru Использовать тот же экземпляр проволочного каркаса, или создать копию. - \en Flag whether to use the same wireframe or make a copy of it. \~ - \param[in] solid - \ru Тело. - \en Solid. \~ - \param[in] same - \ru Использовать ли тот же экземпляр журнала тела или создать копию. - \en Flag whether to use the same creators of the body or make a copy. \~ - \param[in] faceIndices - \ru Номера граней в первой оболочке. - \en The numbers of faces in the first shell. \~ - \param[in] dir - \ru Вектор направления (если его нет, проекция по нормали). - \en The direction vector (if it is absent, the normal projection). \~ - \param[in] createExact - \ru Создавать проекционную кривую при необходимости. - \en Create a projection curve if necessary. \~ - \param[in] truncateByBounds - \ru Усечь границами. - \en Truncate by bounds. \~ - \param[in] snMaker - \ru Именователь с версией. - \en An object defining the names with the version. \~ - \param[out] resFrame - \ru Результирующий проволочный каркас, в котором в атрибутах ребер лежат имена соответствующих граней. - \en The resulting wireframe, the attributes of the edges contain the names of corresponding faces. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) ProjectionCurve( const MbWireFrame & wireFrame, - const bool sameWireFrame, - const MbSolid & solid, - const bool same, - const SArray & faceIndices, - const MbVector3D * dir, - const bool createExact, - const bool truncateByBounds, - const MbSNameMaker & snMaker, - MbWireFrame *& resFrame ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Устранить наложение сегментов проекционной кривой. - \en Eliminate the projection curve segments overlay. \~ - \details \ru Устранить наложение сегментов проекционной кривой (вспомогательная функция для функции ProjectionCurve). \n - \en Eliminate the projection curve segments overlay (an auxiliary function for function ProjectionCurve). \n \~ - \param[in,out] curves - \ru Множество кривых. - \en An array of curves. \~ - \param[in,out] indices - \ru Множество индексов, синхронный с массивом кривых. - \en An array of indices synchronized with the array of curves. \~ - \return \ru Возвращает true, если что-то изменилось в наборе кривых. - \en Returns true if something has modified in the curve set. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (bool) EliminateProjectionCurveOverlay( RPArray & curves, - SArray * indices ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать массив линий очерка поверхности. - \en Create an array of isocline curves of the surface. \~ - \details \ru Создать массив линий очерка поверхности с обрезкой по области определения. \n - \en Create an array of isocline curves of the surface with truncation by the definition domain. \n \~ - \param[in] surface - \ru Поверхность. - \en The surface. \~ - \param[in] eye - \ru Вектор взгляда. - \en The direction of view. \~ - \param[in] perspective - \ru Является ли проекция перспективной. - \en Whether the projection is perspective. \~ - \param[in] removeOnSurfaceBounds - \ru Удалить линии очерка, совпадающие с границами поверхности. - \en Remove the isocline curves coincident with the surface bounds. \~ - \param[out] result - \ru Выходной массив линий очерка. - \en The output array of isocline curves. \~ - \param[in] version - \ru Версия построения. - \en The version. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Mapping -*/ -// --- -MATH_FUNC (MbResultType) SilhouetteCurve( const MbSurface & surface, - const MbVector3D & eye, - bool perspective, - bool removeOnSurfaceBounds, - RPArray & result, - VERSION version = Math::DefaultMathVersion() ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать массив линий очерка грани. - \en Create an array of isocline curves of the face. \~ - \details \ru Создать массив линий очерка грани с обрезкой по области определения. \n - \en Create an array of isocline curves of the face with truncation by the definition domain. \n \~ - \param[in] face - \ru Грани. - \en The face. \~ - \param[in] eye - \ru Вектор взгляда. - \en The direction of view. \~ - \param[in] perspective - \ru Является ли проекция перспективной. - \en Whether the projection is perspective. \~ - \param[out] result - \ru Выходной массив линий очерка. - \en The output array of isocline curves. \~ - \param[in] version - \ru Версия построения. - \en The version. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Mapping -*/ -// --- -MATH_FUNC (MbResultType) SilhouetteCurve( const MbFace & face, - const MbVector3D & eye, - bool perspective, - RPArray & result, - VERSION version = Math::DefaultMathVersion() ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать массив линий очерка поверхности при вращательном движении вокруг оси. - \en Create an array of isocline curves of the rotated surface. \~ - \details \ru Создать массив линий очерка поверхности с обрезкой по области определения. \n - \en Create an array of isocline curves of the surface with truncation by the definition domain. \n \~ - \param[in] surface - \ru Поверхность. - \en The surface. \~ - \param[in] axis - \ru Ось кругового взгляда (ось токарного сечения). - \en The axis of lathe section. \~ - \param[in] removeOnSurfaceBounds - \ru Удалить линии очерка, совпадающие с границами поверхности. - \en Remove the isocline curves coincident with the surface bounds. \~ - \param[out] result - \ru Выходной массив линий очерка. - \en The output array of isocline curves. \~ - \param[in] version - \ru Версия построения. - \en The version. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Mapping -*/ -// --- -MATH_FUNC (MbResultType) SilhouetteCurve( const MbSurface & surface, - const MbAxis3D & axis, - bool removeOnSurfaceBounds, - RPArray & curves, - VERSION version = Math::DefaultMathVersion() ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать массив линий очерка грани при вращательном движении вокруг оси. - \en Create an array of isocline curves of the rotated face. \~ - \details \ru Создать массив линий очерка грани с обрезкой по области определения. \n - \en Create an array of isocline curves of the face with truncation by the definition domain. \n \~ - \param[in] face - \ru Грани. - \en The face. \~ - \param[in] axis - \ru Ось кругового взгляда (ось токарного сечения). - \en The axis of lathe section. \~ - \param[out] result - \ru Выходной массив линий очерка. - \en The output array of isocline curves. \~ - \param[in] version - \ru Версия построения. - \en The version. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Mapping -*/ -// --- -MATH_FUNC (MbResultType) SilhouetteCurve( const MbFace & face, - const MbAxis3D & axis, - RPArray & curves, - VERSION version = Math::DefaultMathVersion() ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кривые пересечения двух поверхностей. - \en Create the intersection curves of two surfaces. \~ - \details \ru Создать кривые пересечения двух поверхностей. Результат - массив кривых пересечения поверхностей. \n - \en Create the intersection curves of two surfaces. The result is an array of intersection curves of surfaces. \n \~ - \param[in] surface1 - \ru Первая поверхность. - \en The first surface. \~ - \param[in] surface2 - \ru Вторая поверхность. - \en The second surface. \~ - \param[in] snMaker - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] result - \ru Каркас с построенными кривыми. - \en The frame with the constructed curves. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \warning \ru Лучше использовать IntersectionCurve на гранях, т.к. границы поверхностей могут бы неточные, \n - что приведет к неточному положению концов кривых пересечения в результате операции. \n - В гранях же границы поверхности точные, т.к. хранятся в виде кривых пересечения, - а не виде двумерных кривых. \n - \en It is better to use IntersectionCurve on faces since the surfaces bounds can be inexact, \n - and it will result in inexact position of intersection curves ends. \n - But the surface bounds in faces are exact since they are stored in the form of intersection curves, - not in the form of two-dimensional curves. \n \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) IntersectionCurve( const MbSurface & surface1, - const MbSurface & surface2, - const MbSNameMaker & snMaker, - MbWireFrame *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кривые пересечения двух граней. - \en Create intersection curves of two faces. \~ - \details \ru Создать кривые пересечения двух граней. Результат - массив кривых пересечения поверхностей. \n - \en Create intersection curves of two faces. The result is an array of intersection curves of surfaces. \n \~ - \param[in] face1 - \ru Первая грань оболочки. - \en The first face of the shell. \~ - \param[in] face2 - \ru Вторая грани оболочки. - \en The second face of the shell. \~ - \param[in] snMaker - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] result - \ru Каркас с построенными кривыми. - \en The frame with the constructed curves. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) IntersectionCurve( MbFace & face1, MbFace & face2, - const MbSNameMaker & snMaker, - MbWireFrame *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кривые пересечения граней двух оболочек. - \en Create intersection curves of two shells faces. \~ - \details \ru Создать кривые пересечения граней двух оболочек. Результат - массив кривых пересечения поверхностей. \n - \en Create intersection curves of two shells faces. The result is an array of intersection curves of surfaces. \n \~ - \param[in] solid1 - \ru Первая оболочка. - \en The first shell. \~ - \param[in] faceIndices1 - \ru Номера граней в первой оболочке. - \en The numbers of faces in the first shell. \~ - \param[in] solid2 - \ru Вторая оболочка. - \en The second shell. \~ - \param[in] faceIndices2 - \ru Номера граней во второй оболочке. - \en The numbers of faces in the second shell. \~ - \param[in] snMaker - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] result - \ru Каркас с построенными кривыми. - \en The frame with the constructed curves. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) IntersectionCurve( const MbSolid & solid1, const SArray & faceIndices1, - const MbSolid & solid2, const SArray & faceIndices2, - const MbSNameMaker & snMaker, - MbWireFrame *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кривые пересечения граней двух оболочек. - \en Create intersection curves of two shells faces. \~ - \details \ru Создать кривые пересечения граней двух оболочек. Результат - массив кривых пересечения поверхностей. \n - \en Create intersection curves of two shells faces. The result is an array of intersection curves of surfaces. \n \~ - \param[in] solid1 - \ru Первая оболочка. - \en The first shell. \~ - \param[in] faceIndices1 - \ru Номера граней в первой оболочке. - \en The numbers of faces in the first shell. \~ - \param[in] same1 - \ru Использовать ли тот же журнал построителей первого тела или сделать копию. - \en Flag whether to use the same creators of the first body or make a copy. \~ - \param[in] solid2 - \ru Вторая оболочка. - \en The second shell. \~ - \param[in] faceIndices2 - \ru Номера граней во второй оболочке. - \en The numbers of faces in the second shell. \~ - \param[in] same2 - \ru Использовать ли тот же самый журнал построителей второго тела или сделать копию. - \en Flag whether to use the same creators of the second body or make a copy. \~ - \param[in] snMaker - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] result - \ru Каркас с построенными кривыми. - \en The frame with the constructed curves. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) IntersectionCurve( const MbSolid & solid1, const SArray & faceIndices1, const bool same1, - const MbSolid & solid2, const SArray & faceIndices2, const bool same2, - const MbSNameMaker & snMaker, MbWireFrame *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать линию пересечения поверхностей. - \en Create an intersection curve of surfaces. \~ - \details \ru Создать линию пересечения поверхностей surf1 и surf2 по известным началу и концу линии пересечения. \n - \en Create an intersection curve of surfaces 'surf1' and 'surf2' from the specified start point and end point of the intersection curve. \n \~ - \param[in] surface1 - \ru Первая поверхность. - \en The first surface. \~ - \param[in] ext1 - \ru На расширенной первой поверхности. - \en Whether to create on the extended surface. \~ - \param[in] uv1beg - \ru Начальная точка на первой поверхности. - \en The start point on the first surface. \~ - \param[in] uv1end - \ru Конечная точка на первой поверхности. - \en The end point on the first surface. \~ - \param[in] surface2 - \ru Вторая поверхность. - \en The second surface. \~ - \param[in] ext2 - \ru На расширенной второй поверхности. - \en Whether to create on the extended second surface. \~ - \param[in] uv2beg - \ru Начальная точка на второй поверхности. - \en The start point on the second surface. \~ - \param[in] uv2end - \ru Конечная точка на второй поверхности. - \en The end point on the second surface. \~ - \param[in] dir - \ru Начальное направление создания линии пересечения. - \en The start direction for intersection curve creation. \~ - \param[out] result1 - \ru Двумерная кривая на первой поверхности. - \en The two-dimensional curve on the first surface. \~ - \param[out] result2 - \ru Двумерная кривая на второй поверхности. - \en The two-dimensional curve on the second surface. \~ - \param[out] label - \ru Тип полученной кривой пересечения. - \en The resultant intersection curve type. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) IntersectionCurve( const MbSurface & surface1, bool ext1, - const MbCartPoint & uv1beg, - const MbCartPoint & uv1end, - const MbSurface & surface2, bool ext2, - const MbCartPoint & uv2beg, - const MbCartPoint & uv2end, - const MbVector3D & dir, - MbCurve *& result1, - MbCurve *& result2, - MbeCurveBuildType & label ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать линию пересечения поверхностей. - \en Create an intersection curve of surfaces. \~ - \details \ru Создать линию пересечения поверхностей surf1 и surf2 по известным началу и концу линии пересечения и вспомогательной кривой. \n - \en Create an intersection curve of surfaces 'surf1' and 'surf2' from the specified start point and end point of the intersection curve - and guide curve that approximates the desired curve. \n \~ - \param[in] surface1 - \ru Первая поверхность. - \en The first surface. \~ - \param[in] ext1 - \ru На расширенной первой поверхности. - \en Whether to create on the extended surface. \~ - \param[in] uv1beg - \ru Начальная точка на первой поверхности. - \en The start point on the first surface. \~ - \param[in] uv1end - \ru Конечная точка на первой поверхности. - \en The end point on the first surface. \~ - \param[in] surface2 - \ru Вторая поверхность. - \en The second surface. \~ - \param[in] ext2 - \ru На расширенной второй поверхности. - \en Whether to create on the extended second surface. \~ - \param[in] uv2beg - \ru Начальная точка на второй поверхности. - \en The start point on the second surface. \~ - \param[in] uv2end - \ru Конечная точка на второй поверхности. - \en The end point on the second surface. \~ - \param[in] guideCurve - \ru Направляющая кривая, приближенно описывающая искомую кривую. - \en The guide curve that approximates the desired curve. \~ - \param[in] useRedetermination - \ru Флаг, определяющий нужно ли уточнять шаг построения следующей точки по сравнению с функцией DeviationStep. - \en The flag that determines whether it is necessary to specify the next point build step as compared to the DeviationStep function. \~ - \param[in] checkPoles - \ru Флаг необходимости проверки и корректировки полюсных точек. - \en The flag that determines whether it is necessary to check and correct pole points. \~ - \param[out] result1 - \ru Двумерная кривая на первой поверхности. - \en The two-dimensional curve on the first surface. \~ - \param[out] result2 - \ru Двумерная кривая на второй поверхности. - \en The two-dimensional curve on the second surface. \~ - \param[out] label - \ru Тип полученной кривой пересечения. - \en The resultant intersection curve type. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC( MbResultType ) IntersectionCurve( const MbSurface & surf1, bool ext1, - const MbCartPoint & uv1beg, - const MbCartPoint & uv1end, - const MbSurface & surf2, bool ext2, - const MbCartPoint & uv2beg, - const MbCartPoint & uv2end, - const MbCurve3D * guideCurve, - bool useRedetermination, - bool checkPoles, - MbCurve *& pCurve1, - MbCurve *& pCurve2, - MbeCurveBuildType & label ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать пространственный сплайн через точки и с сопряжениями. - \en Create a spatial spline through points and with the given derivatives. \~ - \details \ru Создать пространственный сплайн через точки и с сопряжениями. \n - Примечания: \n - Если есть сопряжения, то количество сопряжений должно быть равно количеству точек. \n - Отсутствующие сопряжения должны быть представлены нулевыми указателями в массиве \n - \en Create a spatial spline through points and with the given derivatives. \n - Notes: \n - If derivatives are specified, the number of derivatives should be equal to the number of points. \n - Missing derivatives should be represented by null pointers in the array \n \~ - \param[in] points - \ru Точки. - \en Points. \~ - \param[in] paramType - \ru Тип параметризации. - \en The parametrization type. \~ - \param[in] degree - \ru Порядок сплайна. - \en A spline degree. \~ - \param[in] closed - \ru Замкнутость сплайна. - \en The spline closedness. \~ - \param[in] transitions - \ru Заданные сопряжения. - \en The specified derivatives. \~ - \param[in] snMaker - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] result - \ru Каркас с построенными кривыми. - \en The frame with the constructed curves. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) SpaceSplineThrough( const SArray & points, - MbeSplineParamType paramType, - size_t degree, - bool closed, - RPArray< MbPntMatingData > & transitions, - const MbSNameMaker & snMaker, - MbWireFrame *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать пространственный сплайн по точкам и с сопряжениями. - \en Create a spatial spline from points and derivatives. \~ - \details \ru Создать пространственный сплайн по точкам и с сопряжениями. \n - \en Create a spatial spline from points and derivatives. \n \~ - \param[in] points - \ru Множество точек. - \en An array of points. \~ - \param[in] degree - \ru Порядок сплайна. - \en A spline degree. \~ - \param[in] closed - \ru Строить замкнутый сплайн. - \en Create a closed spline. \~ - \param[in] weights - \ru Множество весов точек. - \en An array of points weights. \~ - \param[in] knots - \ru Узловой вектор сплайна. - \en A knot vector of the spline. \~ - \param[in] begData - \ru Сопряжение в начале. - \en The start derivative. \~ - \param[in] endData - \ru Сопряжение в конце. - \en The end derivative. \~ - \param[in] snMaker - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] result - \ru Каркас с построенными кривыми. - \en The frame with the constructed curves. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) SpaceSplineBy( const SArray & points, - size_t degree, - bool closed, - const SArray * weights, - const SArray * knots, - MbPntMatingData * begData, - MbPntMatingData * endData, - const MbSNameMaker & snMaker, - MbWireFrame *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кривую на поверхности. - \en Create a curve on a surface. \~ - \details \ru Создать кривую на поверхности. \n - Примечания: \n - 1. Если есть сопряжения, то количество сопряжений должно быть равно количеству точек. \n - Отсутствующие сопряжения должны быть представлены нулевыми указателями в массиве \n - 2. Если сплайн строится через точки, то сопряжения могуть быть заданы произвольно. \n - 2. Если сплайн строится по полюсам и он незамкнут, то сопряжения могут быть только на концах. \n - 3. Если сплайн строится по полюсам и он замкнут, то сопряжения должны отсутствовать. \n - 4. Множество весов должен быть пуст или синхронизирован с массивом точек по количеству - (с опцией throughPoints веса игнорируются). \n - \en Create a curve on a surface. \n - Notes: \n - 1. If derivatives are specified, the number of derivatives should be equal to the number of points. \n - Missing derivatives should be represented by null pointers in the array \n - 2. If the spline is created from points, arbitrary derivatives can be defined. \n - 2. If the spline is created from poles and it is open, only the end derivatives can be specified. \n - 3. If the spline is constructed from poles and it is closed, the derivatives cannot be specified. \n - 4. An array of weights should be empty or synchronized with the point array by size - (with option throughPoints weights are ignored). \n \~ - \param[in] surface - \ru Поверхность. - \en The surface. \~ - \param[in] throughPoints - \ru Провести сплайн через точки. - \en Create a spline through points. \~ - \param[in] paramPnts - \ru Множество параметрических точек. - \en An array of parametric points. \~ - \param[in] paramWts - \ru Множество весов параметрических точек. - \en An array of parametric point weights. \~ - \param[in] paramClosed - \ru Строить замкнутый параметрический сплайн. - \en Create a closed parametric spline. \~ - \param[in] spaceTransitions - \ru Сопряжения в точках. - \en Derivatives in the points. \~ - \param[in] snMaker - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] result - \ru Каркас с построенными кривыми. - \en The frame with the constructed curves. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) SurfaceSpline( const MbSurface & surface, - bool throughPoints, - SArray & paramPnts, - SArray & paramWts, - bool paramClosed, - RPArray< MbPntMatingData > & spaceTransitions, - const MbSNameMaker & snMaker, - MbWireFrame *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать изопараметрическую кривую. - \en Create an isoparametric curve. \~ - \details \ru Создать изопараметрическую кривую на поверхности surface. \n - \en Create an isoparametric curve on surface 'surface'. \n \~ - \param[in] surface - \ru Поверхность. - \en The surface. \~ - \param[in] x - \ru Значение по первому параметру. - \en A value of the first parameter. \~ - \param[in] isU - \ru Первый параметр есть U. - \en Whether the first parameter is U. \~ - \param[in] yRange - \ru Диапазон по второму параметру (если не задан, используются параметрические границы поверхности). - \en A range of the second parameter (if not defined, the parametric bounds of the surface are used). \~ - \param[out] result - \ru Изопараметрическая кривая. - \en The isoparametric curve. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) IsoparametricCurve( const MbSurface & surface, - double x, bool isU, const MbRect1D * yRange, - MbCurve3D *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кривую - мостик, соединяющую кривые curve1 и curve2. - \en Create a transition curve connecting curves 'curve1' and 'curve2'. \~ - \details \ru Создать кривую - мостик, соединяющую кривые curve1 и curve2 кубическим сплайном Эрмита. \n - \en Create a transition curve connecting curves 'curve1' and 'curve2' by a cubic Hermite spline. \n \~ - \param[in] curve1 - \ru Сопрягаемая кривая 1. - \en A curve 1 to be connected. \~ - \param[in] t1 - \ru Параметр точки на сопрягаемой кривой 1. - \en A point parameter on the curve 1. \~ - \param[in] sense1 - \ru Начало мостика совпадает с направлением кривой curve1 (true). - \en The beginning of the transition curve is equal to the direction of 'curve1' (true). \~ - \param[in] curve2 - \ru Сопрягаемая кривая 2. - \en A curve 2 to be connected. \~ - \param[in] t2 - \ru Параметр точки на сопрягаемой кривой 2. - \en A point parameter on the curve 2. \~ - \param[in] sense2 - \ru Конец мостика совпадает с направлением кривой curve2 (true). - \en The end of the transition curve is equal to the direction of 'curve2' (true). \~ - \param[in] names - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] result - \ru Каркас с построенными кривыми. - \en The frame with the constructed curves. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) BridgeCurve( const MbCurve3D & curve1, double t1, bool sense1, - const MbCurve3D & curve2, double t2, bool sense2, - const MbSNameMaker & names, - MbWireFrame *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать составную кривую плавного соединения концов двух кривых. - \en Create a composite curve smoothly connecting two curves ends. \~ - \details \ru Создать составную кривую плавного соединения концов двух кривых. \n - Полученная кривая состоит из трёхмерной дуги радиуса radius1, - отрезка (в определенных случаях отрезок отсутствует), - трёхмерной дуги радиуса radius2. - \en Create a composite curve smoothly connecting two curves ends. \n - The constructed curve consists of a three-dimensional arc of radius 'radius1', - a segment (in specific cases a segment is absent), - a three-dimensional arc of radius 'radius2'. \~ - \param[in] curve1 - \ru Соединяемая кривая 1. - \en A curve 1 to be connected. \~ - \param[in] isBegin1 - \ru Начало соединяемой кривой 1 (true). - \en The beginning of the curve 1 (true). \~ - \param[in] radius1 - \ru Радиус сопряжения у соединяемой кривой 1. - \en The conjugation raidus of curve 1. \~ - \param[in] curve2 - \ru Соединяемая кривая 2. - \en A curve 2 to be connected. \~ - \param[in] isBegin2 - \ru Начало соединяемой кривой 2 (true). - \en The beginning of the curve 2 (true). \~ - \param[in] radius2 - \ru Радиус сопряжения у соединяемой кривой 2. - \en The conjugation raidus of curve 2. \~ - \param[in] names - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] result - \ru Каркас с построенными кривыми. - \en The frame with the constructed curves. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) ConnectingCurve( const MbCurve3D & curve1, bool isBegin1, double radius1, - const MbCurve3D & curve2, bool isBegin2, double radius2, - const MbSNameMaker & names, - MbWireFrame *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать соединительную NURBS кривую для кривых curve1 и curve2. - \en Create a connecting NURBS curve for curves 'curve1' and 'curve2'. \~ - \details \ru Создать соединительную NURBS кривую для кривых curve1 и curve2. \n - t1 и t2 - параметры кривых curve1 и curve2, в точках которых начинается и заканчивается соединение.\n - \en Create a connecting NURBS curve for curves 'curve1' and 'curve2'. \n - t1 and t2 are parameters of curves 'curve1' and 'curve2' which correspond to the start point and the end point of the connecting curve.\n \~ - \param[in] curve1 - \ru Соединяемая кривая 1. - \en A curve 1 to be connected. \~ - \param[in] t1 - \ru Параметр точки на кривой 1. - \en A point parameter on curve 1. \~ - \param[in] mating1 - \ru Тип соединения кривой 1. - \en The connection type for curve 1. \~ - \param[in] curve2 - \ru Соединяемая кривая 2. - \en A curve 2 to be connected. \~ - \param[in] t2 - \ru Параметр точки на кривой 2. - \en A point parameter on curve 2. \~ - \param[in] mating2 - \ru Тип соединения кривой 2. - \en The connection type for curve 2. \~ - \param[in] tension1 - \ru Параметр "натяжение" соединительной кривой на стыке с кривой 1 ( 0<= tension1 <=1). - \en The "tension" parameter of the connecting curve at the intersection with the curve 1 (0 <= tension1 <=1). \~ - \param[in] tension2 - \ru Параметр "натяжение" соединительной кривой на стыке с кривой 2( 0<= tension2 <=1). - \en The "tension" parameter of the connecting curve at the intersection with the curve 2 (0 <= tension2 <=1). \~ - \param[in] names - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] result - \ru Каркас с построенными кривыми. - \en The frame with the constructed curves. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbResultType) ConnectingSpline( const MbCurve3D & curve1, double t1, MbeMatingType mating1, - const MbCurve3D & curve2, double t2, MbeMatingType mating2, - double tension1, double tension2, - const MbSNameMaker & names, - MbWireFrame *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кривую для плавного соединения (скругления) кривых. - \en Create a fillet curve for curves. \~ - \details \ru Создать кривую для плавного соединения (скругления) кривых. \n - Для плавного сопряжения кривых curve1 и curve2 строится кривая filletCurve. \n - При входе t1 и t2 - начальные приближения, определяющие сектор построения скругления, - w1 и w2 - не используются. \n - При входе type - тип скругления (обычное или на поверхности). \n - На выходе t1 и t2 - будут равны параметрам касания кривых curve1 и curve2 с кривой filletCurve. \n - На выходе t1 и w1 - определяют параметры сохраняемого участка при обрезке кривой curve1. \n - На выходе t2 и w2 - определяют параметры сохраняемого участка при обрезке кривой curve2. \n - Параметр radius - радиус дуги или цилиндра. \n - Если радиус radius не задан (равен нулю), то он вычисляется из условия, - что начало кривой сопряжения будет находится в точке с параметором t1, - t1 и t2 - параметры кривых curve1 и curve2, в соответствующих точках которых начинается и заканчивается скругление. \n - Параметр sense - прямое или обратное направление кривой скругления. \n - Кривая filletCurve - это кривая сопряжения, дуга (когда surface == NULL) или кривая на поверхности цилиндра surface. \n - Поверхность surface - это цилиндрическая поверхность, на которой строится кривая сопряжения в общем случае. - Поверхность surface нельзя удалять, на этой поверхности построена кривая сопряжения filletCurve, - при удалении filletCurve удалится surface, если не был дополнительно выполнен surface->AddRef(). \n - \en Create a fillet curve for curves. \n - Curve 'filletCurve' is created for smooth connection of curves 'curve1' and 'curve2'. \n - On input t1 and t2 are the initial estimations which determine a sector for fillet construction, - w1 and w2 are not used. \n - On input 'type' is a fillet type (ordinary or on a surface). \n - On output t1 and t2 are the parameters of touching of curves 'curve1' and 'curve2' with curve 'filletCurve'. \n - On output t1 and w1 determines parameters of a part to be kept while trimming curve1. \n - On output t2 and w2 determines parameters of a part to be kept while trimming curve2. \n - Parameter 'radius' is a radius of an arc or a cylinder. \n - If radius 'radius' is not defined (equal to zero), it is computed from the condition - that the fillet curve start is at the point with parameter t1, - t1 and t2 are parameters of curves 'curve1' and 'curve2' which correspond to the start point and the end point of the fillet. \n - Parameter 'sense' determines forward or backward orientation of the fillet curve. \n - Curve filletCurve is a fillet curve, an arc (when 'surface' == NULL) or a curve on a cylindric surface 'surface'. \n - Surface 'surface' is a cylindric surface on which the fillet curve is constructed in general case. - Surface 'surface' must not be deleted since the fillet curve 'filletCurve' is created on this surface; - 'surface' will be deleted while deleting 'filletCurve' if surface->AddRef() was not additionally used. \n \~ - \param[in] curve1 - \ru Соединяемая кривая 1. - \en A curve 1 to be connected. \~ - \param[in/out] t1 - \ru Параметр точки на кривой 1 соединения с кривой соединения. - \en A point parameter on curve 1 of connection with fillet curve. \~ - \param[out] w1 - \ru Параметр края на кривой 1. - \en The parameter of curve 1 end point. \~ - \param[in] curve2 - \ru Соединяемая кривая 2. - \en A curve 2 to be connected. \~ - \param[in/out] t2 - \ru Параметр точки на кривой 2 соединения с кривой соединения. - \en A point parameter on curve 2 of connection with fillet curve. \~ - \param[out] w2 - \ru Параметр края на кривой 2. - \en The parameter of curve 2 end point. \~ - \param[in/out] radius - \ru Радиус дуги или цилиндра. - \en The radius of an arc or a cylinder. \~ - \param[in] sense - \ru Прямое (true) или обратное (false) направление кривой скругления. - \en The forward (true) or the backward (false) direction of the fillet curve. \~ - \param[out] unchanged - \ru Не изменился радиус соединения (true) или изменился (false). - \en The fillet radius has not changed (true) or has changed (false). \~ - \param[in] type - \ru Тип скругления. - \en The fillet type. \~ - \param[in] names - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] surface - \ru Поверхность, на которой базируется соединительная кривая, (может быть NULL). - \en A surface on which the fillet curve is based on (can be NULL). \~ - \param[out] result - \ru Каркас с построенными кривыми. - \en The frame with the constructed curves. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) FilletCurve( const MbCurve3D & curve1, double & t1, double & w1, - const MbCurve3D & curve2, double & t2, double & w2, - double & radius, bool sense, bool & unchanged, - const MbeConnectingType type, - const MbSNameMaker & names, - MbElementarySurface *& surface, - MbWireFrame *& result ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Определить изменение радиуса при перемещении средней точки кривой скругления. - \en Determine the radius variation while translating the middle point of the fillet curve. \~ - \details \ru Определить изменение радиуса при перемещении средней точки кривой скругления \n - от центра на расстояние len (с учётом знака len). \n - \en Determine the radius variation while translating the middle point of the fillet curve \n - from the centre on distance 'len' (signed). \n \~ - \param[in] filletCurve - \ru Кривая скругления. - \en The fillet curve. \~ - \param[in] radius - \ru Радиус скругления. - \en The radius of fillet. \~ - \param[in] sense - \ru Направления смещения средней точки кривой скругления. - \en A direction of the middle point of the fillet curve translation. \~ - \param[in] len - \ru Величина смещения средней точки кривой скругления. - \en A value of translation of the fillet curve middle point. \~ - \param[in] curve1 - \ru Первая сопрягаемая кривая. - \en The first curve to fillet. \~ - \param[in] t1 - \ru Параметр начала кривой скругления на первой сопрягаемой кривой. - \en The parameter of fillet curve start point on the first curve. \~ - \param[in] curve2 - \ru Вторая сопрягаемая кривая. - \en The second curve to fillet. \~ - \param[in] t2 - \ru Параметр конца кривой скругления на второй сопрягаемой кривой. - \en The parameter of fillet curve end point on the second curve. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (double) GetFilletRadiusDelta( const MbCurve3D & filletCurve, - double radius, bool sense, double len, - const MbCurve3D & curve1, double t1, - const MbCurve3D & curve2, double t2 ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Определить изменение радиуса при перемещении средней точки кривой скругления. - \en Determine the radius variation while translating the middle point of the fillet curve. \~ - \details \ru Определить изменение радиуса при перемещении средней точки кривой скругления \n - от центра на расстояние len (с учётом знака len). \n - \en Determine the radius variation while translating the middle point of the fillet curve \n - from the centre on distance 'len' (signed). \n \~ - \param[in] filletCurve - \ru Кривая скругления. - \en The fillet curve. \~ - \param[in] radius - \ru Радиус скругления. - \en The radius of fillet. \~ - \param[in] sense - \ru Направления смещения средней точки кривой скругления. - \en A direction of the middle point of the fillet curve translation. \~ - \param[in] len - \ru Величина смещения средней точки кривой скругления. - \en A value of translation of the fillet curve middle point. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (double) GetFilletRadiusDelta( const MbCurve3D & filletCurve, - double radius, bool sense, double len ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кривую для плавного соединения (скругления) всех кривых контура. - \en Create a curve for fillet of all the curves of a contour. \~ - \details \ru Создать кривую для плавного соединения (скругления) всех кривых контура contour. \n - type - тип скругления (обычное или на поверхности). \n - radiuses - радиусы скругления, i-й радиус соответствует стыку i-го и i+1-го сегмента. \n - Если две кривых в контуре гладко стыкуются, в этом стыке скругление не делается, радиус игнорируется. \n - \en Create a curve for fillet of all the curves of a contour 'contour'. \n - 'type' is a fillet type (ordinary or on a surface). \n - 'radiuses' are the fillet radii, the i-th radius corresponds to the joint of the i-th and the i+1-th segments. \n - If two curves in contours are smoothly connected, the fillet is not created at this joint, the radius is ignored. \n \~ - \param[in] contour - \ru Исходный контур. - \en The initial contour. \~ - \param[in] radiuses - \ru Множество радиусов скругления. - \en An array of fillet radii. \~ - \param[out] result - \ru Контур со скруглениями. Имя сегмента скругления - Hash32SN() имен исходных сегменов. - \en The contour with the fillets. The name of the fillet segment - Hash32SN() of initial segments names. \~ - \param[in] type - \ru Тип выполняемых скруглений. - \en The type of fillets. \~ - \return \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbResultType) CreateContourFillets( const MbContour3D & contour, - SArray & radiuses, - MbCurve3D *& result, - const MbeConnectingType type ); - - -#endif // __ACTION_SURFACE_CURVE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Методы построения трехмерных кривых. + \en Functions for three-dimensional curves construction. \~ + \details \ru На базе кривых строятся рёбра. Рёбра используются в твёрдотельной и каркасной модели. + Кроме того, кривые используются для построения поверхностей, а также могут служить + вспомогательными элементами модели. + \en Edges are created on the basis of curves. Edges are used in solid and wireframe model. + In addition curves are used for construction of surfaces as well as can be used + as auxiliary elements of a model. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ACTION_SURFACE_CURVE_H +#define __ACTION_SURFACE_CURVE_H + + +#include +#include +#include +#include +#include +#include +#include + + +class MATH_CLASS MbAxis3D; +class MATH_CLASS MbCurve; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbContour3D; +class MATH_CLASS MbSurfaceCurve; +class MATH_CLASS MbSurface; +class MATH_CLASS MbElementarySurface; +class MATH_CLASS MbFace; +class MATH_CLASS MbSolid; +class MATH_CLASS MbWireFrame; +class MATH_CLASS MbSNameMaker; + + +//------------------------------------------------------------------------------ +/** \brief \ru Рассчитать вершины ломаной. + \en Compute the vertices of a polyline. \~ + \details \ru Рассчитать вершины ломаной point1 и point2, соединяющей точки origin1 и origin2, + сдвинутые в направлениях direction1 и direction2 на расстояния length1 и length2, + которую можно скруглить радиусами radius1 и radius2. \n + \en Compute the vertices 'point1' and 'point2' of a polyline connecting points 'origin1' and 'origin2' + translated in the directions 'direction1' and 'direction2' by distances 'length1' and 'length2' + which can be rounded with radius 'radius1' and 'radius2'. \n \~ + \param[in] origin1 - \ru Первая точка. + \en The first point. \~ + \param[in] direction1 - \ru Направление сдвига первой точки. + \en The direction of the first point translation. \~ + \param[in] length1 - \ru Величина сдвига первой точки. + \en The distance of the first point translation. \~ + \param[in] radius1 - \ru Радиус скругления для первой точки. + \en The rounding radius for the first point. \~ + \param[in] origin2 - \ru Вторая точка. + \en The second point. \~ + \param[in] direction2 - \ru Направления сдвига для второй точки. + \en The direction of the second point translation. \~ + \param[in] length2 - \ru Величина сдвига для второй точки. + \en The distance of the second point translation. \~ + \param[in] radius2 - \ru Радиус скругления для второй точки. + \en The rounding radius for the second point. \~ + \param[out] result1 - \ru Первая точка ломаной. + \en The first point of the polyline. \~ + \param[out] result2 - \ru Вторая точка ломаной. + \en The second point of the polyline. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) CalculatePipePoints( const MbCartPoint3D & origin1, + const MbVector3D & direction1, + double length1, double radius1, + const MbCartPoint3D & origin2, + const MbVector3D & direction2, + double length2, double radius2, + MbCartPoint3D & result1, MbCartPoint3D & result2 ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать эквидистантную кривую. + \en Create an offset curve. \~ + \details \ru Создать эквидистантную кривую по плоской кривой. \n + \en Create an offset curve from a planar curve. \n \~ + \param[in] curve - \ru Исходная кривая. + \en The initial curve. \~ + \param[in] d - \ru Величина эквидистанты. + \en The offset distance. \~ + \param[out] result - \ru Эквидистантная кривая. + \en The offset curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) OffsetPlaneCurve( const MbCurve3D & curve, + double d, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать эквидистантную кривую. + \en Create an offset curve. \~ + \details \ru Создать эквидистантную кривую по трехмерной кривой и вектору направления. \n + \en Create an offset curve from a three-dimensional curve and a direction vector. \n \~ + \param[in] initCurve - \ru Постранственная кривая, к которой строится эквидистантная. + \en A space curve for which to construct the offset curve. \~ + \param[in] offsetVect - \ru Вектор, задающий смещение в точке кривой. + \en The displacement vector at a point of the curve. \~ + \param[in] useFillet - \ru Если true, то разрывы заполнять скруглением, иначе продолженными кривыми. + \en If 'true', the gaps are to be filled with fillet, otherwise with the extended curves. \~ + \param[in] keepRadius - \ru Если true, то в существующих скруглениях сохранять радиусы. + \en If 'true', the existent fillet radii are to be kept. \~ + \param[in] bluntAngle - \ru Если true, то в притуплять острые углы. + \en If 'true', sharp corners are to be blunt. \~ + \param[in] fromBeg - \ru Вектор смещения привязан к началу. + \en The translation vector is associated with the beginning. \~ + \param[in] snMaker - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] result - \ru Каркас с построенными кривыми. + \en The frame with the constructed curves. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) OffsetCurve( const MbCurve3D & initCurve, + const MbVector3D & offsetVect, + const bool useFillet, + const bool keepRadius, + const bool bluntAngle, + const bool fromBeg, + const MbSNameMaker & snMaker, + MbWireFrame *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать эквидистантную кривую. + \en Create an offset curve. \~ + \details \ru Создать эквидистантную кривую по поверхностной кривой и значению смещения. \n + \en Create an offset curve from a curve on a surface and a shift value. \n \~ + \param[in] curve - \ru Кривая на поверхности грани face. + \en A curve on face 'face' surface. \~ + \param[in] face - \ru Грань, на которой строится эквидистанта. + \en The edge on which to build the offset curve. \~ + \param[in] dirAxis - \ru Направление смещения с точкой приложения. + \en The offset direction with a point of application. \~ + \param[in] dist - \ru Величина смещения. + \en The offset distance. \~ + \param[in] snMaker - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] result - \ru Каркас с построенными кривыми. + \en The frame with the constructed curves. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) OffsetCurve( const MbCurve3D & curve, + const MbFace & face, + const MbAxis3D & dirAxis, + double dist, + const MbSNameMaker & snMaker, + MbWireFrame *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать проекцию кривой на поверхность. + \en Create a curve projection onto the surface. \~ + \details \ru Создать проекцию кривой curve на поверхность surface (направление проецирования direction может быть NULL). \n + \en Create the projection of a curve onto surface 'surface' (the projection direction 'direction' can be NULL). \n \~ + \param[in] surface - \ru Поверхность для проецирования. + \en The surface to project onto. \~ + \param[in] curve - \ru Проецируемая кривая. + \en The curve to project. \~ + \param[in] direction - \ru Направление проецирования (если не указано то проецирование по нормали). + \en The projection direction (if not specified, the projection along the normal). \~ + \param[in] createExact - \ru Создавать проекционную кривую при необходимости. + \en Create a projection curve if necessary. \~ + \param[in] truncateByBounds - \ru Усекать границами поверхности. + \en Truncate by the surface bounds. \~ + \param[in] version - \ru Версия исполнения. + \en The version. \~ + \param[out] result - \ru Множество кривых на поверхности. + \en An array of curves on the surface. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) CurveProjection( const MbSurface & surface, + const MbCurve3D & curve, + MbVector3D * direction, + bool createExact, + bool truncateByBounds, + RPArray & result, + VERSION version = Math::DefaultMathVersion() ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать пространственную кривую по двум плоским проекциям. + \en Create a space curve from two planar projections. \~ + \details \ru Создать пространственную кривую по двум плоским проекциям. \n + \en Create a space curve from two planar projections. \n \~ + \param[in] place1 - \ru Локальная система координат 1. + \en A local coordinate system 1. \~ + \param[in] curve1 - \ru Двумерная кривая 1. + \en A two-dimensional curve 1. \~ + \param[in] place2 - \ru Локальная система координат 2. + \en A local coordinate system 2. \~ + \param[in] curve2 - \ru Двумерная кривая 2. + \en A two-dimensional curve 2. \~ + \param[out] result - \ru Множество трехмерных кривых. + \en The array of three-dimensional curves. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) CurveByTwoProjections( const MbPlacement3D & place1, + const MbCurve & curve1, + const MbPlacement3D & place2, + const MbCurve & curve2, + RPArray & result, + VERSION version = Math::DefaultMathVersion() ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать проекционную кривую по нормали или по направлению. + \en Create a projection curve from a normal or from a direction. \~ + \details \ru Создать проекционную кривую по нормали или по направлению. \n + Если проекция на конструктивную плоскость (плоскость без границ), + то создать на ее основе грань и прислать, а за ее удалением следит приславший. \n + \en Create a projection curve from a normal or from a direction. \n + If the projection is onto the constructive plane (a plane without bounds), + create a face on the basis of this projection. \n \~ + \param[in] curve - \ru Проецируемая кривая. + \en The curve to project. \~ + \param[in] faces - \ru Связный набор граней. + \en A connected set of faces. \~ + \param[in] dir - \ru Вектор направления (если его нет, проекция по нормали). + \en The direction vector (if it is absent, the normal projection). \~ + \param[in] createExact - \ru Создавать проекционную кривую при необходимости. + \en Create a projection curve if necessary. \~ + \param[in] truncateByBounds - \ru Усечь границами. + \en Truncate by bounds. \~ + \param[in] snMaker - \ru Именователь с версией. + \en An object defining the names with the version. \~ + \param[out] result - \ru Проекционные кривые. + \en The projection curves. \~ + \param[out] resultIndices - \ru Индексы соответствия (номера граней в исходном массиве). + \en The indices of faces in the initial array. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) ProjectionCurve( const MbCurve3D & curve, + const RPArray & faces, + const MbVector3D * dir, + const bool createExact, + const bool truncateByBounds, + const MbSNameMaker & snMaker, + RPArray & result, + SArray * resultIndices ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать проекционный проволочный каркас по нормали или по направлению. + \en Create a projection wireframe from a normal or from a direction. \~ + \details \ru Создать проекционный проволочный каркас по нормали или по направлению. \n + Если проекция на конструктивную плоскость (плоскость без границ), + то создать на ее основе грань и прислать, а за ее удалением следит приславший. \n + \en Create a projection curve from a normal or from a direction. \n + If the projection is onto the constructive plane (a plane without bounds), + create a face on the basis of this projection. \n \~ + \param[in] wireFrame - \ru Проецируемый проволочный каркас. + \en The wireframe to project. \~ + \param[in] sameWireFrame - \ru Использовать тот же экземпляр проволочного каркаса, или создать копию. + \en Flag whether to use the same wireframe or make a copy of it. \~ + \param[in] solid - \ru Тело. + \en Solid. \~ + \param[in] same - \ru Использовать ли тот же экземпляр журнала тела или создать копию. + \en Flag whether to use the same creators of the body or make a copy. \~ + \param[in] faceIndices - \ru Номера граней в первой оболочке. + \en The numbers of faces in the first shell. \~ + \param[in] dir - \ru Вектор направления (если его нет, проекция по нормали). + \en The direction vector (if it is absent, the normal projection). \~ + \param[in] createExact - \ru Создавать проекционную кривую при необходимости. + \en Create a projection curve if necessary. \~ + \param[in] truncateByBounds - \ru Усечь границами. + \en Truncate by bounds. \~ + \param[in] snMaker - \ru Именователь с версией. + \en An object defining the names with the version. \~ + \param[out] resFrame - \ru Результирующий проволочный каркас, в котором в атрибутах ребер лежат имена соответствующих граней. + \en The resulting wireframe, the attributes of the edges contain the names of corresponding faces. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) ProjectionCurve( const MbWireFrame & wireFrame, + const bool sameWireFrame, + const MbSolid & solid, + const bool same, + const SArray & faceIndices, + const MbVector3D * dir, + const bool createExact, + const bool truncateByBounds, + const MbSNameMaker & snMaker, + MbWireFrame *& resFrame ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Устранить наложение сегментов проекционной кривой. + \en Eliminate the projection curve segments overlay. \~ + \details \ru Устранить наложение сегментов проекционной кривой (вспомогательная функция для функции ProjectionCurve). \n + \en Eliminate the projection curve segments overlay (an auxiliary function for function ProjectionCurve). \n \~ + \param[in,out] curves - \ru Множество кривых. + \en An array of curves. \~ + \param[in,out] indices - \ru Множество индексов, синхронный с массивом кривых. + \en An array of indices synchronized with the array of curves. \~ + \return \ru Возвращает true, если что-то изменилось в наборе кривых. + \en Returns true if something has modified in the curve set. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (bool) EliminateProjectionCurveOverlay( RPArray & curves, + SArray * indices ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать массив линий очерка поверхности. + \en Create an array of isocline curves of the surface. \~ + \details \ru Создать массив линий очерка поверхности с обрезкой по области определения. \n + \en Create an array of isocline curves of the surface with truncation by the definition domain. \n \~ + \param[in] surface - \ru Поверхность. + \en The surface. \~ + \param[in] eye - \ru Вектор взгляда. + \en The direction of view. \~ + \param[in] perspective - \ru Является ли проекция перспективной. + \en Whether the projection is perspective. \~ + \param[in] removeOnSurfaceBounds - \ru Удалить линии очерка, совпадающие с границами поверхности. + \en Remove the isocline curves coincident with the surface bounds. \~ + \param[out] result - \ru Выходной массив линий очерка. + \en The output array of isocline curves. \~ + \param[in] version - \ru Версия построения. + \en The version. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Mapping +*/ +// --- +MATH_FUNC (MbResultType) SilhouetteCurve( const MbSurface & surface, + const MbVector3D & eye, + bool perspective, + bool removeOnSurfaceBounds, + RPArray & result, + VERSION version = Math::DefaultMathVersion() ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать массив линий очерка грани. + \en Create an array of isocline curves of the face. \~ + \details \ru Создать массив линий очерка грани с обрезкой по области определения. \n + \en Create an array of isocline curves of the face with truncation by the definition domain. \n \~ + \param[in] face - \ru Грани. + \en The face. \~ + \param[in] eye - \ru Вектор взгляда. + \en The direction of view. \~ + \param[in] perspective - \ru Является ли проекция перспективной. + \en Whether the projection is perspective. \~ + \param[out] result - \ru Выходной массив линий очерка. + \en The output array of isocline curves. \~ + \param[in] version - \ru Версия построения. + \en The version. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Mapping +*/ +// --- +MATH_FUNC (MbResultType) SilhouetteCurve( const MbFace & face, + const MbVector3D & eye, + bool perspective, + RPArray & result, + VERSION version = Math::DefaultMathVersion() ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать массив линий очерка поверхности при вращательном движении вокруг оси. + \en Create an array of isocline curves of the rotated surface. \~ + \details \ru Создать массив линий очерка поверхности с обрезкой по области определения. \n + \en Create an array of isocline curves of the surface with truncation by the definition domain. \n \~ + \param[in] surface - \ru Поверхность. + \en The surface. \~ + \param[in] axis - \ru Ось кругового взгляда (ось токарного сечения). + \en The axis of lathe section. \~ + \param[in] removeOnSurfaceBounds - \ru Удалить линии очерка, совпадающие с границами поверхности. + \en Remove the isocline curves coincident with the surface bounds. \~ + \param[out] result - \ru Выходной массив линий очерка. + \en The output array of isocline curves. \~ + \param[in] version - \ru Версия построения. + \en The version. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Mapping +*/ +// --- +MATH_FUNC (MbResultType) SilhouetteCurve( const MbSurface & surface, + const MbAxis3D & axis, + bool removeOnSurfaceBounds, + RPArray & curves, + VERSION version = Math::DefaultMathVersion() ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать массив линий очерка грани при вращательном движении вокруг оси. + \en Create an array of isocline curves of the rotated face. \~ + \details \ru Создать массив линий очерка грани с обрезкой по области определения. \n + \en Create an array of isocline curves of the face with truncation by the definition domain. \n \~ + \param[in] face - \ru Грани. + \en The face. \~ + \param[in] axis - \ru Ось кругового взгляда (ось токарного сечения). + \en The axis of lathe section. \~ + \param[out] result - \ru Выходной массив линий очерка. + \en The output array of isocline curves. \~ + \param[in] version - \ru Версия построения. + \en The version. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Mapping +*/ +// --- +MATH_FUNC (MbResultType) SilhouetteCurve( const MbFace & face, + const MbAxis3D & axis, + RPArray & curves, + VERSION version = Math::DefaultMathVersion() ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кривые пересечения двух поверхностей. + \en Create the intersection curves of two surfaces. \~ + \details \ru Создать кривые пересечения двух поверхностей. Результат - массив кривых пересечения поверхностей. \n + \en Create the intersection curves of two surfaces. The result is an array of intersection curves of surfaces. \n \~ + \param[in] surface1 - \ru Первая поверхность. + \en The first surface. \~ + \param[in] surface2 - \ru Вторая поверхность. + \en The second surface. \~ + \param[in] snMaker - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] result - \ru Каркас с построенными кривыми. + \en The frame with the constructed curves. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \warning \ru Лучше использовать IntersectionCurve на гранях, т.к. границы поверхностей могут бы неточные, \n + что приведет к неточному положению концов кривых пересечения в результате операции. \n + В гранях же границы поверхности точные, т.к. хранятся в виде кривых пересечения, + а не виде двумерных кривых. \n + \en It is better to use IntersectionCurve on faces since the surfaces bounds can be inexact, \n + and it will result in inexact position of intersection curves ends. \n + But the surface bounds in faces are exact since they are stored in the form of intersection curves, + not in the form of two-dimensional curves. \n \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) IntersectionCurve( const MbSurface & surface1, + const MbSurface & surface2, + const MbSNameMaker & snMaker, + MbWireFrame *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кривые пересечения двух граней. + \en Create intersection curves of two faces. \~ + \details \ru Создать кривые пересечения двух граней. Результат - массив кривых пересечения поверхностей. \n + \en Create intersection curves of two faces. The result is an array of intersection curves of surfaces. \n \~ + \param[in] face1 - \ru Первая грань оболочки. + \en The first face of the shell. \~ + \param[in] face2 - \ru Вторая грани оболочки. + \en The second face of the shell. \~ + \param[in] snMaker - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] result - \ru Каркас с построенными кривыми. + \en The frame with the constructed curves. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) IntersectionCurve( MbFace & face1, + MbFace & face2, + const MbSNameMaker & snMaker, + MbWireFrame *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кривые пересечения граней двух оболочек. + \en Create intersection curves of two shells faces. \~ + \details \ru Создать кривые пересечения граней двух оболочек. Результат - массив кривых пересечения поверхностей. \n + \en Create intersection curves of two shells faces. The result is an array of intersection curves of surfaces. \n \~ + \param[in] solid1 - \ru Первая оболочка. + \en The first shell. \~ + \param[in] faceIndices1 - \ru Номера граней в первой оболочке. + \en The numbers of faces in the first shell. \~ + \param[in] solid2 - \ru Вторая оболочка. + \en The second shell. \~ + \param[in] faceIndices2 - \ru Номера граней во второй оболочке. + \en The numbers of faces in the second shell. \~ + \param[in] snMaker - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] result - \ru Каркас с построенными кривыми. + \en The frame with the constructed curves. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) IntersectionCurve( const MbSolid & solid1, const SArray & faceIndices1, + const MbSolid & solid2, const SArray & faceIndices2, + const MbSNameMaker & snMaker, + MbWireFrame *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кривые пересечения граней двух оболочек. + \en Create intersection curves of two shells faces. \~ + \details \ru Создать кривые пересечения граней двух оболочек. Результат - массив кривых пересечения поверхностей. \n + \en Create intersection curves of two shells faces. The result is an array of intersection curves of surfaces. \n \~ + \param[in] solid1 - \ru Первая оболочка. + \en The first shell. \~ + \param[in] faceIndices1 - \ru Номера граней в первой оболочке. + \en The numbers of faces in the first shell. \~ + \param[in] same1 - \ru Использовать ли тот же журнал построителей первого тела или сделать копию. + \en Flag whether to use the same creators of the first body or make a copy. \~ + \param[in] solid2 - \ru Вторая оболочка. + \en The second shell. \~ + \param[in] faceIndices2 - \ru Номера граней во второй оболочке. + \en The numbers of faces in the second shell. \~ + \param[in] same2 - \ru Использовать ли тот же самый журнал построителей второго тела или сделать копию. + \en Flag whether to use the same creators of the second body or make a copy. \~ + \param[in] snMaker - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] result - \ru Каркас с построенными кривыми. + \en The frame with the constructed curves. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) IntersectionCurve( const MbSolid & solid1, const SArray & faceIndices1, const bool same1, + const MbSolid & solid2, const SArray & faceIndices2, const bool same2, + const MbSNameMaker & snMaker, MbWireFrame *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать линию пересечения поверхностей. + \en Create an intersection curve of surfaces. \~ + \details \ru Создать линию пересечения поверхностей surf1 и surf2 по известным началу и концу линии пересечения. \n + \en Create an intersection curve of surfaces 'surf1' and 'surf2' from the specified start point and end point of the intersection curve. \n \~ + \param[in] surface1 - \ru Первая поверхность. + \en The first surface. \~ + \param[in] ext1 - \ru На расширенной первой поверхности. + \en Whether to create on the extended surface. \~ + \param[in] uv1beg - \ru Начальная точка на первой поверхности. + \en The start point on the first surface. \~ + \param[in] uv1end - \ru Конечная точка на первой поверхности. + \en The end point on the first surface. \~ + \param[in] surface2 - \ru Вторая поверхность. + \en The second surface. \~ + \param[in] ext2 - \ru На расширенной второй поверхности. + \en Whether to create on the extended second surface. \~ + \param[in] uv2beg - \ru Начальная точка на второй поверхности. + \en The start point on the second surface. \~ + \param[in] uv2end - \ru Конечная точка на второй поверхности. + \en The end point on the second surface. \~ + \param[in] dir - \ru Начальное направление создания линии пересечения. + \en The start direction for intersection curve creation. \~ + \param[out] result1 - \ru Двумерная кривая на первой поверхности. + \en The two-dimensional curve on the first surface. \~ + \param[out] result2 - \ru Двумерная кривая на второй поверхности. + \en The two-dimensional curve on the second surface. \~ + \param[out] label - \ru Тип полученной кривой пересечения. + \en The resultant intersection curve type. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) IntersectionCurve( const MbSurface & surface1, bool ext1, + const MbCartPoint & uv1beg, + const MbCartPoint & uv1end, + const MbSurface & surface2, bool ext2, + const MbCartPoint & uv2beg, + const MbCartPoint & uv2end, + const MbVector3D & dir, + MbCurve *& result1, + MbCurve *& result2, + MbeCurveBuildType & label ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать линию пересечения поверхностей. + \en Create an intersection curve of surfaces. \~ + \details \ru Создать линию пересечения поверхностей surf1 и surf2 по известным началу и концу линии пересечения и вспомогательной кривой. \n + \en Create an intersection curve of surfaces 'surf1' and 'surf2' from the specified start point and end point of the intersection curve + and guide curve that approximates the desired curve. \n \~ + \param[in] surface1 - \ru Первая поверхность. + \en The first surface. \~ + \param[in] ext1 - \ru На расширенной первой поверхности. + \en Whether to create on the extended surface. \~ + \param[in] uv1beg - \ru Начальная точка на первой поверхности. + \en The start point on the first surface. \~ + \param[in] uv1end - \ru Конечная точка на первой поверхности. + \en The end point on the first surface. \~ + \param[in] surface2 - \ru Вторая поверхность. + \en The second surface. \~ + \param[in] ext2 - \ru На расширенной второй поверхности. + \en Whether to create on the extended second surface. \~ + \param[in] uv2beg - \ru Начальная точка на второй поверхности. + \en The start point on the second surface. \~ + \param[in] uv2end - \ru Конечная точка на второй поверхности. + \en The end point on the second surface. \~ + \param[in] guideCurve - \ru Направляющая кривая, приближенно описывающая искомую кривую. + \en The guide curve that approximates the desired curve. \~ + \param[in] useRedetermination - \ru Флаг, определяющий нужно ли уточнять шаг построения следующей точки по сравнению с функцией DeviationStep. + \en The flag that determines whether it is necessary to specify the next point build step as compared to the DeviationStep function. \~ + \param[in] checkPoles - \ru Флаг необходимости проверки и корректировки полюсных точек. + \en The flag that determines whether it is necessary to check and correct pole points. \~ + \param[out] result1 - \ru Двумерная кривая на первой поверхности. + \en The two-dimensional curve on the first surface. \~ + \param[out] result2 - \ru Двумерная кривая на второй поверхности. + \en The two-dimensional curve on the second surface. \~ + \param[out] label - \ru Тип полученной кривой пересечения. + \en The resultant intersection curve type. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC( MbResultType ) IntersectionCurve( const MbSurface & surf1, bool ext1, + const MbCartPoint & uv1beg, + const MbCartPoint & uv1end, + const MbSurface & surf2, bool ext2, + const MbCartPoint & uv2beg, + const MbCartPoint & uv2end, + const MbCurve3D * guideCurve, + bool useRedetermination, + bool checkPoles, + MbCurve *& pCurve1, + MbCurve *& pCurve2, + MbeCurveBuildType & label ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать пространственный сплайн через точки и с сопряжениями. + \en Create a spatial spline through points and with the given derivatives. \~ + \details \ru Создать пространственный сплайн через точки и с сопряжениями. \n + Примечания: \n + Если есть сопряжения, то количество сопряжений должно быть равно количеству точек. \n + Отсутствующие сопряжения должны быть представлены нулевыми указателями в массиве \n + \en Create a spatial spline through points and with the given derivatives. \n + Notes: \n + If derivatives are specified, the number of derivatives should be equal to the number of points. \n + Missing derivatives should be represented by null pointers in the array \n \~ + \param[in] points - \ru Точки. + \en Points. \~ + \param[in] paramType - \ru Тип параметризации. + \en The parametrization type. \~ + \param[in] degree - \ru Порядок сплайна. + \en A spline degree. \~ + \param[in] closed - \ru Замкнутость сплайна. + \en The spline closedness. \~ + \param[in] transitions - \ru Заданные сопряжения. + \en The specified derivatives. \~ + \param[in] snMaker - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] result - \ru Каркас с построенными кривыми. + \en The frame with the constructed curves. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) SpaceSplineThrough( const SArray & points, + MbeSplineParamType paramType, + size_t degree, + bool closed, + RPArray< MbPntMatingData > & transitions, + const MbSNameMaker & snMaker, + MbWireFrame *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать пространственный сплайн по точкам и с сопряжениями. + \en Create a spatial spline from points and derivatives. \~ + \details \ru Создать пространственный сплайн по точкам и с сопряжениями. \n + \en Create a spatial spline from points and derivatives. \n \~ + \param[in] points - \ru Множество точек. + \en An array of points. \~ + \param[in] degree - \ru Порядок сплайна. + \en A spline degree. \~ + \param[in] closed - \ru Строить замкнутый сплайн. + \en Create a closed spline. \~ + \param[in] weights - \ru Множество весов точек. + \en An array of points weights. \~ + \param[in] knots - \ru Узловой вектор сплайна. + \en A knot vector of the spline. \~ + \param[in] begData - \ru Сопряжение в начале. + \en The start derivative. \~ + \param[in] endData - \ru Сопряжение в конце. + \en The end derivative. \~ + \param[in] snMaker - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] result - \ru Каркас с построенными кривыми. + \en The frame with the constructed curves. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) SpaceSplineBy( const SArray & points, + size_t degree, + bool closed, + const SArray * weights, + const SArray * knots, + MbPntMatingData * begData, + MbPntMatingData * endData, + const MbSNameMaker & snMaker, + MbWireFrame *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кривую на поверхности. + \en Create a curve on a surface. \~ + \details \ru Создать кривую на поверхности. \n + Примечания: \n + 1. Если есть сопряжения, то количество сопряжений должно быть равно количеству точек. \n + Отсутствующие сопряжения должны быть представлены нулевыми указателями в массиве \n + 2. Если сплайн строится через точки, то сопряжения могуть быть заданы произвольно. \n + 2. Если сплайн строится по полюсам и он незамкнут, то сопряжения могут быть только на концах. \n + 3. Если сплайн строится по полюсам и он замкнут, то сопряжения должны отсутствовать. \n + 4. Множество весов должен быть пуст или синхронизирован с массивом точек по количеству + (с опцией throughPoints веса игнорируются). \n + \en Create a curve on a surface. \n + Notes: \n + 1. If derivatives are specified, the number of derivatives should be equal to the number of points. \n + Missing derivatives should be represented by null pointers in the array \n + 2. If the spline is created from points, arbitrary derivatives can be defined. \n + 2. If the spline is created from poles and it is open, only the end derivatives can be specified. \n + 3. If the spline is constructed from poles and it is closed, the derivatives cannot be specified. \n + 4. An array of weights should be empty or synchronized with the point array by size + (with option throughPoints weights are ignored). \n \~ + \param[in] surface - \ru Поверхность. + \en The surface. \~ + \param[in] throughPoints - \ru Провести сплайн через точки. + \en Create a spline through points. \~ + \param[in] paramPnts - \ru Множество параметрических точек. + \en An array of parametric points. \~ + \param[in] paramWts - \ru Множество весов параметрических точек. + \en An array of parametric point weights. \~ + \param[in] paramClosed - \ru Строить замкнутый параметрический сплайн. + \en Create a closed parametric spline. \~ + \param[in] spaceTransitions - \ru Сопряжения в точках. + \en Derivatives in the points. \~ + \param[in] snMaker - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] result - \ru Каркас с построенными кривыми. + \en The frame with the constructed curves. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) SurfaceSpline( const MbSurface & surface, + bool throughPoints, + SArray & paramPnts, + SArray & paramWts, + bool paramClosed, + RPArray< MbPntMatingData > & spaceTransitions, + const MbSNameMaker & snMaker, + MbWireFrame *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать изопараметрическую кривую. + \en Create an isoparametric curve. \~ + \details \ru Создать изопараметрическую кривую на поверхности surface. \n + \en Create an isoparametric curve on surface 'surface'. \n \~ + \param[in] surface - \ru Поверхность. + \en The surface. \~ + \param[in] x - \ru Значение по первому параметру. + \en A value of the first parameter. \~ + \param[in] isU - \ru Первый параметр есть U. + \en Whether the first parameter is U. \~ + \param[in] yRange - \ru Диапазон по второму параметру (если не задан, используются параметрические границы поверхности). + \en A range of the second parameter (if not defined, the parametric bounds of the surface are used). \~ + \param[out] result - \ru Изопараметрическая кривая. + \en The isoparametric curve. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) IsoparametricCurve( const MbSurface & surface, + double x, bool isU, const MbRect1D * yRange, + MbCurve3D *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кривую - мостик, соединяющую кривые curve1 и curve2. + \en Create a transition curve connecting curves 'curve1' and 'curve2'. \~ + \details \ru Создать кривую - мостик, соединяющую кривые curve1 и curve2 кубическим сплайном Эрмита. \n + \en Create a transition curve connecting curves 'curve1' and 'curve2' by a cubic Hermite spline. \n \~ + \param[in] curve1 - \ru Сопрягаемая кривая 1. + \en A curve 1 to be connected. \~ + \param[in] t1 - \ru Параметр точки на сопрягаемой кривой 1. + \en A point parameter on the curve 1. \~ + \param[in] sense1 - \ru Начало мостика совпадает с направлением кривой curve1 (true). + \en The beginning of the transition curve is equal to the direction of 'curve1' (true). \~ + \param[in] curve2 - \ru Сопрягаемая кривая 2. + \en A curve 2 to be connected. \~ + \param[in] t2 - \ru Параметр точки на сопрягаемой кривой 2. + \en A point parameter on the curve 2. \~ + \param[in] sense2 - \ru Конец мостика совпадает с направлением кривой curve2 (true). + \en The end of the transition curve is equal to the direction of 'curve2' (true). \~ + \param[in] names - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] result - \ru Каркас с построенными кривыми. + \en The frame with the constructed curves. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) BridgeCurve( const MbCurve3D & curve1, double t1, bool sense1, + const MbCurve3D & curve2, double t2, bool sense2, + const MbSNameMaker & names, + MbWireFrame *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать составную кривую плавного соединения концов двух кривых. + \en Create a composite curve smoothly connecting two curves ends. \~ + \details \ru Создать составную кривую плавного соединения концов двух кривых. \n + Полученная кривая состоит из трёхмерной дуги радиуса radius1, + отрезка (в определенных случаях отрезок отсутствует), + трёхмерной дуги радиуса radius2. + \en Create a composite curve smoothly connecting two curves ends. \n + The constructed curve consists of a three-dimensional arc of radius 'radius1', + a segment (in specific cases a segment is absent), + a three-dimensional arc of radius 'radius2'. \~ + \param[in] curve1 - \ru Соединяемая кривая 1. + \en A curve 1 to be connected. \~ + \param[in] isBegin1 - \ru Начало соединяемой кривой 1 (true). + \en The beginning of the curve 1 (true). \~ + \param[in] radius1 - \ru Радиус сопряжения у соединяемой кривой 1. + \en The conjugation raidus of curve 1. \~ + \param[in] curve2 - \ru Соединяемая кривая 2. + \en A curve 2 to be connected. \~ + \param[in] isBegin2 - \ru Начало соединяемой кривой 2 (true). + \en The beginning of the curve 2 (true). \~ + \param[in] radius2 - \ru Радиус сопряжения у соединяемой кривой 2. + \en The conjugation raidus of curve 2. \~ + \param[in] names - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] result - \ru Каркас с построенными кривыми. + \en The frame with the constructed curves. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) ConnectingCurve( const MbCurve3D & curve1, bool isBegin1, double radius1, + const MbCurve3D & curve2, bool isBegin2, double radius2, + const MbSNameMaker & names, + MbWireFrame *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать соединительную NURBS кривую для кривых curve1 и curve2. + \en Create a connecting NURBS curve for curves 'curve1' and 'curve2'. \~ + \details \ru Создать соединительную NURBS кривую для кривых curve1 и curve2. \n + t1 и t2 - параметры кривых curve1 и curve2, в точках которых начинается и заканчивается соединение.\n + \en Create a connecting NURBS curve for curves 'curve1' and 'curve2'. \n + t1 and t2 are parameters of curves 'curve1' and 'curve2' which correspond to the start point and the end point of the connecting curve.\n \~ + \param[in] curve1 - \ru Соединяемая кривая 1. + \en A curve 1 to be connected. \~ + \param[in] t1 - \ru Параметр точки на кривой 1. + \en A point parameter on curve 1. \~ + \param[in] mating1 - \ru Тип соединения кривой 1. + \en The connection type for curve 1. \~ + \param[in] curve2 - \ru Соединяемая кривая 2. + \en A curve 2 to be connected. \~ + \param[in] t2 - \ru Параметр точки на кривой 2. + \en A point parameter on curve 2. \~ + \param[in] mating2 - \ru Тип соединения кривой 2. + \en The connection type for curve 2. \~ + \param[in] tension1 - \ru Параметр "натяжение" соединительной кривой на стыке с кривой 1 ( 0<= tension1 <=1). + \en The "tension" parameter of the connecting curve at the intersection with the curve 1 (0 <= tension1 <=1). \~ + \param[in] tension2 - \ru Параметр "натяжение" соединительной кривой на стыке с кривой 2( 0<= tension2 <=1). + \en The "tension" parameter of the connecting curve at the intersection with the curve 2 (0 <= tension2 <=1). \~ + \param[in] names - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] result - \ru Каркас с построенными кривыми. + \en The frame with the constructed curves. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbResultType) ConnectingSpline( const MbCurve3D & curve1, double t1, MbeMatingType mating1, + const MbCurve3D & curve2, double t2, MbeMatingType mating2, + double tension1, double tension2, + const MbSNameMaker & names, + MbWireFrame *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кривую для плавного соединения (скругления) кривых. + \en Create a fillet curve for curves. \~ + \details \ru Создать кривую для плавного соединения (скругления) кривых. \n + Для плавного сопряжения кривых curve1 и curve2 строится кривая filletCurve. \n + При входе t1 и t2 - начальные приближения, определяющие сектор построения скругления, + w1 и w2 - не используются. \n + При входе type - тип скругления (обычное или на поверхности). \n + На выходе t1 и t2 - будут равны параметрам касания кривых curve1 и curve2 с кривой filletCurve. \n + На выходе t1 и w1 - определяют параметры сохраняемого участка при обрезке кривой curve1. \n + На выходе t2 и w2 - определяют параметры сохраняемого участка при обрезке кривой curve2. \n + Параметр radius - радиус дуги или цилиндра. \n + Если радиус radius не задан (равен нулю), то он вычисляется из условия, + что начало кривой сопряжения будет находится в точке с параметором t1, + t1 и t2 - параметры кривых curve1 и curve2, в соответствующих точках которых начинается и заканчивается скругление. \n + Параметр sense - прямое или обратное направление кривой скругления. \n + Кривая filletCurve - это кривая сопряжения, дуга (когда surface == NULL) или кривая на поверхности цилиндра surface. \n + Поверхность surface - это цилиндрическая поверхность, на которой строится кривая сопряжения в общем случае. + Для управления жизненным циклом поверхномти следует миспользовать методы ::AddRefItem(surface) и ::ReleaseItem(surface). \n + \en Create a fillet curve for curves. \n + Curve 'filletCurve' is created for smooth connection of curves 'curve1' and 'curve2'. \n + On input t1 and t2 are the initial estimations which determine a sector for fillet construction, + w1 and w2 are not used. \n + On input 'type' is a fillet type (ordinary or on a surface). \n + On output t1 and t2 are the parameters of touching of curves 'curve1' and 'curve2' with curve 'filletCurve'. \n + On output t1 and w1 determines parameters of a part to be kept while trimming curve1. \n + On output t2 and w2 determines parameters of a part to be kept while trimming curve2. \n + Parameter 'radius' is a radius of an arc or a cylinder. \n + If radius 'radius' is not defined (equal to zero), it is computed from the condition + that the fillet curve start is at the point with parameter t1, + t1 and t2 are parameters of curves 'curve1' and 'curve2' which correspond to the start point and the end point of the fillet. \n + Parameter 'sense' determines forward or backward orientation of the fillet curve. \n + Curve filletCurve is a fillet curve, an arc (when 'surface' == NULL) or a curve on a cylindric surface 'surface'. \n + Surface 'surface' is a cylindric surface on which the fillet curve is constructed in general case. + Use ::AddRefItem(surface) and ::ReleaseItem(surface) methods to manage the surface lifecycle. \n \~ + \param[in] curve1 - \ru Соединяемая кривая 1. + \en A curve 1 to be connected. \~ + \param[in/out] t1 - \ru Параметр точки на кривой 1 соединения с кривой соединения. + \en A point parameter on curve 1 of connection with fillet curve. \~ + \param[out] w1 - \ru Параметр края на кривой 1. + \en The parameter of curve 1 end point. \~ + \param[in] curve2 - \ru Соединяемая кривая 2. + \en A curve 2 to be connected. \~ + \param[in/out] t2 - \ru Параметр точки на кривой 2 соединения с кривой соединения. + \en A point parameter on curve 2 of connection with fillet curve. \~ + \param[out] w2 - \ru Параметр края на кривой 2. + \en The parameter of curve 2 end point. \~ + \param[in/out] radius - \ru Радиус дуги или цилиндра. + \en The radius of an arc or a cylinder. \~ + \param[in] sense - \ru Прямое (true) или обратное (false) направление кривой скругления. + \en The forward (true) or the backward (false) direction of the fillet curve. \~ + \param[out] unchanged - \ru Не изменился радиус соединения (true) или изменился (false). + \en The fillet radius has not changed (true) or has changed (false). \~ + \param[in] type - \ru Тип скругления. + \en The fillet type. \~ + \param[in] names - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] surface - \ru Поверхность, которая будет создана и на которой базируется соединительная кривая, (может быть возращён NULL). + \en A surface on which the fillet curve is based on, it will be created by the method (can be NULL). \~ + \param[out] result - \ru Каркас с построенными кривыми. + \en The frame with the constructed curves. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) FilletCurve( const MbCurve3D & curve1, double & t1, double & w1, + const MbCurve3D & curve2, double & t2, double & w2, + double & radius, bool sense, bool & unchanged, + const MbeConnectingType type, + const MbSNameMaker & names, + MbElementarySurface *& surface, + MbWireFrame *& result ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Определить изменение радиуса при перемещении средней точки кривой скругления. + \en Determine the radius variation while translating the middle point of the fillet curve. \~ + \details \ru Определить изменение радиуса при перемещении средней точки кривой скругления \n + от центра на расстояние len (с учётом знака len). \n + \en Determine the radius variation while translating the middle point of the fillet curve \n + from the centre on distance 'len' (signed). \n \~ + \param[in] filletCurve - \ru Кривая скругления. + \en The fillet curve. \~ + \param[in] radius - \ru Радиус скругления. + \en The radius of fillet. \~ + \param[in] sense - \ru Направления смещения средней точки кривой скругления. + \en A direction of the middle point of the fillet curve translation. \~ + \param[in] len - \ru Величина смещения средней точки кривой скругления. + \en A value of translation of the fillet curve middle point. \~ + \param[in] curve1 - \ru Первая сопрягаемая кривая. + \en The first curve to fillet. \~ + \param[in] t1 - \ru Параметр начала кривой скругления на первой сопрягаемой кривой. + \en The parameter of fillet curve start point on the first curve. \~ + \param[in] curve2 - \ru Вторая сопрягаемая кривая. + \en The second curve to fillet. \~ + \param[in] t2 - \ru Параметр конца кривой скругления на второй сопрягаемой кривой. + \en The parameter of fillet curve end point on the second curve. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (double) GetFilletRadiusDelta( const MbCurve3D & filletCurve, + double radius, bool sense, double len, + const MbCurve3D & curve1, double t1, + const MbCurve3D & curve2, double t2 ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Определить изменение радиуса при перемещении средней точки кривой скругления. + \en Determine the radius variation while translating the middle point of the fillet curve. \~ + \details \ru Определить изменение радиуса при перемещении средней точки кривой скругления \n + от центра на расстояние len (с учётом знака len). \n + \en Determine the radius variation while translating the middle point of the fillet curve \n + from the centre on distance 'len' (signed). \n \~ + \param[in] filletCurve - \ru Кривая скругления. + \en The fillet curve. \~ + \param[in] radius - \ru Радиус скругления. + \en The radius of fillet. \~ + \param[in] sense - \ru Направления смещения средней точки кривой скругления. + \en A direction of the middle point of the fillet curve translation. \~ + \param[in] len - \ru Величина смещения средней точки кривой скругления. + \en A value of translation of the fillet curve middle point. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (double) GetFilletRadiusDelta( const MbCurve3D & filletCurve, + double radius, bool sense, double len ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кривую для плавного соединения (скругления) всех кривых контура. + \en Create a curve for fillet of all the curves of a contour. \~ + \details \ru Создать кривую для плавного соединения (скругления) всех кривых контура contour. \n + type - тип скругления (обычное или на поверхности). \n + radiuses - радиусы скругления, i-й радиус соответствует стыку i-го и i+1-го сегмента. \n + Если две кривых в контуре гладко стыкуются, в этом стыке скругление не делается, радиус игнорируется. \n + \en Create a curve for fillet of all the curves of a contour 'contour'. \n + 'type' is a fillet type (ordinary or on a surface). \n + 'radiuses' are the fillet radii, the i-th radius corresponds to the joint of the i-th and the i+1-th segments. \n + If two curves in contours are smoothly connected, the fillet is not created at this joint, the radius is ignored. \n \~ + \param[in] contour - \ru Исходный контур. + \en The initial contour. \~ + \param[in] radiuses - \ru Множество радиусов скругления. + \en An array of fillet radii. \~ + \param[out] result - \ru Контур со скруглениями. Имя сегмента скругления - Hash32SN() имен исходных сегменов. + \en The contour with the fillets. The name of the fillet segment - Hash32SN() of initial segments names. \~ + \param[in] type - \ru Тип выполняемых скруглений. + \en The type of fillets. \~ + \return \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbResultType) CreateContourFillets( const MbContour3D & contour, + SArray & radiuses, + MbCurve3D *& result, + const MbeConnectingType type ); + + +#endif // __ACTION_SURFACE_CURVE_H diff --git a/C3d/Include/alg_base.h b/C3d/Include/alg_base.h index 6f8e9d7..3f9ef51 100644 --- a/C3d/Include/alg_base.h +++ b/C3d/Include/alg_base.h @@ -287,7 +287,7 @@ inline bool ArFind( const Vector & arParam, double t, ptrdiff_t & id ) } while ( rangeId > 8 ) { // \ru До тех пор пока диапазон больше восьми \en Until the range contains more than eight elements - ptrdiff_t mIndex = ( rangeId / 2 ) + idLeft; // \ru Найти индекс в середине диапазона \en Find the index in the middle of the range + size_t mIndex = ( rangeId / 2 ) + idLeft; // \ru Найти индекс в середине диапазона \en Find the index in the middle of the range if ( arParam[mIndex] < t ) // \ru Если t больше серединного параметра \en If t is greater than the middle parameter idLeft = mIndex; // \ru Установить левую границу \en Set the left bound @@ -507,71 +507,8 @@ bool IsMonotonic( const TypeVector & items, bool isAscending, bool allowEqual = \ingroup Base_Algorithms */ // --- -template -bool ArePointsOnLine( const SArray & pnts, double metricEps = METRIC_EPSILON ) -{ - bool onLine = false; - - size_t cnt = pnts.size(); - double lenEps = std_min( LENGTH_EPSILON, metricEps ); - lenEps = std_max( EXTENT_EQUAL, lenEps ); - - if ( cnt > 2 ) { - const Point & pnt0 = pnts[0]; - - Vector tau( pnt0, pnts[1] ); - double tauLen = tau.Length(); - if ( tauLen <= lenEps ) { - for ( size_t k = 2; k < cnt; k++ ) { - const Point & pntk = pnts[k]; - tau.Init( pnt0, pntk ); - tauLen = tau.Length(); - if ( tauLen > lenEps ) - break; - } - } - if ( tauLen > lenEps ) { - onLine = true; - tau /= tauLen; - Vector vect; - Point pnt; - for ( size_t k = 1; k < cnt; k++ ) { - const Point & pntk = pnts[k]; - vect.Init( pnt0, pntk ); - vect = tau * (vect * tau); - pnt = pnt0 + vect; - if ( pntk.DistanceToPoint( pnt ) > metricEps ) { - onLine = false; - break; - } - } - } - } - else if ( cnt == 2 ) { - if ( pnts[0].DistanceToPoint( pnts[1] ) > lenEps ) - onLine = true; - } - - return onLine; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Лежат ли точки на линии. - \en Whether points lie on the line. \~ - \details \ru Лежат ли точки на линии (улучшенный вариант IsPointOnLine). \n - \en Whether points lie on the line (improved variant of IsPointOnLine). \n \~ - \param[in] pnts - \ru Набор точек. - \en Point set. \~ - \param[in] metricEps - \ru Точность проверки. - \en Check accuracy. \~ - \return \ru true, если точки лежат на линии, \n иначе false - \en True if points lie on line, \n otherwise false. \~ - \ingroup Base_Algorithms -*/ -// --- -template -bool ArePointsOnLine( const std::vector & pnts, double metricEps = METRIC_EPSILON ) +template +bool ArePointsOnLine( const PointsVector & pnts, double metricEps = METRIC_EPSILON ) { bool onLine = false; @@ -635,8 +572,8 @@ bool ArePointsOnLine( const std::vector & pnts, double metricEps = METRIC \ingroup Base_Algorithms */ // --- -template -bool IsPlanar( const PointsVector & pnts, MbPlacement3D * place, double mEps = METRIC_EPSILON ) +template +bool IsPlanar( const SpacePointsVector & pnts, MbPlacement3D * place, double mEps = METRIC_EPSILON ) { bool isPlanar = false; mEps = ::fabs( mEps ); @@ -743,8 +680,8 @@ bool IsPlanar2( const Array2 & pnts, MbPlacement3D * place, double mEps = \en Set the range of parameter. \~ \details \ru Установить (репараметризовать) область изменения параметра в массиве. \n \en Set (reparametrize) range of parameter in the array. \n \~ - \param[in,out] params - \ru Множество параметров, упорядоченный по возрастанию параметра. - \en The array of parameters sorted in ascending order. \~ + \param[in,out] tarr - \ru Множество параметров, упорядоченных по возрастанию параметра. + \en The array of parameters sorted in ascending order. \~ \param[in] pmin - \ru Минимальное значение параметра. \en Minimal value of parameter. \~ \param[in] pmax - \ru Максимальное значение параметра. diff --git a/C3d/Include/alg_circle_curve.h b/C3d/Include/alg_circle_curve.h index 8769b69..5a442d5 100644 --- a/C3d/Include/alg_circle_curve.h +++ b/C3d/Include/alg_circle_curve.h @@ -233,7 +233,7 @@ private: \ingroup Curve_Modeling */ // --- -MATH_FUNC (ptrdiff_t) CircleTanLineLineRad( MbLine & pl1, MbLine & pl2, double rad, MbTempCircle * pc ); +MATH_FUNC (ptrdiff_t) CircleTanLineLineRad( const MbLine & pl1, const MbLine & pl2, double rad, MbTempCircle * pc ); //------------------------------------------------------------------------------ @@ -280,7 +280,7 @@ MATH_FUNC (ptrdiff_t) CircleTanLineCircleRadius( const MbLine & pl1, const MbArc \ingroup Curve_Modeling */ // --- -MATH_FUNC (ptrdiff_t) CircleTanCircleCircleRad( MbArc & pc1, MbArc & pc2, double rad, +MATH_FUNC (ptrdiff_t) CircleTanCircleCircleRad( const MbArc & pc1, const MbArc & pc2, double rad, MbTempCircle * pc ); @@ -300,7 +300,7 @@ MATH_FUNC (ptrdiff_t) CircleTanCircleCircleRad( MbArc & pc1, MbArc & pc2, double \ingroup Curve_Modeling */ // --- -MATH_FUNC (void) CircleTanCurveCentre( const MbCurve & pCurve, MbCartPoint & pnt, +MATH_FUNC (void) CircleTanCurveCentre( const MbCurve & pCurve, const MbCartPoint & pnt, PArray & pCircle ); @@ -322,9 +322,10 @@ MATH_FUNC (void) CircleTanCurveCentre( const MbCurve & pCurve, MbCartPoint & pnt \ingroup Curve_Modeling */ // --- -MATH_FUNC (void) CircleTangentCurveTwoPoints( const MbCurve & pCurve, - MbCartPoint & on1, MbCartPoint & on2, - PArray & pCircle ); +MATH_FUNC (void) CircleTangentCurveTwoPoints( const MbCurve & pCurve, + const MbCartPoint & on1, + const MbCartPoint & on2, + PArray & pCircle ); //------------------------------------------------------------------------------ @@ -345,8 +346,10 @@ MATH_FUNC (void) CircleTangentCurveTwoPoints( const MbCurve & pCurve, \ingroup Curve_Modeling */ // --- -MATH_FUNC (void) CircleTangentCurveRPointOn( const MbCurve & pCurve, double radius, MbCartPoint & on, - PArray & pCircle ); +MATH_FUNC (void) CircleTangentCurveRPointOn( const MbCurve & pCurve, + double radius, + const MbCartPoint & on, + PArray & pCircle ); //------------------------------------------------------------------------------ @@ -367,8 +370,10 @@ MATH_FUNC (void) CircleTangentCurveRPointOn( const MbCurve & pCurve, double radi \ingroup Curve_Modeling */ // --- -MATH_FUNC (void) CircleTanTwoCurvesRadius( const MbCurve & pCurve1, const MbCurve & pCurve2, double rad, - PArray & pCircle ); +MATH_FUNC (void) CircleTanTwoCurvesRadius( const MbCurve & pCurve1, + const MbCurve & pCurve2, + double rad, + PArray & pCircle ); //------------------------------------------------------------------------------ @@ -389,8 +394,10 @@ MATH_FUNC (void) CircleTanTwoCurvesRadius( const MbCurve & pCurve1, const MbCurv \ingroup Curve_Modeling */ // --- -MATH_FUNC (void) CircleTanTwoCurvesPointOn( const MbCurve & pCurve1, const MbCurve & pCurve2, const MbCartPoint & pOn, - PArray & pCircle ); +MATH_FUNC (void) CircleTanTwoCurvesPointOn( const MbCurve & pCurve1, + const MbCurve & pCurve2, + const MbCartPoint & pOn, + PArray & pCircle ); //------------------------------------------------------------------------------ @@ -411,8 +418,10 @@ MATH_FUNC (void) CircleTanTwoCurvesPointOn( const MbCurve & pCurve1, const MbCur \ingroup Curve_Modeling */ // --- -MATH_FUNC (void) CircleOriginOneTangentTwo( const MbCurve & pCurve1, const MbCurve & pCurve2, const MbCartPoint & pp, - RPArray & pCircle ); +MATH_FUNC (void) CircleOriginOneTangentTwo( const MbCurve & pCurve1, + const MbCurve & pCurve2, + const MbCartPoint & pp, + RPArray & pCircle ); //------------------------------------------------------------------------------ @@ -441,7 +450,7 @@ MATH_FUNC (void) CircleOriginOneTangentTwo( const MbCurve & pCurve1, const MbCur \ingroup Curve_Modeling */ // --- -MATH_FUNC (void) CircleTanCurvePointOnAngle( MbCurve & curve, MbCartPoint & p1, double angle, +MATH_FUNC (void) CircleTanCurvePointOnAngle( const MbCurve & curve, const MbCartPoint & p1, double angle, PArray & circles ); @@ -463,9 +472,10 @@ MATH_FUNC (void) CircleTanCurvePointOnAngle( MbCurve & curve, MbCartPoint & p1, \ingroup Curve_Modeling */ // --- -MATH_FUNC (void) ArcTangentCurveTwoPoints( const MbCurve & pCurve, - MbCartPoint & on1, MbCartPoint & on2, - PArray & arc ); +MATH_FUNC (void) ArcTangentCurveTwoPoints( const MbCurve & pCurve, + const MbCartPoint & on1, + const MbCartPoint & on2, + PArray & arc ); //------------------------------------------------------------------------------ @@ -486,8 +496,10 @@ MATH_FUNC (void) ArcTangentCurveTwoPoints( const MbCurve & pCurve, \ingroup Curve_Modeling */ // --- -MATH_FUNC (void) ArcTangentCurveRPointOn( const MbCurve & pCurve, double radius, MbCartPoint & on, - PArray & arc ); +MATH_FUNC (void) ArcTangentCurveRPointOn( const MbCurve & pCurve, + double radius, + const MbCartPoint & on, + PArray & arc ); //------------------------------------------------------------------------------ @@ -506,8 +518,9 @@ MATH_FUNC (void) ArcTangentCurveRPointOn( const MbCurve & pCurve, double radius, \ingroup Curve_Modeling */ // --- -MATH_FUNC (void) ArcTangentCurveContinue( MbLine & line, MbCartPoint & p2, - PArray & arc ); +MATH_FUNC (void) ArcTangentCurveContinue( const MbLine & line, + const MbCartPoint & p2, + PArray & arc ); //------------------------------------------------------------------------------ @@ -530,8 +543,10 @@ MATH_FUNC (void) ArcTangentCurveContinue( MbLine & line, MbCartPoint & p2, \ingroup Curve_Modeling */ // --- -MATH_FUNC (void) ArcTangentCurveRadContinue( MbLine & line, double rad, MbCartPoint & p2, - PArray & arc ); +MATH_FUNC (void) ArcTangentCurveRadContinue( const MbLine & line, + double rad, + const MbCartPoint & p2, + PArray & arc ); //------------------------------------------------------------------------------ @@ -556,9 +571,11 @@ MATH_FUNC (void) ArcTangentCurveRadContinue( MbLine & line, double rad, MbCartPo \ingroup Curve_Modeling */ // --- -MATH_FUNC (void) CircleTanThreeCurves( const MbCurve * curve1, const MbCurve * curve2, const MbCurve * curve3, - MbCartPoint & pnt, - PArray & circle ); +MATH_FUNC (void) CircleTanThreeCurves( const MbCurve * curve1, + const MbCurve * curve2, + const MbCurve * curve3, + const MbCartPoint & pnt, + PArray & circle ); //----------------------------------------------------------------------------- @@ -576,7 +593,7 @@ MATH_FUNC (void) CircleTanThreeCurves( const MbCurve * curve1, const MbCurve * c */ // --- MATH_FUNC (void) CreateNewCircles( PArray & cTmp, - PArray & pCircle ); + PArray & pCircle ); #endif // __ALG_CIRCLE_CURVE_H diff --git a/C3d/Include/alg_curve_delete_part.h b/C3d/Include/alg_curve_delete_part.h index 7721781..d141186 100644 --- a/C3d/Include/alg_curve_delete_part.h +++ b/C3d/Include/alg_curve_delete_part.h @@ -38,9 +38,10 @@ \ingroup Algorithms_2D */ // --- -MATH_FUNC (MbeState) DeleteCurvePart( List & curveList, - const MbCartPoint & pnt, - MbCurve * curve, MbCurve *& part2 ); +MATH_FUNC (MbeState) DeleteCurvePart( List & curveList, + const MbCartPoint & pnt, + MbCurve * curve, + MbCurve *& part2 ); //------------------------------------------------------------------------------ @@ -70,7 +71,8 @@ MATH_FUNC (MbeState) DeleteCurvePart( List & curveList, MATH_FUNC (MbeState) DeleteCurvePart( const MbCartPoint & p1, const MbCartPoint & p2, const MbCartPoint & p3, - MbCurve * curve, MbCurve *& part2 ); + MbCurve * curve, + MbCurve *& part2 ); //------------------------------------------------------------------------------ @@ -99,9 +101,10 @@ MATH_FUNC (MbeState) DeleteCurvePart( const MbCartPoint & p1, \ingroup Algorithms_2D */ // --- -MATH_FUNC (MbeState) TrimmCurvePart( List & curveList, - const MbCartPoint & pnt, - MbCurve * curve, MbCurve *& part2 ); +MATH_FUNC (MbeState) TrimmCurvePart( List & curveList, + const MbCartPoint & pnt, + MbCurve * curve, + MbCurve *& part2 ); //------------------------------------------------------------------------------ @@ -133,13 +136,14 @@ MATH_FUNC (MbeState) TrimmCurvePart( List & curveList, MATH_FUNC (MbeState) TrimmCurvePart( const MbCartPoint & p1, const MbCartPoint & p2, const MbCartPoint & p3, - MbCurve * curve, MbCurve *& part2 ); + MbCurve * curve, + MbCurve *& part2 ); //------------------------------------------------------------------------------ -/** \brief \ru Выровнить кривую. +/** \brief \ru Выровнять кривую. \en Justify the curve. \~ - \details \ru Выровнить кривую по отношению к заданной кривой и точке на кривой.\n + \details \ru Выровнять кривую по отношению к заданной кривой и точке на кривой.\n Кривая усекается точкой пересечения ее с граничной кривой, ближайшей к заданной точке. Остается часть кривой со стороны указанной точки. \en Justify the curve relative to the given curve and a point on the curve.\n @@ -160,8 +164,10 @@ MATH_FUNC (MbeState) TrimmCurvePart( const MbCartPoint & p1, \ingroup Algorithms_2D */ // --- -MATH_FUNC (MbeState) JustifyCurve( MbCurve * curve, MbCurve * limitCurve, - const MbCartPoint & pnt, MbCurve *& part2 ); +MATH_FUNC (MbeState) JustifyCurve( MbCurve * curve, + const MbCurve * limitCurve, + const MbCartPoint & pnt, + MbCurve *& part2 ); //------------------------------------------------------------------------------ @@ -266,15 +272,16 @@ MATH_FUNC (MbeState) BreakByCurvesArr( MbCurve & curve, \ingroup Algorithms_2D */ // --- -MATH_FUNC (MbeState) BreakCurve( MbCurve & curve, - const MbCartPoint & p1, const MbCartPoint & p2, +MATH_FUNC (MbeState) BreakCurve( MbCurve & curve, + const MbCartPoint & p1, + const MbCartPoint & p2, PArray & part2 ); //------------------------------------------------------------------------------ /** \brief \ru Разбить кривую.. \en Split the curve. \~ - \details \ru Разбить кривую на ресколько равных частей. + \details \ru Разбить кривую на несколько равных частей. \en Split the curve by several equal pieces. \~ \param[in, out] curve - \ru Разбиваемая кривая. \en The curve for splitting. \~ @@ -294,22 +301,23 @@ MATH_FUNC (MbeState) BreakCurveNParts( MbCurve & curve, ptrdiff_t partsCount, co //------------------------------------------------------------------------------ -/** \brief \ru Удлиннить кривую. +/** \brief \ru Удлинить кривую. \en Extend the curve. \~ - \details \ru Удлиннить кривую curve до кривой-границы limitCurve с конца ближайшего к точке pnt + \details \ru Удлинить кривую curve до кривой-границы limitCurve с конца ближайшего к точке pnt \en Extend the curve to the given curve-boundary limitCurve from the end nearest to the given point pnt. \~ \param[in, out] curve - \ru Изменяемая кривая. \en The modified curve. \~ \param[in] limitCurve - \ru Кривая-граница. \en The curve-boundary \~ - \param[in] pnt - \ru Точка, показывающая удлинняемый конец кривой. + \param[in] pnt - \ru Точка, показывающая удлиняемый конец кривой. \en The point indicating the extended end of a curve. \~ \return \ru Состояние кривой после ее модификации. \en The state of a curve after modification. \~ \ingroup Algorithms_2D */ // --- -MATH_FUNC (MbeState) ExtendCurveToCurve( MbCurve * curve, const MbCurve * limitCurve, +MATH_FUNC (MbeState) ExtendCurveToCurve( MbCurve * curve, + const MbCurve * limitCurve, const MbCartPoint & pnt ); diff --git a/C3d/Include/alg_curve_distance.h b/C3d/Include/alg_curve_distance.h index 853aae7..f7b4ec4 100644 --- a/C3d/Include/alg_curve_distance.h +++ b/C3d/Include/alg_curve_distance.h @@ -260,15 +260,14 @@ MATH_FUNC (void) CircleCentreOnCurveTwoPoints( const MbCurve & pCurve, const MbC //------------------------------------------------------------------------------ -/** \brief \ru Расстояние между объектами. - \en Distance between objects. \~ - \details \ru Расстояние между двумя объектами.\n - \en The distance between two objects.\n \~ +/** \brief \ru Расстояние между кривыми. + \en Distance between curves. \~ + \details \ru Расстояние между двумя кривыми.\n + \en The distance between two curves.\n \~ \ingroup Algorithms_2D */ // --- -class MATH_CLASS MbDistance { -public : +struct MATH_CLASS MbDistance { double u; ///< \ru Параметр на первой кривой. \en Parameter on the first curve. double v; ///< \ru Параметр на второй кривой. \en Parameter on the second curve. double d; ///< \ru Минимальное расстояние. \en Minimal distance. diff --git a/C3d/Include/alg_curve_envelope.h b/C3d/Include/alg_curve_envelope.h index f545999..2eebdb3 100644 --- a/C3d/Include/alg_curve_envelope.h +++ b/C3d/Include/alg_curve_envelope.h @@ -31,17 +31,18 @@ \en The array of points of intersection with the nearest curve. \~ \param[out] contour - \ru Контур для добавления сегмента. \en The contour for adding the segment to. \~ - \param[out] crossRight - \ru Узел - массив точек пересечения кривой в сторону продолжения конутра. + \param[out] crossRight - \ru Узел - массив точек пересечения кривой в сторону продолжения контура. \en The node - the array of intersection points in the side of contour extension. \~ \return \ru true, если сегмент добавлен. \en true if a segment has been added. \~ \ingroup Algorithms_2D */ // --- -MATH_FUNC (bool) BeginEnvelopeContour( MbCartPoint & insidePnt, const MbCurve * selectCurve, - SArray & cross, - MbContour & contour, - SArray & crossRight ); +MATH_FUNC (bool) BeginEnvelopeContour( const MbCartPoint & insidePnt, + const MbCurve * selectCurve, + SArray & cross, + MbContour & contour, + SArray & crossRight ); //------------------------------------------------------------------------------ @@ -58,7 +59,7 @@ MATH_FUNC (bool) BeginEnvelopeContour( MbCartPoint & insidePnt, const MbCurve * \ingroup Algorithms_2D */ // --- -MATH_FUNC (MbCurve *) FindNearestCurve( List & curveList, MbCartPoint & pnt ); +MATH_FUNC (MbCurve *) FindNearestCurve( List & curveList, const MbCartPoint & pnt ); //------------------------------------------------------------------------------ @@ -79,9 +80,10 @@ MATH_FUNC (MbCurve *) FindNearestCurve( List & curveList, MbCartPoint & \ingroup Algorithms_2D */ // --- -MATH_FUNC (void) IntersectWithAll( const MbCurve * selectCurve, - LIterator & fromCurve, - SArray & cross, bool self ); +MATH_FUNC (void) IntersectWithAll( const MbCurve * selectCurve, + LIterator & fromCurve, + SArray & cross, + bool self ); //------------------------------------------------------------------------------ diff --git a/C3d/Include/alg_curve_equid.h b/C3d/Include/alg_curve_equid.h index 35c5abe..1f4e536 100644 --- a/C3d/Include/alg_curve_equid.h +++ b/C3d/Include/alg_curve_equid.h @@ -31,10 +31,10 @@ 1 - справа по направлению,\n 2 - с двух сторон. \en Attribute defining the side to construct:\n - 0 - on the left by derection,\n - 1 - on the right by derection,\n + 0 - on the left by direction,\n + 1 - on the right by direction,\n 2 - on the both sides. \~ - \param[in] arcMode - \ru Cпособ обхода углов:\n + \param[in] arcMode - \ru Способ обхода углов:\n true - дугой, false - срезом. \en The way of traverse of angles:\n @@ -53,9 +53,14 @@ \ingroup Algorithms_2D */ // --- -MATH_FUNC (void) Equid( const MbCurve *curve, double radLeft, double radRight, - int side, bool arcMode, bool degState, - PArray &equLeft, PArray &equRight ); +MATH_FUNC (void) Equid( const MbCurve * curve, + double radLeft, + double radRight, + int side, + bool arcMode, + bool degState, + PArray & equLeft, + PArray & equRight ); //------------------------------------------------------------------------------ diff --git a/C3d/Include/alg_curve_fillet.h b/C3d/Include/alg_curve_fillet.h index 21d3c9f..67d2852 100644 --- a/C3d/Include/alg_curve_fillet.h +++ b/C3d/Include/alg_curve_fillet.h @@ -48,12 +48,12 @@ class MATH_CLASS MbContour; \ingroup Algorithms_2D */ // --- -MATH_FUNC (bool) Fillet( MbCurve * curve1, const MbCartPoint & pnt1, bool trim1, - MbCurve * curve2, const MbCartPoint & pnt2, bool trim2, - double rad, +MATH_FUNC (bool) Fillet( MbCurve * curve1, const MbCartPoint & pnt1, bool trim1, + MbCurve * curve2, const MbCartPoint & pnt2, bool trim2, + double rad, MbeState & state1, MbeState & state2, - MbArc *& arc ); + MbArc *& arc ); //------------------------------------------------------------------------------ @@ -94,11 +94,13 @@ MATH_FUNC (bool) Fillet( MbCurve * curve1, const MbCartPoint & pnt1, bool trim1, \ingroup Algorithms_2D */ // --- -MATH_FUNC (bool) Chamfer( MbCurve * curve1, const MbCartPoint & pnt1, bool trim1, - MbCurve * curve2, const MbCartPoint & pnt2, bool trim2, - double len, double angle, bool type, - MbeState & state1, - MbeState & state2, +MATH_FUNC (bool) Chamfer( MbCurve * curve1, const MbCartPoint & pnt1, bool trim1, + MbCurve * curve2, const MbCartPoint & pnt2, bool trim2, + double len, + double angle, + bool type, + MbeState & state1, + MbeState & state2, MbLineSegment *& lineseg ); diff --git a/C3d/Include/alg_curve_hatch.h b/C3d/Include/alg_curve_hatch.h index f315c35..4cb5e74 100644 --- a/C3d/Include/alg_curve_hatch.h +++ b/C3d/Include/alg_curve_hatch.h @@ -20,16 +20,18 @@ Для штриховки. \en Find intersection of a curve with horizontal line. For hatching. \~ - \param[in] y - \ru Координата у горизонтальной прямой. - \en The coordinate of a horizontal line. \~ - \param[in] curve - \ru Кривая. - \en The curve. \~ - \param[out] crossPnt - \ru Точки пересечения. - \en Intersection points. \~ + \param[in] y - \ru Координата у горизонтальной прямой. + \en The coordinate of a horizontal line. \~ + \param[in] curve - \ru Кривая. + \en The curve. \~ + \param[out] crossPnt - \ru Точки пересечения. + \en Intersection points. \~ + \param[in] skipTouchPoints - \ru Пропускать точки касания. true - пропускаем точки касания, false - старое поведение функции. + \en Skip touch points. true - skip touch points, false - old function behavior. \~ \ingroup Algorithms_2D */ // --- -MATH_FUNC (void) HatchIntersectLine( double y, MbCurve * curve, SArray & crossPnt ); +MATH_FUNC (void) HatchIntersectLine( double y, const MbCurve * curve, SArray & crossPnt, bool skipTouchPoints = false ); //------------------------------------------------------------------------------ @@ -48,7 +50,7 @@ MATH_FUNC (void) HatchIntersectLine( double y, MbCurve * curve, SArray & crossPnt ); +MATH_FUNC (void) HatchIntersectCircle( const MbCurve * circle, const MbCurve * curve, SArray & crossPnt ); #endif // __ALG_CURVE_HATCH_H diff --git a/C3d/Include/alg_curve_tangent.h b/C3d/Include/alg_curve_tangent.h index d16da71..6dfa427 100644 --- a/C3d/Include/alg_curve_tangent.h +++ b/C3d/Include/alg_curve_tangent.h @@ -17,8 +17,8 @@ /** \brief \ru Построить касательные прямые. \en Construct a line. \~ \details \ru Построить все возможные прямые через точку касательно данной кривой.\n - Базовая точка прямой сопадает с точкой касания. - \en Construct a line passing throgh a point and tangent to a given curve.\n + Базовая точка прямой совпадает с точкой касания. + \en Construct a line passing through a point and tangent to a given curve.\n A line origin is coincident with a tangency point. \~ \param[in] pnt - \ru Точка, через которую проходит прямая. \en The point which the line passing through. \~ @@ -26,12 +26,12 @@ \en The curve which the constructed line should be tangent to. \~ \param[out] pLine - \ru Набор прямых. \en The set of lines. \~ - \param[in] lineAsCurve - \ru Обрабатывать прямую, ломаную и отрезок как кривую в общеи мслучае. + \param[in] lineAsCurve - \ru Обрабатывать прямую, ломаную и отрезок как кривую в общем случае. \en Work with MbLline, MbPolyline, MbLineSegment as with MbCurve. \~ \ingroup Curve_Modeling */ // --- -MATH_FUNC (void) LinePointTangentCurve( MbCartPoint & pnt, const MbCurve & pCurve, +MATH_FUNC (void) LinePointTangentCurve( const MbCartPoint & pnt, const MbCurve & pCurve, PArray & pLine, bool lineAsCurve = false ); @@ -39,7 +39,7 @@ MATH_FUNC (void) LinePointTangentCurve( MbCartPoint & pnt, const MbCurve & pCurv /** \brief \ru Построить прямые под углом. \en Construct lines passing at angle. \~ \details \ru Построить прямые, проходящие под углом angle к оси 0X и касательные к кривой.\n - Базовая точка прямой сопадает с точкой касания. + Базовая точка прямой совпадает с точкой касания. \en Construct lines at angle "angle" to the axis OX and tangent to the curve.\n A line origin is coincident with a tangency point. \~ \param[in] angle - \ru Угол к оси абсцисс. @@ -60,7 +60,7 @@ MATH_FUNC (void) LineAngleTangentCurve( double angle, const MbCurve & pCurve, \en Construct lines tangent to circles. \~ \details \ru Построить прямые, касательные к двум окружностям, заданным центрами и радиусами.\n - Базовая точка прямой сопадает с точкой касания первой окружности. + Базовая точка прямой совпадает с точкой касания первой окружности. Функция строит от 0 до 4 прямых. \en Construct lines tangent to two circles. with given centers and radii.\n @@ -94,7 +94,7 @@ MATH_FUNC (ptrdiff_t) LineTan2Circles( const MbCartPoint & centre1, double radiu /** \brief \ru Построить касательную прямую. \en Construct a tangent line. \~ \details \ru Построить прямую, касательную двум кривым.\n - Базовая точка прямой сопадает с точкой касания первой кривой. + Базовая точка прямой совпадает с точкой касания первой кривой. \en Construct a line tangent to two curves.\n A line origin is coincident with a point of tangency on the first curve. \~ \param[in] pCurve1 - \ru Первая кривая, которой должна касаться построенная прямая. @@ -141,9 +141,9 @@ MATH_FUNC (ptrdiff_t) LineAngleTanCircle( double angle, const MbCartPoint & cent //------------------------------------------------------------------------------ -/** \brief \ru Перестывить прямые. +/** \brief \ru Переставить прямые. \en Swap lines. \~ - \details \ru Перестывить прямые местами. + \details \ru Переставить прямые местами. \en Swap lines. \~ \param[in] l1 - \ru Первая прямая. \en The first line. \~ diff --git a/C3d/Include/alg_dimension.h b/C3d/Include/alg_dimension.h index d80d839..3b69601 100644 --- a/C3d/Include/alg_dimension.h +++ b/C3d/Include/alg_dimension.h @@ -1,446 +1,446 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Радиальный размер к поверхности. Расстояние между поверхностями. - \en Radial dimension of surface. Distance between surfaces. \~ - \details \ru Функции построения окружности или дуги для радиального размера к поверхности. - Функция вычисления экстремальных расстояний между поверхностями. - \en Functions of construction of a circle or an arc for radial dimension of surface. - A function of calculation of extreme distances between surfaces. \~ -*/ -//////////////////////////////////////////////////////////////////////////////// - - -#ifndef __ALG_DIMENSION_H -#define __ALG_DIMENSION_H - - -#include -#include -#include - - -class MATH_CLASS MbCartPoint3D; -class MATH_CLASS MbVector3D; -class MATH_CLASS MbPlacement3D; -class MATH_CLASS MbAxis3D; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbPlaneCurve; -class MATH_CLASS MbSurface; -class IProgressIndicator; - - -//////////////////////////////////////////////////////////////////////////////// -// -// \ru Построение окружности или дуги для радиального размера к поверхности, \en Construction of a circle or an arc for radial dimension of surface -// \ru имеющей круговую параметрическую линию u=const или v=const \en which has a circular parametric line u=const or v=const. -// \ru Перечень поверхностей, имеющих параметрическую линию u=const или u=const: \en The enumeration of surfaces which have a parametric line u=const or u=const: -// MbCylinderSurface, v=const -// MbConeSurface, v=const -// MbSphereSurface, u=const -// MbTorusSurface, u=const -// MbLoftedSurface, v=const -// MbElevationSurface, v=const -// MbExtrusionSurface, v=const -// MbRevolutionSurface, u=const -// MbEvolutionSurface, u=const -// MbExactionSurface, u=const -// -//////////////////////////////////////////////////////////////////////////////// - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить окружность или дугу для радиального размера к поверхности. - \en Construct a circle or an arc for radial dimension of surface. \~ - \details \ru Построение выполняется по заданной параметрической точке поверхности. Поверхность - должна иметь круговую параметрическую линию u=const или v=const. Перечень поверхностей, - имеющих параметрическую линию u=const или v=const: - MbCylinderSurface v=const, \n MbConeSurface v=const, \n - MbSphereSurface u=const, \n MbTorusSurface u=const, \n - MbLoftedSurface v=const, \n MbElevationSurface v=const, \n - MbExtrusionSurface v=const, \n MbRevolutionSurface u=const, \n - MbEvolutionSurface u=const, \n MbExactionSurface u=const. - \en Construction is performed by the given parametric point on surface. A surface - should have a circular parametric line u=const or v=const. The enumeration of surfaces - which have a parametric line u=const or v=const. - MbCylinderSurface v=const, \n MbConeSurface v=const, \n - MbSphereSurface u=const, \n MbTorusSurface u=const, \n - MbLoftedSurface v=const, \n MbElevationSurface v=const, \n - MbExtrusionSurface v=const, \n MbRevolutionSurface u=const, \n - MbEvolutionSurface u=const, \n MbExactionSurface u=const. \~ - \param[in] surface - \ru Исходная поверхность. - \en The initial surface. \~ - \param[in] surface_uv - \ru Координаты исходной точки на поверхности. - \en Coordinates of the initial point on surface. \~ - \param[out] plane_curve - \ru Требуемая окружность или дуга. - \en The required circle or an arc. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (void) RadiusDimension3D( const MbSurface & surface, - const MbCartPoint & surface_uv, - MbPlaneCurve *& plane_curve ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить окружность или дугу для радиального размера к поверхности. - \en Construct a circle or an arc for radial dimension of surface. \~ - \details \ru Построение выполняется по заданной по заданной пространственной точке. Поверхность - должна иметь круговую параметрическую линию u=const или v=const. Перечень поверхностей, - имеющих параметрическую линию u=const или u=const: - MbCylinderSurface v=const, \n MbConeSurface v=const, \n - MbSphereSurface u=const, \n MbTorusSurface u=const, \n - MbLoftedSurface v=const, \n MbElevationSurface v=const, \n - MbExtrusionSurface v=const, \n MbRevolutionSurface u=const, \n - MbEvolutionSurface u=const, \n MbExactionSurface u=const. - \en Construction is performed by the given spatial point. A surface - should have a circular parametric line u=const or v=const. The enumeration of surfaces - which have a parametric line u=const or v=const. - MbCylinderSurface v=const, \n MbConeSurface v=const, \n - MbSphereSurface u=const, \n MbTorusSurface u=const, \n - MbLoftedSurface v=const, \n MbElevationSurface v=const, \n - MbExtrusionSurface v=const, \n MbRevolutionSurface u=const, \n - MbEvolutionSurface u=const, \n MbExactionSurface u=const. \~ - \param[in] surface - \ru Исходная поверхность. - \en The initial surface. \~ - \param[in] point - \ru Пространственные координаты исходной точки. - \en Space coordinates of the initial point. \~ - \param[out] plane_curve - \ru Требуемая окружность или дуга. - \en The required circle or an arc. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (void) RadiusDimension3D( const MbSurface & surface, - const MbCartPoint3D & point, - MbPlaneCurve *& plane_curve ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить окружность или дугу для радиального размера к поверхности. - \en Construct a circle or an arc for radial dimension of surface. \~ - \details \ru Построение выполняется по заданному плейсменту. Поверхность должна иметь - круговую параметрическую линию u=const или v=const. Перечень поверхностей, имеющих - параметрическую линию u=const или u=const: - MbCylinderSurface v=const, \n MbConeSurface v=const, \n - MbSphereSurface u=const, \n MbTorusSurface u=const, \n - MbLoftedSurface v=const, \n MbElevationSurface v=const, \n - MbExtrusionSurface v=const, \n MbRevolutionSurface u=const, \n - MbEvolutionSurface u=const, \n MbExactionSurface u=const. - \en Construction is performed by the given placement. A surface should have - a circular parametric line u=const or v=const. The enumeration of surfaces with - a parametric line u=const or v=const. - MbCylinderSurface v=const, \n MbConeSurface v=const, \n - MbSphereSurface u=const, \n MbTorusSurface u=const, \n - MbLoftedSurface v=const, \n MbElevationSurface v=const, \n - MbExtrusionSurface v=const, \n MbRevolutionSurface u=const, \n - MbEvolutionSurface u=const, \n MbExactionSurface u=const. \~ - \param[in] surface - \ru Исходная поверхность. - \en The initial surface. \~ - \param[in] place - \ru Исходный плейсмент. - \en The initial placement. \~ - \param[out] plane_curve - \ru Требуемая окружность или дуга. - \en The required circle or an arc. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (void) RadiusDimension3D( const MbSurface & surface, - const MbPlacement3D & place, - MbPlaneCurve *& plane_curve ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Можно ли построить окружность или дугу для радиального размера к поверхности. - \en Whether a circle or an arc can be constructed for radial dimension of surface. \~ - \details \ru Можно построить, если тип базовой поверхности: st_CylinderSurface или st_ConeSurface, - или st_SphereSurface, или st_TorusSurface. - \en It can be constructed if the type of a base surface is st_CylinderSurface or st_ConeSurface, - or st_SphereSurface, or st_TorusSurface. \~ - \param[in] surface - \ru Исходная поверхность. - \en The initial surface. \~ - \return \ru true, если можно построить. - \en true if it can be constructed. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) IsPossibleRadiusDimension3D( const MbSurface & surface ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Результат замера расстояния и угла между поверхностями. - \en The result of measurement of dimension and angle between surfaces. \~ - \details \ru Результат замера расстояния и угла между поверхностями. - \en The result of measurement of dimension and angle between surfaces. \~ - \ingroup Algorithms_3D -*/ -// --- -enum MbeSurfAxesMeasureRes -{ - // \ru ошибочные результат \en mistaken result - samr_SurfSurf_Failed = -3, ///< \ru Ошибка при работе с поверхностями. \en An error is occurred while working with surfaces. - samr_AxisSurf_Failed = -2, ///< \ru Ошибка при работе с осью и поверхностю. \en An error is occurred while working with axis and surface. - samr_AxisAxis_Failed = -1, ///< \ru Ошибка при работе с осями. \en An error is occurred while working with axes. - // \ru пустой результат \en an empty result. - samr_Undefined = 0, ///< \ru Не получилось или не измерялось. \en Failed or didn't measured. - // \ru две оси \en two axes - samr_AxisAxis_Coaxial, ///< \ru Оси совпадают. \en Axes are coincident. - samr_AxisAxis_Parallel, ///< \ru Оси параллельны. \en Axes are parallel. - samr_AxisAxis_Intersecting, ///< \ru Оси пересекаются. \en Axes are crossed. - samr_AxisAxis_Distant, ///< \ru Оси на расстоянии. \en Axes are located at a distance. - // \ru одна ось (какая из осей есть, см. по возвращаемому флагу функции замера) \en one axis (see the returned flag of measurement function to detect which one exactly) - samr_AxisSurf_Colinear, ///< \ru Ось лежит на поверхности. \en The axis lies on the surface. - samr_AxisSurf_Parallel, ///< \ru Ось параллельна поверхности. \en The axis is parallel to the surface. - samr_AxisSurf_Intersecting, ///< \ru Ось пересекает поверхность. \en The axis crosses the surface. - samr_AxisSurf_Distant, ///< \ru Ось на расстоянии от поверхности. \en The axis is located at a distance from the surface. - // \ru две плоские поверхности \en two planar surfaces - samr_SurfSurf_Colinear, ///< \ru Одна поверхность лежит на другой. \en One surface lies on another one. - samr_SurfSurf_Parallel, ///< \ru Поверхности параллельны. \en Surfaces are parallel. - samr_SurfSurf_Intersecting, ///< \ru Поверхности пересекаются. \en Surfaces are intersecting inside domain. - // \ru samr_SurfSurf_Distant, // находятся на расстоянии \en samr_SurfSurf_Distant, // located at a distance - -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Расстояние между осями поверхностей. - \en Distance between axes of surfaces. \~ - \details \ru Рассчитывается расстояние между осями поверхностей, имеющих оси вращения, - или расстояние между поверхностью, имеющей ось, и плоской поверхностью. - \en Calculate distance between axes of revolution surfaces - or distance between revolution surface and planar surface. \~ - \param[in] surface1, sameSense1 - \ru Первая поверхность и ее направление. - \en The first surface and its direction. \~ - \param[in] surface2, sameSense2 - \ru Вторая поверхность и ее направление. - \en The second surface and its direction. \~ - \param[out] axis1, exist1 - \ru Ось первой поверхности и флаг ее наличия. - \en The axis of the first surface and the flag of its existence. \~ - \param[out] axis2, exist2 - \ru Ось второй поверхности и флаг ее наличия. - \en The axis of the second surface and the flag of its existence. \~ - \param[out] p1 - \ru Точка на первой оси или поверхности. - \en The point on the first axis or surface. \~ - \param[out] p2 - \ru Точка на второй оси или поверхности. - \en The point on the second axis or surface. \~ - \param[out] angle - \ru Угол между осями или осью поверхностью. - \en The angle between axes or between an axis and a surface. \~ - \param[out] distance - \ru Минимальное расстояние между осями. - \en Minimal distance between axes. \~ - \param[in] angle - \ru Угловая погрешность. - \en The angle accuracy. \~ - \return \ru Вариант полученного замера или вариант ошибки. - \en The variant of the obtained measurement or the variant of error. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (MbeSurfAxesMeasureRes) SurfAxesDistAngle( const MbSurface & surface1, bool sameSense1, - const MbSurface & surface2, bool sameSense2, - MbAxis3D & axis1, bool & exist1, - MbAxis3D & axis2, bool & exist2, - MbCartPoint3D & p1, - MbCartPoint3D & p2, - double & angle, - double & distance, - double angleEps = ANGLE_EPSILON ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Расстояние между точками на поверхности. - \en Distance between points on surface. \~ - \details \ru Класс содержит данные о расстоянии между точками и координатами этих точек - на поверхностях. - \en The class contains data about the distance between points and their coordinates - on surfaces. \~ - \ingroup Algorithms_3D -*/ -// --- -class MATH_CLASS MbSurfDist { - friend class MbMinMaxSurfDists; -private: - double d; ///< \ru Расстояние. \en Distance. - MbCartPoint uv1; ///< \ru Параметр на первой поверхности. \en Parameter on the first surface. - MbCartPoint uv2; ///< \ru Параметр на второй поверхности. \en Parameter on the second surface. - uint8 sign; ///< \ru Знак расстояния. \en Sign of direction. - -public: - /// \ru Конструктор. \en Constructor. - MbSurfDist() : d( UNDEFINED_DBL ), uv1(), uv2(), sign( 1 ) {} - /// \ru Конструктор по данным. \en The constructor by data. - MbSurfDist( double _d, const MbCartPoint & _uv1, const MbCartPoint & _uv2, bool plus ) { Init( _d, _uv1, _uv2, plus ); } - /// \ru Конструктор копирования. \en Copy constructor. - MbSurfDist( const MbSurfDist & other ) { Init( other ); } - /// \ru Деструктор. \en The destructor. - virtual ~MbSurfDist() {} -public: - /// \ru Функция копирования. \en Copy function. - void Init( const MbSurfDist & obj ) { d = obj.d; uv1 = obj.uv1; uv2 = obj.uv2; sign = obj.sign; } - /// \ru Получить расстояние. \en Get distance. - double GetDistance() const { return d; } - /// \ru Получить точку на первой поверхности. \en Get the point on the first surface. - const MbCartPoint & GetPointOne() const { return uv1; } - /// \ru Получить точку на второй поверхности. \en Get the point on the second surface. - const MbCartPoint & GetPointTwo() const { return uv2; } - /// \ru Расстояние положительное? \en Is the distance positive? - bool IsPositive() const { return (sign > 0); } - /// \ru Расстояние отрицательное? \en Is the distance negative? - bool IsNegative() const { return (sign < 1); } - /// \ru Оператор присваивания. \en Assignment operator. - const MbSurfDist & operator = ( const MbSurfDist & other ) { Init( other ); return (*this); } - -private: - void Init( double _d, const MbCartPoint & _uv1, const MbCartPoint & _uv2, bool plus ); -}; - - -//------------------------------------------------------------------------------ -// \ru инициализатор \en Initializer -// --- -inline void MbSurfDist::Init( double _d, const MbCartPoint & _uv1, const MbCartPoint & _uv2, bool plus ) -{ - d = _d; - uv1 = _uv1; - uv2 = _uv2; - sign = plus ? 1 : 0; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Расстояния с точками между поверхностями. - \en Distances between surfaces with points. \~ - \details \ru Расстояния с точками между поверхностями. - \en Distances between surfaces with points. \~ - \ingroup Algorithms_3D -*/ -// --- -class MATH_CLASS MbMinMaxSurfDists { -private : - SArray surfDistances; ///< \ru Расстояние и параметры на поверхностях. \en Distance and parameters on surfaces. - mutable double midDistance; ///< \ru Среднее расстояние. \en Average distance. - mutable double minDistance; ///< \ru Минимальное расстояние. \en Minimal distance. - mutable double maxDistance; ///< \ru Максимальное расстояние. \en Maximal distance. - mutable bool sorted; ///< \ru Признак сортированности. \en Attribute of being sorted. - -public: - MbMinMaxSurfDists( size_t nReserve = 0 ); ///< \ru Конструктор. \en Constructor. - virtual ~MbMinMaxSurfDists(); ///< \ru Деструктор. \en Destructor. - -public: - bool IsEmpty() const { return (surfDistances.Count() < 1); } ///< \ru Есть ли замеры? \en Are there any measurements? - size_t GetCount() const { return surfDistances.Count(); } ///< \ru Количество замеров. \en The number of measurements. - ptrdiff_t GetMaxIndex() const { return surfDistances.MaxIndex(); } ///< \ru Индекс последнего замера. \en Index of the last measurement - void Reserve( size_t nReserve ); ///< \ru Зарезервировать память под nReserve элементов. \en Reserve memory for 'nReserve' elements. - void RemoveAll( bool bAdjustMemory ); ///< \ru Удалить все элементы \en Delete all elements. - void AdjustMemory(); ///< \ru Освободить лишнюю память \en Free the unnecessary memory. - - /// \ru Получить расстояние по индексу. \en Get the distance by the index. - bool GetDistance( size_t k, double & d ) const; - /// \ru Получить расстояние со знаком, по индексу. \en Get the signed distance by the index. - bool GetSignedDistance( size_t k, double & d ) const; - /// \ru Считаем ли вы расстояние отрицательным. \en Whether the distance is negative. - bool IsNegativeDistance( size_t k ) const { return ((k < surfDistances.Count()) ? surfDistances[k].IsNegative() : false); } - /// \ru Получить минимальное расстояние. \en Get minimal distance. - bool GetMinDistance( double & d ) const; - /// \ru Получить максимальное расстояние. \en Get maximal distance. - bool GetMaxDistance( double & d ) const; - /// \ru Получить среднее расстояние. \en Get average distance. - bool GetMidDistance( double & d ) const; - /// \ru Получить расстояние и точки на поверхностях. \en Get distance and points on surface. - bool GetSurfDistance( size_t k, double & d, MbCartPoint & uv1, MbCartPoint & uv2 ) const; - /// \ru Получить расстояние и точки на поверхностях. \en Get distance and points on surface. - bool GetSurfDistance( size_t k, double & d, bool & plus, MbCartPoint & uv1, MbCartPoint & uv2 ) const; - /// \ru Добавить расстояние и точки на поверхностях. \en Add distance and points on surface. - bool AddSurfDistance( double distance, bool plus, const MbCartPoint & uv1, const MbCartPoint & uv2, - bool bAddEqual, double eps = LENGTH_EPSILON ); - /// \ru Сортировать по возрастанию расстояния. \en Sort by distance in the ascending order. - void Sort(); - /// \ru Убрать объекты с одинаковыми расстояниями. \en Remove objects with similar distances. - void RemoveEqualDistances( double eps = LENGTH_EPSILON ); - - void operator = ( const MbMinMaxSurfDists & ); ///< \ru Оператор присваивания. \en Assignment operator. - -private: - MbMinMaxSurfDists( const MbMinMaxSurfDists & ); -}; - - -//------------------------------------------------------------------------------ -// \ru выдать расстояние \en get the distance -// --- -inline bool MbMinMaxSurfDists::GetDistance( size_t k, double & d ) const -{ - if ( k < surfDistances.Count() ) { - d = surfDistances[k].GetDistance(); - return true; - } - return false; -} - - -//------------------------------------------------------------------------------ -// \ru выдать расстояние со знаком \en get signed distance -// --- -inline bool MbMinMaxSurfDists::GetSignedDistance( size_t k, double & d ) const -{ - if ( k < surfDistances.Count() ) { - d = surfDistances[k].GetDistance(); - if ( surfDistances[k].IsNegative() ) - d = -d; - return true; - } - return false; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Экстремальные расстояния между поверхностями. - \en Extreme distances between surfaces. \~ - \details \ru Экстремальные расстояния между поверхностями по сетке на первой поверхности, - причем замеры осуществляются в заданном направлении (если есть вектор) - или по нормалям к первой поверхности. - \en Extreme distances between surfaces by mesh on the first surface, - measurements are performed in a given direction (if the vector is set) - or by normals of the first surface. \~ - \param[in] surface1 - \ru Первая поверхность. - \en The first surface. \~ - \param[in] u1cnt - \ru Количество точек по u (первая поверхность). - \en The number of points by u (the first surface) \~ - \param[in] v1cnt - \ru Количество точек по v (первая поверхность). - \en The number of points by v (the first surface) \~ - \param[in] dir - \ru Вектор заданного направления (если нет, то по нормали). - \en The vector of direction (if not set then by the normal). \~ - \param[in] orient - \ru Направление поиска. - \en Direction of search. \~ - \param[in] useEqualDistances - \ru Оставлять равные равные расстояния. - \en Whether to use the equal distances. \~ - \param[in] surface2 - \ru Вторая поверхность. - \en The second surface. \~ - \param[in,out] nMin - \ru Кол-во регистрируемых минимумов. - \en The number of registrated minimums. \~ - \param[in,out] nMax - \ru Кол-во регистрируемых максимумов. - \en The number of registrated maximums. \~ - \param[out] allResults - \ru Все результаты. - \en All results. \~ - \param[out] minResults - \ru Результаты-минимумы. - \en Results-minimums. \~ - \param[out] maxResults - \ru Результаты-максимумы. - \en Results-maximums. \~ - \param[in,out] indicator - \ru Интерфейс-индикатор процесса выполнения. - \en Interface-indicator of the execution process. \~ - \return \ru Возвращает результат замера (получен, не получен или же процесс был прерван). - \en Returns the result of measurement (obtained, not obtained, or the process has been aborted). \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (MbeProcessState) MinMaxDistances( const MbSurface & surface1, - ptrdiff_t u1cnt, - ptrdiff_t v1cnt, - const MbVector3D * dir, - const MbeSenseValue & orient, - bool useEqualDistances, - const MbSurface & surface2, - ptrdiff_t & nMin, - ptrdiff_t & nMax, - MbMinMaxSurfDists & allResults, - MbMinMaxSurfDists & minResults, - MbMinMaxSurfDists & maxResults, - IProgressIndicator * indicator = NULL ); - - -#endif // __ALG_DIMENSION_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Радиальный размер к поверхности. Расстояние между поверхностями. + \en Radial dimension of surface. Distance between surfaces. \~ + \details \ru Функции построения окружности или дуги для радиального размера к поверхности. + Функция вычисления экстремальных расстояний между поверхностями. + \en Functions of construction of a circle or an arc for radial dimension of surface. + A function of calculation of extreme distances between surfaces. \~ +*/ +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef __ALG_DIMENSION_H +#define __ALG_DIMENSION_H + + +#include +#include +#include + + +class MATH_CLASS MbCartPoint3D; +class MATH_CLASS MbVector3D; +class MATH_CLASS MbPlacement3D; +class MATH_CLASS MbAxis3D; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbPlaneCurve; +class MATH_CLASS MbSurface; +class IProgressIndicator; + + +//////////////////////////////////////////////////////////////////////////////// +// +// \ru Построение окружности или дуги для радиального размера к поверхности, \en Construction of a circle or an arc for radial dimension of surface +// \ru имеющей круговую параметрическую линию u=const или v=const \en which has a circular parametric line u=const or v=const. +// \ru Перечень поверхностей, имеющих параметрическую линию u=const или u=const: \en The enumeration of surfaces which have a parametric line u=const or u=const: +// MbCylinderSurface, v=const +// MbConeSurface, v=const +// MbSphereSurface, u=const +// MbTorusSurface, u=const +// MbLoftedSurface, v=const +// MbElevationSurface, v=const +// MbExtrusionSurface, v=const +// MbRevolutionSurface, u=const +// MbEvolutionSurface, u=const +// MbExactionSurface, u=const +// +//////////////////////////////////////////////////////////////////////////////// + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить окружность или дугу для радиального размера к поверхности. + \en Construct a circle or an arc for radial dimension of surface. \~ + \details \ru Построение выполняется по заданной параметрической точке поверхности. Поверхность + должна иметь круговую параметрическую линию u=const или v=const. Перечень поверхностей, + имеющих параметрическую линию u=const или v=const: + MbCylinderSurface v=const, \n MbConeSurface v=const, \n + MbSphereSurface u=const, \n MbTorusSurface u=const, \n + MbLoftedSurface v=const, \n MbElevationSurface v=const, \n + MbExtrusionSurface v=const, \n MbRevolutionSurface u=const, \n + MbEvolutionSurface u=const, \n MbExactionSurface u=const. + \en Construction is performed by the given parametric point on surface. A surface + should have a circular parametric line u=const or v=const. The enumeration of surfaces + which have a parametric line u=const or v=const. + MbCylinderSurface v=const, \n MbConeSurface v=const, \n + MbSphereSurface u=const, \n MbTorusSurface u=const, \n + MbLoftedSurface v=const, \n MbElevationSurface v=const, \n + MbExtrusionSurface v=const, \n MbRevolutionSurface u=const, \n + MbEvolutionSurface u=const, \n MbExactionSurface u=const. \~ + \param[in] surface - \ru Исходная поверхность. + \en The initial surface. \~ + \param[in] surface_uv - \ru Координаты исходной точки на поверхности. + \en Coordinates of the initial point on surface. \~ + \param[out] plane_curve - \ru Требуемая окружность или дуга. + \en The required circle or an arc. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (void) RadiusDimension3D( const MbSurface & surface, + const MbCartPoint & surface_uv, + MbPlaneCurve *& plane_curve ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить окружность или дугу для радиального размера к поверхности. + \en Construct a circle or an arc for radial dimension of surface. \~ + \details \ru Построение выполняется по заданной по заданной пространственной точке. Поверхность + должна иметь круговую параметрическую линию u=const или v=const. Перечень поверхностей, + имеющих параметрическую линию u=const или u=const: + MbCylinderSurface v=const, \n MbConeSurface v=const, \n + MbSphereSurface u=const, \n MbTorusSurface u=const, \n + MbLoftedSurface v=const, \n MbElevationSurface v=const, \n + MbExtrusionSurface v=const, \n MbRevolutionSurface u=const, \n + MbEvolutionSurface u=const, \n MbExactionSurface u=const. + \en Construction is performed by the given spatial point. A surface + should have a circular parametric line u=const or v=const. The enumeration of surfaces + which have a parametric line u=const or v=const. + MbCylinderSurface v=const, \n MbConeSurface v=const, \n + MbSphereSurface u=const, \n MbTorusSurface u=const, \n + MbLoftedSurface v=const, \n MbElevationSurface v=const, \n + MbExtrusionSurface v=const, \n MbRevolutionSurface u=const, \n + MbEvolutionSurface u=const, \n MbExactionSurface u=const. \~ + \param[in] surface - \ru Исходная поверхность. + \en The initial surface. \~ + \param[in] point - \ru Пространственные координаты исходной точки. + \en Space coordinates of the initial point. \~ + \param[out] plane_curve - \ru Требуемая окружность или дуга. + \en The required circle or an arc. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (void) RadiusDimension3D( const MbSurface & surface, + const MbCartPoint3D & point, + MbPlaneCurve *& plane_curve ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить окружность или дугу для радиального размера к поверхности. + \en Construct a circle or an arc for radial dimension of surface. \~ + \details \ru Построение выполняется по заданному плейсменту. Поверхность должна иметь + круговую параметрическую линию u=const или v=const. Перечень поверхностей, имеющих + параметрическую линию u=const или u=const: + MbCylinderSurface v=const, \n MbConeSurface v=const, \n + MbSphereSurface u=const, \n MbTorusSurface u=const, \n + MbLoftedSurface v=const, \n MbElevationSurface v=const, \n + MbExtrusionSurface v=const, \n MbRevolutionSurface u=const, \n + MbEvolutionSurface u=const, \n MbExactionSurface u=const. + \en Construction is performed by the given placement. A surface should have + a circular parametric line u=const or v=const. The enumeration of surfaces with + a parametric line u=const or v=const. + MbCylinderSurface v=const, \n MbConeSurface v=const, \n + MbSphereSurface u=const, \n MbTorusSurface u=const, \n + MbLoftedSurface v=const, \n MbElevationSurface v=const, \n + MbExtrusionSurface v=const, \n MbRevolutionSurface u=const, \n + MbEvolutionSurface u=const, \n MbExactionSurface u=const. \~ + \param[in] surface - \ru Исходная поверхность. + \en The initial surface. \~ + \param[in] place - \ru Исходный плейсмент. + \en The initial placement. \~ + \param[out] plane_curve - \ru Требуемая окружность или дуга. + \en The required circle or an arc. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (void) RadiusDimension3D( const MbSurface & surface, + const MbPlacement3D & place, + MbPlaneCurve *& plane_curve ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Можно ли построить окружность или дугу для радиального размера к поверхности. + \en Whether a circle or an arc can be constructed for radial dimension of surface. \~ + \details \ru Можно построить, если тип базовой поверхности: st_CylinderSurface или st_ConeSurface, + или st_SphereSurface, или st_TorusSurface. + \en It can be constructed if the type of a base surface is st_CylinderSurface or st_ConeSurface, + or st_SphereSurface, or st_TorusSurface. \~ + \param[in] surface - \ru Исходная поверхность. + \en The initial surface. \~ + \return \ru true, если можно построить. + \en true if it can be constructed. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) IsPossibleRadiusDimension3D( const MbSurface & surface ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Результат замера расстояния и угла между поверхностями. + \en The result of measurement of dimension and angle between surfaces. \~ + \details \ru Результат замера расстояния и угла между поверхностями. + \en The result of measurement of dimension and angle between surfaces. \~ + \ingroup Algorithms_3D +*/ +// --- +enum MbeSurfAxesMeasureRes +{ + // \ru ошибочные результат \en mistaken result + samr_SurfSurf_Failed = -3, ///< \ru Ошибка при работе с поверхностями. \en An error is occurred while working with surfaces. + samr_AxisSurf_Failed = -2, ///< \ru Ошибка при работе с осью и поверхностю. \en An error is occurred while working with axis and surface. + samr_AxisAxis_Failed = -1, ///< \ru Ошибка при работе с осями. \en An error is occurred while working with axes. + // \ru пустой результат \en an empty result. + samr_Undefined = 0, ///< \ru Не получилось или не измерялось. \en Failed or didn't measured. + // \ru две оси \en two axes + samr_AxisAxis_Coaxial, ///< \ru Оси совпадают. \en Axes are coincident. + samr_AxisAxis_Parallel, ///< \ru Оси параллельны. \en Axes are parallel. + samr_AxisAxis_Intersecting, ///< \ru Оси пересекаются. \en Axes are crossed. + samr_AxisAxis_Distant, ///< \ru Оси на расстоянии. \en Axes are located at a distance. + // \ru одна ось (какая из осей есть, см. по возвращаемому флагу функции замера) \en one axis (see the returned flag of measurement function to detect which one exactly) + samr_AxisSurf_Colinear, ///< \ru Ось лежит на поверхности. \en The axis lies on the surface. + samr_AxisSurf_Parallel, ///< \ru Ось параллельна поверхности. \en The axis is parallel to the surface. + samr_AxisSurf_Intersecting, ///< \ru Ось пересекает поверхность. \en The axis crosses the surface. + samr_AxisSurf_Distant, ///< \ru Ось на расстоянии от поверхности. \en The axis is located at a distance from the surface. + // \ru две плоские поверхности \en two planar surfaces + samr_SurfSurf_Colinear, ///< \ru Одна поверхность лежит на другой. \en One surface lies on another one. + samr_SurfSurf_Parallel, ///< \ru Поверхности параллельны. \en Surfaces are parallel. + samr_SurfSurf_Intersecting, ///< \ru Поверхности пересекаются. \en Surfaces are intersecting inside domain. + // \ru samr_SurfSurf_Distant, // находятся на расстоянии \en samr_SurfSurf_Distant, // located at a distance + +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Расстояние между осями поверхностей. + \en Distance between axes of surfaces. \~ + \details \ru Рассчитывается расстояние между осями поверхностей, имеющих оси вращения, + или расстояние между поверхностью, имеющей ось, и плоской поверхностью. + \en Calculate distance between axes of revolution surfaces + or distance between revolution surface and planar surface. \~ + \param[in] surface1, sameSense1 - \ru Первая поверхность и ее направление. + \en The first surface and its direction. \~ + \param[in] surface2, sameSense2 - \ru Вторая поверхность и ее направление. + \en The second surface and its direction. \~ + \param[out] axis1, exist1 - \ru Ось первой поверхности и флаг ее наличия. + \en The axis of the first surface and the flag of its existence. \~ + \param[out] axis2, exist2 - \ru Ось второй поверхности и флаг ее наличия. + \en The axis of the second surface and the flag of its existence. \~ + \param[out] p1 - \ru Точка на первой оси или поверхности. + \en The point on the first axis or surface. \~ + \param[out] p2 - \ru Точка на второй оси или поверхности. + \en The point on the second axis or surface. \~ + \param[out] angle - \ru Угол между осями или осью поверхностью. + \en The angle between axes or between an axis and a surface. \~ + \param[out] distance - \ru Минимальное расстояние между осями. + \en Minimal distance between axes. \~ + \param[in] angle - \ru Угловая погрешность. + \en The angle accuracy. \~ + \return \ru Вариант полученного замера или вариант ошибки. + \en The variant of the obtained measurement or the variant of error. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (MbeSurfAxesMeasureRes) SurfAxesDistAngle( const MbSurface & surface1, bool sameSense1, + const MbSurface & surface2, bool sameSense2, + MbAxis3D & axis1, bool & exist1, + MbAxis3D & axis2, bool & exist2, + MbCartPoint3D & p1, + MbCartPoint3D & p2, + double & angle, + double & distance, + double angleEps = ANGLE_EPSILON ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Расстояние между точками на поверхности. + \en Distance between points on surface. \~ + \details \ru Класс содержит данные о расстоянии между точками и координатами этих точек + на поверхностях. + \en The class contains data about the distance between points and their coordinates + on surfaces. \~ + \ingroup Algorithms_3D +*/ +// --- +class MATH_CLASS MbSurfDist { + friend class MbMinMaxSurfDists; +private: + double d; ///< \ru Расстояние. \en Distance. + MbCartPoint uv1; ///< \ru Параметр на первой поверхности. \en Parameter on the first surface. + MbCartPoint uv2; ///< \ru Параметр на второй поверхности. \en Parameter on the second surface. + uint8 sign; ///< \ru Знак расстояния. \en Sign of direction. + +public: + /// \ru Конструктор. \en Constructor. + MbSurfDist() : d( UNDEFINED_DBL ), uv1(), uv2(), sign( 1 ) {} + /// \ru Конструктор по данным. \en The constructor by data. + MbSurfDist( double _d, const MbCartPoint & _uv1, const MbCartPoint & _uv2, bool plus ) { Init( _d, _uv1, _uv2, plus ); } + /// \ru Конструктор копирования. \en Copy constructor. + MbSurfDist( const MbSurfDist & other ) { Init( other ); } + /// \ru Деструктор. \en The destructor. + virtual ~MbSurfDist() {} +public: + /// \ru Функция копирования. \en Copy function. + void Init( const MbSurfDist & obj ) { d = obj.d; uv1 = obj.uv1; uv2 = obj.uv2; sign = obj.sign; } + /// \ru Получить расстояние. \en Get distance. + double GetDistance() const { return d; } + /// \ru Получить точку на первой поверхности. \en Get the point on the first surface. + const MbCartPoint & GetPointOne() const { return uv1; } + /// \ru Получить точку на второй поверхности. \en Get the point on the second surface. + const MbCartPoint & GetPointTwo() const { return uv2; } + /// \ru Расстояние положительное? \en Is the distance positive? + bool IsPositive() const { return (sign > 0); } + /// \ru Расстояние отрицательное? \en Is the distance negative? + bool IsNegative() const { return (sign < 1); } + /// \ru Оператор присваивания. \en Assignment operator. + const MbSurfDist & operator = ( const MbSurfDist & other ) { Init( other ); return (*this); } + +private: + void Init( double _d, const MbCartPoint & _uv1, const MbCartPoint & _uv2, bool plus ); +}; + + +//------------------------------------------------------------------------------ +// \ru инициализатор \en Initializer +// --- +inline void MbSurfDist::Init( double _d, const MbCartPoint & _uv1, const MbCartPoint & _uv2, bool plus ) +{ + d = _d; + uv1 = _uv1; + uv2 = _uv2; + sign = plus ? 1 : 0; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Расстояния с точками между поверхностями. + \en Distances between surfaces with points. \~ + \details \ru Расстояния с точками между поверхностями. + \en Distances between surfaces with points. \~ + \ingroup Algorithms_3D +*/ +// --- +class MATH_CLASS MbMinMaxSurfDists { +private : + SArray surfDistances; ///< \ru Расстояние и параметры на поверхностях. \en Distance and parameters on surfaces. + mutable double midDistance; ///< \ru Среднее расстояние. \en Average distance. + mutable double minDistance; ///< \ru Минимальное расстояние. \en Minimal distance. + mutable double maxDistance; ///< \ru Максимальное расстояние. \en Maximal distance. + mutable bool sorted; ///< \ru Признак сортированности. \en Attribute of being sorted. + +public: + MbMinMaxSurfDists( size_t nReserve = 0 ); ///< \ru Конструктор. \en Constructor. + virtual ~MbMinMaxSurfDists(); ///< \ru Деструктор. \en Destructor. + +public: + bool IsEmpty() const { return (surfDistances.Count() < 1); } ///< \ru Есть ли замеры? \en Are there any measurements? + size_t GetCount() const { return surfDistances.Count(); } ///< \ru Количество замеров. \en The number of measurements. + ptrdiff_t GetMaxIndex() const { return surfDistances.MaxIndex(); } ///< \ru Индекс последнего замера. \en Index of the last measurement + void Reserve( size_t nReserve ); ///< \ru Зарезервировать память под nReserve элементов. \en Reserve memory for 'nReserve' elements. + void RemoveAll( bool bAdjustMemory ); ///< \ru Удалить все элементы \en Delete all elements. + void AdjustMemory(); ///< \ru Освободить лишнюю память \en Free the unnecessary memory. + + /// \ru Получить расстояние по индексу. \en Get the distance by the index. + bool GetDistance( size_t k, double & d ) const; + /// \ru Получить расстояние со знаком, по индексу. \en Get the signed distance by the index. + bool GetSignedDistance( size_t k, double & d ) const; + /// \ru Считаем ли вы расстояние отрицательным. \en Whether the distance is negative. + bool IsNegativeDistance( size_t k ) const { return ((k < surfDistances.Count()) ? surfDistances[k].IsNegative() : false); } + /// \ru Получить минимальное расстояние. \en Get minimal distance. + bool GetMinDistance( double & d ) const; + /// \ru Получить максимальное расстояние. \en Get maximal distance. + bool GetMaxDistance( double & d ) const; + /// \ru Получить среднее расстояние. \en Get average distance. + bool GetMidDistance( double & d ) const; + /// \ru Получить расстояние и точки на поверхностях. \en Get distance and points on surface. + bool GetSurfDistance( size_t k, double & d, MbCartPoint & uv1, MbCartPoint & uv2 ) const; + /// \ru Получить расстояние и точки на поверхностях. \en Get distance and points on surface. + bool GetSurfDistance( size_t k, double & d, bool & plus, MbCartPoint & uv1, MbCartPoint & uv2 ) const; + /// \ru Добавить расстояние и точки на поверхностях. \en Add distance and points on surface. + bool AddSurfDistance( double distance, bool plus, const MbCartPoint & uv1, const MbCartPoint & uv2, + bool bAddEqual, double eps = LENGTH_EPSILON ); + /// \ru Сортировать по возрастанию расстояния. \en Sort by distance in the ascending order. + void Sort(); + /// \ru Убрать объекты с одинаковыми расстояниями. \en Remove objects with similar distances. + void RemoveEqualDistances( double eps = LENGTH_EPSILON ); + + void operator = ( const MbMinMaxSurfDists & ); ///< \ru Оператор присваивания. \en Assignment operator. + +private: + MbMinMaxSurfDists( const MbMinMaxSurfDists & ); +}; + + +//------------------------------------------------------------------------------ +// \ru выдать расстояние \en get the distance +// --- +inline bool MbMinMaxSurfDists::GetDistance( size_t k, double & d ) const +{ + if ( k < surfDistances.Count() ) { + d = surfDistances[k].GetDistance(); + return true; + } + return false; +} + + +//------------------------------------------------------------------------------ +// \ru выдать расстояние со знаком \en get signed distance +// --- +inline bool MbMinMaxSurfDists::GetSignedDistance( size_t k, double & d ) const +{ + if ( k < surfDistances.Count() ) { + d = surfDistances[k].GetDistance(); + if ( surfDistances[k].IsNegative() ) + d = -d; + return true; + } + return false; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Экстремальные расстояния между поверхностями. + \en Extreme distances between surfaces. \~ + \details \ru Экстремальные расстояния между поверхностями по сетке на первой поверхности, + причем замеры осуществляются в заданном направлении (если есть вектор) + или по нормалям к первой поверхности. + \en Extreme distances between surfaces by mesh on the first surface, + measurements are performed in a given direction (if the vector is set) + or by normals of the first surface. \~ + \param[in] surface1 - \ru Первая поверхность. + \en The first surface. \~ + \param[in] u1cnt - \ru Количество точек по u (первая поверхность). + \en The number of points by u (the first surface) \~ + \param[in] v1cnt - \ru Количество точек по v (первая поверхность). + \en The number of points by v (the first surface) \~ + \param[in] dir - \ru Вектор заданного направления (если нет, то по нормали). + \en The vector of direction (if not set then by the normal). \~ + \param[in] orient - \ru Направление поиска. + \en Direction of search. \~ + \param[in] useEqualDistances - \ru Оставлять равные равные расстояния. + \en Whether to use the equal distances. \~ + \param[in] surface2 - \ru Вторая поверхность. + \en The second surface. \~ + \param[in,out] nMin - \ru Кол-во регистрируемых минимумов. + \en The number of registrated minimums. \~ + \param[in,out] nMax - \ru Кол-во регистрируемых максимумов. + \en The number of registrated maximums. \~ + \param[out] allResults - \ru Все результаты. + \en All results. \~ + \param[out] minResults - \ru Результаты-минимумы. + \en Results-minimums. \~ + \param[out] maxResults - \ru Результаты-максимумы. + \en Results-maximums. \~ + \param[in,out] indicator - \ru Интерфейс-индикатор процесса выполнения. + \en Interface-indicator of the execution process. \~ + \return \ru Возвращает результат замера (получен, не получен или же процесс был прерван). + \en Returns the result of measurement (obtained, not obtained, or the process has been aborted). \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (MbeProcessState) MinMaxDistances( const MbSurface & surface1, + ptrdiff_t u1cnt, + ptrdiff_t v1cnt, + const MbVector3D * dir, + const MbeSenseValue & orient, + bool useEqualDistances, + const MbSurface & surface2, + ptrdiff_t & nMin, + ptrdiff_t & nMax, + MbMinMaxSurfDists & allResults, + MbMinMaxSurfDists & minResults, + MbMinMaxSurfDists & maxResults, + IProgressIndicator * indicator = NULL ); + + +#endif // __ALG_DIMENSION_H diff --git a/C3d/Include/alg_diskrete_length_data.h b/C3d/Include/alg_diskrete_length_data.h index 72434a7..0bbbeb4 100644 --- a/C3d/Include/alg_diskrete_length_data.h +++ b/C3d/Include/alg_diskrete_length_data.h @@ -59,7 +59,7 @@ public: class MATH_CLASS CosinusoidPar { public : static const double maxAmpl; ///< \ru Максимальное значение амплитуды. \en Maximal value of amplitude. - static const double minAmpl; ///< \ru Минимальное значение амплитуды. \en Minimal value of amplitude. + static const double minAmpl; ///< \ru Минимальное значение амплитуды. \en Minimal value of amplitude. Param m_WaveLineAmpl; ///< \ru Величина амплитуды. \en Amplitude value. Param m_WaveLineAmplByPercent; ///< \ru Амплитуда задается в процентах от длины волны. \en The amplitude is defined as a percentage of the wave length. diff --git a/C3d/Include/alg_draw.h b/C3d/Include/alg_draw.h index 48f85a9..8d077cb 100644 --- a/C3d/Include/alg_draw.h +++ b/C3d/Include/alg_draw.h @@ -10,7 +10,6 @@ #ifndef __ALG_DRAW_H #define __ALG_DRAW_H - #include #include #include @@ -41,6 +40,8 @@ #define TRGB_CERISE 255, 0, 125 ///< \ru Светло-вишневый цвет. \en Cerise color. \~ \ingroup Drawing #define TRGB_OLIVE 128, 128, 0 ///< \ru Оливковый цвет. \en Olive color. \~ \ingroup Drawing #define TRGB_SPRINGGREEN 0, 255, 127 ///< \ru Весенне-зеленый цвет. \en Spring green color. \~ \ingroup Drawing +#define TRGB_AVIATION 125, 225, 255 ///< \ru Авиационный цвет. \en Aviation color. \~ \ingroup Drawing +#define TRGB_GOLD 205, 255, 25 ///< \ru Золотой цвет. \en Gold color. \~ \ingroup Drawing #define TRGB_DELETE TRGB_GREEN // \ru цвет удаляемых объектов \en A color of deleted objects #define TRGB_NEW TRGB_RED // \ru цвет вновь создаваемых объектов \en A color of new objects @@ -145,6 +146,11 @@ public: virtual void DrawItem( const MbCurve * curve, const MbSurface * surface, int R, int G, int B, int width = 1 ) = 0; /// \ru Отрисовать карту кривой на поверхности. \en Draw a map of a curve on a surface. virtual void DrawCurveMap( const MbCurve * curve, const MbSurface * surface, int R, int G, int B ) = 0; + // \ru Отрисовать карту кривой на поверхности в масштабе, определяемом так, чтобы активная область окна покрывалась заданным прямоугольником rect. + // Флаг isotropic отвечает за отрисовку объектов равных масштабах по осям. Если isotropic==false, окружность может отобразиться как эллипс, квадрат как прямоугольник и т.п. + // \en Draw a map of a curve on a surface with a specified scale determined as a way of covering an active window by the given rectangle rect. + // The flag isotropic is responsible for drawing in equal scales along the axes. If isotropic==false then a circle can be shown as an ellipsis, a square as an rectangle etc. + virtual void DrawCurveMap( const MbCurve * curve, const MbSurface * surface, const MbRect & rect, int R, int G, int B, const int boundaryWidth, const int curveWidth, const bool isotropic, const bool showSurfaceBoundaries ) = 0; /// \ru Отрисовать кривую на поверхности. \en Draw a curve on a surface. virtual void DrawSurfaceCurveMap( const MbSurfaceCurve * scurve, int R, int G, int B ) = 0; /// \ru Отрисовать точку на поверхности. \en Draw a point on a surface. @@ -192,7 +198,6 @@ OBVIOUS_PRIVATE_COPY( IfDrawGI ) #if defined(_DRAWGI) - //------------------------------------------------------------------------------ /** \brief \ru Функции отладочной отрисовки объектов приложения. \en Functions of debug drawing of application objects. \~ @@ -1001,7 +1006,7 @@ void DrawItem( SPtr & item, int R, int G, int B ) { template void DrawItems( const PtrArray & items, int R, int G, int B ) { - for ( size_t k = 0, cnt = items.Count(); k < cnt; k++ ) + for ( size_t k = 0, cnt = items.size(); k < cnt; ++k ) DrawGI::DrawItem( items[k], R, G, B ); } @@ -1022,7 +1027,7 @@ void DrawVertexEdges( const Vertex * vertex, int vR, int vG, int vB, MbMesh edgeMesh; MbStepData stepData( ist_SpaceStep, Math::visualSag ); MbFormNote note(true, false); - for ( size_t k = 0, cnt = edges.Count(); k < cnt; k++ ) { + for ( size_t k = 0, cnt = edges.size(); k < cnt; ++k ) { if ( edges[k] != NULL ) { edges[k]->GetCurve().CalculateMesh( stepData, note, edgeMesh ); DrawGI::DrawMesh( &edgeMesh, TRGB_WHITE ); diff --git a/C3d/Include/alg_max_distance.h b/C3d/Include/alg_max_distance.h index 32b07f8..8c0dcfb 100644 --- a/C3d/Include/alg_max_distance.h +++ b/C3d/Include/alg_max_distance.h @@ -1,165 +1,165 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Определение расстояния между объектами. - \en Definition of distance between objects. \~ - \details \ru Функции определения максимальных расстояний между различными - трехмерными объектами. - \en Functions for definition of maximal distances between different - three-dimensional objects. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - - -#ifndef __ALG_MAX_DISTANCE_H -#define __ALG_MAX_DISTANCE_H - - -#include - - -class MATH_CLASS MbCartPoint; -class MATH_CLASS MbCartPoint3D; -class MATH_CLASS MbAxis3D; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbSurface; - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти максимальное расстояние между точкой и кривой. - \en Find the maximal distance between a point and a curve. \~ - \details \ru Максимальное расстояние между точкой и кривой. - \en The maximal distance between a point and a curve. \~ - \param[in] pnt - \ru Исходная точка. - \en The initial point. \~ - \param[in] curv - \ru Исходная кривая. - \en The initial curve. \~ - \param[out] t - \ru Параметр на кривой, при котором достигается искомое расстояние. - \en The parameter on a curve where the required distance is reached. \~ - \param[out] distance - \ru Искомое расстояние. - \en The required distance. \~ - \return \ru true, если максимальное расстояние было найдено. - \en true if the maximal distance has been found. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) MaxDistance( const MbCartPoint3D & pnt, const MbCurve3D & curv, - double & t, - double & distance ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти максимальное расстояние между двумя кривыми. - \en Find the maximal distance between two curves. \~ - \details \ru Найти максимальное расстояние между двумя кривыми. - \en Find the maximal distance between two curves. \~ - \param[in] curv1, curv2 - \ru Исходные кривая. - \en The initial curves. \~ - \param[out] t1, t2 - \ru Параметры на кривых, при которых достигается искомое расстояние. - \en The parameters on curves where the required distance is reached. \~ - \param[out] distance - \ru Искомое расстояние. - \en The required distance. \~ - \return \ru true, если максимальное расстояние было найдено. - \en true if the maximal distance has been found. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) MaxDistance( const MbCurve3D & curv1, const MbCurve3D & curv2, - double & t1, double & t2, - double & distance ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти максимальное расстояние между точкой и поверхностью. - \en Find the maximal distance between a point and a surface. \~ - \details \ru Найти максимальное расстояние между точкой и поверхностью. - \en Find the maximal distance between a point and a surface. \~ - \param[in] pnt - \ru Исходная точка. - \en The initial point. \~ - \param[in] surf - \ru Исходная поверхность. - \en The initial surface. \~ - \param[out] uv - \ru Параметры точки на поверхности, при которой достигается искомое расстояние. - \en The point parameters on a surface where the required distance is reached. \~ - \param[out] distance - \ru Искомое расстояние. - \en The required distance. \~ - \return \ru true, если максимальное расстояние было найдено. - \en true if the maximal distance has been found. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) MaxDistance( const MbCartPoint3D & pnt, const MbSurface & surf, - MbCartPoint & uv, - double & distance ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти максимальное расстояние между кривой и поверхностью. - \en Find the maximal distance between a curve and a surface. \~ - \details \ru Найти максимальное расстояние между кривой и поверхностью. - \en Find the maximal distance between a curve and a surface. \~ - \param[in] curv - \ru Исходная кривая. - \en The initial curve. \~ - \param[in] surf - \ru Исходная поверхность. - \en The initial surface. \~ - \param[out] t - \ru Параметр на кривой, при котором достигается искомое расстояние. - \en The parameter on a curve where the required distance is reached. \~ - \param[out] uv - \ru Параметры точки на поверхности, при которой достигается искомое расстояние. - \en The point parameters on a surface where the required distance is reached. \~ - \param[out] distance - \ru Искомое расстояние. - \en The required distance. \~ - \return \ru true, если максимальное расстояние было найдено. - \en true if the maximal distance has been found. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) MaxDistance( const MbCurve3D & curv, const MbSurface & surf, - double & t, MbCartPoint & uv, - double & distance ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти максимальное расстояние между поверхностями. - \en Find the maximal distance between two surfaces. \~ - \details \ru Найти максимальное расстояние между поверхностями. - \en Find the maximal distance between two surfaces. \~ - \param[in] surf1, surf2 - \ru Исходные поверхности. - \en The initial surfaces. \~ - \param[out] uv1, uv2 - \ru Параметры точек на поверхностях, при которых достигается искомое расстояние. - \en The parameters on surfaces where the required distance is reached. \~ - \param[out] distance - \ru Искомое расстояние. - \en The required distance. \~ - \return \ru true, если максимальное расстояние было найдено. - \en true if the maximal distance has been found. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) MaxDistance( const MbSurface & surf1, const MbSurface & surf2, - MbCartPoint & uv1, MbCartPoint & uv2, - double & distance ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти максимальное расстояние от оси до кривой. - \en Find the maximal distance between an axis an a curve. \~ - \details \ru Ищется максимальное расстояние от оси до кривой перпендикулярно оси. - \en Find the maximal distance between an axis and a curve perpendicularly to an axis. \~ - \param[in] axis - \ru Исходная ось. - \en The initial axis. \~ - \param[in] curve - \ru Исходная кривая. - \en The initial curve. \~ - \param[out] param - \ru Параметр на кривой, при котором достигается искомое расстояние. - \en The parameter on a curve where the required distance is reached. \~ - \param[out] distance - \ru Искомое расстояние. - \en The required distance. \~ - \return \ru true, если максимальное расстояние было найдено. - \en true if the maximal distance has been found. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) MaxDistance( const MbAxis3D & axis, const MbCurve3D & curve, - double & param, - double & distance ); - - -#endif // __ALG_MAX_DISTANCE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Определение расстояния между объектами. + \en Definition of distance between objects. \~ + \details \ru Функции определения максимальных расстояний между различными + трехмерными объектами. + \en Functions for definition of maximal distances between different + three-dimensional objects. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef __ALG_MAX_DISTANCE_H +#define __ALG_MAX_DISTANCE_H + + +#include + + +class MATH_CLASS MbCartPoint; +class MATH_CLASS MbCartPoint3D; +class MATH_CLASS MbAxis3D; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbSurface; + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти максимальное расстояние между точкой и кривой. + \en Find the maximal distance between a point and a curve. \~ + \details \ru Максимальное расстояние между точкой и кривой. + \en The maximal distance between a point and a curve. \~ + \param[in] pnt - \ru Исходная точка. + \en The initial point. \~ + \param[in] curv - \ru Исходная кривая. + \en The initial curve. \~ + \param[out] t - \ru Параметр на кривой, при котором достигается искомое расстояние. + \en The parameter on a curve where the required distance is reached. \~ + \param[out] distance - \ru Искомое расстояние. + \en The required distance. \~ + \return \ru true, если максимальное расстояние было найдено. + \en true if the maximal distance has been found. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) MaxDistance( const MbCartPoint3D & pnt, const MbCurve3D & curv, + double & t, + double & distance ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти максимальное расстояние между двумя кривыми. + \en Find the maximal distance between two curves. \~ + \details \ru Найти максимальное расстояние между двумя кривыми. + \en Find the maximal distance between two curves. \~ + \param[in] curv1, curv2 - \ru Исходные кривая. + \en The initial curves. \~ + \param[out] t1, t2 - \ru Параметры на кривых, при которых достигается искомое расстояние. + \en The parameters on curves where the required distance is reached. \~ + \param[out] distance - \ru Искомое расстояние. + \en The required distance. \~ + \return \ru true, если максимальное расстояние было найдено. + \en true if the maximal distance has been found. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) MaxDistance( const MbCurve3D & curv1, const MbCurve3D & curv2, + double & t1, double & t2, + double & distance ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти максимальное расстояние между точкой и поверхностью. + \en Find the maximal distance between a point and a surface. \~ + \details \ru Найти максимальное расстояние между точкой и поверхностью. + \en Find the maximal distance between a point and a surface. \~ + \param[in] pnt - \ru Исходная точка. + \en The initial point. \~ + \param[in] surf - \ru Исходная поверхность. + \en The initial surface. \~ + \param[out] uv - \ru Параметры точки на поверхности, при которой достигается искомое расстояние. + \en The point parameters on a surface where the required distance is reached. \~ + \param[out] distance - \ru Искомое расстояние. + \en The required distance. \~ + \return \ru true, если максимальное расстояние было найдено. + \en true if the maximal distance has been found. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) MaxDistance( const MbCartPoint3D & pnt, const MbSurface & surf, + MbCartPoint & uv, + double & distance ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти максимальное расстояние между кривой и поверхностью. + \en Find the maximal distance between a curve and a surface. \~ + \details \ru Найти максимальное расстояние между кривой и поверхностью. + \en Find the maximal distance between a curve and a surface. \~ + \param[in] curv - \ru Исходная кривая. + \en The initial curve. \~ + \param[in] surf - \ru Исходная поверхность. + \en The initial surface. \~ + \param[out] t - \ru Параметр на кривой, при котором достигается искомое расстояние. + \en The parameter on a curve where the required distance is reached. \~ + \param[out] uv - \ru Параметры точки на поверхности, при которой достигается искомое расстояние. + \en The point parameters on a surface where the required distance is reached. \~ + \param[out] distance - \ru Искомое расстояние. + \en The required distance. \~ + \return \ru true, если максимальное расстояние было найдено. + \en true if the maximal distance has been found. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) MaxDistance( const MbCurve3D & curv, const MbSurface & surf, + double & t, MbCartPoint & uv, + double & distance ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти максимальное расстояние между поверхностями. + \en Find the maximal distance between two surfaces. \~ + \details \ru Найти максимальное расстояние между поверхностями. + \en Find the maximal distance between two surfaces. \~ + \param[in] surf1, surf2 - \ru Исходные поверхности. + \en The initial surfaces. \~ + \param[out] uv1, uv2 - \ru Параметры точек на поверхностях, при которых достигается искомое расстояние. + \en The parameters on surfaces where the required distance is reached. \~ + \param[out] distance - \ru Искомое расстояние. + \en The required distance. \~ + \return \ru true, если максимальное расстояние было найдено. + \en true if the maximal distance has been found. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) MaxDistance( const MbSurface & surf1, const MbSurface & surf2, + MbCartPoint & uv1, MbCartPoint & uv2, + double & distance ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти максимальное расстояние от оси до кривой. + \en Find the maximal distance between an axis an a curve. \~ + \details \ru Ищется максимальное расстояние от оси до кривой перпендикулярно оси. + \en Find the maximal distance between an axis and a curve perpendicularly to an axis. \~ + \param[in] axis - \ru Исходная ось. + \en The initial axis. \~ + \param[in] curve - \ru Исходная кривая. + \en The initial curve. \~ + \param[out] param - \ru Параметр на кривой, при котором достигается искомое расстояние. + \en The parameter on a curve where the required distance is reached. \~ + \param[out] distance - \ru Искомое расстояние. + \en The required distance. \~ + \return \ru true, если максимальное расстояние было найдено. + \en true if the maximal distance has been found. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) MaxDistance( const MbAxis3D & axis, const MbCurve3D & curve, + double & param, + double & distance ); + + +#endif // __ALG_MAX_DISTANCE_H diff --git a/C3d/Include/alg_mesh_to_brep.h b/C3d/Include/alg_mesh_to_brep.h index 46b28a9..d19605d 100644 --- a/C3d/Include/alg_mesh_to_brep.h +++ b/C3d/Include/alg_mesh_to_brep.h @@ -1,96 +1,102 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief Функции преобразования полигональной модели в граничное представление. - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ALG_MESH_TO_BREP_H -#define __ALG_MESH_TO_BREP_H - -#include -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbCartPoint3D; -class MATH_CLASS MbVector3D; -class MATH_CLASS MbFaceShell; -class MATH_CLASS MbMesh; -class MATH_CLASS MbGrid; -class MATH_CLASS MbCollection; -class MATH_CLASS MbSNameMaker; -class MATH_CLASS MbTriangle; -class MATH_CLASS IProgressIndicator; -class MATH_CLASS ProgressBarWrapper; -struct MATH_CLASS GridsToShellValues; - - -//------------------------------------------------------------------------------ -// Удалить дублирующие с заданной точностью друг друга точки. -// --- -bool RemoveRedundantPoints( std::vector & points, - std::vector & triangles, - double epsilon, - ProgressBarWrapper * baseProgBar ); - -//------------------------------------------------------------------------------ -// Удалить дублирующие с заданной точностью друг друга точки. -// --- -bool RemoveRedundantPoints( std::vector< std::pair > & pointNormals, - std::vector & triangles, - double epsilon, - ProgressBarWrapper * baseProgBar ); - -//------------------------------------------------------------------------------ -// Удалить дублирующие с заданной точностью друг друга точки. -// --- -bool RemoveRedundantPoints( std::vector & points, - std::vector & indexes, - double epsilon ); - -//------------------------------------------------------------------------------ -// Объединить ребра двух смежных плоских граней с полигональной границей -// (возвращает общее после сшивки ребро) -// --- -MbCurveEdge * StitchAdjacentGridsEdges( MbFace & face1, MbOrientedEdge & edge1, - MbFace & face2, MbLoop & loop2, size_t e2Ind ); - -//------------------------------------------------------------------------------ -// Обеспечить связность треугольных граней -// --- -bool ConnectTriangleFaces( const c3d::FacesSPtrVector & faces, - const std::vector< std::pair > & edgesPairs, - std::vector< std::pair > * combinedPairs, - ProgressBarWrapper * baseProgBar ); - -//------------------------------------------------------------------------------ -// Преобразовать триангуляцию в оболочку. -// --- -MbFaceShell * ConvertGridToShell( const MbGrid & grid, const GridsToShellValues & params, const MbSNameMaker & snMaker, - MbResultType & res, IProgressIndicator * progBar = NULL ); - - -//------------------------------------------------------------------------------ -// Преобразовать полигональную модель в оболочку. -// --- -MbFaceShell * ConvertMeshToShell( const MbMesh & mesh, const GridsToShellValues & params, const MbSNameMaker & snMaker, - MbResultType & res, IProgressIndicator * progBar = NULL ); - - -//------------------------------------------------------------------------------ -// Преобразовать триангуляцию в оболочку. -// --- -MbFaceShell * ConvertCollectionToShell( const MbCollection & grid, - bool mergeFaces, - const MbSNameMaker & snMaker, - MbResultType & res, - IProgressIndicator * progIndicator ); - - -#endif // __ALG_UTILITES_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief Функции преобразования полигональной модели в граничное представление. + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ALG_MESH_TO_BREP_H +#define __ALG_MESH_TO_BREP_H + +#include +#include +#include +#include +#include +#include +#include +#include + + +class MATH_CLASS MbCartPoint3D; +class MATH_CLASS MbVector3D; +class MATH_CLASS MbFaceShell; +class MATH_CLASS MbMesh; +class MATH_CLASS MbGrid; +class MATH_CLASS MbCollection; +class MATH_CLASS MbSNameMaker; +class MATH_CLASS IProgressIndicator; +class MATH_CLASS ProgressBarWrapper; +struct MATH_CLASS GridsToShellValues; + + +//------------------------------------------------------------------------------ +// Удалить дублирующие с заданной точностью друг друга точки. +// --- +bool RemoveRedundantPoints( c3d::SpacePointsVector & points, + c3d::MeshTrianglesVector & triangles, + double epsilon, + ProgressBarWrapper * baseProgBar ); + +//------------------------------------------------------------------------------ +// Удалить дублирующие с заданной точностью друг друга точки. +// --- +bool RemoveRedundantPoints( std::vector & pointNormals, + c3d::MeshTrianglesVector & triangles, + double epsilon, + ProgressBarWrapper * baseProgBar ); + +//------------------------------------------------------------------------------ +// Удалить дублирующие с заданной точностью друг друга точки. +// --- +bool RemoveRedundantPoints( c3d::SpacePointsVector & points, + c3d::UintVector & indexes, + double epsilon ); + +//------------------------------------------------------------------------------ +// Объединить ребра двух смежных плоских граней с полигональной границей +// (возвращает общее после сшивки ребро) +// --- +MbCurveEdge * StitchAdjacentGridsEdges( MbFace & face1, MbOrientedEdge & edge1, + MbFace & face2, MbLoop & loop2, size_t e2Ind ); + +//---------------------------------------------------------s--------------------- +// Обеспечить связность треугольных граней +// --- +bool ConnectTriangleFaces( const c3d::FacesSPtrVector & faces, + const std::vector & edgesPairs, + std::vector * combinedPairs, + ProgressBarWrapper * baseProgBar ); + +//------------------------------------------------------------------------------ +// Преобразовать триангуляцию в оболочку. +// --- +MbFaceShell * ConvertGridToShell( const MbGrid & grid, + const GridsToShellValues & params, + const MbSNameMaker & snMaker, + MbResultType & res, + IProgressIndicator * progBar = NULL ); + + +//------------------------------------------------------------------------------ +// Преобразовать полигональную модель в оболочку. +// --- +MbFaceShell * ConvertMeshToShell( const MbMesh & mesh, + const GridsToShellValues & params, + const MbSNameMaker & snMaker, + MbResultType & res, + IProgressIndicator * progBar = NULL ); + + +//------------------------------------------------------------------------------ +// Преобразовать триангуляцию в оболочку. +// --- +MbFaceShell * ConvertCollectionToShell( const MbCollection & grid, + bool mergeFaces, + const MbSNameMaker & snMaker, + MbResultType & res, + IProgressIndicator * progIndicator ); + + +#endif // __ALG_UTILITES_H diff --git a/C3d/Include/alg_nurbs_conic.h b/C3d/Include/alg_nurbs_conic.h index eb11ebc..637741f 100644 --- a/C3d/Include/alg_nurbs_conic.h +++ b/C3d/Include/alg_nurbs_conic.h @@ -1,377 +1,377 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Построение конических сечений в виде NURBS-кривой. - \en Construction of conic sections as NURBS curves. \~ - \details \ru Построение конических сечений производится следующими способами: - по двум точкам, вершине и дискриминанту, по трем точкам и вершине, - по трем точкам и двум наклонам, по двум точкам, двум наклонам и дискриминанту, - по четырем точкам и наклону и по пяти точкам. \n - NURBS кривая, описывающая конику, строится по трем точкам: началу и концу коники и - средней точке (вершине угола, в который надо вписать конику). - Принимая весы начальной и конечной точки равными 1 и рассчитывая вес средней точки, - по трем точкам и трем весам строится NURBS 3-го порядка, который будет искомой коникой. - \en Construction of conic sections is performed in the following way: - by two points, a vertex and a discriminant, by three points and a vertex, - by three points and two inclinations, by two points, two inclinations and discriminant, - by four points and inclination and by five points. \n - A NURBS curve describing a conic is constructed by three points: a start and an end of a conic and - an average point (a vertex of angle which should be inscribed into the conic). - Let weights of the start point and the end point be equal to 1. After calculating of the weight of the average point - NURBS of third degree is constructed by these three weights. This NURBS is the required conic. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - - -#ifndef __ALG_NURBS_CONIC_H -#define __ALG_NURBS_CONIC_H - - -#include - - -class MbCurve3D; -class MbNurbs3D; -class MbCartPoint3D; -class MbVector3D; - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить коническое сечение по двум точкам вершине и дискриминанту. - \en Construct a conic section by two points, an angle vertex and a discriminant. \~ - \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по - двум точкам, которые задают начало и конец кривой, вершине инженерного - треугольника и дискриминанту, который используется для определения третьей точки кривой. - \en Construction of a conic section as a NURBS curve of the third degree by - two points setting ends of a curve, a vertex of enginer - triangle and a discriminant which is used for the definition of the third point. \~ - \param[in] mbPoint0 - \ru Координаты начала коники. - \en Coordinates of the conic start point. \~ - \param[in] mbPoint1 - \ru Координаты вершины угла, в который надо вписать конику. - \en Coordinates of the vertex of angle which should be inscribed into the conic. \~ - \param[in] mbPoint2 - \ru Координаты конца коники. - \en Coordinates of the conic end point. \~ - \param[in] fDiscr - \ru Дискриминант < 1, если задать дискриминант >= 1, то он - автоматически будет сброшен до значения 0.99999999. - \en The discriminant is less than 1. Otherwise it - will be set to 0.99999999 automatically. \~ - \return \ru Указатель на построенную кривую \n - NULL, если не удалось построить конику для заданных параметров. - \en The pointer to the constructed curve \n - is NULL if a try to construct a conic for a given parameters has failed. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC ( MbCurve3D * ) NurbsConic_1( const MbCartPoint3D & mbPoint0, const MbCartPoint3D & mbPoint1, - const MbCartPoint3D & mbPoint2, double fDiscr ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить коническое сечение по двум точкам вершине и дискриминанту. - \en Construct a conic section by two points, an angle vertex and a discriminant. \~ - \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по - двум точкам, которые задают начало и конец кривой, вершине инженерного - треугольника и дискриминанту, который используется для определения третьей точки кривой. - \en Construction of a conic section as a NURBS curve of the third degree by - two points setting ends of a curve, a vertex of enginer - triangle and a discriminant which is used for the definition of the third point. \~ - \param[in] mbPoint0 - \ru Координаты начала коники. - \en Coordinates of the conic start point. \~ - \param[in] mbPoint1 - \ru Координаты вершины угла, в который надо вписать конику. - \en Coordinates of the vertex of angle which should be inscribed into the conic. \~ - \param[in] mbPoint2 - \ru Координаты конца коники. - \en Coordinates of the conic end point. \~ - \param[in] fDiscr - \ru Дискриминант < 1, если задать дискриминант >= 1, то он - автоматически будет сброшен до значения 0.99999999. - \en The discriminant is less than 1. Otherwise it - will be set to 0.99999999 automatically. \~ - \return \ru Указатель на построенную кривую \n - NULL, если не удалось построить конику для заданных параметров. - \en The pointer to the constructed curve \n - is NULL if a try to construct a conic for a given parameters has failed. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC ( MbCurve * ) NurbsConic_1( const MbCartPoint & mbPoint0, const MbCartPoint & mbPoint1, - const MbCartPoint & mbPoint2, double fDiscr ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить коническое сечение по трем точкам и вершине. - \en Construct a conic section by three points, and an angle vertex. \~ - \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по - трем точкам: началу, концу и средней точке кривой, а также вершине угла, в который - должна быть вписана коника. - \en Construction of a conic section as a NURBS curve of the third degree by - three points: ends of a curve, its average point and by a vertex of an angle, - a conic should be inscribed in. \~ - \param[in] vmbConicPoints - \ru Контейнер точек коники: начало, средняя точка, конец; - точек должно быть 3. - \en The container for points of a conic: start point, average point and end point; - there should be exactly 3 points. \~ - \param[in] mbVertex - \ru Координаты вершины угла, в который надо вписать конику. - \en Coordinates of the vertex of angle which should be inscribed into the conic. \~ - \return \ru Указатель на построенную кривую \n - NULL, если не удалось постороить конику для заданных параметров. - \en The pointer to the constructed curve \n - is NULL if a try to construct a conic for given parameters has failed. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC ( MbCurve3D * ) NurbsConic_2( std::vector & vmbConicPoints, const MbCartPoint3D & mbVertex ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить коническое сечение по трем точкам и вершине. - \en Construct a conic section by three points, and an angle vertex. \~ - \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по - трем точкам: началу, концу и средней точке кривой, а также вершине угла, в который - должна быть вписана коника. - \en Construction of a conic section as a NURBS curve of the third degree by - three points: ends of a curve, its average point and by a vertex of an angle, - a conic should be inscribed in. \~ - \param[in] vmbConicPoints - \ru Контейнер точек коники: начало, средняя точка, конец; - точек должно быть 3. - \en The container for points of a conic: start point, average point and end point; - there should be exactly 3 points. \~ - \param[in] mbVertex - \ru Координаты вершины угла, в который надо вписать конику. - \en Coordinates of the vertex of angle which should be inscribed into the conic. \~ - \return \ru Указатель на построенную кривую \n - NULL, если не удалось постороить конику для заданных параметров. - \en The pointer to the constructed curve \n - is NULL if a try to construct a conic for given parameters has failed. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC ( MbCurve * ) NurbsConic_2( std::vector & vmbConicPoints, const MbCartPoint & mbVertex ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить коническое сечение по трем точкам и двум наклонам. - \en Construct a conic section by three points and two inclinations. \~ - \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по - 3-ем точкам, которые задают начало, конец и среднюю точку кривой и двум - наклонам, выходящим из начальной и конечной точек. - \en Construction of a conic section as a NURBS curve of the third degree by - 3 points setting begin, end and an average point of a curve and two - inclinations outgoing from the start point and from the end point \~ - \param[in] vmbConicPoints - \ru Контейнер точек коники: начало, средняя точка, конец; - точек должно быть 3. - \en The container for points of a conic: start point, average point and end point; - there should be exactly 3 points. \~ - \param[in] mbTangent1 - \ru Наклон в начале кривой. - \en Inclination at start of a curve. \~ - \param[in] mbTangent2 - \ru Наклон в конце кривой. - \en Inclination at end of a curve. \~ - \return \ru Указатель на построенную кривую \n - NULL, если не удалось постороить конику для заданных параметров. - \en The pointer to the constructed curve \n - is NULL if a try to construct a conic for given parameters has failed. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC ( MbCurve3D * ) NurbsConic_3( const std::vector & vmbConicPoints, - MbVector3D & mbTangent1, MbVector3D & mbTangent2 ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить коническое сечение по трем точкам и двум наклонам. - \en Construct a conic section by three points and two inclinations. \~ - \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по - 3-ем точкам, которые задают начало, конец и среднюю точку кривой и двум - наклонам, выходящим из начальной и конечной точек. - \en Construction of a conic section as a NURBS curve of the third degree by - 3 points setting begin, end and an average point of a curve and two - inclinations outgoing from the start point and from the end point \~ - \param[in] vmbConicPoints - \ru Контейнер точек коники: начало, средняя точка, конец; - точек должно быть 3. - \en The container for points of a conic: start point, average point and end point; - there should be exactly 3 points. \~ - \param[in] mbTangent1 - \ru Наклон в начале кривой. - \en Inclination at start of a curve. \~ - \param[in] mbTangent2 - \ru Наклон в конце кривой. - \en Inclination at end of a curve. \~ - \return \ru Указатель на построенную кривую \n - NULL, если не удалось постороить конику для заданных параметров. - \en The pointer to the constructed curve \n - is NULL if a try to construct a conic for given parameters has failed. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC ( MbCurve * ) NurbsConic_3( const std::vector & vmbConicPoints, MbVector & mbTangent1, MbVector & mbTangent2 ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить коническое сечение по двум точкам, двум наклонам и дискриминанту. - \en Construct a conic section by two points, two inclinations and a discriminant. \~ - \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по - 2-ум точкам, которые задают начало и конец кривой, двум наклонам, выходящим из этих точек - и дискриминанту. - \en Construction of a conic section as a NURBS curve of the third degree by - 2 points setting start and end of a curve, two incllinations outgoing from these points - and a discriminant. \~ - \param[in] mbPoint1 - \ru Координаты начала коники. - \en Coordinates of the conic start point. \~ - \param[in] mbPoint2 - \ru Координаты конца коники. - \en Coordinates of the conic end point. \~ - \param[in] mbTangent1 - \ru Наклон в начале коники. - \en Inclination at start of conic. \~ - \param[in] mbTangent2 - \ru Наклон в конце коники. - \en Inclination at end of conic. \~ - \param[in] fDiscr - \ru Дискриминант < 1, если задать дискриминант >= 1, то он - автоматически будет сброшен до значения 0.99999999. - \en The discriminant is less than 1. Otherwise it - will be set to 0.99999999 automatically. \~ - \return \ru Указатель на построенную кривую \n - NULL, если не удалось построить конику для заданных параметров. - \en The pointer to the constructed curve \n - is NULL if a try to construct a conic for given parameters has failed. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC ( MbCurve3D * ) NurbsConic_4( const MbCartPoint3D & mbPoint1, const MbCartPoint3D & mbPoint2, - const MbVector3D & mbTangent1, const MbVector3D & mbTangent2, double fDiscr ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить коническое сечение по двум точкам, двум наклонам и дискриминанту. - \en Construct a conic section by two points, two inclinations and a discriminant. \~ - \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по - 2-ум точкам, которые задают начало и конец кривой, двум наклонам, выходящим из этих точек - и дискриминанту. - \en Construction of a conic section as a NURBS curve of the third degree by - 2 points setting start and end of a curve, two incllinations outgoing from these points - and a discriminant. \~ - \param[in] mbPoint1 - \ru Координаты начала коники. - \en Coordinates of the conic start point. \~ - \param[in] mbPoint2 - \ru Координаты конца коники. - \en Coordinates of the conic end point. \~ - \param[in] mbTangent1 - \ru Наклон в начале коники. - \en Inclination at start of conic. \~ - \param[in] mbTangent2 - \ru Наклон в конце коники. - \en Inclination at end of conic. \~ - \param[in] fDiscr - \ru Дискриминант < 1, если задать дискриминант >= 1, то он - автоматически будет сброшен до значения 0.99999999. - \en The discriminant is less than 1. Otherwise it - will be set to 0.99999999 automatically. \~ - \return \ru Указатель на построенную кривую \n - NULL, если не удалось построить конику для заданных параметров. - \en The pointer to the constructed curve \n - is NULL if a try to construct a conic for given parameters has failed. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC ( MbCurve * ) NurbsConic_4( const MbCartPoint & mbPoint1, const MbCartPoint & mbPoint2, - const MbVector & mbTangent1, const MbVector & mbTangent2, double fDiscr ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить коническое сечение по четырем точкам и наклону. - \en Construct a conic section by four points, and an inclination. \~ - \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по - 4-ем точкам и наклону в первой из них. \n - Путем подставления начальных точек в общее уравнение коники Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 - и касательной к ней в начальной точке (x1, y1): (2Ax1 + By1 + D)(x - x1) + (2Cy1 + Bx1 + E)(y - y1) = 0 - получим СЛАУ. Решив СЛАУ относительно параметров A,B,C,D,E, найдем искомую конику. - \en Construction of a conic section as a NURBS curve of the third degree by - 4 points and inclination in the first of them. \n - By substituting of start points in the common equation of the conic Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 - and its tangent at the start point (x1, y1): (2Ax1 + By1 + D)(x - x1) + (2Cy1 + Bx1 + E)(y - y1) = 0 - we get the SLAE. Having SLAE solved relative to parameters A,B,C,D,E we find the required conic. \~ - \param[in] vmbConicPoints - \ru Контейнер точек коники: первая точка начальная, последняя - конечная; - точек должно быть 4. - \en The container for points of a conic: the first point is start point, the last point is end point. - there should be exactly 4 points. \~ - \param[in] mbTangent1 - \ru Наклон в точке коники. - \en Inclination at point of conic. \~ - \param[in] tanPntNb - \ru Номер точке, в которой задан наклон. - \en Point number at which the inclination is specified. \~ - \return \ru Указатель на построенную кривую \n - NULL, если не удалось постороить конику для заданных параметров. - \en The pointer to the constructed curve \n - is NULL if a try to construct a conic for given parameters has failed. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC ( MbCurve3D * ) NurbsConic_5( const std::vector & vmbConicPoints, MbVector3D & mbTangent1, size_t tanPntNb = 1 ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить коническое сечение по четырем точкам и наклону. - \en Construct a conic section by four points, and an inclination. \~ - \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по - 4-ем точкам и наклону в первой из них. \n - Путем подставления начальных точек в общее уравнение коники Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 - и касательной к ней в начальной точке (x1, y1): (2Ax1 + By1 + D)(x - x1) + (2Cy1 + Bx1 + E)(y - y1) = 0 - получим СЛАУ. Решив СЛАУ относительно параметров A,B,C,D,E, найдем искомую конику. - \en Construction of a conic section as a NURBS curve of the third degree by - 4 points and inclination in the first of them. \n - By substituting of start points in the common equation of the conic Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 - and its tangent at the start point (x1, y1): (2Ax1 + By1 + D)(x - x1) + (2Cy1 + Bx1 + E)(y - y1) = 0 - we get the SLAE. Having SLAE solved relative to parameters A,B,C,D,E we find the required conic. \~ - \param[in] vmbConicPoints - \ru Контейнер точек коники: первая точка начальная, последняя - конечная; - точек должно быть 4. - \en The container for points of a conic: the first point is start point, the last point is end point. - there should be exactly 4 points. \~ - \param[in] mbTangent1 - \ru Наклон в точке коники. - \en Inclination at point of conic. \~ - \param[in] tanPntNb - \ru Номер точке, в которой задан наклон. - \en Point number at which the inclination is specified. \~ - \return \ru Указатель на построенную кривую \n - NULL, если не удалось постороить конику для заданных параметров. - \en The pointer to the constructed curve \n - is NULL if a try to construct a conic for given parameters has failed. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC ( MbCurve * ) NurbsConic_5( const std::vector & vmbConicPoints, MbVector & mbTangent1, size_t tanPntNb = 1 ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить коническое сечение по пяти точкам. - \en Construct a conic section by five points. \~ - \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по 5-ти точкам.\n - Путем подставления начальных точек в общее уравнение коники Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 получим СЛАУ. - Решив СЛАУ относительно параметров A,B,C,D,E, найдем искомую конику. - \en Construction of a conic section as a NURBS curve of the third degree by 5 points.\n - By substituting of start points in the common equation of the conic Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 we get the SLAE. - Having SLAE solved relative to parameters A,B,C,D,E we find the required conic. \~ - \param[in] vmbConicPoints - \ru Контейнер точек коники: первая точка начальная, последняя - конечная; - точек должно быть 5. - \en The container for points of a conic: the first point is start point, the last point is end point. - there should be exactly 5 points. \~ - \return \ru Указатель на построенную кривую \n - NULL, если не удалось постороить конику для заданных параметров. - \en The pointer to the constructed curve \n - is NULL if a try to construct a conic for given parameters has failed. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC ( MbCurve3D * ) NurbsConic_6( const std::vector & vmbConicPoints ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить коническое сечение по пяти точкам. - \en Construct a conic section by five points. \~ - \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по 5-ти точкам.\n - Путем подставления начальных точек в общее уравнение коники Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 получим СЛАУ. - Решив СЛАУ относительно параметров A,B,C,D,E, найдем искомую конику. - \en Construction of a conic section as a NURBS curve of the third degree by 5 points.\n - By substituting of start points in the common equation of the conic Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 we get the SLAE. - Having SLAE solved relative to parameters A,B,C,D,E we find the required conic. \~ - \param[in] vmbConicPoints - \ru Контейнер точек коники: первая точка начальная, последняя - конечная; - точек должно быть 5. - \en The container for points of a conic: the first point is start point, the last point is end point. - there should be exactly 5 points. \~ - \return \ru Указатель на построенную кривую \n - NULL, если не удалось постороить конику для заданных параметров. - \en The pointer to the constructed curve \n - is NULL if a try to construct a conic for given parameters has failed. \~ - \ingroup Curve_Modeling -*/ -// --- -MATH_FUNC ( MbCurve * ) NurbsConic_6( const std::vector & vmbConicPoints ); - - -#endif // __ALG_NURBS_CONIC_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Построение конических сечений в виде NURBS-кривой. + \en Construction of conic sections as NURBS curves. \~ + \details \ru Построение конических сечений производится следующими способами: + по двум точкам, вершине и дискриминанту, по трем точкам и вершине, + по трем точкам и двум наклонам, по двум точкам, двум наклонам и дискриминанту, + по четырем точкам и наклону и по пяти точкам. \n + NURBS кривая, описывающая конику, строится по трем точкам: началу и концу коники и + средней точке (вершине угола, в который надо вписать конику). + Принимая весы начальной и конечной точки равными 1 и рассчитывая вес средней точки, + по трем точкам и трем весам строится NURBS 3-го порядка, который будет искомой коникой. + \en Construction of conic sections is performed in the following way: + by two points, a vertex and a discriminant, by three points and a vertex, + by three points and two inclinations, by two points, two inclinations and discriminant, + by four points and inclination and by five points. \n + A NURBS curve describing a conic is constructed by three points: a start and an end of a conic and + an average point (a vertex of angle which should be inscribed into the conic). + Let weights of the start point and the end point be equal to 1. After calculating of the weight of the average point + NURBS of third degree is constructed by these three weights. This NURBS is the required conic. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef __ALG_NURBS_CONIC_H +#define __ALG_NURBS_CONIC_H + + +#include + + +class MbCurve3D; +class MbNurbs3D; +class MbCartPoint3D; +class MbVector3D; + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить коническое сечение по двум точкам вершине и дискриминанту. + \en Construct a conic section by two points, an angle vertex and a discriminant. \~ + \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по + двум точкам, которые задают начало и конец кривой, вершине инженерного + треугольника и дискриминанту, который используется для определения третьей точки кривой. + \en Construction of a conic section as a NURBS curve of the third degree by + two points setting ends of a curve, a vertex of enginer + triangle and a discriminant which is used for the definition of the third point. \~ + \param[in] mbPoint0 - \ru Координаты начала коники. + \en Coordinates of the conic start point. \~ + \param[in] mbPoint1 - \ru Координаты вершины угла, в который надо вписать конику. + \en Coordinates of the vertex of angle which should be inscribed into the conic. \~ + \param[in] mbPoint2 - \ru Координаты конца коники. + \en Coordinates of the conic end point. \~ + \param[in] fDiscr - \ru Дискриминант < 1, если задать дискриминант >= 1, то он + автоматически будет сброшен до значения 0.99999999. + \en The discriminant is less than 1. Otherwise it + will be set to 0.99999999 automatically. \~ + \return \ru Указатель на построенную кривую \n + NULL, если не удалось построить конику для заданных параметров. + \en The pointer to the constructed curve \n + is NULL if a try to construct a conic for a given parameters has failed. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC ( MbCurve3D * ) NurbsConic_1( const MbCartPoint3D & mbPoint0, const MbCartPoint3D & mbPoint1, + const MbCartPoint3D & mbPoint2, double fDiscr ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить коническое сечение по двум точкам вершине и дискриминанту. + \en Construct a conic section by two points, an angle vertex and a discriminant. \~ + \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по + двум точкам, которые задают начало и конец кривой, вершине инженерного + треугольника и дискриминанту, который используется для определения третьей точки кривой. + \en Construction of a conic section as a NURBS curve of the third degree by + two points setting ends of a curve, a vertex of enginer + triangle and a discriminant which is used for the definition of the third point. \~ + \param[in] mbPoint0 - \ru Координаты начала коники. + \en Coordinates of the conic start point. \~ + \param[in] mbPoint1 - \ru Координаты вершины угла, в который надо вписать конику. + \en Coordinates of the vertex of angle which should be inscribed into the conic. \~ + \param[in] mbPoint2 - \ru Координаты конца коники. + \en Coordinates of the conic end point. \~ + \param[in] fDiscr - \ru Дискриминант < 1, если задать дискриминант >= 1, то он + автоматически будет сброшен до значения 0.99999999. + \en The discriminant is less than 1. Otherwise it + will be set to 0.99999999 automatically. \~ + \return \ru Указатель на построенную кривую \n + NULL, если не удалось построить конику для заданных параметров. + \en The pointer to the constructed curve \n + is NULL if a try to construct a conic for a given parameters has failed. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC ( MbCurve * ) NurbsConic_1( const MbCartPoint & mbPoint0, const MbCartPoint & mbPoint1, + const MbCartPoint & mbPoint2, double fDiscr ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить коническое сечение по трем точкам и вершине. + \en Construct a conic section by three points, and an angle vertex. \~ + \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по + трем точкам: началу, концу и средней точке кривой, а также вершине угла, в который + должна быть вписана коника. + \en Construction of a conic section as a NURBS curve of the third degree by + three points: ends of a curve, its average point and by a vertex of an angle, + a conic should be inscribed in. \~ + \param[in] vmbConicPoints - \ru Контейнер точек коники: начало, средняя точка, конец; + точек должно быть 3. + \en The container for points of a conic: start point, average point and end point; + there should be exactly 3 points. \~ + \param[in] mbVertex - \ru Координаты вершины угла, в который надо вписать конику. + \en Coordinates of the vertex of angle which should be inscribed into the conic. \~ + \return \ru Указатель на построенную кривую \n + NULL, если не удалось постороить конику для заданных параметров. + \en The pointer to the constructed curve \n + is NULL if a try to construct a conic for given parameters has failed. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC ( MbCurve3D * ) NurbsConic_2( std::vector & vmbConicPoints, const MbCartPoint3D & mbVertex ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить коническое сечение по трем точкам и вершине. + \en Construct a conic section by three points, and an angle vertex. \~ + \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по + трем точкам: началу, концу и средней точке кривой, а также вершине угла, в который + должна быть вписана коника. + \en Construction of a conic section as a NURBS curve of the third degree by + three points: ends of a curve, its average point and by a vertex of an angle, + a conic should be inscribed in. \~ + \param[in] vmbConicPoints - \ru Контейнер точек коники: начало, средняя точка, конец; + точек должно быть 3. + \en The container for points of a conic: start point, average point and end point; + there should be exactly 3 points. \~ + \param[in] mbVertex - \ru Координаты вершины угла, в который надо вписать конику. + \en Coordinates of the vertex of angle which should be inscribed into the conic. \~ + \return \ru Указатель на построенную кривую \n + NULL, если не удалось постороить конику для заданных параметров. + \en The pointer to the constructed curve \n + is NULL if a try to construct a conic for given parameters has failed. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC ( MbCurve * ) NurbsConic_2( std::vector & vmbConicPoints, const MbCartPoint & mbVertex ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить коническое сечение по трем точкам и двум наклонам. + \en Construct a conic section by three points and two inclinations. \~ + \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по + 3-ем точкам, которые задают начало, конец и среднюю точку кривой и двум + наклонам, выходящим из начальной и конечной точек. + \en Construction of a conic section as a NURBS curve of the third degree by + 3 points setting begin, end and an average point of a curve and two + inclinations outgoing from the start point and from the end point \~ + \param[in] vmbConicPoints - \ru Контейнер точек коники: начало, средняя точка, конец; + точек должно быть 3. + \en The container for points of a conic: start point, average point and end point; + there should be exactly 3 points. \~ + \param[in] mbTangent1 - \ru Наклон в начале кривой. + \en Inclination at start of a curve. \~ + \param[in] mbTangent2 - \ru Наклон в конце кривой. + \en Inclination at end of a curve. \~ + \return \ru Указатель на построенную кривую \n + NULL, если не удалось постороить конику для заданных параметров. + \en The pointer to the constructed curve \n + is NULL if a try to construct a conic for given parameters has failed. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC ( MbCurve3D * ) NurbsConic_3( const std::vector & vmbConicPoints, + MbVector3D & mbTangent1, MbVector3D & mbTangent2 ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить коническое сечение по трем точкам и двум наклонам. + \en Construct a conic section by three points and two inclinations. \~ + \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по + 3-ем точкам, которые задают начало, конец и среднюю точку кривой и двум + наклонам, выходящим из начальной и конечной точек. + \en Construction of a conic section as a NURBS curve of the third degree by + 3 points setting begin, end and an average point of a curve and two + inclinations outgoing from the start point and from the end point \~ + \param[in] vmbConicPoints - \ru Контейнер точек коники: начало, средняя точка, конец; + точек должно быть 3. + \en The container for points of a conic: start point, average point and end point; + there should be exactly 3 points. \~ + \param[in] mbTangent1 - \ru Наклон в начале кривой. + \en Inclination at start of a curve. \~ + \param[in] mbTangent2 - \ru Наклон в конце кривой. + \en Inclination at end of a curve. \~ + \return \ru Указатель на построенную кривую \n + NULL, если не удалось постороить конику для заданных параметров. + \en The pointer to the constructed curve \n + is NULL if a try to construct a conic for given parameters has failed. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC ( MbCurve * ) NurbsConic_3( const std::vector & vmbConicPoints, MbVector & mbTangent1, MbVector & mbTangent2 ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить коническое сечение по двум точкам, двум наклонам и дискриминанту. + \en Construct a conic section by two points, two inclinations and a discriminant. \~ + \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по + 2-ум точкам, которые задают начало и конец кривой, двум наклонам, выходящим из этих точек + и дискриминанту. + \en Construction of a conic section as a NURBS curve of the third degree by + 2 points setting start and end of a curve, two incllinations outgoing from these points + and a discriminant. \~ + \param[in] mbPoint1 - \ru Координаты начала коники. + \en Coordinates of the conic start point. \~ + \param[in] mbPoint2 - \ru Координаты конца коники. + \en Coordinates of the conic end point. \~ + \param[in] mbTangent1 - \ru Наклон в начале коники. + \en Inclination at start of conic. \~ + \param[in] mbTangent2 - \ru Наклон в конце коники. + \en Inclination at end of conic. \~ + \param[in] fDiscr - \ru Дискриминант < 1, если задать дискриминант >= 1, то он + автоматически будет сброшен до значения 0.99999999. + \en The discriminant is less than 1. Otherwise it + will be set to 0.99999999 automatically. \~ + \return \ru Указатель на построенную кривую \n + NULL, если не удалось построить конику для заданных параметров. + \en The pointer to the constructed curve \n + is NULL if a try to construct a conic for given parameters has failed. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC ( MbCurve3D * ) NurbsConic_4( const MbCartPoint3D & mbPoint1, const MbCartPoint3D & mbPoint2, + const MbVector3D & mbTangent1, const MbVector3D & mbTangent2, double fDiscr ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить коническое сечение по двум точкам, двум наклонам и дискриминанту. + \en Construct a conic section by two points, two inclinations and a discriminant. \~ + \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по + 2-ум точкам, которые задают начало и конец кривой, двум наклонам, выходящим из этих точек + и дискриминанту. + \en Construction of a conic section as a NURBS curve of the third degree by + 2 points setting start and end of a curve, two incllinations outgoing from these points + and a discriminant. \~ + \param[in] mbPoint1 - \ru Координаты начала коники. + \en Coordinates of the conic start point. \~ + \param[in] mbPoint2 - \ru Координаты конца коники. + \en Coordinates of the conic end point. \~ + \param[in] mbTangent1 - \ru Наклон в начале коники. + \en Inclination at start of conic. \~ + \param[in] mbTangent2 - \ru Наклон в конце коники. + \en Inclination at end of conic. \~ + \param[in] fDiscr - \ru Дискриминант < 1, если задать дискриминант >= 1, то он + автоматически будет сброшен до значения 0.99999999. + \en The discriminant is less than 1. Otherwise it + will be set to 0.99999999 automatically. \~ + \return \ru Указатель на построенную кривую \n + NULL, если не удалось построить конику для заданных параметров. + \en The pointer to the constructed curve \n + is NULL if a try to construct a conic for given parameters has failed. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC ( MbCurve * ) NurbsConic_4( const MbCartPoint & mbPoint1, const MbCartPoint & mbPoint2, + const MbVector & mbTangent1, const MbVector & mbTangent2, double fDiscr ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить коническое сечение по четырем точкам и наклону. + \en Construct a conic section by four points, and an inclination. \~ + \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по + 4-ем точкам и наклону в первой из них. \n + Путем подставления начальных точек в общее уравнение коники Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 + и касательной к ней в начальной точке (x1, y1): (2Ax1 + By1 + D)(x - x1) + (2Cy1 + Bx1 + E)(y - y1) = 0 + получим СЛАУ. Решив СЛАУ относительно параметров A,B,C,D,E, найдем искомую конику. + \en Construction of a conic section as a NURBS curve of the third degree by + 4 points and inclination in the first of them. \n + By substituting of start points in the common equation of the conic Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 + and its tangent at the start point (x1, y1): (2Ax1 + By1 + D)(x - x1) + (2Cy1 + Bx1 + E)(y - y1) = 0 + we get the SLAE. Having SLAE solved relative to parameters A,B,C,D,E we find the required conic. \~ + \param[in] vmbConicPoints - \ru Контейнер точек коники: первая точка начальная, последняя - конечная; + точек должно быть 4. + \en The container for points of a conic: the first point is start point, the last point is end point. + there should be exactly 4 points. \~ + \param[in] mbTangent1 - \ru Наклон в точке коники. + \en Inclination at point of conic. \~ + \param[in] tanPntNb - \ru Номер точке, в которой задан наклон. + \en Point number at which the inclination is specified. \~ + \return \ru Указатель на построенную кривую \n + NULL, если не удалось постороить конику для заданных параметров. + \en The pointer to the constructed curve \n + is NULL if a try to construct a conic for given parameters has failed. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC ( MbCurve3D * ) NurbsConic_5( const std::vector & vmbConicPoints, MbVector3D & mbTangent1, size_t tanPntNb = 1 ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить коническое сечение по четырем точкам и наклону. + \en Construct a conic section by four points, and an inclination. \~ + \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по + 4-ем точкам и наклону в первой из них. \n + Путем подставления начальных точек в общее уравнение коники Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 + и касательной к ней в начальной точке (x1, y1): (2Ax1 + By1 + D)(x - x1) + (2Cy1 + Bx1 + E)(y - y1) = 0 + получим СЛАУ. Решив СЛАУ относительно параметров A,B,C,D,E, найдем искомую конику. + \en Construction of a conic section as a NURBS curve of the third degree by + 4 points and inclination in the first of them. \n + By substituting of start points in the common equation of the conic Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 + and its tangent at the start point (x1, y1): (2Ax1 + By1 + D)(x - x1) + (2Cy1 + Bx1 + E)(y - y1) = 0 + we get the SLAE. Having SLAE solved relative to parameters A,B,C,D,E we find the required conic. \~ + \param[in] vmbConicPoints - \ru Контейнер точек коники: первая точка начальная, последняя - конечная; + точек должно быть 4. + \en The container for points of a conic: the first point is start point, the last point is end point. + there should be exactly 4 points. \~ + \param[in] mbTangent1 - \ru Наклон в точке коники. + \en Inclination at point of conic. \~ + \param[in] tanPntNb - \ru Номер точке, в которой задан наклон. + \en Point number at which the inclination is specified. \~ + \return \ru Указатель на построенную кривую \n + NULL, если не удалось постороить конику для заданных параметров. + \en The pointer to the constructed curve \n + is NULL if a try to construct a conic for given parameters has failed. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC ( MbCurve * ) NurbsConic_5( const std::vector & vmbConicPoints, MbVector & mbTangent1, size_t tanPntNb = 1 ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить коническое сечение по пяти точкам. + \en Construct a conic section by five points. \~ + \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по 5-ти точкам.\n + Путем подставления начальных точек в общее уравнение коники Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 получим СЛАУ. + Решив СЛАУ относительно параметров A,B,C,D,E, найдем искомую конику. + \en Construction of a conic section as a NURBS curve of the third degree by 5 points.\n + By substituting of start points in the common equation of the conic Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 we get the SLAE. + Having SLAE solved relative to parameters A,B,C,D,E we find the required conic. \~ + \param[in] vmbConicPoints - \ru Контейнер точек коники: первая точка начальная, последняя - конечная; + точек должно быть 5. + \en The container for points of a conic: the first point is start point, the last point is end point. + there should be exactly 5 points. \~ + \return \ru Указатель на построенную кривую \n + NULL, если не удалось постороить конику для заданных параметров. + \en The pointer to the constructed curve \n + is NULL if a try to construct a conic for given parameters has failed. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC ( MbCurve3D * ) NurbsConic_6( const std::vector & vmbConicPoints ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить коническое сечение по пяти точкам. + \en Construct a conic section by five points. \~ + \details \ru Построение конического сечения в виде NURBS-кривой 3-го порядка по 5-ти точкам.\n + Путем подставления начальных точек в общее уравнение коники Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 получим СЛАУ. + Решив СЛАУ относительно параметров A,B,C,D,E, найдем искомую конику. + \en Construction of a conic section as a NURBS curve of the third degree by 5 points.\n + By substituting of start points in the common equation of the conic Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 we get the SLAE. + Having SLAE solved relative to parameters A,B,C,D,E we find the required conic. \~ + \param[in] vmbConicPoints - \ru Контейнер точек коники: первая точка начальная, последняя - конечная; + точек должно быть 5. + \en The container for points of a conic: the first point is start point, the last point is end point. + there should be exactly 5 points. \~ + \return \ru Указатель на построенную кривую \n + NULL, если не удалось постороить конику для заданных параметров. + \en The pointer to the constructed curve \n + is NULL if a try to construct a conic for given parameters has failed. \~ + \ingroup Curve_Modeling +*/ +// --- +MATH_FUNC ( MbCurve * ) NurbsConic_6( const std::vector & vmbConicPoints ); + + +#endif // __ALG_NURBS_CONIC_H diff --git a/C3d/Include/assembly.h b/C3d/Include/assembly.h index 85ee33a..971b4c9 100644 --- a/C3d/Include/assembly.h +++ b/C3d/Include/assembly.h @@ -1,392 +1,397 @@ -////////////////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Сборочная единица. - \en Assembly unit. \~ -*/ -////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef __ASSEMBLY_H -#define __ASSEMBLY_H - -#include -#include -#include -#include -#include - - -class MbConstraintSystem; -class MATH_CLASS MtGeomArgument; -class MATH_CLASS MtGeomConstraint; -class MATH_CLASS MtConstraintIter; -struct ItAssemblyReactor; -struct ItAssemblyImportData; -struct ItModelVisitor; -class MbModelTreeReader; -class MATH_CLASS MbAssembly; - -namespace c3d // namespace C3D -{ -typedef SPtr AssemblySPtr; -typedef SPtr ConstAssemblySPtr; - -typedef std::vector AssembliesVector; -typedef std::vector ConstAssembliesVector; - -typedef std::vector AssembliesSPtrVector; -typedef std::vector ConstAssembliesSPtrVector; -} - - -//---------------------------------------------------------------------------------------- -/** \brief \ru Сборочная единица. - \en Assembly unit. \~ - \details \ru Сборка состоит из множества объектов геометрической модели MbItem. - Сборка может содержать объекты любого подкласса MbItem, в том числе и сборочные - единицы (тип MbAssembly). - \en The assembly consists of a set of objects of geometric model MbItem. - The assembly may contain objects of any sub-class of MbItem, including - assembly units (of type MbAssembly). - \par \ru Отношение "часть-целое". - \en Relationship "is a part of". - \ru Сборочная единица - это объект модели объединяющий в себе набор других объектов. - Такое объединение рассматривается как агрегация, устанавливающая отношение - владения между сборкой и её собственными суб-объектами. Это предполагает что любой - объект модели типа MbItem может принадлежать только одной сборке. - \en Assembly unit is object of model aggregating a collection of other objects. - Such an association is regarded as an aggregation establishing an ownership - between the assembly and its proper sub-objects. This implies that any - model object of type MbItem can belong to only assembly. - \~ - \ingroup Model_Items -*/ -//--- -class MATH_CLASS MbAssembly : public MbItem -{ -private: - typedef sorting_array ItemContainer; - typedef ItemContainer::iterator item_iterator; - -private: - ItemContainer assemblyItems; ///< \ru Множество объектов сборки. \en A set of assembly objects. - MbConstraintSystem * constraintSystem; ///< \ru Система ограничений сборки. \en Constraint system of assembly unit. - mutable ItAssemblyReactor * m_reactor; ///< \ru Обработчик события, связанные с решением сборки. \en The event handles related to solving the assembly. - -protected: - /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. - explicit MbAssembly( const MbAssembly & init, MbRegDuplicate * iReg ); - -public: - /// \ru Конструктор пустой сборки. \en Construct an empty assembly. - MbAssembly(); - /// \ru Конструктор по объекту. \en The constructor by an object. - explicit MbAssembly( MbItem & ); - /// \ru Конструктор по объектам в локальной системе координат. \en The constructor by objects in a local coordinate system. - template - MbAssembly( const ItemsVector & items ); - // \ru Деструктор. \en Destructor. - virtual ~MbAssembly(); - -public: - VISITING_CLASS( MbAssembly ); - - // \ru Общие функции геометрического объекта \en Common functions of a geometric object - - virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en An object type. - virtual MbSpaceItem & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Создать копию. \en Create a copy. - virtual void Transform( const MbMatrix3D &, MbRegTransform * iReg = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. - virtual void Move( const MbVector3D &, MbRegTransform * iReg = NULL ); // \ru Сдвинуть вдоль вектора. \en Move along a vector. - virtual void Rotate( const MbAxis3D &, double angle, MbRegTransform * iReg = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. - virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Are the objects equal? - virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными? \en Are the objects similar? - virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать объекты равным \en Make the objects equal - virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate distance to point. - virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Добавь свой габарит в куб. \en Add own bounding box to the bounding box. - virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate the bounding box in a local coordinate system. - virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. - - virtual MbProperty & CreateProperty( MbePrompt n ) const; // \ru Создать собственное свойство. \en Create own property. - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. - virtual void GetBasisItems( RPArray & ); // \ru Дать базовые объекты. \en Get the basis objects. - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - // \ru Выдать локальную систему координат объектов сборки. \en Get the local coordinate system of assembly items. - virtual bool GetPlacement( MbPlacement3D & ) const; - // \ru Установить локальную систему координат объектов сборки. \en Set coordinate system of assembly items. - virtual bool SetPlacement( const MbPlacement3D & ); - // \ru Перестроить объект по журналу построения. \en Rebuild object according to the history tree. - virtual bool RebuildItem( MbeCopyMode sameShell, RPArray * items, IProgressIndicator * progInd ); - // \ru Создать полигональный объект - упрощенную копию данного объекта. \en Create a polygonal object - a simplified copy of the given object. - // \note \ru В многопоточном режиме выполняется параллельно. \en In multithreaded mode runs in parallel. - virtual MbItem * CreateMesh( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * iReg ) const; - // \ru Добавить полигональную сетку объекта. \en Add a polygonal mesh of the object. - virtual bool AddYourMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; - // \ru Разрезать полигональный объект одной или двумя параллельными плоскостями. \en Cut the polygonal object by one or two parallel planes. - virtual MbItem * CutMesh( const MbPlacement3D & cutPlace, double distance ) const; - // \ru Найти ближайший объект или имя ближайшего объекта. \en Find the closest object or its name. - virtual bool NearestMesh( MbeSpaceType sType, MbeTopologyType tType, MbePlaneType pType, - const MbAxis3D & axis, double maxDistance, bool gridPriority, double & t, double & dMin, - MbItem *& find, SimpleName & findName, - MbRefItem *& element, SimpleName & elementName, - MbPath & path, MbMatrix3D & from ) const; - // \ru Дать все объекты указанного типа. \en Get all objects by type. \~ - virtual bool GetItems( MbeSpaceType type, const MbMatrix3D & from, - RPArray & items, SArray & matrs ); - // \ru Дать все полигональные объекты, отображающие геометрические элементы, участвующие в геометрических огриничениях.\en Get all polygonal objects for drawing the elements participated in geometric constraints. \~ - bool GetConstraintMesh( std::vector & meshes ) const; - // \ru Дать все уникальные объекты указанного типа. \en Get all unique objects by type . \~ - virtual bool GetUniqItems( MbeSpaceType type, CSSArray & items ) const; - // \ru Дать объект по его пути положения в модели и матрицу преобразования объекта в глобальную систему координат. \en Get the object by its path in the model and get the matrix of transformation of the object to the global coordinate system. - virtual const MbItem * GetItemByPath( const MbPath & path, size_t ind, MbMatrix3D & from, size_t currInd = 0 ) const; - // \ru Найти объект по геометрическому объекту (MbSpaceItem). \en Find the object by a geometric object (MbSpaceItem). - virtual const MbItem * FindItem( const MbSpaceItem * s, MbPath & path, MbMatrix3D & from ) const; - // \ru Найти объект по геометрическому объекту (MbPlaneItem). \en Find the object by a geometric object (MbSpaceItem). - virtual const MbItem * FindItem( const MbPlaneItem * s, MbPath & path, MbMatrix3D & from ) const; - // \ru Найти объект и матрицу его преобразования в глобальную систему координат. \en Find the object and the matrix of its transformation to the global coordinate system. - virtual const MbItem * FindItem( const MbItem * s, MbPath & path, MbMatrix3D & from ) const; - // \ru Дать объект с заданным именем и матрицу его преобразования в глобальную систему координат. \en Get the object with the specified name and the matrix of its transformation to the global coordinate system. - virtual const MbItem * GetItemByName( SimpleName n, MbPath & path, MbMatrix3D & from ) const; - - // \ru Преобразовать согласно матрице c использованием регистратора селектированные содержимые объекты. \en Transform selected objects according to the matrix using the registrator. - virtual void TransformSelected( const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); - // \ru Сдвинуть вдоль вектора с использованием регистратора селектированные содержимые объекты. \en Move selected objects along the vector using the registrator. - virtual void MoveSelected( const MbVector3D & to, MbRegTransform * iReg = NULL ); - // \ru Повернуть вокруг оси на заданный угол с использованием регистратора селектированные содержимые объекты. \en Rotate selected objects about the axis by the given angle using the registrator. - virtual void RotateSelected( const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); - /// \ru Отдать селектированные содержимые объекты. \en Get selected objects. - bool DetachSelected( RPArray & items, SArray & matrs, bool selected = true ); - /// \ru Отцепить все видимые или невидимые объекты. \en Detach all visible or invisible objects. \~ - bool DetachInvisible( RPArray & items, SArray & matrs, bool invisible = true ); - /// \ru Отцепить все объекты с указанным свойством. \en Detach all objects with pointed attribute. \~ - bool DetachByAttribute( RPArray & items, SArray & matrs, int attribute ); - /** \brief \ru Алгоритм общего назначения для обхода дерева модели в глубину. - \en General-purpose algorithm traversing the model graph in depth. */ - void Traverse( ItModelVisitor & ) const; - -public: - /** \ru \name Функции сборочной единицы. - \en \name The assembly unit functions. - \{ */ - /// \ru Выдать непосредственный объект сборки по идентификатору. \en Get the immediate item of assembly by identifier. - const MbItem * SubItem( SimpleName n ) const { return _ItemByName(n); } - /// \ru Добавить объект в сборку. \en Add an item to the assembly. - MbItem * AddItem( MbItem & item ); - /** - \brief \ru Добавить вставку геометрического объекта. - \en Add an instance of the geometric object. \~ - \param item - \ru Источник, на котором основан экземпляр вставки. - \en A source item on which the instance is based. - \param lcs - \ru Локальная система координат экземпляра вставляемого объекта. - \en Local coordinate system of the instanced object. \~ - \return \ru Экземпляр класса MbInstance, размещающего объект в пространстве сборки. - \en An Instance of class MbInstance placing the item in the space of the assembly. - */ - MbItem * AddInstance( MbItem & item, const MbPlacement3D & lcs ); - /** \brief \ru Заменить объект. - \en Replace an item. \~ - \details \ru Заменить объект новым. - \en Replace an item by a new one. \~ - \param[in] item - \ru Заменяемый объект. - \en An item to be replaced. \~ - \param[in] newItem - \ru Новый объект. - \en A new item. \~ - \return \ru Возвращает true, если замена была выполнена. - \en Returns true if the replacement has been performed. \~ - */ - bool ReplaceItem( const MbItem & item, MbItem & newItem, bool saveName = false ); - - /// \ru Выдать все объекты. \en Get all the items. - void GetItems( RPArray & items ) const; - /// \ru Выдать все объекты. \en Get all the items. - void GetItems( RPArray & items ); - - /// \ru Отцепить объект по индексу. \en Detach the item by index. - MbItem* DetachItem ( size_t ind ); - /// \ru Отцепить объект, если такой есть в сборке. \en Detach the item if it belongs to the assembly. - bool DetachItem ( MbItem * obj ); - /// \ru Удалить объект, если такой есть в сборке или в подсборках. \en Delete the item if it belongs to the assembly or its sub-assemblies. - bool DeleteItem ( MbItem * obj ); - /// \ru Удалить все объекты сборки. \en Delete all the assembly items. - void DeleteItems(); - /// \ru Выдать количество объектов сборки. \en Get the assembly item count. - size_t ItemsCount() const { return assemblyItems.size(); } - /// \ru Вернуть true, если сборка не содержит геометрических объекты. \en Return true, if the assembly has no geometric objects. - bool IsEmpty() const { return assemblyItems.empty(); } - /// \ru Выдать объект по индексу. \en Get the item by index. - const MbItem * GetItem( size_t i ) const; - /// \ru Выдать объект по индексу для модификации. \en Get the item by index for modification. - MbItem * SetItem( size_t i ); - /// \ru Содержит ли сборка присланный объект? \en Does the assembly contain the given item? - bool ContainsItem( const MbItem * obj ) const; - /// \ru Вычислить габарит сборки. \en Calculate the bounding box of the assembly. - void CalculateGabarit( MbCube & cube ) const; - /// \ru Выдать количество граней. \en Get the number of faces. - size_t GetFacesCount() const; - /// \ru Заполнить контейнер гранями тела. \en Fill container by solid faces. - template - void GetFacesSet( FacesVector & faces ) const; -public: - /** \} - \ru \name Функции системы ограничений. - \en \name The constraint system functions. - \{ */ - /// \ru Добавить ограничение для пары геометрических объектов. \en Add geometric constraint. - MtGeomConstraint AddConstraint( MtMateType, const MtGeomArgument &, const MtGeomArgument &, MtParVariant = MtParVariant::undef ); - /// \ru Изменить значение управляющего размера. \en Change the value of driving dimension. - MtResultCode3D ChangeDimension( MtGeomConstraint & dimCon, double newVal ); - /// \ru Решить ограничения сборки. \en Evaluate constraints. - MtResultCode3D EvaluateConstraints(); - /// \ru Выдать диапазон итераторов для обхода всех ограничений сборки. \en Get a range of iterators to traverse all assembly constraints. - void GetConstraints( MtConstraintIter & begIter, MtConstraintIter & endIter ) const; - /// \ru Задать или сбросить обработчик событий решателя. \en Set or reset an handler of constraint solving events. - void SetReactor( ItAssemblyReactor * ) const; - /// \ru Импортировать систему ограничений из приложения САПР. \ru Import the constraint system from CAD application. - bool Import( ItAssemblyImportData & ); - -public: - /** \} */ - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbAssembly ); - - static const MbPlacement3D & GetPlacement() { return MbPlacement3D::global; } // This function is deprecated. Use MbInstanse to give the assembly its own placement. - - friend class MbModelTreeReader; - -private: - // \ru Инициализатор по массиву составляющих объектов. // \en Initializer to aggregate items in the assembly. - template - void _Init( const ItemsVector & ); - // Найти объект по геометрическому объекту - template - const MbItem * _FindItem( const ItemType * s, MbPath & path, MbMatrix3D & from ) const; - // Поиск в глубину среди подчиненных - template - const MbItem * _FindRecursively( const ItemType * s, MbPath & path, MbMatrix3D & from ) const; - // \ru Выдать объект по идентификатору. \en Get the item by identifier. - const MbItem * _ItemByName( SimpleName ) const; - // \ru Генерация имени для нового элемента сборки. \en Generate identifier for new assembly item. - SimpleName _NewItemName() const; - /// \ru Добавить в сборку объекты сборки без трансформации. \en Add assembly items to the assembly without transformation. - bool _AddAssemblyItems( MbAssembly & ); - - OBVIOUS_PRIVATE_COPY( MbAssembly ); -}; // MbAssembly - -IMPL_PERSISTENT_OPS( MbAssembly ) - - -//---------------------------------------------------------------------------------------- -// Экспериментальный посетитель дерева модели -/* - Возможные применения: - - Сбор любых данных об/из иерархии модели; - - Загрузка подсборок и вставок в утилиту поиска соударений (MbCollisionDetectionUtility); - - Геометрический поиск с выдачей маршрута(MbPath) и матрицу отображения МСК вставок и подсборок; - - Восстановление текущей матрицы и маршрута MbPath по hash-коду ссылок в системе - геометрических ограничений; -*/ -//--- -struct ItModelVisitor -{ -public: - virtual void VisitItem( const MbItem * ) = 0; - virtual void FinishItem( const MbItem * ) = 0; - virtual bool ExamineSubItem( const MbItem * owner, const MbItem * subItem ) = 0; - virtual void ExamineInstance( const MbInstance * inst, const MbItem * srcItem ) = 0; -}; - - -//---------------------------------------------------------------------------------------- -// \ru Конструктор по объектам. \en The constructor by objects. -//--- -template -MbAssembly::MbAssembly( const ItemsVector & items ) - : MbItem() - , assemblyItems() - , constraintSystem( NULL ) - , m_reactor( NULL ) -{ -#ifdef C3D_DEBUG - // Check a condition of the single owner. - for ( size_t i = 0, iCount = items.size(); i < iCount; ++i ) - { - if ( items[i]->GetItemName() != UNDEFINED_SNAME ) - { - C3D_ASSERT_UNCONDITIONAL( false ); // The item has already a name. It's probably means that the item is owned another assembly. - break; - } - } -#endif // C3D_DEBUG - - _Init( items ); -} - - -//---------------------------------------------------------------------------------------- -// \ru Инициализатор по массиву составляющих объектов. -// \en Initializer to aggregate items in the assembly. -//--- -template -void MbAssembly::_Init( const ItemsVector & items ) -{ - C3D_ASSERT( assemblyItems.empty() && (constraintSystem == NULL) ); - SimpleName idCounter = 0; - - for ( size_t i = 0, iCount = items.size(); i < iCount; ++i ) - { - if ( MbItem * item = items[i] ) { - if ( item->GetItemName() == UNDEFINED_SNAME ) { - item->SetItemName( idCounter ); - } - else { - C3D_ASSERT( idCounter <= item->GetItemName() ); - idCounter = max_of( idCounter, item->GetItemName() ); - } - ++idCounter; - item->AddRef(); - assemblyItems.push_back( item ); - } - } -} - - -//---------------------------------------------------------------------------------------- -// \ru Заполнить контейнер гранями тела. \en Fill container by solid faces. -//--- -template -void MbAssembly::GetFacesSet( FacesVector & faces ) const -{ - for ( size_t i = assemblyItems.size(); i--; ) - { - if ( const MbItem * assemblyItem = assemblyItems[i] ) - { - if ( assemblyItem->IsA() == st_Solid ) - static_cast(*assemblyItem).GetFacesSet( faces ); - else if ( assemblyItem->IsA() == st_Instance ) - static_cast(*assemblyItem).GetFacesSet( faces ); - else if ( assemblyItem->IsA() == st_Assembly ) - static_cast(*assemblyItem).GetFacesSet( faces ); - } - } -} - -//---------------------------------------------------------------------------------------- -// \ru Заполнить контейнер гранями тела. \en Fill container by solid faces. -//--- -template -void MbInstance::GetFacesSet( FacesVector & faces ) const -{ - if ( item != NULL ) { - if ( item->IsA() == st_Solid ) - static_cast( *item ).GetFacesSet( faces ); - else if ( item->IsA() == st_Assembly ) - static_cast( *item ).GetFacesSet( faces ); - else if ( item->IsA() == st_Instance ) - static_cast( *item ).GetFacesSet( faces ); - } -} - - -#endif // __ASSEMBLY_H +////////////////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Сборочная единица. + \en Assembly unit. \~ +*/ +////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef __ASSEMBLY_H +#define __ASSEMBLY_H + +#include +#include +#include +#include +#include + + +class MbConstraintSystem; +class MATH_CLASS MtGeomArgument; +class MATH_CLASS MtGeomConstraint; +class MATH_CLASS MtConstraintIter; +struct ItAssemblyReactor; +struct ItAssemblyImportData; +struct ItModelVisitor; +class MbModelTreeReader; +class MATH_CLASS MbAssembly; + +namespace c3d // namespace C3D +{ +typedef SPtr AssemblySPtr; +typedef SPtr ConstAssemblySPtr; + +typedef std::vector AssembliesVector; +typedef std::vector ConstAssembliesVector; + +typedef std::vector AssembliesSPtrVector; +typedef std::vector ConstAssembliesSPtrVector; +} + + +//---------------------------------------------------------------------------------------- +/** \brief \ru Сборочная единица. + \en Assembly unit. \~ + \details \ru Сборка состоит из множества объектов геометрической модели MbItem. + Сборка может содержать объекты любого подкласса MbItem, в том числе и сборочные + единицы (тип MbAssembly). + \en The assembly consists of a set of objects of geometric model MbItem. + The assembly may contain objects of any sub-class of MbItem, including + assembly units (of type MbAssembly). + \par \ru Отношение "часть-целое". + \en Relationship "is a part of". + \ru Сборочная единица - это объект модели объединяющий в себе набор других объектов. + Такое объединение рассматривается как агрегация, устанавливающая отношение + владения между сборкой и её собственными суб-объектами. Это предполагает что любой + объект модели типа MbItem может принадлежать только одной сборке. + \en Assembly unit is object of model aggregating a collection of other objects. + Such an association is regarded as an aggregation establishing an ownership + between the assembly and its proper sub-objects. This implies that any + model object of type MbItem can belong to only assembly. + \~ + \ingroup Model_Items +*/ +//--- +class MATH_CLASS MbAssembly : public MbItem +{ +private: + typedef sorting_array ItemContainer; + typedef ItemContainer::iterator item_iterator; + +private: + ItemContainer assemblyItems; ///< \ru Множество объектов сборки. \en A set of assembly objects. + MbConstraintSystem * constraintSystem; ///< \ru Система ограничений сборки. \en Constraint system of assembly unit. + mutable ItAssemblyReactor * m_reactor; ///< \ru Обработчик события, связанные с решением сборки. \en The event handles related to solving the assembly. + +protected: + /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. + explicit MbAssembly( const MbAssembly & init, MbRegDuplicate * iReg ); + +public: + /// \ru Конструктор пустой сборки. \en Construct an empty assembly. + MbAssembly(); + /// \ru Конструктор по объекту. \en The constructor by an object. + explicit MbAssembly( MbItem & ); + /// \ru Конструктор по объектам в локальной системе координат. \en The constructor by objects in a local coordinate system. + template + MbAssembly( const ItemsVector & items ); + // \ru Деструктор. \en Destructor. + virtual ~MbAssembly(); + +public: + VISITING_CLASS( MbAssembly ); + + // \ru Общие функции геометрического объекта \en Common functions of a geometric object + + virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en An object type. + virtual MbSpaceItem & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Создать копию. \en Create a copy. + virtual void Transform( const MbMatrix3D &, MbRegTransform * iReg = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. + virtual void Move( const MbVector3D &, MbRegTransform * iReg = NULL ); // \ru Сдвинуть вдоль вектора. \en Move along a vector. + virtual void Rotate( const MbAxis3D &, double angle, MbRegTransform * iReg = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. + virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Are the objects equal? + virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными? \en Are the objects similar? + virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать объекты равным \en Make the objects equal + virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate distance to point. + virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Добавь свой габарит в куб. \en Add own bounding box to the bounding box. + virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate the bounding box in a local coordinate system. + virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. + + virtual MbProperty & CreateProperty( MbePrompt n ) const; // \ru Создать собственное свойство. \en Create own property. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. + virtual void GetBasisItems( RPArray & ); // \ru Дать базовые объекты. \en Get the basis objects. + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + // \ru Выдать локальную систему координат объектов сборки. \en Get the local coordinate system of assembly items. + virtual bool GetPlacement( MbPlacement3D & ) const; + // \ru Установить локальную систему координат объектов сборки. \en Set coordinate system of assembly items. + virtual bool SetPlacement( const MbPlacement3D & ); + // \ru Перестроить объект по журналу построения. \en Rebuild object according to the history tree. + virtual bool RebuildItem( MbeCopyMode sameShell, RPArray * items, IProgressIndicator * progInd ); + // \ru Создать полигональный объект - упрощенную копию данного объекта. \en Create a polygonal object - a simplified copy of the given object. + // \note \ru В многопоточном режиме выполняется параллельно. \en In multithreaded mode runs in parallel. + virtual MbItem * CreateMesh( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * iReg ) const; + // \ru Добавить полигональную сетку объекта. \en Add a polygonal mesh of the object. + virtual bool AddYourMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; + // \ru Разрезать полигональный объект одной или двумя параллельными плоскостями. \en Cut the polygonal object by one or two parallel planes. + virtual MbItem * CutMesh( const MbPlacement3D & cutPlace, double distance ) const; + // \ru Найти ближайший объект или имя ближайшего объекта. \en Find the closest object or its name. + virtual bool NearestMesh( MbeSpaceType sType, MbeTopologyType tType, MbePlaneType pType, + const MbAxis3D & axis, double maxDistance, bool gridPriority, double & t, double & dMin, + MbItem *& find, SimpleName & findName, + MbRefItem *& element, SimpleName & elementName, + MbPath & path, MbMatrix3D & from ) const; + // \ru Дать все объекты указанного типа. \en Get all objects by type. \~ + virtual bool GetItems( MbeSpaceType type, const MbMatrix3D & from, + RPArray & items, SArray & matrs ); + // \ru Дать все полигональные объекты, отображающие геометрические элементы, участвующие в геометрических огриничениях.\en Get all polygonal objects for drawing the elements participated in geometric constraints. \~ + bool GetConstraintMesh( std::vector & meshes ) const; + // \ru Дать все уникальные объекты указанного типа. \en Get all unique objects by type . \~ + virtual bool GetUniqItems( MbeSpaceType type, CSSArray & items ) const; + // \ru Дать объект по его пути положения в модели и матрицу преобразования объекта в глобальную систему координат. \en Get the object by its path in the model and get the matrix of transformation of the object to the global coordinate system. + virtual const MbItem * GetItemByPath( const MbPath & path, size_t ind, MbMatrix3D & from, size_t currInd = 0 ) const; + // \ru Найти объект по геометрическому объекту (MbSpaceItem). \en Find the object by a geometric object (MbSpaceItem). + virtual const MbItem * FindItem( const MbSpaceItem * s, MbPath & path, MbMatrix3D & from ) const; + // \ru Найти объект по геометрическому объекту (MbPlaneItem). \en Find the object by a geometric object (MbSpaceItem). + virtual const MbItem * FindItem( const MbPlaneItem * s, MbPath & path, MbMatrix3D & from ) const; + // \ru Найти объект и матрицу его преобразования в глобальную систему координат. \en Find the object and the matrix of its transformation to the global coordinate system. + virtual const MbItem * FindItem( const MbItem * s, MbPath & path, MbMatrix3D & from ) const; + // \ru Дать объект с заданным именем и матрицу его преобразования в глобальную систему координат. \en Get the object with the specified name and the matrix of its transformation to the global coordinate system. + virtual const MbItem * GetItemByName( SimpleName n, MbPath & path, MbMatrix3D & from ) const; + + // \ru Преобразовать согласно матрице c использованием регистратора селектированные содержимые объекты. \en Transform selected objects according to the matrix using the registrator. + virtual void TransformSelected( const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); + // \ru Сдвинуть вдоль вектора с использованием регистратора селектированные содержимые объекты. \en Move selected objects along the vector using the registrator. + virtual void MoveSelected( const MbVector3D & to, MbRegTransform * iReg = NULL ); + // \ru Повернуть вокруг оси на заданный угол с использованием регистратора селектированные содержимые объекты. \en Rotate selected objects about the axis by the given angle using the registrator. + virtual void RotateSelected( const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); + /// \ru Отдать селектированные содержимые объекты. \en Get selected objects. + bool DetachSelected( RPArray & items, SArray & matrs, bool selected = true ); + /// \ru Отцепить все видимые или невидимые объекты. \en Detach all visible or invisible objects. \~ + bool DetachInvisible( RPArray & items, SArray & matrs, bool invisible = true ); + /// \ru Отцепить все объекты с указанным свойством. \en Detach all objects with pointed attribute. \~ + bool DetachByAttribute( RPArray & items, SArray & matrs, int attribute ); + /** \brief \ru Алгоритм общего назначения для обхода дерева модели в глубину. + \en General-purpose algorithm traversing the model graph in depth. */ + void Traverse( ItModelVisitor & ) const; + +public: + /** \ru \name Функции сборочной единицы. + \en \name The assembly unit functions. + \{ */ + /// \ru Выдать непосредственный объект сборки по идентификатору. \en Get the immediate item of assembly by identifier. + const MbItem * SubItem( SimpleName n ) const { return _ItemByName(n); } + /// \ru Добавить объект в сборку. \en Add an item to the assembly. + MbItem * AddItem( MbItem & item ); + /** + \brief \ru Добавить вставку геометрического объекта. + \en Add an instance of the geometric object. \~ + \param item - \ru Источник, на котором основан экземпляр вставки. + \en A source item on which the instance is based. + \param lcs - \ru Локальная система координат экземпляра вставляемого объекта. + \en Local coordinate system of the instanced object. \~ + \return \ru Экземпляр класса MbInstance, размещающего объект в пространстве сборки. + \en An Instance of class MbInstance placing the item in the space of the assembly. + */ + MbItem * AddInstance( MbItem & item, const MbPlacement3D & lcs ); + /** \brief \ru Заменить объект. + \en Replace an item. \~ + \details \ru Заменить объект новым. + \en Replace an item by a new one. \~ + \param[in] item - \ru Заменяемый объект. + \en An item to be replaced. \~ + \param[in] newItem - \ru Новый объект. + \en A new item. \~ + \return \ru Возвращает true, если замена была выполнена. + \en Returns true if the replacement has been performed. \~ + */ + bool ReplaceItem( const MbItem & item, MbItem & newItem, bool saveName = false ); + + /// \ru Выдать все объекты. \en Get all the items. + void GetItems( RPArray & items ) const; + /// \ru Выдать все объекты. \en Get all the items. + void GetItems( RPArray & items ); + + /// \ru Отцепить объект по индексу. \en Detach the item by index. + MbItem* DetachItem ( size_t ind ); + /// \ru Отцепить объект, если такой есть в сборке. \en Detach the item if it belongs to the assembly. + bool DetachItem ( MbItem * obj ); + /// \ru Удалить объект, если такой есть в сборке или в подсборках. \en Delete the item if it belongs to the assembly or its sub-assemblies. + bool DeleteItem ( MbItem * obj ); + /// \ru Удалить все объекты сборки. \en Delete all the assembly items. + void DeleteItems(); + /// \ru Выдать количество объектов сборки. \en Get the assembly item count. + size_t ItemsCount() const { return assemblyItems.size(); } + /// \ru Вернуть true, если сборка не содержит геометрических объекты. \en Return true, if the assembly has no geometric objects. + bool IsEmpty() const { return assemblyItems.empty(); } + /// \ru Выдать объект по индексу. \en Get the item by index. + const MbItem * GetItem( size_t i ) const; + /// \ru Выдать объект по индексу для модификации. \en Get the item by index for modification. + MbItem * SetItem( size_t i ); + /// \ru Содержит ли сборка присланный объект? \en Does the assembly contain the given item? + bool ContainsItem( const MbItem * obj ) const; + /// \ru Вычислить габарит сборки. \en Calculate the bounding box of the assembly. + void CalculateGabarit( MbCube & cube ) const; + /// \ru Выдать количество граней. \en Get the number of faces. + size_t GetFacesCount() const; + /// \ru Заполнить контейнер гранями тела. \en Fill container by solid faces. + template + void GetFacesSet( FacesVector & faces ) const; +public: + /** \} + \ru \name Функции системы ограничений. + \en \name The constraint system functions. + \{ */ + /// \ru Добавить ограничение для пары геометрических объектов. \en Add geometric constraint. + MtGeomConstraint AddConstraint( MtMateType, const MtGeomArgument &, const MtGeomArgument &, MtParVariant = MtParVariant::undef ); + /// \ru Изменить значение управляющего размера. \en Change the value of driving dimension. + MtResultCode3D ChangeDimension( MtGeomConstraint & dimCon, double newVal ); + /// \ru Решить ограничения сборки. \en Evaluate constraints. + MtResultCode3D EvaluateConstraints(); + /// \ru Выдать диапазон итераторов для обхода всех ограничений сборки. \en Get a range of iterators to traverse all assembly constraints. + void GetConstraints( MtConstraintIter & begIter, MtConstraintIter & endIter ) const; + /// \ru Задать или сбросить обработчик событий решателя. \en Set or reset an handler of constraint solving events. + void SetReactor( ItAssemblyReactor * ) const; + /// \ru Импортировать систему ограничений из приложения САПР. \ru Import the constraint system from CAD application. + bool Import( ItAssemblyImportData & ); + +public: + /** \} */ + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbAssembly ); + + static const MbPlacement3D & GetPlacement() { return MbPlacement3D::global; } // This function is deprecated. Use MbInstanse to give the assembly its own placement. + + friend class MbModelTreeReader; + +private: + // \ru Инициализатор по массиву составляющих объектов. // \en Initializer to aggregate items in the assembly. + template + void _Init( const ItemsVector & ); + // Найти объект по геометрическому объекту + template + const MbItem * _FindItem( const ItemType * s, MbPath & path, MbMatrix3D & from ) const; + // Поиск в глубину среди подчиненных + template + const MbItem * _FindRecursively( const ItemType * s, MbPath & path, MbMatrix3D & from ) const; + // \ru Выдать объект по идентификатору. \en Get the item by identifier. + const MbItem * _ItemByName( SimpleName ) const; + // \ru Генерация имени для нового элемента сборки. \en Generate identifier for new assembly item. + SimpleName _NewItemName() const; + /// \ru Добавить в сборку объекты сборки без трансформации. \en Add assembly items to the assembly without transformation. + bool _AddAssemblyItems( MbAssembly & ); + + OBVIOUS_PRIVATE_COPY( MbAssembly ); +}; // MbAssembly + +IMPL_PERSISTENT_OPS( MbAssembly ) + + +//---------------------------------------------------------------------------------------- +// Экспериментальный посетитель дерева модели +/* + Возможные применения: + - Сбор любых данных об/из иерархии модели; + - Загрузка подсборок и вставок в утилиту поиска соударений (MbCollisionDetectionUtility); + - Геометрический поиск с выдачей маршрута(MbPath) и матрицу отображения МСК вставок и подсборок; + - Восстановление текущей матрицы и маршрута MbPath по hash-коду ссылок в системе + геометрических ограничений; +*/ +//--- +struct ItModelVisitor +{ +public: + virtual void VisitItem( const MbItem * ) = 0; + virtual void FinishItem( const MbItem * ) = 0; + virtual bool ExamineSubItem( const MbRefItem * owner, const MbItem * subItem ) = 0; + virtual void ExamineInstance( const MbInstance * inst, const MbItem * srcItem ) = 0; +}; + +//---------------------------------------------------------------------------------------- +// Алгоритм общего назначения для обхода дерева модели в глубину. +// General-purpose algorithm traversing the model graph in depth. +//--- +MATH_FUNC(void) Traverse( const MbItem *, ItModelVisitor & ); + +//---------------------------------------------------------------------------------------- +// \ru Конструктор по объектам. \en The constructor by objects. +//--- +template +MbAssembly::MbAssembly( const ItemsVector & items ) + : MbItem() + , assemblyItems() + , constraintSystem( NULL ) + , m_reactor( NULL ) +{ +#ifdef C3D_DEBUG + // Check a condition of the single owner. + for ( size_t i = 0, iCount = items.size(); i < iCount; ++i ) + { + if ( items[i]->GetItemName() != c3d::UNDEFINED_SNAME ) + { + C3D_ASSERT_UNCONDITIONAL( false ); // The item has already a name. It's probably means that the item is owned another assembly. + break; + } + } +#endif // C3D_DEBUG + + _Init( items ); +} + + +//---------------------------------------------------------------------------------------- +// \ru Инициализатор по массиву составляющих объектов. +// \en Initializer to aggregate items in the assembly. +//--- +template +void MbAssembly::_Init( const ItemsVector & items ) +{ + C3D_ASSERT( assemblyItems.empty() && (constraintSystem == NULL) ); + SimpleName idCounter = 0; + + for ( size_t i = 0, iCount = items.size(); i < iCount; ++i ) + { + if ( MbItem * item = items[i] ) { + if ( item->GetItemName() == c3d::UNDEFINED_SNAME ) { + item->SetItemName( idCounter ); + } + else { + C3D_ASSERT( idCounter <= item->GetItemName() ); + idCounter = max_of( idCounter, item->GetItemName() ); + } + ++idCounter; + item->AddRef(); + assemblyItems.push_back( item ); + } + } +} + + +//---------------------------------------------------------------------------------------- +// \ru Заполнить контейнер гранями тела. \en Fill container by solid faces. +//--- +template +void MbAssembly::GetFacesSet( FacesVector & faces ) const +{ + for ( size_t i = assemblyItems.size(); i--; ) + { + if ( const MbItem * assemblyItem = assemblyItems[i] ) + { + if ( assemblyItem->IsA() == st_Solid ) + static_cast(*assemblyItem).GetFacesSet( faces ); + else if ( assemblyItem->IsA() == st_Instance ) + static_cast(*assemblyItem).GetFacesSet( faces ); + else if ( assemblyItem->IsA() == st_Assembly ) + static_cast(*assemblyItem).GetFacesSet( faces ); + } + } +} + +//---------------------------------------------------------------------------------------- +// \ru Заполнить контейнер гранями тела. \en Fill container by solid faces. +//--- +template +void MbInstance::GetFacesSet( FacesVector & faces ) const +{ + if ( item != NULL ) { + if ( item->IsA() == st_Solid ) + static_cast( *item ).GetFacesSet( faces ); + else if ( item->IsA() == st_Assembly ) + static_cast( *item ).GetFacesSet( faces ); + else if ( item->IsA() == st_Instance ) + static_cast( *item ).GetFacesSet( faces ); + } +} + + +#endif // __ASSEMBLY_H diff --git a/C3d/Include/assisting_item.h b/C3d/Include/assisting_item.h index 34f3a39..73fa9df 100644 --- a/C3d/Include/assisting_item.h +++ b/C3d/Include/assisting_item.h @@ -1,100 +1,100 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Вспомогательный объект геометрической модели. - \en Assisting item of the geometric model. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ASSISTING_ITEM_H -#define __ASSISTING_ITEM_H - - -#include -#include -#include -#include - - -class MATH_CLASS MbCube; -class MATH_CLASS MbProperties; -class MATH_CLASS MbMesh; - - -//------------------------------------------------------------------------------ -/** \brief \ru Вспомогательный объект геометрической модели. - \en Assisting item of the geometric model. \~ - \details \ru Вспомогательный объект позволяет использовать в геометрической модели такие объекты, - как локальная система координат, ось, матрица преобразования для позиционирования других объектов.\n - \en The assisting item allows to use such an objects in a geometric model - as a local coordinate system, axis, transformation matrix for the other objects location.\n \~ - \ingroup Model_Items -*/ -// --- -class MATH_CLASS MbAssistingItem : public MbItem { -protected : - MbPlacement3D place; ///< \ru Локальная система координат. \en Local coordinate system. - -protected : - /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. - explicit MbAssistingItem( const MbAssistingItem &, MbRegDuplicate * ); -public : - /// \ru Конструктор по локальной системе координат. \en Constructor by a local coordinate system. - MbAssistingItem( const MbPlacement3D & ); -public : - /// \ru Деструктор. \en Destructor. - virtual ~MbAssistingItem(); - -public : - VISITING_CLASS( MbAssistingItem ); - - // \ru Общие функции геометрического объекта. \en Common functions of a geometric object. - virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en An object type. - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Создать копию. \en Create a copy. - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвинуть вдоль вектора. \en Move along a vector. - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Whether the objects are equal? - virtual bool IsSimilar( const MbSpaceItem & init ) const; // \ru Являются ли объекты подобными? \en Whether the objects are similar? - virtual bool SetEqual ( const MbSpaceItem & init ); // \ru Сделать объекты равным. \en Make the objects equal. - virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate distance to point. - virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Добавь свой габарит в куб. \en Add own bounding box to the bounding box. - virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate the bounding box in a local coordinate system. - virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. - - virtual MbProperty & CreateProperty( MbePrompt name ) const; // \ru Создать собственное свойство. \en Create own property. - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & properties ); // \ru Установить свойства объекта. \en Set properties of the object. - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - /// \ru Получить систему координат объекта. \en Get the coordinate system of an item. - virtual bool GetPlacement( MbPlacement3D & ) const; - /// \ru Установить систему координат объекта. \en Set the coordinate system of an item. - virtual bool SetPlacement( const MbPlacement3D & p ); - - // \ru Создать полигональный объект - упрощенную копию данного объекта. \en Create a polygonal object - a simplified copy of the given object. - virtual MbItem * CreateMesh( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * iReg ) const; - - /// \ru Дать матрицу преобразования из локальной системы объекта. \en Get transform matrix from local coordinate system of object. - virtual bool GetMatrixFrom( MbMatrix3D & from ) const; - /// \ru Дать матрицу преобразования в локальную систему объекта. \en Get transform matrix into local coordinate system of object. - virtual bool GetMatrixInto( MbMatrix3D & into ) const; - - /** \ru \name Функции вспомогательного объекта. - \en \name Functions of assisting item. - \{ */ - /// \ru Выдать систему координат объекта. \en Get the coordinate system of an item. - const MbPlacement3D & GetPlacement() const { return place; } - /// \ru Выдать систему координат объекта для редактирования. \en Get the coordinate system of an item for editing. - MbPlacement3D & SetPlacement() { return place; } - /** \} */ - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbAssistingItem ) -OBVIOUS_PRIVATE_COPY( MbAssistingItem ) -}; - -IMPL_PERSISTENT_OPS( MbAssistingItem ) - -#endif // __ASSISTING_ITEM_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Вспомогательный объект геометрической модели. + \en Assisting item of the geometric model. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ASSISTING_ITEM_H +#define __ASSISTING_ITEM_H + + +#include +#include +#include +#include + + +class MATH_CLASS MbCube; +class MATH_CLASS MbProperties; +class MATH_CLASS MbMesh; + + +//------------------------------------------------------------------------------ +/** \brief \ru Вспомогательный объект геометрической модели. + \en Assisting item of the geometric model. \~ + \details \ru Вспомогательный объект позволяет использовать в геометрической модели такие объекты, + как локальная система координат, ось, матрица преобразования для позиционирования других объектов.\n + \en The assisting item allows to use such an objects in a geometric model + as a local coordinate system, axis, transformation matrix for the other objects location.\n \~ + \ingroup Model_Items +*/ +// --- +class MATH_CLASS MbAssistingItem : public MbItem { +protected : + MbPlacement3D place; ///< \ru Локальная система координат. \en Local coordinate system. + +protected : + /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. + explicit MbAssistingItem( const MbAssistingItem &, MbRegDuplicate * ); +public : + /// \ru Конструктор по локальной системе координат. \en Constructor by a local coordinate system. + MbAssistingItem( const MbPlacement3D & ); +public : + /// \ru Деструктор. \en Destructor. + virtual ~MbAssistingItem(); + +public : + VISITING_CLASS( MbAssistingItem ); + + // \ru Общие функции геометрического объекта. \en Common functions of a geometric object. + virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en An object type. + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Создать копию. \en Create a copy. + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвинуть вдоль вектора. \en Move along a vector. + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Whether the objects are equal? + virtual bool IsSimilar( const MbSpaceItem & init ) const; // \ru Являются ли объекты подобными? \en Whether the objects are similar? + virtual bool SetEqual ( const MbSpaceItem & init ); // \ru Сделать объекты равным. \en Make the objects equal. + virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate distance to point. + virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Добавь свой габарит в куб. \en Add own bounding box to the bounding box. + virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate the bounding box in a local coordinate system. + virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. + + virtual MbProperty & CreateProperty( MbePrompt name ) const; // \ru Создать собственное свойство. \en Create own property. + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & properties ); // \ru Установить свойства объекта. \en Set properties of the object. + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + /// \ru Получить систему координат объекта. \en Get the coordinate system of an item. + virtual bool GetPlacement( MbPlacement3D & ) const; + /// \ru Установить систему координат объекта. \en Set the coordinate system of an item. + virtual bool SetPlacement( const MbPlacement3D & p ); + + // \ru Создать полигональный объект - упрощенную копию данного объекта. \en Create a polygonal object - a simplified copy of the given object. + virtual MbItem * CreateMesh( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * iReg ) const; + + /// \ru Дать матрицу преобразования из локальной системы объекта. \en Get transform matrix from local coordinate system of object. + virtual bool GetMatrixFrom( MbMatrix3D & from ) const; + /// \ru Дать матрицу преобразования в локальную систему объекта. \en Get transform matrix into local coordinate system of object. + virtual bool GetMatrixInto( MbMatrix3D & into ) const; + + /** \ru \name Функции вспомогательного объекта. + \en \name Functions of assisting item. + \{ */ + /// \ru Выдать систему координат объекта. \en Get the coordinate system of an item. + const MbPlacement3D & GetPlacement() const { return place; } + /// \ru Выдать систему координат объекта для редактирования. \en Get the coordinate system of an item for editing. + MbPlacement3D & SetPlacement() { return place; } + /** \} */ + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbAssistingItem ) +OBVIOUS_PRIVATE_COPY( MbAssistingItem ) +}; + +IMPL_PERSISTENT_OPS( MbAssistingItem ) + +#endif // __ASSISTING_ITEM_H diff --git a/C3d/Include/attr_color.h b/C3d/Include/attr_color.h index 0b57bab..2205b3a 100644 --- a/C3d/Include/attr_color.h +++ b/C3d/Include/attr_color.h @@ -11,11 +11,11 @@ #define __ATTR_COLOR_H -#include +#include #include -#define __RGB__ 3 +const_expr uint __RGB__ = 3; //------------------------------------------------------------------------------ @@ -44,6 +44,33 @@ inline uint32 RGB2uint32( double r, double g, double b ) } +//------------------------------------------------------------------------------ +/** \brief \ru Преобразовать цвет по трём компонентам в uint32. + \en Convert a color by 3 components in uint32. \~ + \details + \warning \ru Значения компонент цвета должны лежать в диапазоне [ 0; 1 ]. + \en Values of color components should belong to the range [ 0; 1 ]. \~ + \ingroup Model_Attributes +*/ +// --- +inline uint32 RGB2uint32( float r, float g, float b, float a ) +{ + const float f1 = 255.0 / 256.0; + uint32 uinturgb[4]; + const uint32 bt = 256; + uinturgb[0] = uint32 ( 256.0 * r * f1 ); + uinturgb[1] = uint32 ( 256.0 * g * f1 ); + uinturgb[2] = uint32 ( 256.0 * b * f1 ); + uinturgb[3] = uint32 ( 256.0 * a * f1 ); + for ( int n = 0; n < 4; n++ ) + if ( uinturgb[n] >= bt ) { + uinturgb[n] = bt - 1; + //C3D_ASSERT_UNCONDITIONAL( false ); + } + return uinturgb[0] + bt * ( uinturgb[1] + bt * ( uinturgb[2] + bt * uinturgb[3] ) ); +} + + //------------------------------------------------------------------------------ /** \brief \ru Преобразовать unit32 в три компоненты цвета. \en Convert unit32 to 3 components of color. \~ @@ -64,6 +91,72 @@ void uint322RGB( uint32 color, float_t& r, float_t& g, float_t& b ) { } +//------------------------------------------------------------------------------ +/** \brief \ru Преобразовать цвет из модели HSV в uint32. + \en Convert a color from HSV model in uint32. \~ + \details \ru Преобразовать цвет из модели HSV в uint32. \n + \en Convert a color from HSV model in uint32. \n \~ + \ingroup Model_Attributes +*/ +// --- +inline +uint32 HSV2uint32( double h, double s, double v ) +{ + double hh, p, q, t, ff; + long i; + double r, g, b; + if ( s <= 0.0 ) { + r = v; + g = v; + b = v; + return ::RGB2uint32( r, g, b ); + } + hh = h; + if ( hh >= 360.0 ) + hh = 0.0; + hh /= 60.0; + i = (long)hh; + ff = hh - i; + p = v * (1.0 - s); + q = v * (1.0 - (s * ff)); + t = v * (1.0 - (s * (1.0 - ff))); + + switch ( i ) { + case 0 : { + r = v; + g = t; + b = p; + } break; + case 1 : { + r = q; + g = v; + b = p; + } break; + case 2 : { + r = p; + g = v; + b = t; + } break; + case 3 : { + r = p; + g = q; + b = v; + } break; + case 4 : { + r = t; + g = p; + b = v; + } break; + default : { + r = v; + g = p; + b = q; + } break; + } + return ::RGB2uint32( r, g, b ); +} + + //------------------------------------------------------------------------------ /** \brief \ru Цвет. \en Color. \~ @@ -243,7 +336,7 @@ public : /// \ru Установить свойства для OpenGL. \en Set properties for OpenGL. void Init( float a = MB_AMBIENT, float d = MB_DIFFUSE, float s = MB_SPECULARITY, - float h = MB_SHININESS, float t = MB_OPACITY, float e = MB_EMISSION, uint rgb = 0 ) { + float h = MB_SHININESS, float t = MB_OPACITY, float e = MB_EMISSION, uint rgb = 0 ) { ambient[rgb%__RGB__] = a; // \ru Коэффициент общего фона. \en Coefficient of ambient background. diffuse[rgb%__RGB__] = d; // \ru Коэффициент диффузного отражения. \en Coefficient of diffuse reflection. specularity[rgb%__RGB__] = s; // \ru Коэффициент зеркального отражения света. \en Coefficient of specular reflection for light. diff --git a/C3d/Include/attr_common_attribute.h b/C3d/Include/attr_common_attribute.h new file mode 100644 index 0000000..ae22d14 --- /dev/null +++ b/C3d/Include/attr_common_attribute.h @@ -0,0 +1,302 @@ +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Подтип обобщенные атрибуты. + \en Common attributes subtype. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ATTR_COMMON_ATTRIBUE_H +#define __ATTR_COMMON_ATTRIBUE_H + + +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Обобщенный атрибут - базовый класс. + \en Common attribute - the base class. \~ + \details \ru Обобщенный атрибут - базовый класс. \n + \en Common attribute - the base class. \n \~ + \ingroup Model_Attributes + */ +class MATH_CLASS MbCommonAttribute : public MbAttribute { +protected : + c3d::string_t prompt_; ///< \ru Строка описания. \en String of description. + bool changeable; ///< \ru Признак редактируемости. \en Attribute of editability. + +protected : + /// \ru Конструктор. \en Constructor. + MbCommonAttribute( const c3d::string_t & prompt, const bool change ); + /// \ru Конструктор. \en Constructor. + explicit MbCommonAttribute( const bool change ); + /// \ru Деструктор. \en Destructor. + virtual ~MbCommonAttribute(); + +public : + virtual MbeAttributeType AttributeFamily() const; // \ru Выдать тип атрибута. \en Get attribute type. + virtual MbeAttributeType AttributeType() const = 0; // \ru Выдать подтип атрибута. \en Get subtype of an attribute. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const = 0; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ) = 0; // \ru Инициализировать данные по присланным. \en Initialize data. + + // \ru Выполнить действия при изменении владельца, не связанное с другими действиями. \en Perform actions which are not associated with other actions when changing the owner. + virtual void OnChangeOwner( const MbAttributeContainer & owner ); + // \ru Выполнить действия при конвертации владельца. \en Perform actions when converting the owner. + virtual void OnConvertOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // \ru Выполнить действия при трансформировании владельца. \en Perform actions when transforming the owner. + virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D &, MbRegTransform * = NULL ); + // \ru Выполнить действия при перемещении владельца. \en Perform actions when moving the owner. + virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D &, MbRegTransform * = NULL ); + // \ru Выполнить действия при вращении владельца. \en Perform actions when rotating the owner. + virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D &, double angle, MbRegTransform * = NULL ); + // \ru Выполнить действия при копировании владельца. \en Perform actions when copying the owner. + virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * = NULL ); + // \ru Выполнить действия при объединении владельца. \en Perform actions when merging he owner. + virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // \ru Выполнить действия при замене владельца. \en Perform actions when replacing the owner. + virtual void OnReplaceOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // \ru Выполнить действия при разделении владельца. \en Perform actions when splitting the owner. + virtual void OnSplitOwner( const MbAttributeContainer & owner, const std::vector & others ); + // \ru Выполнить действия при удалении владельца. \en Perform actions when deleting the owner. + virtual void OnDeleteOwner( const MbAttributeContainer & owner ); + + virtual void GetCharValue( TCHAR * v ) const = 0; // \ru Выдать строковое значение свойства. \en Get a string value of the property. + virtual void GetProperties( MbProperties & ) = 0; // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ) = 0; // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + + /** \brief \ru Выдать подсказку атрибута. \en Get a prompt of attribute. + \details \ru Строковое значение, которое может быть использовано, как совего рода тэг, имя или пометка атрибута. + \en String value which can be used as some kind of tag, name or label of an attribute. + */ + const c3d::string_t & GetPrompt() const; + /// \ru Выдать признак изменяемости. \en Get an attribute of changeability. + bool IsChangeable() const; + +DECLARE_PERSISTENT_CLASS( MbCommonAttribute ) +OBVIOUS_PRIVATE_COPY( MbCommonAttribute ) +}; + +IMPL_PERSISTENT_OPS( MbCommonAttribute ) + +//------------------------------------------------------------------------------ +/** \brief \ru bool атрибут. + \en Bool attribute. \~ + \details \ru bool атрибут. \n + \en Bool attribute. \n \~ + \ingroup Model_Attributes + */ +class MATH_CLASS MbBoolAttribute : public MbCommonAttribute { +private: + bool value_; ///< \ru Значение. \en The value. + +public: + /// \ru Конструктор. \en Constructor. + explicit MbBoolAttribute( const c3d::string_t & prompt, bool change, bool initValue ); + /// \ru Деструктор. \en Destructor. + virtual ~MbBoolAttribute(); + +public: + virtual MbeAttributeType AttributeType() const; // \ru Выдать подтип атрибута. \en Get subtype of an attribute. + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get a string value of the property. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + + bool GetValue() const; // \ru Выдать значение свойства. \en Get a value of the property. + bool SetValue( bool val ); // \ru Установить новое значение свойства. \en Set new value of the property. + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbBoolAttribute ) +OBVIOUS_PRIVATE_COPY( MbBoolAttribute ) +}; + +IMPL_PERSISTENT_OPS( MbBoolAttribute ) + +//------------------------------------------------------------------------------ +/** \brief \ru int атрибут. + \en Int attribute. \~ + \details \ru int атрибут. \n + \en Int attribute. \n \~ + \ingroup Model_Attributes +*/ +class MATH_CLASS MbIntAttribute : public MbCommonAttribute { +private: + int value_; ///< \ru Значение. \en The value. + +public: + /// \ru Конструктор. \en Constructor. + explicit MbIntAttribute( const c3d::string_t & prompt, bool change, int initValue ); + /// \ru Деструктор. \en Destructor. + virtual ~MbIntAttribute(); + +public: + virtual MbeAttributeType AttributeType() const; // \ru Выдать подтип атрибута. \en Get subtype of an attribute. + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get a string value of the property. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + + int GetValue() const; // \ru Выдать значение свойства. \en Get a value of the property. + bool SetValue( int val ); // \ru Установить новое значение свойства. \en Set new value of the property. + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbIntAttribute ) +OBVIOUS_PRIVATE_COPY( MbIntAttribute ) +}; + +IMPL_PERSISTENT_OPS( MbIntAttribute ) + +//------------------------------------------------------------------------------ +/** \brief \ru int64 атрибут. + \en Int64 attribute. \~ + \details \ru int64 атрибут. \n + \en Int64 attribute. \n \~ + \ingroup Model_Attributes +*/ +class MATH_CLASS MbInt64Attribute : public MbCommonAttribute { +private: + int64 value_; ///< \ru Значение. \en The value. + +public: + /// \ru Конструктор. \en Constructor. + explicit MbInt64Attribute( const c3d::string_t & prompt, bool change, int64 initValue ); + /// \ru Деструктор. \en Destructor. + virtual ~MbInt64Attribute(); + +public: + virtual MbeAttributeType AttributeType() const; // \ru Выдать подтип атрибута. \en Get subtype of an attribute. + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get a string value of the property. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + + int64 GetValue() const; // \ru Выдать значение свойства. \en Get a value of the property. + bool SetValue( int64 val ); // \ru Установить новое значение свойства. \en Set new value of the property. + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbInt64Attribute ) +OBVIOUS_PRIVATE_COPY( MbInt64Attribute ) +}; + +IMPL_PERSISTENT_OPS( MbInt64Attribute ) + +//------------------------------------------------------------------------------ +/** \brief \ru double атрибут. + \en Double attribute. \~ + \details \ru double атрибут. \n + \en Double attribute. \n \~ + \ingroup Model_Attributes + */ +class MATH_CLASS MbDoubleAttribute : public MbCommonAttribute { +private: + double value_; ///< \ru Значение. \en The value. + +public: + /// \ru Конструктор. \en Constructor. + explicit MbDoubleAttribute( const c3d::string_t & prompt, bool change, double initValue ); + /// \ru Деструктор. \en Destructor. + virtual ~MbDoubleAttribute(); + +public: + virtual MbeAttributeType AttributeType() const; // \ru Выдать подтип атрибута. \en Get subtype of an attribute. + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get a string value of the property. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + + double GetValue() const; // \ru Выдать значение свойства. \en Get a value of the property. + bool SetValue( double val ); // \ru Установить новое значение свойства. \en Set new value of the property. + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbDoubleAttribute ) +OBVIOUS_PRIVATE_COPY( MbDoubleAttribute ) +}; + +IMPL_PERSISTENT_OPS( MbDoubleAttribute ) + +//------------------------------------------------------------------------------ +/** \brief \ru String атрибут. + \en String attribute. \~ + \details \ru String атрибут. \n + \en String attribute. \n \~ + \ingroup Model_Attributes + */ +class MATH_CLASS MbStringAttribute : public MbCommonAttribute { +private: + c3d::string_t value_; ///< \ru Значение. \en The value. + +public: + /// \ru Конструктор. \en Constructor. + explicit MbStringAttribute( const c3d::string_t & prompt, bool change, const c3d::string_t & string ); + +public: + virtual MbeAttributeType AttributeType() const; // \ru Выдать подтип атрибута. \en Get subtype of an attribute. + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get a string value of the property. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + + c3d::string_t GetValue() const; // \ru Выдать значение свойства. \en Get a value of the property. + bool SetValue( c3d::string_t & val ); // \ru Установить новое значение свойства. \en Set new value of the property. + +protected: + virtual ~MbStringAttribute(); // Use AddRef/Release or smart pointer SPtr to destruct it correctly. + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbStringAttribute ) +OBVIOUS_PRIVATE_COPY( MbStringAttribute ) +}; + +IMPL_PERSISTENT_OPS( MbStringAttribute ) + +//------------------------------------------------------------------------------ +/** \brief \ru Бинарный атрибут. + \en Binary attribute. \~ + \details \ru Бинарный атрибут. \n + \en Binary attribute. \n \~ + \ingroup Model_Attributes +*/ +class MATH_CLASS MbBinaryAttribute : public MbCommonAttribute { +private: + std::vector value_; ///< \ru Значение. \en The value. + +public: + /// \ru Конструктор. \en Constructor. + explicit MbBinaryAttribute( const c3d::string_t & prompt, bool change, const std::vector & value ); + +public: + virtual MbeAttributeType AttributeType() const; // \ru Выдать подтип атрибута. \en Get subtype of an attribute. + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get a string value of the property. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + + std::vector GetValue() const; // \ru Выдать значение свойства. \en Get a value of the property. + bool SetValue( std::vector & val ); // \ru Установить новое значение свойства. \en Set new value of the property. + +protected: + virtual ~MbBinaryAttribute(); // Use AddRef/Release or smart pointer SPtr to destruct it correctly. + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbBinaryAttribute ) +OBVIOUS_PRIVATE_COPY( MbBinaryAttribute ) +}; + +IMPL_PERSISTENT_OPS( MbBinaryAttribute ) + +#endif // __ATTR_COMMON_ATTRIBUE_H diff --git a/C3d/Include/attr_dencity.h b/C3d/Include/attr_dencity.h index e4738af..025963d 100644 --- a/C3d/Include/attr_dencity.h +++ b/C3d/Include/attr_dencity.h @@ -1,163 +1,163 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Атрибуты. Плотность. - \en Attributes. Density. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ATTR_DENCITY_H -#define __ATTR_DENCITY_H - - -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Плотность. - \en Density. \~ - \details \ru Плотность. \n - \en Density. \n \~ - \ingroup Model_Attributes -*/ -// --- -class MATH_CLASS MbDencity : public MbElementaryAttribute { -protected : - double dencity; ///< \ru Плотность. \en Density. - -protected : - /// \ru Конструктор. \en Constructor. - MbDencity( const MbDencity & init ); -public : - /// \ru Конструктор. \en Constructor. - MbDencity( double init ); - /// \ru Деструктор. \en Destructor. - virtual ~MbDencity(); - - // \ru Общие функции объекта \en Common functions of object. - - virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. - virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. - - /// \ru Установить плотность. \en Set a density. - void Init( double init ) { dencity = init; } - /// \ru Дать плотность. \en Get a density. - double Dencity() const { return dencity; } - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - -private: - void operator = ( const MbDencity & ); // \ru Не реализовано \en Not implemented - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbDencity ) - -}; // MbDencity - -IMPL_PERSISTENT_OPS( MbDencity ) - - -//------------------------------------------------------------------------------ -/** \brief \ru Жесткость. - \en The stiffness. \~ - \details \ru Механические характеристики материала: модуль Юнга и коэффициент Пуассана. \n - \en Mechanical properties of the material: Young's modulus and Poisson's ratio. \n \~ - \ingroup Model_Attributes -*/ -// --- -class MATH_CLASS MbElasticity : public MbElementaryAttribute { -protected : - double young; ///< \ru Модуль Юнга. \en The Young's modulus of material. - double poisson; ///< \ru Коэффициент Пуассона. \en The Poisson's ratio of material. - -protected : - /// \ru Конструктор. \en Constructor. - MbElasticity( const MbElasticity & init ); -public : - /// \ru Конструктор. \en Constructor. - MbElasticity( double e, double v ); - /// \ru Деструктор. \en Destructor. - virtual ~MbElasticity(); - - // \ru Общие функции объекта \en Common functions of object. - - virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. - virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. - - /// \ru Установить свойства. \en Set a density. - void Init( double e_, double v_ ) { young = e_; poisson = v_; } - /// \ru Дать Модуль Юнга. \en Get an Young's modulus. - double YoungModulus() const { return young; } - /// \ru Дать Коэффициент Пуассона. \en Get a Poisson's ratio. - double PoissonRatio() const { return poisson; } - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - -private: - void operator = ( const MbElasticity & ); // \ru Не реализовано \en Not implemented - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbElasticity ) - -}; // MbElasticity - -IMPL_PERSISTENT_OPS( MbElasticity ) - - -//------------------------------------------------------------------------------ -/** \brief \ru Деформации / Напряжения. - \en The strains / The tensions. \~ - \details \ru Напряжённо деформированное состояние объекта - три деформации или три напряжения. \n - \en Tension strain state of an object. \n \~ - \ingroup Model_Attributes -*/ -// --- -class MATH_CLASS MbStrains : public MbElementaryAttribute { -protected : - double strain1; ///< \ru Деформация 1 / Напряжение 1. \en The strain 1 / The tension 1. - double strain2; ///< \ru Деформация 2 / Напряжение 2. \en The strain 2 / The tension 2. - double strain3; ///< \ru Деформация 3 / Напряжение 3. \en The strain 3 / The tension 3. - -protected : - /// \ru Конструктор. \en Constructor. - MbStrains( const MbStrains & init ); -public : - /// \ru Конструктор. \en Constructor. - MbStrains( double e1, double e2, double e3 ); - /// \ru Деструктор. \en Destructor. - virtual ~MbStrains(); - - // \ru Общие функции объекта \en Common functions of object. - - virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. - virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. - - /// \ru Установить свойства. \en Set a density. - void Init( double e1, double e2, double e3 ) { strain1 = e1; strain2 = e2; strain3 = e3; } - /// \ru Дать деформированное состояние объекта. \en Get a deformed state. - double Strain( size_t i ) const { if ( i <= 1 ) return strain1; else if ( i == 2 ) return strain1; else return strain3; } - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - -private: - void operator = ( const MbStrains & ); // \ru Не реализовано \en Not implemented - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbStrains ) - -}; // MbStrains - -IMPL_PERSISTENT_OPS( MbStrains ) - - -#endif // __ATTR_DENCITY_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Атрибуты. Плотность. + \en Attributes. Density. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ATTR_DENCITY_H +#define __ATTR_DENCITY_H + + +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Плотность. + \en Density. \~ + \details \ru Плотность. \n + \en Density. \n \~ + \ingroup Model_Attributes +*/ +// --- +class MATH_CLASS MbDencity : public MbElementaryAttribute { +protected : + double dencity; ///< \ru Плотность. \en Density. + +protected : + /// \ru Конструктор. \en Constructor. + MbDencity( const MbDencity & init ); +public : + /// \ru Конструктор. \en Constructor. + MbDencity( double init ); + /// \ru Деструктор. \en Destructor. + virtual ~MbDencity(); + + // \ru Общие функции объекта \en Common functions of object. + + virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. + + /// \ru Установить плотность. \en Set a density. + void Init( double init ) { dencity = init; } + /// \ru Дать плотность. \en Get a density. + double Dencity() const { return dencity; } + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + +private: + void operator = ( const MbDencity & ); // \ru Не реализовано \en Not implemented + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbDencity ) + +}; // MbDencity + +IMPL_PERSISTENT_OPS( MbDencity ) + + +//------------------------------------------------------------------------------ +/** \brief \ru Жесткость. + \en The stiffness. \~ + \details \ru Механические характеристики материала: модуль Юнга и коэффициент Пуассана. \n + \en Mechanical properties of the material: Young's modulus and Poisson's ratio. \n \~ + \ingroup Model_Attributes +*/ +// --- +class MATH_CLASS MbElasticity : public MbElementaryAttribute { +protected : + double young; ///< \ru Модуль Юнга. \en The Young's modulus of material. + double poisson; ///< \ru Коэффициент Пуассона. \en The Poisson's ratio of material. + +protected : + /// \ru Конструктор. \en Constructor. + MbElasticity( const MbElasticity & init ); +public : + /// \ru Конструктор. \en Constructor. + MbElasticity( double e, double v ); + /// \ru Деструктор. \en Destructor. + virtual ~MbElasticity(); + + // \ru Общие функции объекта \en Common functions of object. + + virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. + + /// \ru Установить свойства. \en Set a density. + void Init( double e_, double v_ ) { young = e_; poisson = v_; } + /// \ru Дать Модуль Юнга. \en Get an Young's modulus. + double YoungModulus() const { return young; } + /// \ru Дать Коэффициент Пуассона. \en Get a Poisson's ratio. + double PoissonRatio() const { return poisson; } + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + +private: + void operator = ( const MbElasticity & ); // \ru Не реализовано \en Not implemented + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbElasticity ) + +}; // MbElasticity + +IMPL_PERSISTENT_OPS( MbElasticity ) + + +//------------------------------------------------------------------------------ +/** \brief \ru Деформации / Напряжения. + \en The strains / The tensions. \~ + \details \ru Напряжённо деформированное состояние объекта - три деформации или три напряжения. \n + \en Tension strain state of an object. \n \~ + \ingroup Model_Attributes +*/ +// --- +class MATH_CLASS MbStrains : public MbElementaryAttribute { +protected : + double strain1; ///< \ru Деформация 1 / Напряжение 1. \en The strain 1 / The tension 1. + double strain2; ///< \ru Деформация 2 / Напряжение 2. \en The strain 2 / The tension 2. + double strain3; ///< \ru Деформация 3 / Напряжение 3. \en The strain 3 / The tension 3. + +protected : + /// \ru Конструктор. \en Constructor. + MbStrains( const MbStrains & init ); +public : + /// \ru Конструктор. \en Constructor. + MbStrains( double e1, double e2, double e3 ); + /// \ru Деструктор. \en Destructor. + virtual ~MbStrains(); + + // \ru Общие функции объекта \en Common functions of object. + + virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. + + /// \ru Установить свойства. \en Set a density. + void Init( double e1, double e2, double e3 ) { strain1 = e1; strain2 = e2; strain3 = e3; } + /// \ru Дать деформированное состояние объекта. \en Get a deformed state. + double Strain( size_t i ) const { if ( i <= 1 ) return strain1; else if ( i == 2 ) return strain1; else return strain3; } + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + +private: + void operator = ( const MbStrains & ); // \ru Не реализовано \en Not implemented + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbStrains ) + +}; // MbStrains + +IMPL_PERSISTENT_OPS( MbStrains ) + + +#endif // __ATTR_DENCITY_H diff --git a/C3d/Include/attr_elementary_attribute.h b/C3d/Include/attr_elementary_attribute.h new file mode 100644 index 0000000..c5fd3bf --- /dev/null +++ b/C3d/Include/attr_elementary_attribute.h @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Подтип элементарные атрибуты. + \en Elementary attributes subtype. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ATTR_ELEMENTARY_ATTRIBUTE_H +#define __ATTR_ELEMENTARY_ATTRIBUTE_H + + +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Элементарный атрибут - базовый класс. + \en Elementary attribute - the base class. \~ + \details \ru Элементарный атрибут - базовый класс. \n + \en Elementary attribute - the base class. \n \~ + \ingroup Model_Attributes + */ +class MATH_CLASS MbElementaryAttribute : public MbAttribute { +protected: + MbElementaryAttribute(); +public: + virtual ~MbElementaryAttribute(); + +public : + virtual MbeAttributeType AttributeFamily() const; // \ru Тип атрибута \en Type of an attribute + virtual MbeAttributeType AttributeType() const = 0; // \ru Выдать подтип атрибута \en Get subtype of an attribute. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const = 0; // \ru Сделать копию элемента \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const = 0; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ) = 0; // \ru Инициализировать данные по присланным \en Initialize data. + + // \ru Действия при изменении владельца, не связанное с другими действиями. \en Actions which are not associated with other actions when changing the owner. + virtual void OnChangeOwner( const MbAttributeContainer & owner ); + // \ru Действия при конвертации владельца. \en Actions when converting the owner. + virtual void OnConvertOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + /// \ru Действия при трансформировании владельца. \en Actions when transforming the owner. + virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D &, MbRegTransform * = NULL ); + // \ru Действия при перемещении владельца. \en Actions when moving the owner. + virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D &, MbRegTransform * = NULL ); + // \ru Действия при вращении владельца. \en Actions when rotating the owner. + virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D &, double angle, MbRegTransform * = NULL ); + // \ru Действия при копировании владельца. \en Actions when copying the owner. + virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * = NULL ); + // \ru Действия при объединении владельца. \en Actions when merging the owner. + virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // \ru Действия при замене владельца. \en Actions when replacing the owner. + virtual void OnReplaceOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // \ru Действия при разделении владельца. \en Actions when splitting the owner. + virtual void OnSplitOwner( const MbAttributeContainer & owner, const std::vector & others ); + // \ru Действия при удалении владельца. \en Actions when merging the owner. + virtual void OnDeleteOwner( const MbAttributeContainer & owner ); + + virtual void GetProperties( MbProperties & ) = 0; // \ru Выдать свойства объекта \en Get properties of the object + virtual size_t SetProperties( const MbProperties & ) = 0; // \ru Установить свойства объекта \en Set properties of object + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property + +DECLARE_PERSISTENT_CLASS( MbElementaryAttribute ) +OBVIOUS_PRIVATE_COPY( MbElementaryAttribute ) +}; + +IMPL_PERSISTENT_OPS( MbElementaryAttribute ) + +#endif // __ATTR_ELEMENTARY_ATTRIBUTE_H diff --git a/C3d/Include/attr_flange_attribute.h b/C3d/Include/attr_flange_attribute.h new file mode 100644 index 0000000..a76278f --- /dev/null +++ b/C3d/Include/attr_flange_attribute.h @@ -0,0 +1,79 @@ +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Атрибут отбортовки листового тела. + \en Swept flange attribute of a sheet solid. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ATTR_FLANGE_ATTRIBUTE_H +#define __ATTR_FLANGE_ATTRIBUTE_H + + +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Атрибут отбортовки листового тела. + \en Swept flange attribute of a sheet solid. \~ + \details \ru . + \en \n \~ + \ingroup Model_Attributes + */ +class MATH_CLASS MbSheetFlangingAttribute : public MbCommonAttribute { +protected : + MbBendByEdgeValues params; ///< \ru Параметры операции. \en The operation parameters. + MbSNameMaker names; ///< \ru Именователь операции. \en An object defining names generation in the operation. + +public : + /// \ru Конструктор. \en Constructor. + MbSheetFlangingAttribute( const MbBendByEdgeValues & pars, const MbSNameMaker & n, const bool changeable ); + /// \ru Конструктор. \en Constructor. + MbSheetFlangingAttribute( const MbBendByEdgeValues & pars, const MbSNameMaker & n, const bool changeable, const c3d::string_t & itemPrompt ); + /// \ru Деструктор. \en Destructor. + virtual ~MbSheetFlangingAttribute(); + +private: + // \ru Конструктор копирования. \en Copy constructor. + MbSheetFlangingAttribute( const MbSheetFlangingAttribute & init, MbRegDuplicate * iReg ); + +public: + // \ru Выдать подтип атрибута. \en Get subtype of an attribute. + virtual MbeAttributeType AttributeType() const; + // \ru Сделать копию элемента. \en Create a copy of the element. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; + // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; + // \ru Инициализировать данные по присланным. \en Initialize data. + virtual bool Init( const MbAttribute & ); + + // \ru Выполнить действия при трансформировании владельца. \en Perform actions when transforming the owner. + virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D &, MbRegTransform * = NULL ); + // \ru Выполнить действия при перемещении владельца. \en Perform actions when moving the owner. + virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D &, MbRegTransform * = NULL ); + // \ru Выполнить действия при вращении владельца. \en Perform actions when rotating the owner. + virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D &, double angle, MbRegTransform * = NULL ); + // \ru Выполнить действия при копировании владельца. \en Perform actions when copying the owner. + virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * = NULL ); + // \ru Выполнить действия при объединении владельца. \en Perform actions when merging the owner. + virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get a string value of the property. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + + /// \ru Дать параметры операции. \en Get operation parameters. + const MbBendByEdgeValues & GetFlangeValues() const { return params; } + /// \ru Дать именователь операции. \en Get an object defining a name of the operation. + const MbSNameMaker & GetNameMaker() const { return names; } + +DECLARE_PERSISTENT_CLASS( MbSheetFlangingAttribute ) +OBVIOUS_PRIVATE_COPY( MbSheetFlangingAttribute ) +}; + +IMPL_PERSISTENT_OPS( MbSheetFlangingAttribute ) + +#endif // __ATTR_FLANGE_ATTRIBUTE_H diff --git a/C3d/Include/attr_geometric_attribute.h b/C3d/Include/attr_geometric_attribute.h new file mode 100644 index 0000000..05968b1 --- /dev/null +++ b/C3d/Include/attr_geometric_attribute.h @@ -0,0 +1,89 @@ +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Геометрический атрибут. + \en Geometric attribute. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ATTR_GEOMETRIC_ATTRIBUTE_H +#define __ATTR_GEOMETRIC_ATTRIBUTE_H + + +#include +#include +#include +#include + + +class MATH_CLASS MbSpaceItem; +class MATH_CLASS MbProperty; +class MATH_CLASS MbProperties; +class MbRegTransform; +class MbRegDuplicate; + + +//------------------------------------------------------------------------------ +/** \brief \ru Геометрический атрибут. + \en Geometric attribute. \~ + \details \ru Геометрический атрибут. \n + \en Geometric attribute. \n \~ + \ingroup Model_Attributes + */ +class MATH_CLASS MbGeomAttribute : public MbCommonAttribute { +protected : + MbSpaceItem * spaceItem; ///< \ru Геометрический объект. \en A geometric object. + MbeCreatorType type; ///< \ru Тип операции. \en Operation type. + bool keepItem; ///< \ru Сохранять исходный объект при копировании. \en Save the initial object when copying. + +private: + // \ru Конструктор копирования. \en Copy constructor. + MbGeomAttribute( const MbGeomAttribute & init, MbRegDuplicate * iReg ); +public : + /// \ru Конструктор. \en Constructor. + MbGeomAttribute( const MbSpaceItem & item, MbeCreatorType t, bool keepItem ); + /// \ru Конструктор. \en Constructor. + MbGeomAttribute( const MbSpaceItem & item, MbeCreatorType t, bool keepItem, const c3d::string_t & itemPrompt ); + /// \ru Деструктор. \en Destructor. + virtual ~MbGeomAttribute(); + +public: + // \ru Выдать подтип атрибута. \en Get subtype of an attribute. + virtual MbeAttributeType AttributeType() const; + // \ru Сделать копию элемента. \en Create a copy of the element. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; + // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; + // \ru Инициализировать данные по присланным. \en Initialize data. + virtual bool Init( const MbAttribute & ); + // \ru Выполнить действия при трансформировании владельца. \en Perform actions when transforming the owner. + virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D &, MbRegTransform * = NULL ); + // \ru Выполнить действия при перемещении владельца. \en Perform actions when moving the owner. + virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D &, MbRegTransform * = NULL ); + // \ru Выполнить действия при вращении владельца. \en Perform actions when rotating the owner. + virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D &, double angle, MbRegTransform * = NULL ); + // \ru Выполнить действия при копировании владельца. \en Perform actions when copying the owner. + virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * = NULL ); + // \ru Выполнить действия при объединении владельца. \en Perform actions when merging the owner. + virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get a string value of the property. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + /// \ru Дать геометрический объект. \en Get geometric object. + const MbSpaceItem * GetSpaceItem() const { return spaceItem; } + MbSpaceItem * SetSpaceItem() { return spaceItem; } + /// \ru Заменить геометрический объект. \en Replace geometric object. + void ChangeSpaceItem( MbSpaceItem & init ); + /// \ru Дать тип операции. \en Get operation type. + MbeCreatorType GetOperationType() const { return type; } + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbGeomAttribute ) +OBVIOUS_PRIVATE_COPY( MbGeomAttribute ) +}; + +IMPL_PERSISTENT_OPS( MbGeomAttribute ) + +#endif // __ATTR_GEOMETRIC_ATTRIBUTE_H diff --git a/C3d/Include/attr_identifier.h b/C3d/Include/attr_identifier.h index 16bff54..adfe68e 100644 --- a/C3d/Include/attr_identifier.h +++ b/C3d/Include/attr_identifier.h @@ -1,322 +1,322 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Идентификатор объекта. - \en Object identifier. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ATTR_IDENTIFIER_H -#define __ATTR_IDENTIFIER_H - - -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Идентификатор объекта. - \en Object identifier. \~ - \details \ru Идентификатор объекта. \n - \en Object identifier. \n \~ - \ingroup Model_Attributes -*/ -// --- -class MATH_CLASS MbIdentifier : public MbElementaryAttribute { -protected : - int32 identifier; ///< \ru Идентификатор объекта. \en Object identifier. - -protected : - /// \ru Конструктор. \en Constructor. - MbIdentifier( const MbIdentifier & ); -public : - /// \ru Конструктор. \en Constructor. - MbIdentifier( int32 init ); - /// \ru Деструктор. \en Destructor. - virtual ~MbIdentifier(); - - // \ru Общие функции объекта. \en Common functions of object. - - virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. - virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - - // \ru Специфические свойства объекта. \en Specific functions of object. - - /// \ru Установить идентификатор. \en Set identifier. - void Init( int32 init ) { identifier = init; } - /// \ru Дать идентификатор объекта. \en Get identifier of object. - int32 Identifier() const { return identifier; } - -private: - MbIdentifier & operator = ( const MbIdentifier & ); - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbIdentifier ) -}; // MbIdentifier - -IMPL_PERSISTENT_OPS( MbIdentifier ) - - -//------------------------------------------------------------------------------ -/** \brief \ru Топологическое имя. - \en Topological name. \~ - \details \ru Топологическое имя. \n - \en Topological name. \n \~ - \ingroup Model_Attributes -*/ -// --- -class MATH_CLASS MbNameAttribute : public MbElementaryAttribute { - typedef std::vector NameAttributesVector; -protected : - MbName tName; ///< \ru Топологическое имя объекта. \en A name of a topological object -private: - NameAttributesVector parentNames; ///< \ru Топологические имена родителей объекта. \en Topological names of object parents. - mutable bool isTemporal; ///< \ru Атрибут временный, на время операции (Этот признак не пишется и не читается). \en Attribute is temporary, for the duration of the operation only (This tag is not read or written). - -protected : - /// \ru Конструктор копирования. \en Copy constructor. - MbNameAttribute( const MbNameAttribute & ); -public : - /// \ru Конструктор. \en Constructor. - MbNameAttribute( bool isTemporal = false ); - /// \ru Конструктор. \en Constructor. - MbNameAttribute( const MbName &, bool isTemporal = false ); - /// \ru Деструктор. \en Destructor. - virtual ~MbNameAttribute(); - - // \ru Общие функции объекта \en Common functions of object. - - virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. - virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. - - // \ru Выполнить действия при объединении владельца. \en Perform actions when merging the owner. - virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); - - /// \ru Выдать имя. \en Get name. - const MbName & GetName() const { return tName; } - /// \ru Выдать имя. \en Get name. - MbName & SetName() { return tName; } - /// \ru Установить имя. \en Set name. - void SetName( const MbName &, bool deleteParentNames = true ); - - /// \ru Определить, есть ли хоть одно имя родительского объекта. \en Determine whether at least one name of parent object exists. - bool IsAnyParentName() const { return (parentNames.size() > 0); } - /// \ru Выдать количество родительских имен первого уровня. \en Get the number of parent names of the first level. - size_t GetParentNamesCount() const { return parentNames.size(); } - /// \ru Удалить имена родительских объектов. \en Delete names of parent objects. - void DeleteParentNames(); - /// \ru Добавить имя родительского объекта. \en Add a name of parent object. - bool AddParentName( const MbName &, bool isTemporal = false ); - /// \ru Добавить имена родительских объектов. \en Add names of parent objects. - bool AddParentNames( const MbNameAttribute &, double accuracy ); - /// \ru Получить имена родительских объектов. \en Get names of parent objects. - void GetParentNames( std::vector & ) const; - ///< \ru Является ли атрибут временным. \en Whether this attribute is temporary. - bool IsTemporal() const { return isTemporal; } - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - -private: - MbNameAttribute & operator = ( const MbNameAttribute & ); // \ru Не реализовано \en Not implemented - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbNameAttribute ) -}; - -IMPL_PERSISTENT_OPS( MbNameAttribute ) - -//------------------------------------------------------------------------------ -/** \brief \ru Метка времени обновления. - \en Stamp of update time. \~ - \details \ru Метка времени обновления. \n - \en Stamp of update time. \n \~ - \ingroup Model_Attributes -*/ -class MATH_CLASS MbUpdateStamp : public MbElementaryAttribute -{ -protected : - uint32 updStamp; ///< \ru Значение метки. \en The value of stamp. - -protected : - /// \ru Конструктор копирования. \en Copy constructor. - MbUpdateStamp( const MbUpdateStamp & ); -public : - /// \ru Конструктор. \en Constructor. - MbUpdateStamp(); - /// \ru Конструктор. \en Constructor. - MbUpdateStamp( uint32 stampVal ); - /// \ru Деструктор. \en Destructor. - virtual ~MbUpdateStamp(); - - // \ru Общие функции объекта \en Common functions of object - - virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. - virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. - - /// \ru Сбросить значение метки времени обновления. \en Reset a value of a stamp of update time. - void ResetStamp() { updStamp = 0; } - /// \ru Проверить, равно ли значение метки нулю. \en Check whether the value of a stamp is null. - bool IsNull () const { return updStamp == 0; } - /// \ru Дать значение метки времени обновления. \en Get the value of a stamp of update time. - uint32 GetStamp () const { return updStamp; } - - /// \ru Увеличить значение метки на единицу. \en Increase the value of stamp by one. - void Increment () { updStamp++; } - /// \ru Установить значение метки максимальным из присланного и действующего. \en Set the value of stamp to the maximum from the given value and the current value. - void Maximize ( uint32 val ) { if (val > updStamp) updStamp = val; } - - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - -private: - MbUpdateStamp & operator = ( const MbUpdateStamp & ); // \ru Не реализовано \en Not implemented - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbUpdateStamp ) -}; - -IMPL_PERSISTENT_OPS( MbUpdateStamp ) - -//------------------------------------------------------------------------------ -/** \brief \ru Атрибут "якорь". - \en Attribute "anchor". \~ - \details \ru Атрибут "якорь". \n - \en Attribute "anchor". \n \~ - \ingroup Model_Attributes -*/ -// --- -class MATH_CLASS MbAnchorAttribute : public MbAttribute { -public: - enum AnchorType { - ant_Undefined = 0, ///< \ru Неопределенный тип. \en An undefined type. - ant_TopoName, ///< \ru Для топологического имени. \en For a topological name. - }; - -protected : - uint8 aType; ///< \ru Тип якорного атрибута. \en Type of an anchor attribute. - -protected : - /// \ru Конструктор копирования. \en Copy constructor. - MbAnchorAttribute( const MbAnchorAttribute & ); -public : - /// \ru Конструктор. \en Constructor. - MbAnchorAttribute(); - /// \ru Конструктор. \en Constructor. - MbAnchorAttribute( AnchorType type ); - /// \ru Деструктор. \en Destructor. - virtual ~MbAnchorAttribute(); - - // \ru Общие функции объекта. \en Common functions of object. - - virtual MbeAttributeType AttributeFamily() const; // \ru Дать тип атрибута. \en Get type of an attribute. - virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. - virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. - - /// \ru Дать тип якорного атрибута. \en Get type of an anchor attribute. - AnchorType GetAnchorType() { return static_cast(aType); } - - // \ru Выполнить действия при изменении владельца, не связанное с другими действиями. \en Perform actions which are not associated with other actions when changing the owner. - virtual void OnChangeOwner( const MbAttributeContainer & owner ); - // \ru Выполнить действия при конвертации владельца \en Perform actions when converting the owner. - virtual void OnConvertOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); - // \ru Выполнить действия при трансформировании владельца. \en Perform actions when transforming the owner. - virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); - // \ru Выполнить действия при перемещении владельца. \en Perform actions when moving the owner. - virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D & to, MbRegTransform * iReg = NULL ); - // \ru Выполнить действия при вращении владельца. \en Perform actions when rotating the owner. - virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); - // \ru Выполнить действия при копировании владельца. \en Perform actions when copying the owner. - virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * iReg = NULL ); - // \ru Выполнить действия при объединении владельца. \en Perform actions when merging the owner. - virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); - // \ru Выполнить действия при замене владельца. \en Perform actions when replacing the owner. - virtual void OnReplaceOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); - // \ru Выполнить действия при разделении владельца. \en Perform actions when splitting the owner. - virtual void OnSplitOwner( const MbAttributeContainer & owner, const std::vector & others ); - // \ru Выполнить действия при удалении владельца. \en Perform actions when deleting the owner. - virtual void OnDeleteOwner( const MbAttributeContainer & owner ); - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - -private: - MbAnchorAttribute & operator = ( const MbAnchorAttribute & ); // \ru Не реализовано \en Not implemented - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbAnchorAttribute ) -}; - -IMPL_PERSISTENT_OPS( MbAnchorAttribute ) - -//------------------------------------------------------------------------------ -/** \brief \ru Признак исполнения (варианта реализации модели). - \en Indication of embodiment (variant of model implementation). \~ - \details \ru Признак исполнения (варианта реализации модели). \n - \en Indication of embodiment (variant of model implementation). \n \~ - \ingroup Model_Attributes -*/ -// --- -class MATH_CLASS MbEmbodimentAttribute : public MbElementaryAttribute { -protected: - SimpleName m_name; ///< \ru Имя исполнения. \en Name of embodiment. - SimpleName m_parent; ///< \ru Имя родительского исполнения. \en Name of parent embodiment. - bool m_current; ///< \ru Признак, является ли исполнение текущим. \en Flag, whether the embodiment is current. - -protected: - // \ru Конструктор. \en Constructor. - MbEmbodimentAttribute( const MbEmbodimentAttribute & ); -public: - // \ru Конструктор. \en Constructor. - MbEmbodimentAttribute(); - // \ru Конструктор. \en Constructor. - MbEmbodimentAttribute( const SimpleName & name1, const SimpleName & name2, bool curr = false ); - // \ru Деструктор. \en Destructor. - virtual ~MbEmbodimentAttribute(); - - // \ru Общие функции объекта. \en Common functions of object. - - virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame( const MbAttribute &, double ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. - virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по атрибуту. \en Initialize by attribute. - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - - // \ru Специфические функции объекта. \en Specific functions of object. - - // \ru Установить родительское исполнение. \en Set a parent embodiment. - void Init( const SimpleName & name1, const SimpleName & name2, bool curr = false ) { - m_name = name1; m_parent = name2; m_current = curr; - } - // \ru Выдать имя исполнения. \en Get a name of embodiment. - SimpleName Name() const { return m_name; } - // \ru Выдать имя родительского исполнения. \en Get a name of parent embodiment. - SimpleName ParentName() const { return m_parent; } - // \ru Является ли исполнение текущим. \en Whether the embodiment is current. - bool IsCurrent() const { return m_current; } - -private: - void operator = ( const MbEmbodimentAttribute & ); // \ru Не реализовано. \en Not implemented. - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbEmbodimentAttribute ) - -}; // MbEmbodimentAttribute - -IMPL_PERSISTENT_OPS( MbEmbodimentAttribute ) - - -#endif // __ATTR_IDENTIFIER_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Идентификатор объекта. + \en Object identifier. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ATTR_IDENTIFIER_H +#define __ATTR_IDENTIFIER_H + + +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Идентификатор объекта. + \en Object identifier. \~ + \details \ru Идентификатор объекта. \n + \en Object identifier. \n \~ + \ingroup Model_Attributes +*/ +// --- +class MATH_CLASS MbIdentifier : public MbElementaryAttribute { +protected : + int32 identifier; ///< \ru Идентификатор объекта. \en Object identifier. + +protected : + /// \ru Конструктор. \en Constructor. + MbIdentifier( const MbIdentifier & ); +public : + /// \ru Конструктор. \en Constructor. + MbIdentifier( int32 init ); + /// \ru Деструктор. \en Destructor. + virtual ~MbIdentifier(); + + // \ru Общие функции объекта. \en Common functions of object. + + virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + + // \ru Специфические свойства объекта. \en Specific functions of object. + + /// \ru Установить идентификатор. \en Set identifier. + void Init( int32 init ) { identifier = init; } + /// \ru Дать идентификатор объекта. \en Get identifier of object. + int32 Identifier() const { return identifier; } + +private: + MbIdentifier & operator = ( const MbIdentifier & ); + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbIdentifier ) +}; // MbIdentifier + +IMPL_PERSISTENT_OPS( MbIdentifier ) + + +//------------------------------------------------------------------------------ +/** \brief \ru Топологическое имя. + \en Topological name. \~ + \details \ru Топологическое имя. \n + \en Topological name. \n \~ + \ingroup Model_Attributes +*/ +// --- +class MATH_CLASS MbNameAttribute : public MbElementaryAttribute { + typedef std::vector NameAttributesVector; +protected : + MbName tName; ///< \ru Топологическое имя объекта. \en A name of a topological object +private: + NameAttributesVector parentNames; ///< \ru Топологические имена родителей объекта. \en Topological names of object parents. + bool isTemporal; ///< \ru Атрибут временный, на время операции (Этот признак не пишется и не читается). \en Attribute is temporary, for the duration of the operation only (This tag is not read or written). + +protected : + /// \ru Конструктор копирования. \en Copy constructor. + MbNameAttribute( const MbNameAttribute & ); +public : + /// \ru Конструктор. \en Constructor. + MbNameAttribute( bool isTemporal = false ); + /// \ru Конструктор. \en Constructor. + MbNameAttribute( const MbName &, bool isTemporal = false ); + /// \ru Деструктор. \en Destructor. + virtual ~MbNameAttribute(); + + // \ru Общие функции объекта \en Common functions of object. + + virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. + + // \ru Выполнить действия при объединении владельца. \en Perform actions when merging the owner. + virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + + /// \ru Выдать имя. \en Get name. + const MbName & GetName() const { return tName; } + /// \ru Выдать имя. \en Get name. + MbName & SetName() { return tName; } + /// \ru Установить имя. \en Set name. + void SetName( const MbName &, bool deleteParentNames = true ); + + /// \ru Определить, есть ли хоть одно имя родительского объекта. \en Determine whether at least one name of parent object exists. + bool IsAnyParentName() const { return (parentNames.size() > 0); } + /// \ru Выдать количество родительских имен первого уровня. \en Get the number of parent names of the first level. + size_t GetParentNamesCount() const { return parentNames.size(); } + /// \ru Удалить имена родительских объектов. \en Delete names of parent objects. + void DeleteParentNames(); + /// \ru Добавить имя родительского объекта. \en Add a name of parent object. + bool AddParentName( const MbName &, bool isTemporal = false ); + /// \ru Добавить имена родительских объектов. \en Add names of parent objects. + bool AddParentNames( const MbNameAttribute &, double accuracy ); + /// \ru Получить имена родительских объектов. \en Get names of parent objects. + void GetParentNames( c3d::ConstNamesVector & ) const; + ///< \ru Является ли атрибут временным. \en Whether this attribute is temporary. + bool IsTemporal() const { return isTemporal; } + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + +private: + MbNameAttribute & operator = ( const MbNameAttribute & ); // \ru Не реализовано \en Not implemented + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbNameAttribute ) +}; + +IMPL_PERSISTENT_OPS( MbNameAttribute ) + +//------------------------------------------------------------------------------ +/** \brief \ru Метка времени обновления. + \en Stamp of update time. \~ + \details \ru Метка времени обновления. \n + \en Stamp of update time. \n \~ + \ingroup Model_Attributes +*/ +class MATH_CLASS MbUpdateStamp : public MbElementaryAttribute +{ +protected : + uint32 updStamp; ///< \ru Значение метки. \en The value of stamp. + +protected : + /// \ru Конструктор копирования. \en Copy constructor. + MbUpdateStamp( const MbUpdateStamp & ); +public : + /// \ru Конструктор. \en Constructor. + MbUpdateStamp(); + /// \ru Конструктор. \en Constructor. + MbUpdateStamp( uint32 stampVal ); + /// \ru Деструктор. \en Destructor. + virtual ~MbUpdateStamp(); + + // \ru Общие функции объекта \en Common functions of object + + virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. + + /// \ru Сбросить значение метки времени обновления. \en Reset a value of a stamp of update time. + void ResetStamp() { updStamp = 0; } + /// \ru Проверить, равно ли значение метки нулю. \en Check whether the value of a stamp is null. + bool IsNull () const { return updStamp == 0; } + /// \ru Дать значение метки времени обновления. \en Get the value of a stamp of update time. + uint32 GetStamp () const { return updStamp; } + + /// \ru Увеличить значение метки на единицу. \en Increase the value of stamp by one. + void Increment () { updStamp++; } + /// \ru Установить значение метки максимальным из присланного и действующего. \en Set the value of stamp to the maximum from the given value and the current value. + void Maximize ( uint32 val ) { if (val > updStamp) updStamp = val; } + + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + +private: + MbUpdateStamp & operator = ( const MbUpdateStamp & ); // \ru Не реализовано \en Not implemented + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbUpdateStamp ) +}; + +IMPL_PERSISTENT_OPS( MbUpdateStamp ) + +//------------------------------------------------------------------------------ +/** \brief \ru Атрибут "якорь". + \en Attribute "anchor". \~ + \details \ru Атрибут "якорь". \n + \en Attribute "anchor". \n \~ + \ingroup Model_Attributes +*/ +// --- +class MATH_CLASS MbAnchorAttribute : public MbAttribute { +public: + enum AnchorType { + ant_Undefined = 0, ///< \ru Неопределенный тип. \en An undefined type. + ant_TopoName, ///< \ru Для топологического имени. \en For a topological name. + }; + +protected : + uint8 aType; ///< \ru Тип якорного атрибута. \en Type of an anchor attribute. + +protected : + /// \ru Конструктор копирования. \en Copy constructor. + MbAnchorAttribute( const MbAnchorAttribute & ); +public : + /// \ru Конструктор. \en Constructor. + MbAnchorAttribute(); + /// \ru Конструктор. \en Constructor. + MbAnchorAttribute( AnchorType type ); + /// \ru Деструктор. \en Destructor. + virtual ~MbAnchorAttribute(); + + // \ru Общие функции объекта. \en Common functions of object. + + virtual MbeAttributeType AttributeFamily() const; // \ru Дать тип атрибута. \en Get type of an attribute. + virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data. + + /// \ru Дать тип якорного атрибута. \en Get type of an anchor attribute. + AnchorType GetAnchorType() { return static_cast(aType); } + + // \ru Выполнить действия при изменении владельца, не связанное с другими действиями. \en Perform actions which are not associated with other actions when changing the owner. + virtual void OnChangeOwner( const MbAttributeContainer & owner ); + // \ru Выполнить действия при конвертации владельца \en Perform actions when converting the owner. + virtual void OnConvertOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // \ru Выполнить действия при трансформировании владельца. \en Perform actions when transforming the owner. + virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D &, MbRegTransform * = NULL ); + // \ru Выполнить действия при перемещении владельца. \en Perform actions when moving the owner. + virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D &, MbRegTransform * = NULL ); + // \ru Выполнить действия при вращении владельца. \en Perform actions when rotating the owner. + virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D &, double angle, MbRegTransform * = NULL ); + // \ru Выполнить действия при копировании владельца. \en Perform actions when copying the owner. + virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * = NULL ); + // \ru Выполнить действия при объединении владельца. \en Perform actions when merging the owner. + virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // \ru Выполнить действия при замене владельца. \en Perform actions when replacing the owner. + virtual void OnReplaceOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // \ru Выполнить действия при разделении владельца. \en Perform actions when splitting the owner. + virtual void OnSplitOwner( const MbAttributeContainer & owner, const std::vector & others ); + // \ru Выполнить действия при удалении владельца. \en Perform actions when deleting the owner. + virtual void OnDeleteOwner( const MbAttributeContainer & owner ); + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + +private: + MbAnchorAttribute & operator = ( const MbAnchorAttribute & ); // \ru Не реализовано \en Not implemented + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbAnchorAttribute ) +}; + +IMPL_PERSISTENT_OPS( MbAnchorAttribute ) + +//------------------------------------------------------------------------------ +/** \brief \ru Признак исполнения (варианта реализации модели). + \en Indication of embodiment (variant of model implementation). \~ + \details \ru Признак исполнения (варианта реализации модели). \n + \en Indication of embodiment (variant of model implementation). \n \~ + \ingroup Model_Attributes +*/ +// --- +class MATH_CLASS MbEmbodimentAttribute : public MbElementaryAttribute { +protected: + SimpleName m_name; ///< \ru Имя исполнения. \en Name of embodiment. + SimpleName m_parent; ///< \ru Имя родительского исполнения. \en Name of parent embodiment. + bool m_current; ///< \ru Признак, является ли исполнение текущим. \en Flag, whether the embodiment is current. + +protected: + // \ru Конструктор. \en Constructor. + MbEmbodimentAttribute( const MbEmbodimentAttribute & ); +public: + // \ru Конструктор. \en Constructor. + MbEmbodimentAttribute(); + // \ru Конструктор. \en Constructor. + MbEmbodimentAttribute( const SimpleName & name1, const SimpleName & name2, bool curr = false ); + // \ru Деструктор. \en Destructor. + virtual ~MbEmbodimentAttribute(); + + // \ru Общие функции объекта. \en Common functions of object. + + virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по атрибуту. \en Initialize by attribute. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + + // \ru Специфические функции объекта. \en Specific functions of object. + + // \ru Установить родительское исполнение. \en Set a parent embodiment. + void Init( const SimpleName & name1, const SimpleName & name2, bool curr = false ) { + m_name = name1; m_parent = name2; m_current = curr; + } + // \ru Выдать имя исполнения. \en Get a name of embodiment. + SimpleName Name() const { return m_name; } + // \ru Выдать имя родительского исполнения. \en Get a name of parent embodiment. + SimpleName ParentName() const { return m_parent; } + // \ru Является ли исполнение текущим. \en Whether the embodiment is current. + bool IsCurrent() const { return m_current; } + +private: + void operator = ( const MbEmbodimentAttribute & ); // \ru Не реализовано. \en Not implemented. + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbEmbodimentAttribute ) + +}; // MbEmbodimentAttribute + +IMPL_PERSISTENT_OPS( MbEmbodimentAttribute ) + + +#endif // __ATTR_IDENTIFIER_H diff --git a/C3d/Include/attr_product.h b/C3d/Include/attr_product.h index d5a83b5..8fbf40e 100644 --- a/C3d/Include/attr_product.h +++ b/C3d/Include/attr_product.h @@ -1,432 +1,471 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Атрибуты изделий. - \en Product attributes. -*/ -//////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include -#include -#include -#include -#include -#include - - -#ifndef __ATTR_PRODUCT_H -#define __ATTR_PRODUCT_H - - -//------------------------------------------------------------------------------ -/** \brief \ru Родительский класс атрибутов изделий. - \en Base calss of product attributes. -*/ -// --- -class MATH_CLASS MbProductAttribute : public MbAttribute { -protected : - MbProductAttribute(); // \ru Конструктор. \en Constructor. -public : - // \ru Деструктор. \en Destructor. - virtual ~MbProductAttribute(); - -public : - virtual MbeAttributeType AttributeFamily() const; - // Выдать подтип атрибута (временно). - virtual MbeAttributeType AttributeType() const = 0; - // Сделать копию элемента. - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const = 0; - virtual bool IsSame( const MbAttribute &, double accuracy ) const = 0; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. - // Инициализировать данные по присланным. - virtual bool Init( const MbAttribute & ) = 0; - - virtual MbePrompt GetPropertyName() = 0; - - // Действия при изменении владельца, не связанное с другими действиями. - virtual void OnChangeOwner( const MbAttributeContainer & owner ); - // Действия при конвертации владельца. - virtual void OnConvertOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); - // Действия при трансформировании владельца. - virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); - // Действия при перемещении владельца. - virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D & to, MbRegTransform * iReg = NULL ); - // Действия при вращении владельца. - virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); - // Действия при копировании владельца. - virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * iReg = NULL ); - // Действия при объединении владельца. - virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); - // Действия при замене владельца. - virtual void OnReplaceOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); - // Действия при разделении владельца. - virtual void OnSplitOwner( const MbAttributeContainer & owner, const std::vector & others ); - // Действия при удалении владельца. - virtual void OnDeleteOwner( const MbAttributeContainer & owner ); - -DECLARE_PERSISTENT_CLASS( MbProductAttribute ) -OBVIOUS_PRIVATE_COPY( MbProductAttribute ) -}; - -IMPL_PERSISTENT_OPS( MbProductAttribute ) - -//------------------------------------------------------------------------------ -/** \brief \ru Сведения о лице в организации. - \en Information related to a person and the organization he/she in. -*/ -// --- -class MATH_CLASS MbPersonOrganizationInfo : public MbProductAttribute { - c3d::string_t personId; ///< \ru Идентификатор лица. \en Identifier of the person. - c3d::string_t lastName; ///< \ru Фамилия. \en Last name. - c3d::string_t firstName; ///< \ru Имя. \en First name. - std::list middleNames; ///< \ru Отчество/средние имена. \en Middle names. - std::list prefixTitles; ///< \ru Титулы предшествующие. \en Prefix titles. - std::list suffixTitles; ///< \ru Титулы завершающие. \en Suffix titles. - c3d::string_t orgId; ///< \ru Идентификатор организации. \en Identifier of the organization. - c3d::string_t orgLabel; ///< \ru Название организации. \en Label of the organization. - c3d::string_t orgDescription; ///< \ru Описание организации. \en Description of the organization. - std::set roles; ///< \ru Роли лица по отношению к изделию. \en The person's roles concerning a product. -protected : - // Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. - MbPersonOrganizationInfo( const MbPersonOrganizationInfo & ); -public : - // Конструктор без параметров для наследников. - MbPersonOrganizationInfo(); - // Деструктор. - virtual ~MbPersonOrganizationInfo(); - -public : - // Выдать подтип атрибута (временно). - virtual MbeAttributeType AttributeType() const; - // Сделать копию элемента. - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; - virtual bool IsSame( const MbAttribute &, double accuracy ) const; // Определить, являются ли объекты равными. - // Инициализировать данные по присланным. - virtual bool Init( const MbAttribute & ) ; - virtual void GetProperties( MbProperties & ); // выдать свойства объекта - - virtual MbePrompt GetPropertyName() ; - - /** - \brief \ru Получить данные. \en Get data. \~ - \param[out] oPersonId - \ru Идентификатор лица. \en Identifier of the person. \~ - \param[out] oLast - \ru Фамилия. \en Last name. \~ - \param[out] oFirst - \ru Имя. \en First name. \~ - \param[out] oMid - \ru Итератор для вставки всех строк, соответствующих отчеству/средним именам. \en Insert iterator for middle names. \~ - \param[out] oPre - \ru Итератор для вставки всех строк, соответствующих титулов предшествующих. \en Insert iterator for prefix titles. \~ - \param[out] oSuf - \ru Итератор для вставки всех строк, соответствующих титулов завершающих. \en Insert iterator for suffix titles. \~ - \param[out] oOrgId - \ru Идентификатор организации. \en Identifier of the organization. \~ - \param[out] oOrgLabel - \ru Название организации. \en Label of the organization. \~ - \param[out] oOrgDesc - \ru Описание организации. \en Description of the organization. \~ - */ - template< typename OutMid, typename OutPre, typename OutSuf > - void GetData( c3d::string_t& oPersonId, c3d::string_t& oLast, c3d::string_t& oFirst, - OutMid oMid, OutPre oPre, OutSuf oSuf, - c3d::string_t& oOrgId, c3d::string_t& oOrgLabel, c3d::string_t& oOrgDesc ) const; - - - /** - \brief \ru Получить данные. \en Get data. \~ - \param[out] oPersonId - \ru Идентификатор лица. \en Identifier of the person. \~ - \param[out] oLast - \ru Фамилия. \en Last name. \~ - \param[out] oFirst - \ru Имя. \en First name. \~ - \param[out] oMid - \ru Итератор для вставки всех строк, соответствующих отчеству/средним именам. \en Insert iterator for middle names. \~ - \param[out] oPre - \ru Итератор для вставки всех строк, соответствующих титулов предшествующих. \en Insert iterator for prefix titles. \~ - \param[out] oSuf - \ru Итератор для вставки всех строк, соответствующих титулов завершающих. \en Insert iterator for suffix titles. \~ - \param[out] oOrgId - \ru Идентификатор организации. \en Identifier of the organization. \~ - \param[out] oOrgLabel - \ru Название организации. \en Label of the organization. \~ - \param[out] oOrgDesc - \ru Описание организации. \en Description of the organization. \~ - */ - template< typename OutMid, typename OutPre, typename OutSuf > - void GetPOData( std::string& oPersonId, std::string& oLast, std::string& oFirst, - OutMid oMid, OutPre oPre, OutSuf oSuf, - std::string& oOrgId, std::string& oOrgLabel, std::string& oOrgDesc ) const; - - /** - \brief \ru Получить полное имя с префиксами и суффиксами. \en full name with prefixes and suffixes. \~ - */ - c3d::string_t NameOneLine() const; - - /** - \brief \ru Получить данные организации. \en Get organization data. \~ - \param[out] oOrgId - \ru Идентификатор организации. \en Identifier of the organization. \~ - \param[out] oOrgLabel - \ru Название организации. \en Label of the organization. \~ - \param[out] oOrgDesc - \ru Описание организации. \en Description of the organization. \~ - */ - void GetOrganization( c3d::string_t& oOrgId, c3d::string_t& oOrgLabel, c3d::string_t& oOrgDesc ) const; - - - /** - \brief \ru Получить данные организации. \en Get organization data. \~ - \param[out] oOrgId - \ru Идентификатор организации. \en Identifier of the organization. \~ - \param[out] oOrgLabel - \ru Название организации. \en Label of the organization. \~ - \param[out] oOrgDesc - \ru Описание организации. \en Description of the organization. \~ - */ - void GetOrganizationInfo( std::string& oOrgId, std::string& oOrgLabel, std::string& oOrgDesc ) const; - - /** - \brief \ru Задать данные лица. \en Set person's data. \~ - \param[in] oPersonId - \ru Идентификатор лица. \en Identifier of the person. \~ - \param[in] oLast - \ru Фамилия. \en Last name. \~ - \param[in] oFirst - \ru Имя. \en First name. \~ - \param[in] firstMid - \ru Итератор первой строки, соответствующей отчеству/средним именам. \en First iterator for middle names. \~ - \param[in] lastMid - \ru Итератор за последней строкой, соответствующей отчеству/средним именам. \en Next after last iterator for middle names. \~ - \param[in] firstPre - \ru Итератор первой строки, соответствующей титулам предшествующих. \en First iterator for prefix titles. \~ - \param[in] lastPre - \ru Итератор первой строки, соответствующей титулам предшествующих. \en Next after last iterator for prefix titles. \~ - \param[in] firstSuf - \ru Итератор первой строки, соответствующей титулам завершающих. \en First iterator for suffix titles. \~ - \param[in] lastSuf - \ru Итератор первой строки, соответствующей титулам завершающих. \en Next after last iterator for suffix titles. \~ - */ - template< typename InMid, typename InPre, typename InSuf > - void SetPerson( const c3d::string_t& oPersonId, const c3d::string_t& oLast, const c3d::string_t& oFirst, - InMid firstMid, InMid lastMid, - InPre firstPre, InPre lastPre, - InSuf firstSuf, InSuf lastSuf ); - - /** - \brief \ru Задать данные лица. \en Set person's data. \~ - \param[in] oPersonId - \ru Идентификатор лица. \en Identifier of the person. \~ - \param[in] oLast - \ru Фамилия. \en Last name. \~ - \param[in] oFirst - \ru Имя. \en First name. \~ - \param[in] firstMid - \ru Итератор первой строки, соответствующей отчеству/средним именам. \en First iterator for middle names. \~ - \param[in] lastMid - \ru Итератор за последней строкой, соответствующей отчеству/средним именам. \en Next after last iterator for middle names. \~ - \param[in] firstPre - \ru Итератор первой строки, соответствующей титулам предшествующих. \en First iterator for prefix titles. \~ - \param[in] lastPre - \ru Итератор первой строки, соответствующей титулам предшествующих. \en Next after last iterator for prefix titles. \~ - \param[in] firstSuf - \ru Итератор первой строки, соответствующей титулам завершающих. \en First iterator for suffix titles. \~ - \param[in] lastSuf - \ru Итератор первой строки, соответствующей титулам завершающих. \en Next after last iterator for suffix titles. \~ - */ - template< typename InMid, typename InPre, typename InSuf > - void SetPersonInfo( const std::string& oPersonId, const std::string& oLast, const std::string& oFirst, - InMid firstMid, InMid lastMid, - InPre firstPre, InPre lastPre, - InSuf firstSuf, InSuf lastSuf ); - - /** - \brief \ru Задать данные организации. \en Set organization's data. \~ - \param[in] oOrgId - \ru Идентификатор организации. \en Identifier of the organization. \~ - \param[in] oOrgLabel - \ru Название организации. \en Label of the organization. \~ - \param[in] oOrgDesc - \ru Описание организации. \en Description of the organization. \~ - */ - void SetOrganization( const c3d::string_t& initOrgId, const c3d::string_t& initOrgLabel, const c3d::string_t& initOrgDesc ); - - /** - \brief \ru Задать данные организации. \en Set organization's data. \~ - \param[in] oOrgId - \ru Идентификатор организации. \en Identifier of the organization. \~ - \param[in] oOrgLabel - \ru Название организации. \en Label of the organization. \~ - \param[in] oOrgDesc - \ru Описание организации. \en Description of the organization. \~ - */ - void SetOrganizationInfo( const std::string& initOrgId, const std::string& initOrgLabel, const std::string& initOrgDesc ); - - /** - \brief \ru Задать данные лица и организации в упрощенной форме. \en Set person's and organization's simplified data. \~ - \param[in] person - \ru Фамилия автора. \en Author's second name. \~ - \param[in] organization - \ru Название организации. \en Label of the organization. \~ - */ - void SetPersonOrganization( const c3d::string_t& person, const c3d::string_t& organization ); - - /** - \brief \ru Задать данные лица и организации в упрощенной форме. \en Set person's and organization's simplified data. \~ - \param[in] person - \ru Фамилия автора. \en Author's second name. \~ - \param[in] organization - \ru Название организации. \en Label of the organization. \~ - */ - void SetPersonOrganizationInfo( const std::string& person, const std::string& organization ); - - /// \ru Добавить роль автора. \en Add person's role. - inline void AddRole( const c3d::string_t& role ) { roles.insert( role ); } - - /// \ru Добавить роль автора. \en Add person's role. - inline void AddToRoles( const std::string& role ) { roles.insert( c3d::ToC3Dstring( role ) ); } - - /// \ru Получить роли автора. \en Get person's roles. - template< typename T > void GetRoles( T dest ) const { std::copy( roles.begin(), roles.end(), dest ); } - - /// \ru Добавить роли к приёмнику. \en Add person's roles to destination. - template< typename T > void AddRolesTo( T dest ) const; - -private: - MbPersonOrganizationInfo & operator = ( const MbPersonOrganizationInfo & ); // forbidden - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbPersonOrganizationInfo ) // Атрибуты писать ни к чему, они создаются только для конвертирования -}; - -IMPL_PERSISTENT_OPS( MbPersonOrganizationInfo ) - -//------------------------------------------------------------------------------ -/** \brief \ru Данные об изделии. \en Product data. -*/ -// --- -class MATH_CLASS MbProductInfo : public MbProductAttribute -{ - c3d::string_t id; ///< \ru Идентификатор. \en Identifier. - c3d::string_t name; ///< \ru Название. \en Name. - c3d::string_t description; ///< \ru Описание. \en Description. - bool isAssembly; ///< \ru Является ли сборочной единицей. \en If the product is an assembly. - -protected : - // Объявление (перегрузка) конструктора копирования без реализации, чтобы не было копирования по умолчанию. - MbProductInfo( const MbProductInfo & ); -public : - MbProductInfo( c3d::StringTCRef initId, c3d::StringTCRef initName, c3d::StringTCRef initDesc, bool isAssm ); - - MbProductInfo( const TCHAR* initId, const TCHAR* initName, TCHAR* initDesc, bool isAssm ); - - MbProductInfo( bool isAssm, const std::string & initId, const std::string & initName, const std::string & initDesc ); - // Деструктор. - virtual ~MbProductInfo(); - -public : - // Выдать подтип атрибута (временно). - virtual MbeAttributeType AttributeType() const; - // Сделать копию элемента. - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const ; - virtual bool IsSame( const MbAttribute &, double accuracy ) const; // Определить, являются ли объекты равными. - // Инициализировать данные по присланным. - virtual bool Init( const MbAttribute & ) ; - virtual void GetProperties( MbProperties & ); // выдать свойства объекта - virtual size_t SetProperties( const MbProperties & ); // Установить свойства объекта. - - virtual MbePrompt GetPropertyName() ; - - const c3d::string_t& GetId() const; ///< \ru Получить идентификатор. \en Get id. - - const c3d::string_t& GetName() const; ///< \ru Получить наименование. \en Get name. - - const c3d::string_t& GetDescription() const; ///< \ru Получить описание. \en Get description. - - /// \ru Получить данные. \en Get data. - void GetData( c3d::string_t & oId, c3d::string_t & oName, c3d::string_t & oDesc ) const; - - /// \ru Получить данные. \en Get data. - void GetDataStd( std::string & oId, std::string & oName, std::string & oDesc ) const; - - /// \ru Задать название. \en Set the name of the product. - void SetNameC3D( const c3d::string_t& oName ); - - /// \ru Задать наименование. \en Set the designation of the product. - void SetId( const std::string& oId ); - /// \ru Задать название. \en Set the name of the product. - void SetName( const std::string& iName ); - /// \ru Задать описание. \en Set the description of the product. - void SetDescription( const std::string& oDesc ); - - /// \ru Является ли изделие сборочной единицей. \en If the product is an assembly. - bool IsAssembly() const; - -private: - MbProductInfo & operator = ( const MbProductInfo & ); - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbProductInfo ) // Атрибуты писать ни к чему, они создаются только для конвертирования -}; - -IMPL_PERSISTENT_OPS( MbProductInfo ) - -//////////////////////////////////////////////////////////////////////////////// -// -// Класс Лицо и организация. -// -//////////////////////////////////////////////////////////////////////////////// - - -//------------------------------------------------------------------------------ -// Получить данные -// --- -template< typename OutMid, typename OutPre, typename OutSuf > -void MbPersonOrganizationInfo::GetData( c3d::string_t& oPersonId, c3d::string_t& oLast, c3d::string_t& oFirst, - OutMid oMid, OutPre oPre, OutSuf oSuf, - c3d::string_t& oOrgId, c3d::string_t& oOrgLabel, c3d::string_t& oOrgDesc ) const { - oPersonId = personId; - oLast = lastName; - oFirst = firstName; - std::copy( middleNames.begin(), middleNames.end(), oMid ); - std::copy( prefixTitles.begin(), prefixTitles.end(), oPre ); - std::copy( suffixTitles.begin(), suffixTitles.end(), oSuf ); - oOrgId = orgId; - oOrgLabel = orgLabel; - oOrgDesc = orgDescription; -} - - -//------------------------------------------------------------------------------ -// Получить данные -// --- -template< typename OutMid, typename OutPre, typename OutSuf > -void MbPersonOrganizationInfo::GetPOData( std::string& oPersonId, std::string& oLast, std::string& oFirst, - OutMid oMid, OutPre oPre, OutSuf oSuf, - std::string& oOrgId, std::string& oOrgLabel, std::string& oOrgDesc ) const { - oPersonId = c3d::ToSTDstring( personId ); - oLast = c3d::ToSTDstring( lastName ); - oFirst = c3d::ToSTDstring( firstName ); - std::list< std::string > tmp; - for( std::list::const_iterator itr = middleNames.begin(); itr != middleNames.end(); ++itr ) - tmp.push_back( c3d::ToSTDstring( *itr ) ); - std::copy( tmp.begin(), tmp.end(), oMid ); - tmp.clear(); - for( std::list::const_iterator itr = prefixTitles.begin(); itr != prefixTitles.end(); ++itr ) - tmp.push_back( c3d::ToSTDstring( *itr ) ); - std::copy( tmp.begin(), tmp.end(), oPre ); - tmp.clear(); - for( std::list::const_iterator itr = suffixTitles.begin(); itr != suffixTitles.end(); ++itr ) - tmp.push_back( c3d::ToSTDstring( *itr ) ); - std::copy( tmp.begin(), tmp.end(), oSuf ); - oOrgId = c3d::ToSTDstring( orgId ); - oOrgLabel = c3d::ToSTDstring( orgLabel ); - oOrgDesc = c3d::ToSTDstring( orgDescription ); -} - - -//------------------------------------------------------------------------------ -// Задать данные лица -// --- -template< typename InMid, typename InPre, typename InSuf > -void MbPersonOrganizationInfo::SetPerson( const c3d::string_t& oPersonId, const c3d::string_t& oLast, const c3d::string_t& oFirst, - InMid firstMid, InMid lastMid, - InPre firstPre, InPre lastPre, - InSuf firstSuf, InSuf lastSuf ) { - personId = oPersonId; - lastName = oLast; - firstName = oFirst; - middleNames.assign( firstMid, lastMid ); - prefixTitles.assign( firstPre, lastPre ); - suffixTitles.assign( firstSuf, lastSuf ); -} - - -//------------------------------------------------------------------------------ -// Задать данные лица -// --- -template< typename InMid, typename InPre, typename InSuf > -void MbPersonOrganizationInfo::SetPersonInfo( const std::string& oPersonId, const std::string& oLast, const std::string& oFirst, - InMid firstMid, InMid lastMid, - InPre firstPre, InPre lastPre, - InSuf firstSuf, InSuf lastSuf ) { - personId = c3d::ToC3Dstring( oPersonId ); - lastName = c3d::ToC3Dstring( oLast ); - firstName = c3d::ToC3Dstring( oFirst ); - std::list< c3d::string_t > tmp; - for( InMid itr = firstMid; itr != lastMid; ++itr ) - tmp.push_back( c3d::ToC3Dstring( *itr ) ); - middleNames.swap( tmp ); - tmp.clear(); - for( InPre itr = firstPre; itr != lastPre; ++itr ) - tmp.push_back( c3d::ToC3Dstring( *itr ) ); - prefixTitles.swap(tmp); - tmp.clear(); - for( InSuf itr = firstSuf; itr != lastSuf; ++itr ) - tmp.push_back( c3d::ToC3Dstring( *itr ) ); - suffixTitles.swap(tmp); -} - - -//------------------------------------------------------------------------------ -// Добавить роли к приёмнику. -// --- -template< typename T > -void MbPersonOrganizationInfo::AddRolesTo( T dest ) const { - std::list tmp; - for( std::set::const_iterator itr = roles.begin(); itr != roles.end(); ++itr ) - tmp.push_back( c3d::ToSTDstring( *itr ) ); - std::copy( tmp.begin(), tmp.end(), dest ); -} - - +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Атрибуты изделий. + \en Product attributes. +*/ +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifndef __ATTR_PRODUCT_H +#define __ATTR_PRODUCT_H + + +//------------------------------------------------------------------------------ +/** \brief \ru Родительский класс атрибутов изделий. + \en Base calss of product attributes. +*/ +// --- +class MATH_CLASS MbProductAttribute : public MbAttribute { +protected : + MbProductAttribute(); // \ru Конструктор. \en Constructor. +public : + // \ru Деструктор. \en Destructor. + virtual ~MbProductAttribute(); + +public : + virtual MbeAttributeType AttributeFamily() const; + // Выдать подтип атрибута (временно). + virtual MbeAttributeType AttributeType() const = 0; + // Сделать копию элемента. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const = 0; + virtual bool IsSame( const MbAttribute &, double accuracy ) const = 0; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + // Инициализировать данные по присланным. + virtual bool Init( const MbAttribute & ) = 0; + + virtual MbePrompt GetPropertyName() = 0; + + // Действия при изменении владельца, не связанное с другими действиями. + virtual void OnChangeOwner( const MbAttributeContainer & owner ); + // Действия при конвертации владельца. + virtual void OnConvertOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // Действия при трансформировании владельца. + virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D &, MbRegTransform * = NULL ); + // Действия при перемещении владельца. + virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D &, MbRegTransform * = NULL ); + // Действия при вращении владельца. + virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D &, double angle, MbRegTransform * = NULL ); + // Действия при копировании владельца. + virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * = NULL ); + // Действия при объединении владельца. + virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // Действия при замене владельца. + virtual void OnReplaceOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // Действия при разделении владельца. + virtual void OnSplitOwner( const MbAttributeContainer & owner, const std::vector & others ); + // Действия при удалении владельца. + virtual void OnDeleteOwner( const MbAttributeContainer & owner ); + +DECLARE_PERSISTENT_CLASS( MbProductAttribute ) +OBVIOUS_PRIVATE_COPY( MbProductAttribute ) +}; + +IMPL_PERSISTENT_OPS( MbProductAttribute ) + +//------------------------------------------------------------------------------ +/** \brief \ru Сведения о лице в организации. + \en Information related to a person and the organization he/she in. +*/ +// --- +class MATH_CLASS MbPersonOrganizationInfo : public MbProductAttribute { + c3d::string_t personId; ///< \ru Идентификатор лица. \en Identifier of the person. + c3d::string_t lastName; ///< \ru Фамилия. \en Last name. + c3d::string_t firstName; ///< \ru Имя. \en First name. + std::list middleNames; ///< \ru Отчество/средние имена. \en Middle names. + std::list prefixTitles; ///< \ru Титулы предшествующие. \en Prefix titles. + std::list suffixTitles; ///< \ru Титулы завершающие. \en Suffix titles. + c3d::string_t orgId; ///< \ru Идентификатор организации. \en Identifier of the organization. + c3d::string_t orgLabel; ///< \ru Название организации. \en Label of the organization. + c3d::string_t orgDescription; ///< \ru Описание организации. \en Description of the organization. + std::set roles; ///< \ru Роли лица по отношению к изделию. \en The person's roles concerning a product. +protected : + // Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. + MbPersonOrganizationInfo( const MbPersonOrganizationInfo & ); +public : + // Конструктор без параметров для наследников. + MbPersonOrganizationInfo(); + // Деструктор. + virtual ~MbPersonOrganizationInfo(); + +public : + // Выдать подтип атрибута (временно). + virtual MbeAttributeType AttributeType() const; + // Сделать копию элемента. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // Определить, являются ли объекты равными. + // Инициализировать данные по присланным. + virtual bool Init( const MbAttribute & ) ; + virtual void GetProperties( MbProperties & ); // выдать свойства объекта + + virtual MbePrompt GetPropertyName() ; + + /** + \brief \ru Получить полное имя с префиксами и суффиксами. \en Get full name with prefixes and suffixes. \~ + */ + c3d::string_t NameOneLine() const; + + /** + \brief \ru Получить полное название организации. \en Get full name of organization. \~ + */ + c3d::string_t OrganizationOneLine() const; + + /** + \brief \ru Задать данные организации. \en Set organization's data. \~ + \param[in] oOrgId - \ru Идентификатор организации. \en Identifier of the organization. \~ + \param[in] oOrgLabel - \ru Название организации. \en Label of the organization. \~ + \param[in] oOrgDesc - \ru Описание организации. \en Description of the organization. \~ + */ + void SetOrganization( const c3d::string_t& initOrgId, const c3d::string_t& initOrgLabel, const c3d::string_t& initOrgDesc ); + + /** + \brief \ru Получить данные организации. \en Get organization data. \~ + \param[out] oOrgId - \ru Идентификатор организации. \en Identifier of the organization. \~ + \param[out] oOrgLabel - \ru Название организации. \en Label of the organization. \~ + \param[out] oOrgDesc - \ru Описание организации. \en Description of the organization. \~ + */ + void GetOrganizationDetails( c3d::string_t& oOrgId, c3d::string_t& oOrgLabel, c3d::string_t& oOrgDesc ) const; + + + /** + \brief \ru Задать данные лица и организации в упрощенной форме. \en Set person's and organization's simplified data. \~ + \param[in] person - \ru Фамилия автора. \en Author's second name. \~ + \param[in] organization - \ru Название организации. \en Label of the organization. \~ + */ + void SetPersonOrganization( const c3d::string_t& person, const c3d::string_t& organization ); + + /// \ru Добавить роль автора. \en Add person's role. + inline void AddRole( const c3d::string_t& role ) { roles.insert( role ); } + + /// \ru Добавить роль автора. \en Add person's role. + inline void AddToRoles( const std::string& role ) { roles.insert( c3d::ToC3Dstring( role ) ); } + + /// \ru Получить роли автора. \en Get person's roles. + template< typename T > void GetRoles( T dest ) const { std::copy( roles.begin(), roles.end(), dest ); } + + /// \ru Добавить роли к приёмнику. \en Add person's roles to destination. + template< typename T > void AddRolesTo( T dest ) const; + + + /** + \brief \ru Выдать данные лица. \en Swap person's data. \~ + \param[in] oPersonId - \ru Идентификатор лица. \en Identifier of the person. \~ + \param[in] oLast - \ru Фамилия. \en Last name. \~ + \param[in] oFirst - \ru Имя. \en First name. \~ + \param[in] oMiddles - \ru Список отчеств/средних имен. \en List of middle names. \~ + \param[in] oPrefixes - \ru Список титулов предшествующих. \en List of prefix titles. \~ + \param[in] oSuffixed - \ru Список титулов завершающих. \en List of suffix titles. \~ + */ + void GetPersonDetails( c3d::string_t& oPersonId, c3d::string_t& oLast, c3d::string_t& oFirst, + std::list& oMiddles, + std::list& oPrefixes, + std::list& oSuffixed ) const; + + /** + \brief \ru Обменять данные лица. \en Swap person's data. \~ + \param[in] oPersonId - \ru Идентификатор лица. \en Identifier of the person. \~ + \param[in] oLast - \ru Фамилия. \en Last name. \~ + \param[in] oFirst - \ru Имя. \en First name. \~ + \param[in] oMiddles - \ru Список отчеств/средних имен. \en List of middle names. \~ + \param[in] oPrefixes - \ru Список титулов предшествующих. \en List of prefix titles. \~ + \param[in] oSuffixed - \ru Список титулов завершающих. \en List of suffix titles. \~ + */ + void SwapPersonDetails( c3d::string_t& oPersonId, c3d::string_t& oLast, c3d::string_t& oFirst, + std::list& oMiddles, + std::list& oPrefixes, + std::list& oSuffixed ); + + + /** + \brief \ru Задать данные лица. \en Set person's data. \~ + \param[in] oPersonId - \ru Идентификатор лица. \en Identifier of the person. \~ + \param[in] oLast - \ru Фамилия. \en Last name. \~ + \param[in] oFirst - \ru Имя. \en First name. \~ + \param[in] firstMid - \ru Итератор первой строки, соответствующей отчеству/средним именам. \en First iterator for middle names. \~ + \param[in] lastMid - \ru Итератор за последней строкой, соответствующей отчеству/средним именам. \en Next after last iterator for middle names. \~ + \param[in] firstPre - \ru Итератор первой строки, соответствующей титулам предшествующих. \en First iterator for prefix titles. \~ + \param[in] lastPre - \ru Итератор первой строки, соответствующей титулам предшествующих. \en Next after last iterator for prefix titles. \~ + \param[in] firstSuf - \ru Итератор первой строки, соответствующей титулам завершающих. \en First iterator for suffix titles. \~ + \param[in] lastSuf - \ru Итератор первой строки, соответствующей титулам завершающих. \en Next after last iterator for suffix titles. \~ + */ + template< typename InMid, typename InPre, typename InSuf > + DEPRECATE_DECLARE void SetPerson( const c3d::string_t& oPersonId, const c3d::string_t& oLast, const c3d::string_t& oFirst, + InMid firstMid, InMid lastMid, + InPre firstPre, InPre lastPre, + InSuf firstSuf, InSuf lastSuf ); + + /** + \brief \ru Получить данные. \en Get data. \~ + \param[out] oPersonId - \ru Идентификатор лица. \en Identifier of the person. \~ + \param[out] oLast - \ru Фамилия. \en Last name. \~ + \param[out] oFirst - \ru Имя. \en First name. \~ + \param[out] oMid - \ru Итератор для вставки всех строк, соответствующих отчеству/средним именам. \en Insert iterator for middle names. \~ + \param[out] oPre - \ru Итератор для вставки всех строк, соответствующих титулов предшествующих. \en Insert iterator for prefix titles. \~ + \param[out] oSuf - \ru Итератор для вставки всех строк, соответствующих титулов завершающих. \en Insert iterator for suffix titles. \~ + \param[out] oOrgId - \ru Идентификатор организации. \en Identifier of the organization. \~ + \param[out] oOrgLabel - \ru Название организации. \en Label of the organization. \~ + \param[out] oOrgDesc - \ru Описание организации. \en Description of the organization. \~ + */ + template< typename OutMid, typename OutPre, typename OutSuf > + DEPRECATE_DECLARE void GetData( c3d::string_t& oPersonId, c3d::string_t& oLast, c3d::string_t& oFirst, + OutMid oMid, OutPre oPre, OutSuf oSuf, + c3d::string_t& oOrgId, c3d::string_t& oOrgLabel, c3d::string_t& oOrgDesc ) const; + + /** + \brief \ru Задать данные лица. \en Set person's data. \~ + \param[in] oPersonId - \ru Идентификатор лица. \en Identifier of the person. \~ + \param[in] oLast - \ru Фамилия. \en Last name. \~ + \param[in] oFirst - \ru Имя. \en First name. \~ + \param[in] firstMid - \ru Итератор первой строки, соответствующей отчеству/средним именам. \en First iterator for middle names. \~ + \param[in] lastMid - \ru Итератор за последней строкой, соответствующей отчеству/средним именам. \en Next after last iterator for middle names. \~ + \param[in] firstPre - \ru Итератор первой строки, соответствующей титулам предшествующих. \en First iterator for prefix titles. \~ + \param[in] lastPre - \ru Итератор первой строки, соответствующей титулам предшествующих. \en Next after last iterator for prefix titles. \~ + \param[in] firstSuf - \ru Итератор первой строки, соответствующей титулам завершающих. \en First iterator for suffix titles. \~ + \param[in] lastSuf - \ru Итератор первой строки, соответствующей титулам завершающих. \en Next after last iterator for suffix titles. \~ + */ + template< typename InMid, typename InPre, typename InSuf > + DEPRECATE_DECLARE void SetPersonInfo( const std::string& oPersonId, const std::string& oLast, const std::string& oFirst, + InMid firstMid, InMid lastMid, + InPre firstPre, InPre lastPre, + InSuf firstSuf, InSuf lastSuf ); + + + /** + \brief \ru Получить данные. \en Get data. \~ + \param[out] oPersonId - \ru Идентификатор лица. \en Identifier of the person. \~ + \param[out] oLast - \ru Фамилия. \en Last name. \~ + \param[out] oFirst - \ru Имя. \en First name. \~ + \param[out] oMid - \ru Итератор для вставки всех строк, соответствующих отчеству/средним именам. \en Insert iterator for middle names. \~ + \param[out] oPre - \ru Итератор для вставки всех строк, соответствующих титулов предшествующих. \en Insert iterator for prefix titles. \~ + \param[out] oSuf - \ru Итератор для вставки всех строк, соответствующих титулов завершающих. \en Insert iterator for suffix titles. \~ + \param[out] oOrgId - \ru Идентификатор организации. \en Identifier of the organization. \~ + \param[out] oOrgLabel - \ru Название организации. \en Label of the organization. \~ + \param[out] oOrgDesc - \ru Описание организации. \en Description of the organization. \~ + */ + template< typename OutMid, typename OutPre, typename OutSuf > + DEPRECATE_DECLARE void GetPOData( std::string& oPersonId, std::string& oLast, std::string& oFirst, + OutMid oMid, OutPre oPre, OutSuf oSuf, + std::string& oOrgId, std::string& oOrgLabel, std::string& oOrgDesc ) const; + + + /** + \brief \ru Получить данные организации. \en Get organization data. \~ + \param[out] oOrgId - \ru Идентификатор организации. \en Identifier of the organization. \~ + \param[out] oOrgLabel - \ru Название организации. \en Label of the organization. \~ + \param[out] oOrgDesc - \ru Описание организации. \en Description of the organization. \~ + */ + DEPRECATE_DECLARE void GetOrganizationInfo( std::string& oOrgId, std::string& oOrgLabel, std::string& oOrgDesc ) const; + + + /** + \brief \ru Задать данные организации. \en Set organization's data. \~ + \param[in] oOrgId - \ru Идентификатор организации. \en Identifier of the organization. \~ + \param[in] oOrgLabel - \ru Название организации. \en Label of the organization. \~ + \param[in] oOrgDesc - \ru Описание организации. \en Description of the organization. \~ + */ + DEPRECATE_DECLARE void SetOrganizationInfo( const std::string& initOrgId, const std::string& initOrgLabel, const std::string& initOrgDesc ); + + + /** + \brief \ru Задать данные лица и организации в упрощенной форме. \en Set person's and organization's simplified data. \~ + \param[in] person - \ru Фамилия автора. \en Author's second name. \~ + \param[in] organization - \ru Название организации. \en Label of the organization. \~ + */ + DEPRECATE_DECLARE void SetPersonOrganizationInfo( const std::string& person, const std::string& organization ); + + +private: + MbPersonOrganizationInfo & operator = ( const MbPersonOrganizationInfo & ); // forbidden + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbPersonOrganizationInfo ) // Атрибуты писать ни к чему, они создаются только для конвертирования +}; + +IMPL_PERSISTENT_OPS( MbPersonOrganizationInfo ) + +//------------------------------------------------------------------------------ +/** \brief \ru Данные об изделии. \en Product data. +*/ +// --- +class MATH_CLASS MbProductInfo : public MbProductAttribute +{ + c3d::string_t id; ///< \ru Идентификатор. \en Identifier. + c3d::string_t name; ///< \ru Название. \en Name. + c3d::string_t description; ///< \ru Описание. \en Description. + bool isAssembly; ///< \ru Является ли сборочной единицей. \en If the product is an assembly. + +protected : + // Объявление (перегрузка) конструктора копирования без реализации, чтобы не было копирования по умолчанию. + MbProductInfo( const MbProductInfo & ); +public : + MbProductInfo( c3d::StringTCRef initId, c3d::StringTCRef initName, c3d::StringTCRef initDesc, bool isAssm ); + + MbProductInfo( const TCHAR* initId, const TCHAR* initName, TCHAR* initDesc, bool isAssm ); + + MbProductInfo( bool isAssm, const std::string & initId, const std::string & initName, const std::string & initDesc ); + // Деструктор. + virtual ~MbProductInfo(); + +public : + // Выдать подтип атрибута (временно). + virtual MbeAttributeType AttributeType() const; + // Сделать копию элемента. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const ; + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // Определить, являются ли объекты равными. + // Инициализировать данные по присланным. + virtual bool Init( const MbAttribute & ) ; + virtual void GetProperties( MbProperties & ); // выдать свойства объекта + virtual size_t SetProperties( const MbProperties & ); // Установить свойства объекта. + + virtual MbePrompt GetPropertyName() ; + + const c3d::string_t& GetId() const; ///< \ru Получить идентификатор. \en Get id. + + const c3d::string_t& GetName() const; ///< \ru Получить наименование. \en Get name. + + const c3d::string_t& GetDescription() const; ///< \ru Получить описание. \en Get description. + + /// \ru Получить данные. \en Get data. + void GetData( c3d::string_t & oId, c3d::string_t & oName, c3d::string_t & oDesc ) const; + + /// \ru Получить данные. \en Get data. + void GetDataStd( std::string & oId, std::string & oName, std::string & oDesc ) const; + + /// \ru Задать название. \en Set the name of the product. + void SetNameC3D( const c3d::string_t& oName ); + + /// \ru Задать наименование. \en Set the designation of the product. + void SetId( const std::string& oId ); + /// \ru Задать название. \en Set the name of the product. + void SetName( const std::string& iName ); + /// \ru Задать описание. \en Set the description of the product. + void SetDescription( const std::string& oDesc ); + + /// \ru Является ли изделие сборочной единицей. \en If the product is an assembly. + bool IsAssembly() const; + +private: + MbProductInfo & operator = ( const MbProductInfo & ); + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbProductInfo ) // Атрибуты писать ни к чему, они создаются только для конвертирования +}; + +IMPL_PERSISTENT_OPS( MbProductInfo ) + +//////////////////////////////////////////////////////////////////////////////// +// +// Класс Лицо и организация. +// +//////////////////////////////////////////////////////////////////////////////// + + +//------------------------------------------------------------------------------ +// Получить данные +// --- +template< typename OutMid, typename OutPre, typename OutSuf > +void MbPersonOrganizationInfo::GetData( c3d::string_t& oPersonId, c3d::string_t& oLast, c3d::string_t& oFirst, + OutMid oMid, OutPre oPre, OutSuf oSuf, + c3d::string_t& oOrgId, c3d::string_t& oOrgLabel, c3d::string_t& oOrgDesc ) const { + oPersonId = personId; + oLast = lastName; + oFirst = firstName; + std::copy( middleNames.begin(), middleNames.end(), oMid ); + std::copy( prefixTitles.begin(), prefixTitles.end(), oPre ); + std::copy( suffixTitles.begin(), suffixTitles.end(), oSuf ); + oOrgId = orgId; + oOrgLabel = orgLabel; + oOrgDesc = orgDescription; +} + + +//------------------------------------------------------------------------------ +// Получить данные +// --- +template< typename OutMid, typename OutPre, typename OutSuf > +void MbPersonOrganizationInfo::GetPOData( std::string& oPersonId, std::string& oLast, std::string& oFirst, + OutMid oMid, OutPre oPre, OutSuf oSuf, + std::string& oOrgId, std::string& oOrgLabel, std::string& oOrgDesc ) const { + oPersonId = c3d::ToSTDstring( personId ); + oLast = c3d::ToSTDstring( lastName ); + oFirst = c3d::ToSTDstring( firstName ); + std::list< std::string > tmp; + for( std::list::const_iterator itr = middleNames.begin(); itr != middleNames.end(); ++itr ) + tmp.push_back( c3d::ToSTDstring( *itr ) ); + std::copy( tmp.begin(), tmp.end(), oMid ); + tmp.clear(); + for( std::list::const_iterator itr = prefixTitles.begin(); itr != prefixTitles.end(); ++itr ) + tmp.push_back( c3d::ToSTDstring( *itr ) ); + std::copy( tmp.begin(), tmp.end(), oPre ); + tmp.clear(); + for( std::list::const_iterator itr = suffixTitles.begin(); itr != suffixTitles.end(); ++itr ) + tmp.push_back( c3d::ToSTDstring( *itr ) ); + std::copy( tmp.begin(), tmp.end(), oSuf ); + oOrgId = c3d::ToSTDstring( orgId ); + oOrgLabel = c3d::ToSTDstring( orgLabel ); + oOrgDesc = c3d::ToSTDstring( orgDescription ); +} + + +//------------------------------------------------------------------------------ +// Задать данные лица +// --- +template< typename InMid, typename InPre, typename InSuf > +void MbPersonOrganizationInfo::SetPerson( const c3d::string_t& oPersonId, const c3d::string_t& oLast, const c3d::string_t& oFirst, + InMid firstMid, InMid lastMid, + InPre firstPre, InPre lastPre, + InSuf firstSuf, InSuf lastSuf ) { + personId = oPersonId; + lastName = oLast; + firstName = oFirst; + middleNames.assign( firstMid, lastMid ); + prefixTitles.assign( firstPre, lastPre ); + suffixTitles.assign( firstSuf, lastSuf ); +} + + +//------------------------------------------------------------------------------ +// Задать данные лица +// --- +template< typename InMid, typename InPre, typename InSuf > +void MbPersonOrganizationInfo::SetPersonInfo( const std::string& oPersonId, const std::string& oLast, const std::string& oFirst, + InMid firstMid, InMid lastMid, + InPre firstPre, InPre lastPre, + InSuf firstSuf, InSuf lastSuf ) { + personId = c3d::ToC3Dstring( oPersonId ); + lastName = c3d::ToC3Dstring( oLast ); + firstName = c3d::ToC3Dstring( oFirst ); + std::list< c3d::string_t > tmp; + for( InMid itr = firstMid; itr != lastMid; ++itr ) + tmp.push_back( c3d::ToC3Dstring( *itr ) ); + middleNames.swap( tmp ); + tmp.clear(); + for( InPre itr = firstPre; itr != lastPre; ++itr ) + tmp.push_back( c3d::ToC3Dstring( *itr ) ); + prefixTitles.swap(tmp); + tmp.clear(); + for( InSuf itr = firstSuf; itr != lastSuf; ++itr ) + tmp.push_back( c3d::ToC3Dstring( *itr ) ); + suffixTitles.swap(tmp); +} + + +//------------------------------------------------------------------------------ +// Добавить роли к приёмнику. +// --- +template< typename T > +void MbPersonOrganizationInfo::AddRolesTo( T dest ) const { + std::list tmp; + for( std::set::const_iterator itr = roles.begin(); itr != roles.end(); ++itr ) + tmp.push_back( c3d::ToSTDstring( *itr ) ); + std::copy( tmp.begin(), tmp.end(), dest ); +} + + #endif // __ATTR_PRODUCT_H \ No newline at end of file diff --git a/C3d/Include/attr_registry.h b/C3d/Include/attr_registry.h index c593e39..b704e3c 100644 --- a/C3d/Include/attr_registry.h +++ b/C3d/Include/attr_registry.h @@ -1,91 +1,91 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Инстанс определения атрибута. - \en Attribute definition instance. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ATTR_REGISTRY_H -#define __ATTR_REGISTRY_H - - -#include -#include -#include -#include - - -class IAttrDefinition; - - -//------------------------------------------------------------------------------ -/** \brief \ru Идентификатор пользовательского атрибута. - \en Identifier of external attribute. \~ - \details \ru Идентификатор пользовательского атрибута. - \en Identifier of external attribute. \~ - \ingroup Model_Attributes - */ -// KVT class MbUserAttribType -// KVT { -// KVT public: -// KVT uint subtype1; -// KVT uint subtype2; -// KVT uint subtype3; -// KVT -// KVT public: -// KVT MbUserAttribType() -// KVT : subtype1( 0 ), subtype2( 0 ), subtype3( 0 ) {} -// KVT MbUserAttribType( uint type1, uint type2, uint type3 ) -// KVT : subtype1( type1 ), subtype2( type2 ), subtype3( type3 ) {} -// KVT MbUserAttribType( const MbUserAttribType & other ) -// KVT : subtype1( other.subtype1 ), subtype2( other.subtype2 ), subtype3( other.subtype3 ) {} -// KVT -// KVT bool operator == ( const MbUserAttribType & other ) const -// KVT { return subtype1 == other.subtype1 && subtype2 == other.subtype2 && subtype3 == other.subtype3; } -// KVT bool operator < ( const MbUserAttribType & other ) const -// KVT { -// KVT if (subtype1 != other.subtype1) -// KVT return subtype1 < other.subtype1; -// KVT else if (subtype2 != other.subtype2) -// KVT return subtype2 < other.subtype2; -// KVT else if (subtype3 != other.subtype3) -// KVT return subtype3 < other.subtype3; -// KVT -// KVT return false; -// KVT } -// KVT -// KVT private: -// KVT void operator = ( const MbUserAttribType & ); // \ru Не реализовано \en Not implemented -// KVT }; - -typedef MbUuid MbUserAttribType; - -//------------------------------------------------------------------------------ -/** \brief \ru Инстанс определения атрибута. - \en Attribute definition instance. \~ - \ingroup Model_Attributes - */ -class MATH_CLASS AttrDefInstance -{ -private: - MbUserAttribType id_; -public: - AttrDefInstance( const MbUserAttribType & id ); - virtual ~AttrDefInstance(); - -public: - virtual IAttrDefinition * GetAttrDefinition() = 0; -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти определение пользовательского атрибута. - \en Find an external attribute definition. \~ - \ingroup Model_Attributes -*/ -MATH_FUNC (IAttrDefinition *) GetUserAttrDefinition( const MbUserAttribType & id ); - - -#endif // __ATTR_REGISTRY_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Инстанс определения атрибута. + \en Attribute definition instance. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ATTR_REGISTRY_H +#define __ATTR_REGISTRY_H + + +#include +#include +#include +#include + + +class IAttrDefinition; + + +//------------------------------------------------------------------------------ +/** \brief \ru Идентификатор пользовательского атрибута. + \en Identifier of external attribute. \~ + \details \ru Идентификатор пользовательского атрибута. + \en Identifier of external attribute. \~ + \ingroup Model_Attributes + */ +// KVT class MbUserAttribType +// KVT { +// KVT public: +// KVT uint subtype1; +// KVT uint subtype2; +// KVT uint subtype3; +// KVT +// KVT public: +// KVT MbUserAttribType() +// KVT : subtype1( 0 ), subtype2( 0 ), subtype3( 0 ) {} +// KVT MbUserAttribType( uint type1, uint type2, uint type3 ) +// KVT : subtype1( type1 ), subtype2( type2 ), subtype3( type3 ) {} +// KVT MbUserAttribType( const MbUserAttribType & other ) +// KVT : subtype1( other.subtype1 ), subtype2( other.subtype2 ), subtype3( other.subtype3 ) {} +// KVT +// KVT bool operator == ( const MbUserAttribType & other ) const +// KVT { return subtype1 == other.subtype1 && subtype2 == other.subtype2 && subtype3 == other.subtype3; } +// KVT bool operator < ( const MbUserAttribType & other ) const +// KVT { +// KVT if (subtype1 != other.subtype1) +// KVT return subtype1 < other.subtype1; +// KVT else if (subtype2 != other.subtype2) +// KVT return subtype2 < other.subtype2; +// KVT else if (subtype3 != other.subtype3) +// KVT return subtype3 < other.subtype3; +// KVT +// KVT return false; +// KVT } +// KVT +// KVT private: +// KVT void operator = ( const MbUserAttribType & ); // \ru Не реализовано \en Not implemented +// KVT }; + +typedef MbUuid MbUserAttribType; + +//------------------------------------------------------------------------------ +/** \brief \ru Инстанс определения атрибута. + \en Attribute definition instance. \~ + \ingroup Model_Attributes + */ +class MATH_CLASS AttrDefInstance +{ +private: + MbUserAttribType id_; +public: + AttrDefInstance( const MbUserAttribType & id ); + virtual ~AttrDefInstance(); + +public: + virtual IAttrDefinition * GetAttrDefinition() = 0; +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти определение пользовательского атрибута. + \en Find an external attribute definition. \~ + \ingroup Model_Attributes +*/ +MATH_FUNC (IAttrDefinition *) GetUserAttrDefinition( const MbUserAttribType & id ); + + +#endif // __ATTR_REGISTRY_H diff --git a/C3d/Include/attr_selected.h b/C3d/Include/attr_selected.h index 2ae80b1..da48371 100644 --- a/C3d/Include/attr_selected.h +++ b/C3d/Include/attr_selected.h @@ -1,154 +1,154 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Атрибуты. Селектированность. Видимость. Изменённость. - \en Attributes. Selection. Visibility. Modification. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ATTR_SELECTED_H -#define __ATTR_SELECTED_H - - -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Селектированность. - \en Selection. \~ - \details \ru Селектированность. \n - \en Selection. \n \~ - \ingroup Model_Attributes -*/ -// --- -class MATH_CLASS MbSelected : public MbElementaryAttribute { -protected : - bool selected; ///< \ru Селектированность. \en Selection. - -protected : - /// \ru Конструктор копирования. \en Copy-constructor. - MbSelected( const MbSelected & init ); -public : - /// \ru Конструктор. \en Constructor. - MbSelected( bool init ); - /// \ru Деструктор. \en Destructor. - virtual ~MbSelected(); - - // \ru Общие функции объекта. \en Common functions of object. - virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. - virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data by given attribute. - - /// \ru Установить селектированность. \en Set selection. - void Init( bool init ) { selected = init; } - /// \ru Дать селектированность. \en Get selection. - bool Selected() const { return selected; } - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - -private: - void operator = ( const MbSelected & ); // \ru Не реализовано \en Not implemented - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSelected ) - -}; // MbSelected - -IMPL_PERSISTENT_OPS( MbSelected ) - -//------------------------------------------------------------------------------ -/** \brief \ru Видимость. - \en Visibility. \~ - \details \ru Видимость. \n - \en Visibility. \n \~ - \ingroup Model_Attributes -*/ -// --- -class MATH_CLASS MbVisible : public MbElementaryAttribute { -protected : - bool visible; ///< \ru Видимость. \en Visibility. - -protected : - /// \ru Конструктор копирования. \en Copy-constructor. - MbVisible( const MbVisible & init ); -public : - /// \ru Конструктор. \en Constructor. - MbVisible( bool init ); - /// \ru Деструктор. \en Destructor. - virtual ~MbVisible(); - - // \ru Общие функции объекта. \en Common functions of object. - - virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. - virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data by given attribute. - - /// \ru Установить видимость. \en Set visibility. - void Init( bool init ) { visible = init; } - /// \ru Дать видимость. \en Get visibility. - bool Visible() const { return visible; } - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - -private: - void operator = ( const MbVisible & ); // \ru Не реализовано \en Not implemented - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbVisible ) - -}; // MbVisible - -IMPL_PERSISTENT_OPS( MbVisible ) - -//------------------------------------------------------------------------------ -/** \brief \ru Изменённость. - \en Modification. \~ - \details \ru Изменённость. \n - \en Modification. \n \~ - \ingroup Model_Attributes -*/ -// --- -class MATH_CLASS MbChanged : public MbElementaryAttribute { -protected : - bool changed; ///< \ru Изменённость. \en Modification. - -protected : - /// \ru Конструктор копирования. \en Copy-constructor. - MbChanged( const MbChanged & init ); -public : - /// \ru Конструктор. \en Constructor. - MbChanged( bool init ); - /// \ru Деструктор. \en Destructor. - virtual ~MbChanged(); - - // \ru Общие функции объекта. \en Common functions of object. - - virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. - virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data by given attribute. - - /// \ru Установить изменённость. \en Set modification. - void Init( bool init ) { changed = init; } - /// \ru Дать изменённость. \en Get modification. - bool Changed() const { return changed; } - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - -private: - void operator = ( const MbChanged & ); // \ru Не реализовано \en Not implemented - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbChanged ) - -}; // MbChanged - -IMPL_PERSISTENT_OPS( MbChanged ) - -#endif // __ATTR_SELECTED_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Атрибуты. Селектированность. Видимость. Изменённость. + \en Attributes. Selection. Visibility. Modification. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ATTR_SELECTED_H +#define __ATTR_SELECTED_H + + +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Селектированность. + \en Selection. \~ + \details \ru Селектированность. \n + \en Selection. \n \~ + \ingroup Model_Attributes +*/ +// --- +class MATH_CLASS MbSelected : public MbElementaryAttribute { +protected : + bool selected; ///< \ru Селектированность. \en Selection. + +protected : + /// \ru Конструктор копирования. \en Copy-constructor. + MbSelected( const MbSelected & init ); +public : + /// \ru Конструктор. \en Constructor. + MbSelected( bool init ); + /// \ru Деструктор. \en Destructor. + virtual ~MbSelected(); + + // \ru Общие функции объекта. \en Common functions of object. + virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data by given attribute. + + /// \ru Установить селектированность. \en Set selection. + void Init( bool init ) { selected = init; } + /// \ru Дать селектированность. \en Get selection. + bool Selected() const { return selected; } + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + +private: + void operator = ( const MbSelected & ); // \ru Не реализовано \en Not implemented + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSelected ) + +}; // MbSelected + +IMPL_PERSISTENT_OPS( MbSelected ) + +//------------------------------------------------------------------------------ +/** \brief \ru Видимость. + \en Visibility. \~ + \details \ru Видимость. \n + \en Visibility. \n \~ + \ingroup Model_Attributes +*/ +// --- +class MATH_CLASS MbVisible : public MbElementaryAttribute { +protected : + bool visible; ///< \ru Видимость. \en Visibility. + +protected : + /// \ru Конструктор копирования. \en Copy-constructor. + MbVisible( const MbVisible & init ); +public : + /// \ru Конструктор. \en Constructor. + MbVisible( bool init ); + /// \ru Деструктор. \en Destructor. + virtual ~MbVisible(); + + // \ru Общие функции объекта. \en Common functions of object. + + virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data by given attribute. + + /// \ru Установить видимость. \en Set visibility. + void Init( bool init ) { visible = init; } + /// \ru Дать видимость. \en Get visibility. + bool Visible() const { return visible; } + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + +private: + void operator = ( const MbVisible & ); // \ru Не реализовано \en Not implemented + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbVisible ) + +}; // MbVisible + +IMPL_PERSISTENT_OPS( MbVisible ) + +//------------------------------------------------------------------------------ +/** \brief \ru Изменённость. + \en Modification. \~ + \details \ru Изменённость. \n + \en Modification. \n \~ + \ingroup Model_Attributes +*/ +// --- +class MATH_CLASS MbChanged : public MbElementaryAttribute { +protected : + bool changed; ///< \ru Изменённость. \en Modification. + +protected : + /// \ru Конструктор копирования. \en Copy-constructor. + MbChanged( const MbChanged & init ); +public : + /// \ru Конструктор. \en Constructor. + MbChanged( bool init ); + /// \ru Деструктор. \en Destructor. + virtual ~MbChanged(); + + // \ru Общие функции объекта. \en Common functions of object. + + virtual MbeAttributeType AttributeType() const; // \ru Дать подтип атрибута. \en Get subtype of an attribute. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data by given attribute. + + /// \ru Установить изменённость. \en Set modification. + void Init( bool init ) { changed = init; } + /// \ru Дать изменённость. \en Get modification. + bool Changed() const { return changed; } + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + +private: + void operator = ( const MbChanged & ); // \ru Не реализовано \en Not implemented + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbChanged ) + +}; // MbChanged + +IMPL_PERSISTENT_OPS( MbChanged ) + +#endif // __ATTR_SELECTED_H diff --git a/C3d/Include/attr_stamprib_attribut.h b/C3d/Include/attr_stamprib_attribute.h similarity index 94% rename from C3d/Include/attr_stamprib_attribut.h rename to C3d/Include/attr_stamprib_attribute.h index 16752b1..2798adc 100644 --- a/C3d/Include/attr_stamprib_attribut.h +++ b/C3d/Include/attr_stamprib_attribute.h @@ -1,93 +1,93 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Атрибут ребра жесткости листового тела. - \en Attribute of reinforsed rib of sheet solid. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ATTR_STAMPRIB_ATTRIBUTE_H -#define __ATTR_STAMPRIB_ATTRIBUTE_H - - -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Атрибут ребра жесткости листового тела. - \en Attribute of reinforsed rib of sheet solid. \~ - \details \ru Атрибут ребра жесткости листового тела. Двумерный контур ребра - жесткости и локальная система координат, в плоскости XY которой - расположен двумерный контур содержатся в MbGeomAttribute. - \en Attribute of reinforsed rib of sheet solid. Two-dimensional contour - of a rib and a local coordinate system the two-dimensional contour - is located in XY plane of are stored in MbGeomAttribute \n \~ - \ingroup Model_Attributes - */ -class MATH_CLASS MbStampRibAttribute : public MbGeomAttribute -{ -protected : - size_t index; ///< \ru Индекс сегмента в контуре, от которого будет установлено направление уклона. \en Index of a segment in the contour at which the inclination direction will be set. - SheetRibValues pars; ///< \ru Параметры операции. \en The operation parameters. - MbSNameMaker names; ///< \ru Именователь операции. \en An object defining names generation in the operation. - MbVector3D bendNorm; ///< \ru Нормаль поверхности сгиба (только для внутреннего использования). \en A normal to bend surface (for internal usage only). - MbCartPoint3D bendPoint; ///< \ru Точка на оси сгиба сгиба (только для внутреннего использования). \en A point on bend axis (for internal usage only). -private: - // \ru Конструктор копирования. \en Copy constructor. - MbStampRibAttribute( const MbStampRibAttribute & init, MbRegDuplicate * iReg ); -public : - /// \ru Конструктор. \en Constructor. - MbStampRibAttribute( const MbSpaceItem & item, MbeCreatorType t, size_t index, const SheetRibValues & pars, const MbSNameMaker & n, bool keepItem); - /// \ru Конструктор. \en Constructor. - MbStampRibAttribute( const MbSpaceItem & item, MbeCreatorType t, size_t index, const SheetRibValues & pars, const MbSNameMaker & n, bool keepItem, const c3d::string_t & itemPrompt ); - /// \ru Деструктор. \en Destructor. - virtual ~MbStampRibAttribute(); - -public: - // \ru Выдать подтип атрибута. \en Get subtype of an attribute. - virtual MbeAttributeType AttributeType() const; - // \ru Сделать копию элемента. \en Create a copy of the element. - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; - // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. - virtual bool IsSame( const MbAttribute &, double accuracy ) const; - // \ru Инициализировать данные по присланным. \en Initialize data. - virtual bool Init( const MbAttribute & ); - - // \ru Выполнить действия при трансформировании владельца. \en Perform actions when transforming the owner. - virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D & matr, MbRegTransform * iReg ); - // \ru Выполнить действия при перемещении владельца. \en Perform actions when moving the owner. - virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D & to, MbRegTransform * iReg = NULL ); - // \ru Выполнить действия при вращении владельца. \en Perform actions when rotating the owner. - virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); - // \ru Выполнить действия при копировании владельца. \en Perform actions when copying the owner. - virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * iReg ); - // \ru Выполнить действия при объединении владельца. \en Perform actions when merging the owner. - virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); - - virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get a string value of the property. - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - /// \ru Дать индекс сегмента в контуре. \en Get index of a segment in the contour. - const size_t & GetIndex() const { return index; } - /// \ru Дать параметры операции. \en Get operation parameters. - const SheetRibValues & GetRibValues() const { return pars; } - /// \ru Дать именователь операции. \en Get an object defining a name of the operation. - const MbSNameMaker & GetNameMaker() const { return names; } - /// \ru Дать нормаль к поверхности сгиба. \en Get normal to bend surface. - const MbVector3D & GetBendNormal() const { return bendNorm; } - /// \ru Установить нормаль к поверхности сгиба. \en Set normal to bend surface. - void SetBendNormal( const MbVector3D & n ) { bendNorm = n; } - /// \ru Дать точку на оси сгиба. \en Get point on bend axis. - const MbCartPoint3D & GetBendPoint() const { return bendPoint; } - /// \ru Установить точку на оси сгиба. \en Set point on bend axis. - void SetBendPoint( const MbCartPoint3D & p ) { bendPoint = p; } -DECLARE_PERSISTENT_CLASS( MbStampRibAttribute ) -OBVIOUS_PRIVATE_COPY( MbStampRibAttribute ) -}; - -IMPL_PERSISTENT_OPS( MbStampRibAttribute ) - -#endif // __ATTR_STAMPRIB_ATTRIBUTE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Атрибут ребра жесткости листового тела. + \en Attribute of reinforsed rib of sheet solid. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ATTR_STAMPRIB_ATTRIBUTE_H +#define __ATTR_STAMPRIB_ATTRIBUTE_H + + +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Атрибут ребра жесткости листового тела. + \en Attribute of reinforsed rib of sheet solid. \~ + \details \ru Атрибут ребра жесткости листового тела. Двумерный контур ребра + жесткости и локальная система координат, в плоскости XY которой + расположен двумерный контур содержатся в MbGeomAttribute. + \en Attribute of reinforsed rib of sheet solid. Two-dimensional contour + of a rib and a local coordinate system the two-dimensional contour + is located in XY plane of are stored in MbGeomAttribute \n \~ + \ingroup Model_Attributes + */ +class MATH_CLASS MbStampRibAttribute : public MbGeomAttribute +{ +protected : + size_t index; ///< \ru Индекс сегмента в контуре, от которого будет установлено направление уклона. \en Index of a segment in the contour at which the inclination direction will be set. + SheetRibValues pars; ///< \ru Параметры операции. \en The operation parameters. + MbSNameMaker names; ///< \ru Именователь операции. \en An object defining names generation in the operation. + MbVector3D bendNorm; ///< \ru Нормаль поверхности сгиба (только для внутреннего использования). \en A normal to bend surface (for internal usage only). + MbCartPoint3D bendPoint; ///< \ru Точка на оси сгиба сгиба (только для внутреннего использования). \en A point on bend axis (for internal usage only). +private: + // \ru Конструктор копирования. \en Copy constructor. + MbStampRibAttribute( const MbStampRibAttribute & init, MbRegDuplicate * iReg ); +public : + /// \ru Конструктор. \en Constructor. + MbStampRibAttribute( const MbSpaceItem & item, MbeCreatorType t, size_t index, const SheetRibValues & pars, const MbSNameMaker & n, bool keepItem); + /// \ru Конструктор. \en Constructor. + MbStampRibAttribute( const MbSpaceItem & item, MbeCreatorType t, size_t index, const SheetRibValues & pars, const MbSNameMaker & n, bool keepItem, const c3d::string_t & itemPrompt ); + /// \ru Деструктор. \en Destructor. + virtual ~MbStampRibAttribute(); + +public: + // \ru Выдать подтип атрибута. \en Get subtype of an attribute. + virtual MbeAttributeType AttributeType() const; + // \ru Сделать копию элемента. \en Create a copy of the element. + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; + // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; + // \ru Инициализировать данные по присланным. \en Initialize data. + virtual bool Init( const MbAttribute & ); + + // \ru Выполнить действия при трансформировании владельца. \en Perform actions when transforming the owner. + virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D &, MbRegTransform * = NULL ); + // \ru Выполнить действия при перемещении владельца. \en Perform actions when moving the owner. + virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D &, MbRegTransform * = NULL ); + // \ru Выполнить действия при вращении владельца. \en Perform actions when rotating the owner. + virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D &, double angle, MbRegTransform * = NULL ); + // \ru Выполнить действия при копировании владельца. \en Perform actions when copying the owner. + virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * = NULL ); + // \ru Выполнить действия при объединении владельца. \en Perform actions when merging the owner. + virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get a string value of the property. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + /// \ru Дать индекс сегмента в контуре. \en Get index of a segment in the contour. + const size_t & GetIndex() const { return index; } + /// \ru Дать параметры операции. \en Get operation parameters. + const SheetRibValues & GetRibValues() const { return pars; } + /// \ru Дать именователь операции. \en Get an object defining a name of the operation. + const MbSNameMaker & GetNameMaker() const { return names; } + /// \ru Дать нормаль к поверхности сгиба. \en Get normal to bend surface. + const MbVector3D & GetBendNormal() const { return bendNorm; } + /// \ru Установить нормаль к поверхности сгиба. \en Set normal to bend surface. + void SetBendNormal( const MbVector3D & n ) { bendNorm = n; } + /// \ru Дать точку на оси сгиба. \en Get point on bend axis. + const MbCartPoint3D & GetBendPoint() const { return bendPoint; } + /// \ru Установить точку на оси сгиба. \en Set point on bend axis. + void SetBendPoint( const MbCartPoint3D & p ) { bendPoint = p; } +DECLARE_PERSISTENT_CLASS( MbStampRibAttribute ) +OBVIOUS_PRIVATE_COPY( MbStampRibAttribute ) +}; + +IMPL_PERSISTENT_OPS( MbStampRibAttribute ) + +#endif // __ATTR_STAMPRIB_ATTRIBUTE_H diff --git a/C3d/Include/attr_user_attribut.h b/C3d/Include/attr_user_attribute.h similarity index 87% rename from C3d/Include/attr_user_attribut.h rename to C3d/Include/attr_user_attribute.h index 8e24113..e986818 100644 --- a/C3d/Include/attr_user_attribut.h +++ b/C3d/Include/attr_user_attribute.h @@ -1,426 +1,412 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Пользовательские атрибуты. - \en User attributes. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ATTR_USER_ATTRIBUT_H -#define __ATTR_USER_ATTRIBUT_H - - -#include -#include -#include -#include -#include -#include -#include - -class MATH_CLASS MbExternalAttribute; -class MATH_CLASS MbUserAttribute; -class MATH_CLASS MbFixAttrSet; - - -//------------------------------------------------------------------------------ -/** \brief \ru Интерфейс определения атрибута. - \en Attribute definition interface. \~ - \details \ru Интерфейс определения атрибута. Определение атрибута - объект используемый - для преобразования пользовательских внесистемных атрибутов в пользовательские системные, - а так же для разборки пользовательских системных атрибутов - на составные части - другие атрибуты системные атрибуты, и обратной сборки. - \en Attribute definition interface. Attribute definition - the object used - for converting user external attributes to user system attributes - and for a disassembly of user system attributes - to their components - other system attributes, and for reassembly. \~ - \ingroup Model_Attributes - */ -class IAttrDefinition -{ -public: - /// \ru Преобразовать из пользовательского в "системный". \en Convert user attribute to "system" one. - virtual MbUserAttribute * ReduceUserAttrib ( const MbExternalAttribute & source ) = 0; - - /// \ru Преобразовать из "системного" в пользовательский. \en Convert "system" attribute to user one. - virtual MbExternalAttribute * AdvanceUserAttrib( const MbUserAttribute & source ) = 0; - - /// \ru "Разобрать" на составляющие атрибуты. \en Disassemble on attributes. - virtual MbFixAttrSet * DisassembleUsetAttrib( const MbExternalAttribute & source ) = 0; - - /// \ru "Собрать" из составляющих атрибутов. \en Reassemble from attributes. - virtual bool ReassembleUsetAttrib ( const MbFixAttrSet & source, MbExternalAttribute & targer ) = 0; -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Шаблон "определения" пользовательского атрибута. - \en A template of user attribute definition. \~ - \details \ru Шаблонный класс "Определения" пользовательского атрибута - используется для создания - стандартных определений, с предопределенным функционалом. - \en Template class "Definition" of user attribute - used for creation - of standard definitions with predefined functionality. \~ - \ingroup Model_Attributes - */ -template -class UserAttrDefinition : public IAttrDefinition -{ -public: - /// \ru Преобразовать из пользовательского в "системный". \en Convert user attribute to "system" one. - virtual MbUserAttribute * ReduceUserAttrib ( const MbExternalAttribute & source ); - - /// \ru Преобразовать из "системного" в пользовательский. \en Convert "system" attribute to user one. - virtual MbExternalAttribute * AdvanceUserAttrib( const MbUserAttribute & source ); - - /// \ru "Разобрать" на составляющие атрибуты. \en Disassemble on attributes. - virtual MbFixAttrSet * DisassembleUsetAttrib( const MbExternalAttribute & source ); - - /// \ru "Собрать" из составляющих атрибутов. \en Reassemble from attributes. - virtual bool ReassembleUsetAttrib( const MbFixAttrSet & source, MbExternalAttribute & targer ); -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Пользовательский системный атрибут. - \en User system attribute. \~ - \details \ru Пользовательский системный атрибут. \n - \en User system attribute. \n \~ - \ingroup Model_Attributes - */ -class MATH_CLASS MbUserAttribute : public MbAttribute, public MbSyncItem { - typedef std_unique_ptr UniqueMembufPtr; -protected : - MbUserAttribType userType_; ///< \ru Тип пользовательского атрибута. \en Type of user attribute. - c3d::string_t prompt_; ///< \ru Строка описания. \en String of description. -private: - SPtr extAttr; - mutable UniqueMembufPtr userBuf; - -private: // public: // You must inherit from MbExternalAttribute only!!! - /// \ru Конструктор. \en Constructor. - MbUserAttribute( const TCHAR * prompt, const MbUserAttribType & id ); - -public: - virtual MbeAttributeType AttributeFamily() const; // \ru Дать тип атрибута. \en Get type of an attribute. - virtual MbeAttributeType AttributeType() const; // \ru Выдать подтип атрибута. \en Get subtype of an attribute. - - /// \ru Выдать подтип пользовательского атрибута по пользовательскому типу. \en Get subtype of an user attribute by user-defined type. - static MbeAttributeType AttributeType( const MbUserAttribType & userType ); - - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. - virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data by given attribute. - - // \ru Выполнить действия при изменении владельца не связанное с другими действиями \en Perform actions which are not associated with other actions when changing the owner - virtual void OnChangeOwner( const MbAttributeContainer & owner ); - - // \ru Выполнить действия при конвертации владельца \en Perform actions when converting the owner - virtual void OnConvertOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); - - // \ru Выполнить действия при трансформировании владельца \en Perform actions when transforming the owner - virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D & matr, MbRegTransform * iReg ); - - // \ru Выполнить действия при перемещении владельца. \en Perform actions when moving the owner. - virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D & to, MbRegTransform * iReg = NULL ); - - // \ru Выполнить действия при вращении владельца. \en Perform actions when rotating the owner. - virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); - - // \ru Выполнить действия при копировании владельца \en Perform actions when copying the owner. - virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * iReg ); - - // \ru Выполнить действия при объединении владельца \en Perform actions when merging the owner. - virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); - - // \ru Выполнить действия при замене владельца. \en Perform actions when replacing the owner. - virtual void OnReplaceOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); - - // \ru Выполнить действия при разделении владельца. \en Perform actions when splitting the owner. - virtual void OnSplitOwner( const MbAttributeContainer & owner, const std::vector & others ); - - // \ru Выполнить действия при удалении владельца. \en Perform actions when deleting the owner. - virtual void OnDeleteOwner( const MbAttributeContainer & owner ); - - /// \ru Выдать подсказку. \en Get a hint. - const TCHAR * GetPrompt() const; - /// \ru Выдать идентификатор хранимого атрибута. \en Get identifier of stored attribute. - void GetUserAttribId( MbUserAttribType & attrId ) const; - - /// \ru Установить пользовательские данные. \en Set user data. - void SetUserData( const char * extAttrMemory ); - /// \ru Установить пользовательские данные. \en Set user data. - void SetUserData( const std::vector & extAttrData ); - /// \ru Получить пользовательские данные. \en Get user data. - bool GetUserData( membuf & memBuf ) const; - /// \ru Создать пользовательский внесистемный атрибут по пользовательским данным. \en Make a user external attribute using user data. - bool MakeExternalAttribute( bool keepExisting ); - /// \ru Обновить пользовательские данные по внесистемному атрибуту пользователя. \en Update user data using the user external attribute. - bool UpdateByExternalAttribute() const; - - /// \ru Выдать пользовательский внесистемный атрибут. \en Get a user external attribute. - const MbExternalAttribute * GetExternalAttribute() const { return extAttr; } - /// \ru Установить пользовательский внесистемный атрибут. \en Set a user external attribute. - bool SetExternalAttribute( MbExternalAttribute * ); - /// \ru Установить пользовательский внесистемный атрибут (его копию). \en Set a user external attribute (сopy). - void SetExternalAttribute( const MbExternalAttribute & ); - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - - template - friend MbUserAttribute * UserAttrDefinition::ReduceUserAttrib( const MbExternalAttribute & ); - -protected: - virtual ~MbUserAttribute(); // Use AddRef/Release or smart pointer SPtr to destruct it correctly. - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbUserAttribute ) -OBVIOUS_PRIVATE_COPY( MbUserAttribute ) -}; - -IMPL_PERSISTENT_OPS( MbUserAttribute ) - -class MATH_CLASS MbFixAttrSet; - - -//------------------------------------------------------------------------------ -/** \brief \ru Пользовательский внесистемный атрибут - базовый класс. - \en User external attribute - the base class. \~ - \details \ru Пользовательский внесистемный атрибут - базовый класс. \n - \en User external attribute - the base class. \n \~ - \ingroup Model_Attributes - */ -class MATH_CLASS MbExternalAttribute : public MbAttribute -{ -public : - /// \ru Конструктор. \en Constructor. - MbExternalAttribute(); - /// \ru Деструктор. \en Destructor. - virtual ~MbExternalAttribute(); - - virtual MbeAttributeType AttributeFamily() const; // \ru Дать тип атрибута. \en Get type of an attribute. - virtual MbeAttributeType AttributeType() const; // \ru Выдать подтип атрибута. \en Get subtype of an attribute. - /// \ru Выдать подтип атрибута. \en Get subtype of an attribute. - virtual MbUserAttribType AttrTypeEx() const = 0; - - virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const = 0; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame( const MbAttribute &, double accuracy ) const = 0; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. - virtual bool Init( const MbAttribute & attr ) = 0; // \ru Инициализировать данные по присланным. \en Initialize data. - - // \ru Выполнить действия при изменении владельца, не связанное с другими действиями. \en Perform actions which are not associated with other actions when changing the owner. - virtual void OnChangeOwner( const MbAttributeContainer & owner ); - // \ru Выполнить действия при конвертации владельца. \en Perform actions when converting the owner. - virtual void OnConvertOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); - // \ru Выполнить действия при трансформировании владельца. \en Perform actions when transforming the owner. - virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); - // \ru Выполнить действия при перемещении владельца. \en Perform actions when moving the owner. - virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D & to, MbRegTransform * iReg = NULL ); - // \ru Выполнить действия при вращении владельца. \en Perform actions when rotating the owner. - virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); - // \ru Выполнить действия при копировании владельца. \en Perform actions when copying the owner. - virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * iReg = NULL ); - // \ru Выполнить действия при объединении владельца. \en Perform actions when merging the owner. - virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); - // \ru Выполнить действия при замене владельца. \en Perform actions when replacing the owner. - virtual void OnReplaceOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); - // \ru Выполнить действия при разделении владельца. \en Perform actions when splitting the owner. - virtual void OnSplitOwner( const MbAttributeContainer & owner, const std::vector & others ); - // \ru Выполнить действия при удалении владельца. \en Perform actions when deleting the owner. - virtual void OnDeleteOwner( const MbAttributeContainer & owner ); - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - -protected: - static MbFixAttrSet * CreateFixAttrSet( const MbUserAttribType &, c3d::AttrVector & ); - -OBVIOUS_PRIVATE_COPY( MbExternalAttribute ) -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Фиксированный набор атрибутов - \en Fixed set of attributes. \~ - \details \ru Набор атрибутов, состав которого нельзя изменить, но никто не запрещает - менять значение самих атрибутов. - \en A set of attributes the structure of which cannot be changed, but it is possible - to change values of the attributes. \~ - \ingroup Model_Attributes -*/ -class MATH_CLASS MbFixAttrSet -{ -private: - MbUserAttribType userAttrId; ///< \ru Идентификатор соответствующего пользовательского атрибута. \en Identifier of the corresponding external attribute. - c3d::AttrVector attributes; ///< \ru Атрибуты. \en Attributes. - -private: - /// \ru Конструктор. \en Constructor. - MbFixAttrSet( c3d::AttrVector & attrs ); -public: - /// \ru Деструктор. \en Destructor. - ~MbFixAttrSet() { std::for_each( attributes.begin(), attributes.end(), ReleaseItem ); } - -public: - /// \ru Выдать идентификатор атрибута. \en Get attribute identifier. - const MbUserAttribType & GetUserAttrId() const; - - /// \ru Установить атрибуты. \en Set attributes. - void SetAttribute ( size_t index, const MbAttribute & attrib ); - /// \ru Выдать атрибуты. \en Get attributes. - const MbAttribute & GetAttribute ( size_t index, const MbAttribute & attrib ) const; - - // \ru Выдать количество атрибутов. \en Get the number of attributes. - size_t AttributesCount() const { return attributes.size(); } - - // \ru Доступ хотелось бы ограничить только функцией. \en Access should be constrained only by a function. - // static MbFixAttrSet * MbExternalAttribute::CreateFixAttrSet( const MbUserAttribType & attrId, std::vector & attrs ); - friend class MbExternalAttribute; - -OBVIOUS_PRIVATE_COPY( MbFixAttrSet ) -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Шаблон явления "Определения" пользовательского атрибута. - \en A template of "Definition" phenomenon of user attribute. \~ - \ingroup Model_Attributes - */ -template -class UserAttrDefinitionInstance : public AttrDefInstance -{ -private: - AttrDefClass * attrDef; ///< \ru "Определение" пользовательского атрибута. \en "Definition" of user attribute. - -public: - /// \ru Конструктор. \en Constructor. - UserAttrDefinitionInstance( const MbUserAttribType & type ); - /// \ru Деструктор. \en Destructor. - virtual ~UserAttrDefinitionInstance(); - -public: - // \ru Дать "определение" пользовательского атрибута. \en Get a "definition" of user attribute. - virtual IAttrDefinition * GetAttrDefinition(); -}; - - -//------------------------------------------------------------------------------ -/// \ru Преобразовать из пользовательского в "системный". \en Convert user attribute to "system" one. -// --- -template -MbUserAttribute * UserAttrDefinition::ReduceUserAttrib( const MbExternalAttribute & source ) -{ - MbUserAttribType attrId( source.AttrTypeEx() ); - - MbUserAttribute * resAttr = new MbUserAttribute( _T("AttrClass"), attrId ); - resAttr->InitActions( source ); - { - const char * charBuf = NULL; - size_t memLen = 0; - { - membuf memBuf; - { - const AttrClass * attrPtr = static_cast(&source); - writer out( memBuf, io::out ); - if ( out.good() ) - out << attrPtr; - } - memBuf.closeBuff(); // before memBuf.getMemLen!!! - - memLen = memBuf.getMemLen(); - charBuf = new char[memLen]; - memBuf.toMemory( charBuf, memLen ); - } - resAttr->SetUserData( charBuf ); - delete [] charBuf; - } - - return resAttr; -} - - -//------------------------------------------------------------------------------ -/// \ru Преобразовать из "системного" в пользовательский. \en Convert "system" attribute to user one. -// --- -template -MbExternalAttribute * UserAttrDefinition::AdvanceUserAttrib( const MbUserAttribute & source ) -{ - AttrClass * resAttr = NULL; - MbUserAttribType attrId; - source.GetUserAttribId( attrId ); - { - membuf memBuf; - { - bool canRead = true; - if ( !source.GetUserData( memBuf ) ) { - canRead = false; - if ( source.UpdateByExternalAttribute() ) { - canRead = source.GetUserData( memBuf ); - } - } - if ( canRead ) { - reader in( memBuf, io::in ); - if ( in.good() ) - in >> resAttr; - } - } - memBuf.closeBuff(); - } - return resAttr; -} - - -//------------------------------------------------------------------------------ -/// \ru "Разобрать" на составляющие атрибуты. \en Disassemble on attributes. -// --- -template -MbFixAttrSet * UserAttrDefinition::DisassembleUsetAttrib( const MbExternalAttribute & /*source*/ ) { - return NULL; -} - - -//------------------------------------------------------------------------------ -/// \ru "Собрать" из составляющих атрибутов. \en Reassemble from attributes. -// --- -template -bool UserAttrDefinition::ReassembleUsetAttrib( const MbFixAttrSet & /*source*/, MbExternalAttribute & /*targer*/ ) { - return false; -} - - -//------------------------------------------------------------------------------ -// \ru Конструктор. \en Constructor. -// --- -template -UserAttrDefinitionInstance::UserAttrDefinitionInstance(const MbUserAttribType & type) - : AttrDefInstance( type ) - , attrDef( NULL ) -{ -} - - -//------------------------------------------------------------------------------ -// \ru Деструктор. \en Destructor. -// --- -template -UserAttrDefinitionInstance::~UserAttrDefinitionInstance() -{ - if ( attrDef != NULL ) - delete attrDef; -} - - -//------------------------------------------------------------------------------ -// \ru Дать "определение" пользовательского атрибута. \en Get a "definition" of user attribute. -// --- -template -IAttrDefinition * UserAttrDefinitionInstance::GetAttrDefinition() -{ - if ( attrDef == NULL ) - attrDef = new AttrDefClass(); - return attrDef; -} - - -#endif // __ATTR_USER_ATTRIBUT_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Пользовательские атрибуты. + \en User attributes. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ATTR_USER_ATTRIBUT_H +#define __ATTR_USER_ATTRIBUT_H + + +#include +#include +#include +#include +#include +#include +#include + +class MATH_CLASS MbExternalAttribute; +class MATH_CLASS MbUserAttribute; +class MATH_CLASS MbFixAttrSet; + + +//------------------------------------------------------------------------------ +/** \brief \ru Интерфейс определения атрибута. + \en Attribute definition interface. \~ + \details \ru Интерфейс определения атрибута. Определение атрибута - объект используемый + для преобразования пользовательских внесистемных атрибутов в пользовательские системные, + а так же для разборки пользовательских системных атрибутов + на составные части - другие атрибуты системные атрибуты, и обратной сборки. + \en Attribute definition interface. Attribute definition - the object used + for converting user external attributes to user system attributes + and for a disassembly of user system attributes + to their components - other system attributes, and for reassembly. \~ + \ingroup Model_Attributes + */ +class IAttrDefinition +{ +public: + /// \ru Преобразовать из пользовательского в "системный". \en Convert user attribute to "system" one. + virtual MbUserAttribute * ReduceUserAttrib ( const MbExternalAttribute & source ) = 0; + + /// \ru Преобразовать из "системного" в пользовательский. \en Convert "system" attribute to user one. + virtual MbExternalAttribute * AdvanceUserAttrib( const MbUserAttribute & source ) = 0; + + /// \ru "Разобрать" на составляющие атрибуты. \en Disassemble on attributes. + virtual MbFixAttrSet * DisassembleUserAttrib( const MbExternalAttribute & source ) = 0; + + /// \ru "Собрать" из составляющих атрибутов. \en Reassemble from attributes. + virtual bool ReassembleUserAttrib ( const MbFixAttrSet & source, MbExternalAttribute & target ) = 0; +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Шаблон "определения" пользовательского атрибута. + \en A template of user attribute definition. \~ + \details \ru Шаблонный класс "Определения" пользовательского атрибута - используется для создания + стандартных определений, с предопределенным функционалом. + \en Template class "Definition" of user attribute - used for creation + of standard definitions with predefined functionality. \~ + \ingroup Model_Attributes + */ +template +class UserAttrDefinition : public IAttrDefinition +{ +public: + /// \ru Преобразовать из пользовательского в "системный". \en Convert user attribute to "system" one. + virtual MbUserAttribute * ReduceUserAttrib ( const MbExternalAttribute & source ); + + /// \ru Преобразовать из "системного" в пользовательский. \en Convert "system" attribute to user one. + virtual MbExternalAttribute * AdvanceUserAttrib( const MbUserAttribute & source ); + + /// \ru "Разобрать" на составляющие атрибуты. \en Disassemble on attributes. + virtual MbFixAttrSet * DisassembleUserAttrib( const MbExternalAttribute & source ); + + /// \ru "Собрать" из составляющих атрибутов. \en Reassemble from attributes. + virtual bool ReassembleUserAttrib( const MbFixAttrSet & source, MbExternalAttribute & target ); +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Пользовательский системный атрибут. + \en User system attribute. \~ + \details \ru Пользовательский системный атрибут. \n + \en User system attribute. \n \~ + \ingroup Model_Attributes + */ +class MATH_CLASS MbUserAttribute : public MbAttribute, public MbSyncItem { + typedef std_unique_ptr UniqueMembufPtr; +protected : + MbUserAttribType userType_; ///< \ru Тип пользовательского атрибута. \en Type of user attribute. + c3d::string_t prompt_; ///< \ru Строка описания. \en String of description. +private: + SPtr extAttr; + mutable UniqueMembufPtr userBuf; + +private: // public: // You must inherit from MbExternalAttribute only!!! + /// \ru Конструктор. \en Constructor. + MbUserAttribute( const TCHAR *, const MbUserAttribType & ); + +public: + virtual MbeAttributeType AttributeFamily() const; // \ru Дать тип атрибута. \en Get type of an attribute. + virtual MbeAttributeType AttributeType() const; // \ru Выдать подтип атрибута. \en Get subtype of an attribute. + + /// \ru Выдать подтип пользовательского атрибута по пользовательскому типу. \en Get subtype of an user attribute by user-defined type. + static MbeAttributeType AttributeType( const MbUserAttribType & userType ); + + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ); // \ru Инициализировать данные по присланным. \en Initialize data by given attribute. + + // \ru Выполнить действия при изменении владельца не связанное с другими действиями \en Perform actions which are not associated with other actions when changing the owner + virtual void OnChangeOwner( const MbAttributeContainer & owner ); + // \ru Выполнить действия при конвертации владельца \en Perform actions when converting the owner + virtual void OnConvertOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // \ru Выполнить действия при трансформировании владельца \en Perform actions when transforming the owner + virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D &, MbRegTransform * = NULL ); + // \ru Выполнить действия при перемещении владельца. \en Perform actions when moving the owner. + virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D &, MbRegTransform * = NULL ); + // \ru Выполнить действия при вращении владельца. \en Perform actions when rotating the owner. + virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D &, double angle, MbRegTransform * = NULL ); + // \ru Выполнить действия при копировании владельца \en Perform actions when copying the owner. + virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * = NULL ); + // \ru Выполнить действия при объединении владельца \en Perform actions when merging the owner. + virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // \ru Выполнить действия при замене владельца. \en Perform actions when replacing the owner. + virtual void OnReplaceOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // \ru Выполнить действия при разделении владельца. \en Perform actions when splitting the owner. + virtual void OnSplitOwner( const MbAttributeContainer & owner, const std::vector & others ); + // \ru Выполнить действия при удалении владельца. \en Perform actions when deleting the owner. + virtual void OnDeleteOwner( const MbAttributeContainer & owner ); + + /// \ru Выдать подсказку. \en Get a hint. + const TCHAR * GetPrompt() const; + /// \ru Выдать идентификатор хранимого атрибута. \en Get identifier of stored attribute. + void GetUserAttribId( MbUserAttribType & attrId ) const; + + /// \ru Установить пользовательские данные. \en Set user data. + void SetUserData( const char * extAttrMemory ); + /// \ru Установить пользовательские данные. \en Set user data. + void SetUserData( const std::vector & extAttrData ); + /// \ru Получить пользовательские данные. \en Get user data. + bool GetUserData( membuf & ) const; + /// \ru Создать пользовательский внесистемный атрибут по пользовательским данным. \en Make a user external attribute using user data. + bool MakeExternalAttribute( bool keepExisting ); + /// \ru Обновить пользовательские данные по внесистемному атрибуту пользователя. \en Update user data using the user external attribute. + bool UpdateByExternalAttribute() const; + + /// \ru Выдать пользовательский внесистемный атрибут. \en Get a user external attribute. + const MbExternalAttribute * GetExternalAttribute() const { return extAttr; } + /// \ru Установить пользовательский внесистемный атрибут. \en Set a user external attribute. + bool SetExternalAttribute( MbExternalAttribute * ); + /// \ru Установить пользовательский внесистемный атрибут (его копию). \en Set a user external attribute (copy). + void SetExternalAttribute( const MbExternalAttribute & ); + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + + //warning: dependent nested name specifier 'UserAttrDefinition::' for friend class declaration is not supported; turning off access control for 'MbUserAttribute' + //template + //friend MbUserAttribute * UserAttrDefinition::ReduceUserAttrib( const MbExternalAttribute & ); + + template + friend class UserAttrDefinition; + +protected: + virtual ~MbUserAttribute(); // Use AddRef/Release or smart pointer SPtr to destruct it correctly. + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbUserAttribute ) +OBVIOUS_PRIVATE_COPY( MbUserAttribute ) +}; + +IMPL_PERSISTENT_OPS( MbUserAttribute ) + + +//------------------------------------------------------------------------------ +/** \brief \ru Пользовательский внесистемный атрибут - базовый класс. + \en User external attribute - the base class. \~ + \details \ru Пользовательский внесистемный атрибут - базовый класс. \n + \en User external attribute - the base class. \n \~ + \ingroup Model_Attributes + */ +class MATH_CLASS MbExternalAttribute : public MbAttribute +{ +public : + /// \ru Конструктор. \en Constructor. + MbExternalAttribute(); + /// \ru Деструктор. \en Destructor. + virtual ~MbExternalAttribute(); + + virtual MbeAttributeType AttributeFamily() const; // \ru Дать тип атрибута. \en Get type of an attribute. + virtual MbeAttributeType AttributeType() const; // \ru Выдать подтип атрибута. \en Get subtype of an attribute. + /// \ru Выдать подтип атрибута. \en Get subtype of an attribute. + virtual MbUserAttribType AttrTypeEx() const = 0; + + virtual MbAttribute & Duplicate( MbRegDuplicate * = NULL ) const = 0; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame( const MbAttribute &, double accuracy ) const = 0; // \ru Определить, являются ли объекты равными. \en Determine whether objects are equal. + virtual bool Init( const MbAttribute & ) = 0; // \ru Инициализировать данные по присланным. \en Initialize data. + + // \ru Выполнить действия при изменении владельца, не связанное с другими действиями. \en Perform actions which are not associated with other actions when changing the owner. + virtual void OnChangeOwner( const MbAttributeContainer & owner ); + // \ru Выполнить действия при конвертации владельца. \en Perform actions when converting the owner. + virtual void OnConvertOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // \ru Выполнить действия при трансформировании владельца. \en Perform actions when transforming the owner. + virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D &, MbRegTransform * = NULL ); + // \ru Выполнить действия при перемещении владельца. \en Perform actions when moving the owner. + virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D &, MbRegTransform * = NULL ); + // \ru Выполнить действия при вращении владельца. \en Perform actions when rotating the owner. + virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D &, double angle, MbRegTransform * = NULL ); + // \ru Выполнить действия при копировании владельца. \en Perform actions when copying the owner. + virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * = NULL ); + // \ru Выполнить действия при объединении владельца. \en Perform actions when merging the owner. + virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // \ru Выполнить действия при замене владельца. \en Perform actions when replacing the owner. + virtual void OnReplaceOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ); + // \ru Выполнить действия при разделении владельца. \en Perform actions when splitting the owner. + virtual void OnSplitOwner( const MbAttributeContainer & owner, const std::vector & others ); + // \ru Выполнить действия при удалении владельца. \en Perform actions when deleting the owner. + virtual void OnDeleteOwner( const MbAttributeContainer & owner ); + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual size_t SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + +OBVIOUS_PRIVATE_COPY( MbExternalAttribute ) +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Фиксированный набор атрибутов + \en Fixed set of attributes. \~ + \details \ru Набор атрибутов, состав которого нельзя изменить, но никто не запрещает + менять значение самих атрибутов. + \en A set of attributes the structure of which cannot be changed, but it is possible + to change values of the attributes. \~ + \ingroup Model_Attributes +*/ +class MATH_CLASS MbFixAttrSet +{ +private: + MbUserAttribType userAttrId; ///< \ru Идентификатор соответствующего пользовательского атрибута. \en Identifier of the corresponding external attribute. + c3d::AttrVector attributes; ///< \ru Атрибуты. \en Attributes. + +private: + /// \ru Конструктор. \en Constructor. + MbFixAttrSet( c3d::AttrVector & ); +public: + /// \ru Деструктор. \en Destructor. + ~MbFixAttrSet() { std::for_each( attributes.begin(), attributes.end(), ReleaseItem ); } + +public: + /// \ru Выдать идентификатор атрибута. \en Get attribute identifier. + const MbUserAttribType & GetUserAttrId() const { return userAttrId; } + /// \ru Выдать атрибуты. \en Get attributes. + const MbAttribute * GetAttribute( size_t k ) const { return ((k < attributes.size()) ? attributes[k] : NULL); } + // \ru Выдать количество атрибутов. \en Get the number of attributes. + size_t AttributesCount() const { return attributes.size(); } + + friend class MbExternalAttribute; + +OBVIOUS_PRIVATE_COPY( MbFixAttrSet ) +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Шаблон явления "Определения" пользовательского атрибута. + \en A template of "Definition" phenomenon of user attribute. \~ + \ingroup Model_Attributes + */ +template +class UserAttrDefinitionInstance : public AttrDefInstance, public MbSyncItem +{ +private: + AttrDefClass * attrDef; ///< \ru "Определение" пользовательского атрибута. \en "Definition" of user attribute. + +public: + /// \ru Конструктор. \en Constructor. + UserAttrDefinitionInstance( const MbUserAttribType & ); + /// \ru Деструктор. \en Destructor. + virtual ~UserAttrDefinitionInstance(); + +public: + // \ru Дать "определение" пользовательского атрибута. \en Get a "definition" of user attribute. + virtual IAttrDefinition * GetAttrDefinition(); +}; + + +//------------------------------------------------------------------------------ +/// \ru Преобразовать из пользовательского в "системный". \en Convert user attribute to "system" one. +// --- +template +MbUserAttribute * UserAttrDefinition::ReduceUserAttrib( const MbExternalAttribute & source ) +{ + MbUserAttribType attrId( source.AttrTypeEx() ); + + MbUserAttribute * resAttr = new MbUserAttribute( _T("AttrClass"), attrId ); + resAttr->InitActions( source ); + { + const char * charBuf = NULL; + size_t memLen = 0; + { + membuf memBuf; + { + const AttrClass * attrPtr = static_cast(&source); + writer out( memBuf, io::out ); + if ( out.good() ) + out << attrPtr; + } + memBuf.closeBuff(); // before memBuf.getMemLen!!! + + memLen = memBuf.getMemLen(); + charBuf = new char[memLen]; + memBuf.toMemory( charBuf, memLen ); + } + resAttr->SetUserData( charBuf ); + delete [] charBuf; + } + + return resAttr; +} + + +//------------------------------------------------------------------------------ +/// \ru Преобразовать из "системного" в пользовательский. \en Convert "system" attribute to user one. +// --- +template +MbExternalAttribute * UserAttrDefinition::AdvanceUserAttrib( const MbUserAttribute & source ) +{ + AttrClass * resAttr = NULL; + MbUserAttribType attrId; + source.GetUserAttribId( attrId ); + { + membuf memBuf; + { + bool canRead = true; + if ( !source.GetUserData( memBuf ) ) { + canRead = false; + if ( source.UpdateByExternalAttribute() ) { + canRead = source.GetUserData( memBuf ); + } + } + if ( canRead ) { + reader in( memBuf, io::in ); + if ( in.good() ) + in >> resAttr; + } + } + memBuf.closeBuff(); + } + return resAttr; +} + + +//------------------------------------------------------------------------------ +/// \ru "Разобрать" на составляющие атрибуты. \en Disassemble on attributes. +// --- +template +MbFixAttrSet * UserAttrDefinition::DisassembleUserAttrib( const MbExternalAttribute & /*source*/ ) { + return NULL; +} + + +//------------------------------------------------------------------------------ +/// \ru "Собрать" из составляющих атрибутов. \en Reassemble from attributes. +// --- +template +bool UserAttrDefinition::ReassembleUserAttrib( const MbFixAttrSet & /*source*/, MbExternalAttribute & /*target*/ ) { + return false; +} + + +//------------------------------------------------------------------------------ +// \ru Конструктор. \en Constructor. +// --- +template +UserAttrDefinitionInstance::UserAttrDefinitionInstance( const MbUserAttribType & type ) + : AttrDefInstance( type ) + , attrDef( NULL ) +{ +} + + +//------------------------------------------------------------------------------ +// \ru Деструктор. \en Destructor. +// --- +template +UserAttrDefinitionInstance::~UserAttrDefinitionInstance() +{ + if ( attrDef != NULL ) + delete attrDef; +} + + +//------------------------------------------------------------------------------ +// \ru Дать "определение" пользовательского атрибута. \en Get a "definition" of user attribute. +// --- +template +IAttrDefinition * UserAttrDefinitionInstance::GetAttrDefinition() +{ + if ( attrDef == NULL ) { + ScopedLock ll( GetLock() ); + attrDef = new AttrDefClass(); + } + return attrDef; +} + + +#endif // __ATTR_USER_ATTRIBUT_H diff --git a/C3d/Include/attribute.h b/C3d/Include/attribute.h index 463a095..9c8e197 100644 --- a/C3d/Include/attribute.h +++ b/C3d/Include/attribute.h @@ -1,540 +1,553 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Атрибуты объекта. - \en Object attributes. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ATTRIBUTE_H -#define __ATTRIBUTE_H - - -#include -#include -#include -#include - - -class MATH_CLASS MbVector3D; -class MATH_CLASS MbAxis3D; -class MATH_CLASS MbMatrix3D; -class MATH_CLASS MbProperties; -class MATH_CLASS MbAttributeContainer; -class MbRegDuplicate; -class MbRegTransform; - - -class MATH_CLASS MbAttribute; -namespace c3d // namespace C3D -{ -typedef SPtr AttrSPtr; -typedef SPtr ConstAttrSPtr; - -typedef std::vector AttrVector; -typedef std::vector ConstAttrVector; - -typedef std::vector AttrSPtrVector; -typedef std::vector ConstAttrSPtrVector; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Типы атрибутов. - \en Types of attributes. \~ - \details \ru Типы атрибутов объектов геометрической модели. - Атрибуты объектов группируются по семействам. - \en Types of geometric model objects attributes. - Objects attributes are grouped by families. \~ - \ingroup Model_Attributes - */ -enum MbeAttributeType -{ - at_Undefined = 0, ///< \ru Неопределенный - используется при поиске как "любой". \en Undefined - used as "any" in search. \n - - // \ru Типы простых атрибутов. \en Types of elementary attributes. - at_ElementaryAttribute = 101, ///< \ru Простой атрибут. \en Elementary attribute. - at_Identifier = 102, ///< \ru Идентификатор. \en Identifier. - at_Color = 103, ///< \ru Цвет. \en Color. - at_Width = 104, ///< \ru Ширина линий. \en Lines width. - at_Style = 105, ///< \ru Стиль линий. \en Lines style. - at_Visual = 106, ///< \ru Свойства для OpenGL. \en Properties for OpenGL. - at_Selected = 107, ///< \ru Селектированность. \en Selection. - at_Visible = 108, ///< \ru Видимость. \en Visibility. - at_WireCount = 109, ///< \ru Количество u-линий и v-линий отрисовочной сетки. \en The number of u-mesh and v-mesh drawing lines. \~ - at_Changed = 110, ///< \ru Изменённость. \en Modification. - at_Dencity = 111, ///< \ru Плотность. \en Density. - at_NameAttribute = 112, ///< \ru Топологическое имя. \en Topological name. - at_UpdateStamp = 113, ///< \ru Метка времени обновления. \en Stamp of update time. - at_Embodiment = 114, ///< \ru Признак исполнения (варианта реализации модели). \en Indication of embodiment (variant of model implementation). - at_Elasticity = 115, ///< \ru Механические характеристики: модуль Юнга и коэффициент Пуассана. \en Mechanical properties: Young's modulus and Poisson's ratio. - at_Strains = 116, ///< \ru Деформации. \en The strains. - at_ElementaryLast = 200, /// \ru Простые атрибуты вставлять перед этим значением. \en Elementary attributes should be inserted before this value. \n - - // \ru Типы обобщенных атрибутов. \en Types of common attributes. - at_CommonAttribute = 201, ///< \ru Обобщенный атрибут. \en Common attribute. - at_BoolAttribute = 202, ///< \ru Булев атрибут. \en Boolean attribute. - at_IntAttribute = 203, ///< \ru Целочисленный атрибут. \en Integer attribute. - at_DoubleAttribute = 204, ///< \ru Действительный атрибут. \en Double attribute. - at_StringAttribute = 205, ///< \ru Строковый атрибут. \en String attribute. - at_GeomAttribute = 206, ///< \ru Геометрический атрибут. \en Geometric attribute. \n - at_StampRibAttribute = 207, ///< \ru Атрибут ребра жесткости листового тела. \en Attribute of reinforcement rib of sheet solid. \n - at_Int64Attribute = 208, ///< \ru Атрибут int64. \en Int64 attribute. - at_BinaryAttribute = 209, ///< \ru Бинарный атрибут. \en Binary attribute. - - // \ru Типы связующих атрибутов. \en Types of linking attributes. - at_LinkingAttribute = 301, ///< \ru Связующий атрибут. \en Linking attribute. - at_AnchorAttribute = 302, ///< \ru Якорь. \en Anchor. \n - - // \ru Типы директивных атрибутов. \en Types of directive attributes. - at_DirectiveAttribute = 401, ///< \ru Директивный атрибут. \en Directive attribute. - at_KeepUniqueKey = 402, ///< \ru Поддерживать уникальность ключей. \en Support unique keys. \n - - // \ru Типы изделия. \en Types of product attributes. - at_ProductAttribute = 501, ///< \ru Атрибут конвертеров \en Converters attribute - at_ModelInfo = 502, ///< \ru Сведения о модели в целом. \en Information about model itself. - at_PersonOrganizationInfo = 503, ///< \ru Лицо и организация. \en Person and organization information. - at_ProductInfo = 504, ///< \ru Сведения об изделии. \en Product info. - at_STEPTextDescription = 505, ///< \ru Описание STEP. \en STEP description. - at_STEPReferenceHolder = 506, ///< \ru Обратная ссылка. \en Back reference. \n - - // \ru Типы пользовательских атрибутов. \en Types of user attributes. - at_UserAttribute = 601, ///< \ru Пользовательский атрибут. \en User attribute. - at_UserFirst = 602, ///< \ru Первый пользовательский атрибут. \en First user attribute. - at_UserLast = 900, ///< \ru Последний пользовательский атрибут. \en Last user attribute. \n - - // \ru Типы внешних (внесистемных) атрибутов. \en Types of external (off-system) attributes. - at_ExternalAttribute = 901, ///< \ru Внешний атрибут. \en External attribute. - at_ExternalAttributeImp = 902, ///< \ru Подтип - внешний атрибут \en Subtype - external attribute. - - at_FreeItem = 1000, ///< \ru Тип для прочих объектов. \en Type for the other objects. - -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Типы контейнеров атрибутов. - \en Types of attribute containers. \~ - \details \ru Типы контейнеров атрибутов наследников контейнера атрибутов. - Каждый отдельный атрибут может содержать свой контейнер атрибутов. - \en Types of attribute containers which are inheritors of attribute container. - Each separate attribute may have its attribute container. \~ - \ingroup Model_Attributes - */ -enum MbeImplicationType -{ - ace_Attribute, ///< \ru Контейнер атрибутов, содержащий другие атрибуты. \en Attribute container which contains other attributes. - ace_ModelItem, ///< \ru Контейнер атрибутов объектов геометрической модели. \en Container of geometric model objects attributes. - ace_TopItem, ///< \ru Контейнер атрибутов именованных топологических объектов. \en Container of named topological objects attributes. - ace_MeshItem, ///< \ru Контейнер атрибутов сеточных примитивов. \en Container of mesh primitives attributes. - ace_Model, ///< \ru Контейнер атрибутов геометрической модели. \en Container of geometric model attributes. - ace_AttribContainer, ///< \ru Контейнер атрибутов. \en Attribute container. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Атрибуты объекта. - \en Object attributes. \~ - \details \ru Атрибуты содержат информацию, дополняющую описание геометрической формы объекта. - Атрибут не является неотъемлемой частью объекта, а является элементом данных, которыми может быть наделен объект.\n - Атрибуты являются агентами передачи данных геометрического ядра от одного приложения другому приложению.\n - Атрибуты могут быть следующих типов.\n - Простой атрибут - атрибут несущий простую, однозначно интерпретируемую, информацию, например, цвет, признак выбора.\n - Обобщенный атрибут - атрибут стандартного типа со строковым наименованием, - например, имя, целое число, вещественное число, строка, точка, вектор, указатель.\n - С помощью таких атрибутов приложения могут обмениваться какой либо специфичной информацией - без необходимости разработки дополнительных комплексных атрибутов.\n - Комплексный атрибут - атрибут состоящий из предопределенного набора данных, - описывающих природу атрибута и его смысловую нагрузку, а так же способ его интерпретации. - Такими атрибутами могут описываться некоторые ограничения или простые зависимости а так же аннотационные объекты.\n - Директивный атрибут - атрибут определяющий предназначения объекта или действия которые необходимо с ним произвести, - например атрибут "вычитание" подразумевает что некое тело предназначено для вычитания из другого тела, - и не важно из какого. - Связующий атрибут - атрибут предназначенный для связи объекта геометрического ядра с абстрактным контейнером данных, - то есть набором данных, формат и смысловая нагрузка которых не может быть описана в рамках других атрибутов.\n - \en Attributes contain information supplementing description of object geometric shape. - Attribute is not an intrinsic part of the object, but it is an element of data which the object may contain. \n - Attributes are geometric kernel agents for transferring data from one application to another. \n - The possible types of attributes are the following. \n - Elementary attribute - an attribute which reflects simple and clearly interpreted information, for example, color or selection attribute.\n - Common attribute - attribute of standard type with string naming, - for example: name, integer value, double value, string, point, vector, pointer. \n - Applications may communicate any specific information using such attributes - without necessity of additional complex attributes developing. \n - Complex attribute - an attribute which consists of predefined data set, - which describe a nature of attribute, its semantic meaning and a way of its interpretation. - Such attributes can describe some of constraints or simple dependences and annotation objects.\n - Directive attribute - an attribute defining the purpose of object or actions which should be performed with it, - for example, an attribute "subtraction" implies that one solid is purposed for subtraction from another, - no matter from what exactly. - Linking attribute - an attribute designed for linking of geometric kernel object with abstract container of data, - i.e. a set of data, which format and semantic meaning can not be described by other attributes. \n \~ - \ingroup Model_Attributes -*/ -// --- -class MATH_CLASS MbAttribute : public MbRefItem, - public TapeBase -{ -public: - /**\ru Поведение атрибута при изменении владельца, не связанном с другими описанными действия. - \en Behavior of attribute which is not associated with other described actions when changing the owner. \~ */ - enum OnChangeOwnerAction { - chn_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnChangeOwner. \en Behavior defined by the virtual function OnChangeOwner. - chn_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. - chn_Keep, ///< \ru Сохранить атрибут, т.е. ничего с ним не делать. \en Save attribute, i.e. do not do anything with it. - chn_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). - }; - - /**\ru Поведение атрибута при перерождении объекта в другой объект. - \en Behavior of attribute when an object regenerates in other object. \~ */ - enum OnConvertOwnerAction { - cnv_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnConvertOwner. \en Behavior defined by the virtual function OnConvertOwner. - cnv_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. - cnv_Keep, ///< \ru Сохранить атрибут, т.е. ничего с ним не делать. \en Save attribute, i.e. do not do anything with it. - cnv_Copy, ///< \ru Скопировать атрибут и прицепить его копию к копии владельца. \en Copy an attribute and attach its copy to an owner copy. - cnv_Convert, ///< \ru Конвертировать атрибут и прицепить результат к копии владельца. \en Copy an attribute and attach the result to an owner copy. - cnv_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). - }; - - /**\ru Поведение атрибута при преобразовании владельца (по матрице). - \en Behaviour of attribute when transforming the owner (by the matrix). \~ */ - enum OnTransformOwnerAction { - trn_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnTransformOwner. \en Behavior defined by the virtual function OnTransformOwner. - trn_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. - trn_Keep, ///< \ru Сохранить атрибут, т.е. ничего с ним не делать. \en Save attribute, i.e. do not do anything with it. - trn_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). - }; - - /**\ru Поведение атрибута при копировании владельца. - \en Behaviour of attribute when copying the owner. \~ */ - enum OnCopyOwnerAction { - cpy_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnCopyOwner. \en Behavior defined by the virtual function OnCopyOwner. - cpy_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. - cpy_Keep, ///< \ru Сохранить атрибут, т.е. ничего с ним не делать. \en Save attribute, i.e. do not do anything with it. - cpy_Copy, ///< \ru Скопировать атрибут и прицепить его копию к копии владельца. \en Copy an attribute and attach its copy to an owner copy. - cpy_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). - }; - - /**\ru Поведение атрибута при объединении владельца с другим объектом. - \en Behaviour of attribute when merging of the owner with another object. \~ */ - enum OnMergeOwnerAction { - mrg_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnMergeOwner. \en Behavior is defined by the virtual function OnMergeOwner. - mrg_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. - mrg_Keep, ///< \ru Сохранить атрибут, т.е. ничего с ним не делать. \en Save attribute, i.e. do not do anything with it. - mrg_KeepAll, ///< \ru Передать атрибут от поглощаемого объекта поглощающему объекту без замещения. \en Transmit attribute from absorbed object to absorbing object without replacing. - mrg_KeepRep, ///< \ru Передать атрибут от поглощаемого объекта поглощающему объекту с замещением. \en Transmit attribute from absorbed object to absorbing object with replacing. - mrg_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). - }; - - /**\ru Поведение атрибута при замещении владельца с другим объектом. - \en Behavior of attribute when replacing the owner by another object. \~ */ - enum OnReplaceOwnerAction { - rep_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnReplaceOwner. \en Behavior is defined by the virtual function OnReplaceOwner. - rep_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. - rep_Keep, ///< \ru Сохранить атрибут, т.е. ничего с ним не делать. \en Save attribute, i.e. do not do anything with it. - rep_KeepAll, ///< \ru Передать атрибут от замещаемого объекта замещающему объекту без замещения. \en Transmit attribute from replaced object to substitutional object without replacing. - rep_KeepRep, ///< \ru Передать атрибут от замещаемого объекта замещающему объекту с замещением. \en Transmit attribute from replaced object to substitutional object with replacing. - rep_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). - }; - - /**\ru Поведение атрибута при разделении владельца. - \en Behavior of attribute when splitting the owner. \~ */ - enum OnSplitOwnerAction { - spl_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnSplitOwner. \en Behavior is defined by the virtual function OnSplitOwner. - spl_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. - spl_Keep, ///< \ru Сохранить атрибут, т.е. ничего с ним не делать. \en Save attribute, i.e. do not do anything with it. - spl_Copy, ///< \ru Размножить(скопировать) атрибут для каждого результата разбиения. \en Duplicate (copy) attribute for each result of splitting. - spl_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). - }; - - /**\ru Поведение атрибута при удалении владельца. - \en Behavior of attribute when deleting the owner. \~ */ - enum OnDeleteOwnerAction { - del_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnDeleteOwner. \en Behavior defined by the virtual function OnDeleteOwner. - del_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. - del_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). - }; - -private : - uint8 forChange; ///< \ru Поведение атрибута при изменении владельца. \en Behavior of attribute when changing the owner. - uint8 forConvert; ///< \ru Поведение атрибута при конвертации владельца. \en Behavior of attribute when converting the owner. - uint8 forTransform; ///< \ru Поведение атрибута при трансформировании владельца. \en Behavior of attribute when transforming the owner. - uint8 forCopy; ///< \ru Поведение атрибута при копировании владельца. \en Behavior of attribute when copying the owner. - uint8 forMerge; ///< \ru Поведение атрибута при объединении владельца. \en Behavior of attribute when merging the owner. - uint8 forReplace; ///< \ru Поведение атрибута при замене владельца. \en Behavior of attribute when replacing the owner. - uint8 forSplit; ///< \ru Поведение атрибута при разделении владельца. \en Behavior of attribute when splitting the owner. - uint8 forDelete; ///< \ru Поведение атрибута при удалении владельца. \en Behavior of attribute when deleting the owner. - bool freeable; ///< \ru Свободность атрибута. \en Attribute freeness - bool copyable; ///< \ru Разрешение копировать атрибут. \en Permission to copy attribute. - -protected : - /// \ru Конструктор без параметров для наследников. \en Constructor without parameters for inheritors. - MbAttribute(); -public : - /// \ru Деструктор. \en Destructor. - virtual ~MbAttribute(); - -public : - /** \ru \name Общие функции атрибутов - \en \name Common functions of attributes - \{ */ - /// \ru Выдать регистрационный тип (для копирования, дублирования). \en Get registrational type (for copying, duplication) - virtual MbeRefType RefType() const; - /// \ru Выдать тип контейнера атрибутов. \en Get attribute container type. - virtual MbeImplicationType ImplicationType() const; - /// \ru Выдать тип атрибута. \en Get attribute type. - virtual MbeAttributeType AttributeFamily() const = 0; - /// \ru Выдать подтип атрибута. \en Get subtype of an attribute. - virtual MbeAttributeType AttributeType() const = 0; - /// \ru Сделать копию элемента. \en Create a copy of the element. - virtual MbAttribute & Duplicate( MbRegDuplicate * iReg = NULL ) const = 0; - /** \brief \ru Определить, являются ли объекты равными. - \en Determine whether objects are equal. \~ - \details \ru Равными считаются однотипные объекты, все данные которых одинаковы (равны). - \en Objects of the same types with similar (equal) data are considered to be equal. \~ - \param[in] item - \ru Объект для сравнения. - \en Objects for comparison. \~ - \param[in] accuracy - \ru Точность сравнения. - \en The accuracy to compare. \~ - \return \ru Равны ли объекты. - \en Whether objects are equal. \~ - */ - virtual bool IsSame( const MbAttribute & item, double accuracy ) const = 0; - /// \ru Инициализировать данные по присланным. \en Initialize data. - virtual bool Init( const MbAttribute & ) = 0; - - /// \ru Проверить тип атрибута. \en Check an attribute type. - bool IsA( MbeAttributeType t ) const { return t == AttributeFamily(); } - /** \} */ - - /** \ru \name Действия над объектами геометрического ядра, влияющие на состояние атрибутов - \en \name Actions with objects of geometric kernel influencing on states of attributes. - \{ */ - /** \brief \ru Выполнить действия при изменении владельца, не связанное с другими действиями. - \en Perform actions which are not associated with other actions when changing the owner. \~ - \details \ru Действия при изменении владельца, не связанное с другими действиями. \n - Вызывается после изменения владеющего объекта при условии GetActionForChange() == chn_Self. - \en Actions which are not associated with other actions when changing the owner. \n - This function is called after changing the owning object in a case when GetActionForChange() == chn_Self. \~ */ - virtual void OnChangeOwner( const MbAttributeContainer & owner ) = 0; - - /**\ru Выполнить действия при конвертации владельца, \n - Вызывается после конвертирования владеющего объекта при условии GetActionForConvert() == cnv_Self. \n - В качестве входного параметра передается результат конвертирования объекта. - \en Perform actions when converting the owner, \n - This function is called after converting the owning object in a case when GetActionForConvert() == cnv_Self. \n - The result of object converting is passed as input parameter. \~ */ - virtual void OnConvertOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ) = 0; - - /**\ru Выполнить действия при трансформировании владельца, \n - Вызывается после трансформирования владеющего объекта при условии GetActionForTransform() == trn_Self. - В качестве входного параметра может передаваться регистратор трансформированных объектов. - \en Perform actions when transforming the owner, \n - This function is called after transforming the owning object in a case when GetActionForTransform() == trn_Self. - The registrator of transformed objects may be passed as input parameter. \~ */ - virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D & matr, MbRegTransform * iReg = NULL ) = 0; - - /**\ru Выполнить действия при перемещении владельца. \n - Вызывается после перемещения владеющего объекта при условии GetActionForTransform() == trn_Self. - В качестве входного параметра может передаваться регистратор трансформированных объектов. - \en Perform actions when moving the owner. \n - This function is called after moving the owning object in a case when GetActionForTransform() == trn_Self. - The registrator of transformed objects may be passed as input parameter. \~ */ - virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D & to, MbRegTransform * iReg = NULL ) = 0; - - /**\ru Выполнить действия при вращении владельца. \n - Вызывается после вращения владеющего объекта при условии GetActionForTransform() == trn_Self. - В качестве входного параметра может передаваться регистратор трансформированных объектов. - \en Perform actions when rotating the owner. \n - This function is called after rotating the owning object in a case when GetActionForTransform() == trn_Self. - The registrator of transformed objects may be passed as input parameter. \~ */ - virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ) = 0; - - /**\ru Выполнить действия при копировании владельца. \n - Вызывается после копирования владеющего объекта при условии GetActionForCopy() == cpy_Self. \n - В качестве входных параметров передаются: копия владеющего объекта и регистратор скопированных объектов. - \en Perform actions when copying the owner. \n - This function is called after copying the owning object in a case when GetActionForCopy() == cpy_Self. \n - The following objects are passed as input parameters: the owning object copy and registrator of copied objects. \~ */ - virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * iReg = NULL ) = 0; - - /**\ru Выполнить действия при объединении владельца. \n - Вызывается перед слиянием владельца при условии GetActionForMerge() == mrg_Self. \n - В качестве входного параметра передается объект который будет поглощен. - \en Perform actions when merging the owner. \n - This function is called before merging the owner in a case when GetActionForMerge() == mrg_Self. \n - The object which will be absorbed is passed as input parameter. \~ */ - virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ) = 0; - - /**\ru Выполнить действия при замене владельца. \n - Вызывается перед выполнением замены владельца при условии GetActionForReplace() == rep_Self. \n - В качестве входного параметра передается объект - заместитель. - \en Perform actions when replacing the owner. \n - This function is called before replacing the owner in a case when GetActionForReplace() == rep_Self. \n - The substitutional object is passed as input parameter. \~ */ - virtual void OnReplaceOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ) = 0; - - /**\ru Выполнить действия при разделении владельца. \n - Вызывается после разбиения владеющего объекта при условии GetActionForSplit() == spl_Self. \n - В качестве входного параметра передается контейнер результатов разбиения. - \en Perform actions when splitting the owner. \n - This function is called after splitting the owning object in a case when GetActionForSplit() == spl_Self. \n - The container of splitting results is passed as input parameter. \~ */ - virtual void OnSplitOwner( const MbAttributeContainer & owner, const std::vector & others ) = 0; - - /**\ru Выполнить действия при удалении владельца. \n - Вызывается перед удалением объекта при условии GetActionForDelete() == spl_Self. - \en Perform actions when deleting the owner. \n - This function is called before deleting the owner in a case when GetActionForDelete() == spl_Self. \~ */ - virtual void OnDeleteOwner( const MbAttributeContainer & owner ) = 0; - /** \} */ - - /// \ru Выдать поведение атрибута при изменении владельца. \en Get behavior of attribute when changing the owner. - OnChangeOwnerAction GetActionForChange () const { return static_cast(forChange); } - /// \ru Выдать поведение атрибута при конвертации владельца. \en Get behavior of attribute when converting the owner. - OnConvertOwnerAction GetActionForConvert () const { return static_cast(forConvert); } - /// \ru Выдать поведение атрибута при трансформировании владельца. \en Get behavior of attribute when transforming the owner. - OnTransformOwnerAction GetActionForTransform() const { return static_cast(forTransform); } - /// \ru Выдать поведение атрибута при копировании владельца. \en Get behavior of attribute when copying the owner. - OnCopyOwnerAction GetActionForCopy () const { return static_cast(forCopy); } - /// \ru Выдать поведение атрибута при объединении владельца. \en Get behavior of attribute when merging the owner. - OnMergeOwnerAction GetActionForMerge () const { return static_cast(forMerge); } - /// \ru Выдать поведение атрибута при замене владельца. \en Get behavior of attribute when replacing the owner. - OnReplaceOwnerAction GetActionForReplace () const { return static_cast(forReplace); } - /// \ru Выдать поведение атрибута при разделении владельца. \en Get behavior of attribute when splitting the owner. - OnSplitOwnerAction GetActionForSplit () const { return static_cast(forSplit); } - /// \ru Выдать поведение атрибута при удалении владельца. \en Get behavior of attribute when deleting the owner. - OnDeleteOwnerAction GetActionForDelete () const { return static_cast(forDelete); } - - /// \ru Задать поведение атрибута при изменении владельца. \en Set behavior of attribute when changing the owner. - void SetActionForChange ( OnChangeOwnerAction a ) { forChange = (uint8)a; } - /// \ru Задать поведение атрибута при конвертации владельца. \en Set behavior of attribute when converting the owner. - void SetActionForConvert ( OnConvertOwnerAction a ) { forConvert = (uint8)a; } - /// \ru Задать поведение атрибута при трансформировании владельца. \en Set behavior of attribute when transforming the owner. - void SetActionForTransform( OnTransformOwnerAction a ) { forTransform = (uint8)a; } - /// \ru Задать поведение атрибута при копировании владельца. \en Set behavior of attribute when copying the owner. - void SetActionForCopy ( OnCopyOwnerAction a ) { forCopy = (uint8)a; } - /// \ru Задать поведение атрибута при объедении владельца. \en Set behavior of attribute when merging the owner. - void SetActionForMerge ( OnMergeOwnerAction a ) { forMerge = (uint8)a; } - /// \ru Задать поведение атрибута при замене владельца. \en Set behavior of attribute when replacing the owner. - void SetActionForReplace ( OnReplaceOwnerAction a ) { forReplace = (uint8)a; } - /// \ru Задать поведение атрибута при разбиении владельца. \en Set behavior of attribute when splitting the owner. - void SetActionForSplit ( OnSplitOwnerAction a ) { forSplit = (uint8)a; } - /// \ru Задать поведение атрибута при удалении владельца. \en Set behavior of attribute when deleting the owner. - void SetActionForDelete ( OnDeleteOwnerAction a ) { forDelete = (uint8)a; } - - /// \ru Определить поведение атрибута по другому атрибуту. \en Define behavior of an attribute by another attribute. - void InitActions ( const MbAttribute & ); - - bool CanBeFree () const { return freeable; } - bool CanBeCopied() const { return copyable; } - - void SetCanBeFree ( bool b ) { freeable = b; } - void SetCanBeCopied( bool b ) { copyable = b; } - - /// \ru Выдать свойства объекта. \en Get properties of the object. - virtual void GetProperties( MbProperties & ); - /// \ru Установить свойства объекта. \en Set properties of object. - virtual size_t SetProperties( const MbProperties & ); - /// \ru Выдать заголовок свойства объекта. \en Get a name of object property. - virtual MbePrompt GetPropertyName() = 0; - - virtual bool IsFamilyRegistrable() const; - -DECLARE_PERSISTENT_CLASS( MbAttribute ) -OBVIOUS_PRIVATE_COPY( MbAttribute ) -}; - -IMPL_PERSISTENT_OPS( MbAttribute ) - -//------------------------------------------------------------------------------ -/** \brief \ru Объект для свойств. - \en Object for properties. \~ - \details \ru Объект для свойств. \n - \en Object for properties. \n \~ - \ingroup Model_Attributes -*/ -// --- -class MATH_CLASS MbAttributeAction : public MbRefItem { -private : - uint8 & forChange; ///< \ru Поведение атрибута при изменении владельца. \en Behavior of attribute when changing the owner. - uint8 & forConvert; ///< \ru Поведение атрибута при конвертации владельца. \en Behavior of attribute when converting the owner. - uint8 & forTransform; ///< \ru Поведение атрибута при трансформировании владельца. \en Behavior of attribute when transforming the owner. - uint8 & forCopy; ///< \ru Поведение атрибута при копировании владельца. \en Behavior of attribute when copying the owner. - uint8 & forMerge; ///< \ru Поведение атрибута при объединении владельца. \en Behavior of attribute when merging the owner. - uint8 & forReplace; ///< \ru Поведение атрибута при замене владельца. \en Behavior of attribute when replacing the owner. - uint8 & forSplit; ///< \ru Поведение атрибута при разделении владельца. \en Behavior of attribute when splitting the owner. - uint8 & forDelete; ///< \ru Поведение атрибута при удалении владельца. \en Behavior of attribute when deleting the owner. - bool & freeable; ///< \ru Свободность атрибута. \en Attribute freeness - bool & copyable; ///< \ru Разрешение копировать атрибут. \en Permission to copy attribute. - -public: - /// \ru Конструктор с параметрами. \en Constructor with parameters. - MbAttributeAction( uint8 & cha, uint8 & con, uint8 & tra, uint8 & cop, uint8 & mer, uint8 & rep, uint8 & spl, uint8 & del, - bool & fre, bool & cob ) - : MbRefItem() - , forChange( cha ) - , forConvert( con ) - , forTransform( tra ) - , forCopy( cop ) - , forMerge( mer ) - , forReplace( rep ) - , forSplit( spl ) - , forDelete( del ) - , freeable( fre ) - , copyable( cob ) {} - /// \ru Деструктор. \en Destructor. - ~MbAttributeAction() {} - -public: - /// \ru Выдать свойства объекта. \en Get properties of the object. - void GetProperties( MbProperties & ); - /// \ru Установить свойства объекта. \en Set properties of object. - void SetProperties( const MbProperties & ); - -OBVIOUS_PRIVATE_COPY( MbAttributeAction ) -}; - - -//------------------------------------------------------------------------------ -// \ru Системные строки атрибутов. \en System strings of attributes. -// --- -namespace c3d // namespace C3D -{ - /// \ru Подсказка для эквидистантной грани c нулевым значением эквидистанты. \en Hint for an offset face with the null value of offset. - const c3d::string_t str_ShellFace ( _T( "c3d_ShellFace" ) ); - /// \ru Подсказка для эквидистантной грани. \en Hint for an offset face. - const c3d::string_t str_OffsetFace ( _T( "c3d_OffsetFace" ) ); - /// \ru Подсказка для вскрываемой грани. \en Hint for an open face. - const c3d::string_t str_OpenFace ( _T( "c3d_OpenFace" ) ); - /// \ru Подсказка для доп.эквидистантного смещения слипшейся грани. \en Hint for an offset of a stuck face. - const c3d::string_t str_StuckOffset ( _T( "c3d_StuckOffset" ) ); - /// \ru Подсказка для удаляемой слипшейся грани. \en Hint for a deleted stuck face. - const c3d::string_t str_StuckDelete ( _T( "c3d_StuckDelete" ) ); - - /// \ru Подсказка для расшивки граней по ребру. \en Hint for separation neighbour faces by an edge. - const c3d::string_t str_UnstitchByEdge( _T( "c3d_UnstitchByEdge" ) ); - - /// \ru Подсказка для проверки идентификатора боковой грани. \en Hint for checking flank's identifier. - const c3d::string_t str_CheckFlankId ( _T( "c3d_CheckFlankId" ) ); - /// \ru Подсказка для порядкового номера оболочки. \en Hint for shell sequence number. - const c3d::string_t str_ShellSequenceNumber( _T( "c3d_ShellSequenceNumber" ) ); - - /// \ru Подсказка для сохраняемого объекта. \en Hint for kept object. - const c3d::string_t str_KeptObject ( _T( "c3d_KeptObject" ) ); - /// \ru Подсказка для удаляемого объекта. \en Hint for deleting object. - const c3d::string_t str_DeletingObject( _T( "c3d_DeletingObject" ) ); - /// \ru Подсказка для временного объекта. \en Hint for temporal object. - const c3d::string_t str_TemporalObject( _T( "c3d_TemporalObject" ) ); - - /**\ru Для плоской грани, сгибаемой в цилиндр - параметр u, который меньше соответствующего параметра любой точки грани, - сгибаемой в конус - угловой параметр луча, выходящего из начала координат плоскости параметров и не пересекающего контуры грани. - \en For a planar face bended in cylinder - u-parameter which is less than corresponding parameter of any point on the face, - bended in cone - angular parameter of the ray which goes out from the parameters plane origin and does not intersect contours of the face. \~*/ - const c3d::string_t str_BendMinAnlge ( _T( "BendMinAnlge" ) ); - /// \ru Для цилиндрической и конической грани параметр u, который меньше соответствующего параметра любой точки грани. \en For a cylindrical and conical face - parameter u which is less than corresponding parameter of any point on the face. - const c3d::string_t str_UnbendMinAngle( _T( "UnbendMinAngle" ) ); -} // namespace C3D - -#endif // __ATTRIBUTE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Атрибуты объекта. + \en Object attributes. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ATTRIBUTE_H +#define __ATTRIBUTE_H + + +#include +#include +#include +#include + + +class MATH_CLASS MbVector3D; +class MATH_CLASS MbAxis3D; +class MATH_CLASS MbMatrix3D; +class MATH_CLASS MbProperties; +class MATH_CLASS MbAttributeContainer; +class MbRegDuplicate; +class MbRegTransform; + + +class MATH_CLASS MbAttribute; +namespace c3d // namespace C3D +{ +typedef SPtr AttrSPtr; +typedef SPtr ConstAttrSPtr; + +typedef std::vector AttrVector; +typedef std::vector ConstAttrVector; + +typedef std::vector AttrSPtrVector; +typedef std::vector ConstAttrSPtrVector; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Типы атрибутов. + \en Types of attributes. \~ + \details \ru Типы атрибутов объектов геометрической модели. + Атрибуты объектов группируются по семействам. + \en Types of geometric model objects attributes. + Objects attributes are grouped by families. \~ + \ingroup Model_Attributes + */ +enum MbeAttributeType +{ + at_Undefined = 0, ///< \ru Неопределенный - используется при поиске как "любой". \en Undefined - used as "any" in search. \n + + // \ru Типы простых атрибутов. \en Types of elementary attributes. + at_ElementaryAttribute = 101, ///< \ru Простой атрибут. \en Elementary attribute. + at_Identifier = 102, ///< \ru Идентификатор. \en Identifier. + at_Color = 103, ///< \ru Цвет. \en Color. + at_Width = 104, ///< \ru Ширина линий. \en Lines width. + at_Style = 105, ///< \ru Стиль линий. \en Lines style. + at_Visual = 106, ///< \ru Свойства для OpenGL. \en Properties for OpenGL. + at_Selected = 107, ///< \ru Селектированность. \en Selection. + at_Visible = 108, ///< \ru Видимость. \en Visibility. + at_WireCount = 109, ///< \ru Количество u-линий и v-линий отрисовочной сетки. \en The number of u-mesh and v-mesh drawing lines. \~ + at_Changed = 110, ///< \ru Изменённость. \en Modification. + at_Dencity = 111, ///< \ru Плотность. \en Density. + at_NameAttribute = 112, ///< \ru Топологическое имя. \en Topological name. + at_UpdateStamp = 113, ///< \ru Метка времени обновления. \en Stamp of update time. + at_Embodiment = 114, ///< \ru Признак исполнения (варианта реализации модели). \en Indication of embodiment (variant of model implementation). + at_Elasticity = 115, ///< \ru Механические характеристики: модуль Юнга и коэффициент Пуассана. \en Mechanical properties: Young's modulus and Poisson's ratio. + at_Strains = 116, ///< \ru Деформации. \en The strains. + at_ElementaryLast = 200, ///< \ru Простые атрибуты вставлять перед этим значением. \en Elementary attributes should be inserted before this value. \n + + // \ru Типы обобщенных атрибутов. \en Types of common attributes. + at_CommonAttribute = 201, ///< \ru Обобщенный атрибут. \en Common attribute. + at_BoolAttribute = 202, ///< \ru Булев атрибут. \en Boolean attribute. + at_IntAttribute = 203, ///< \ru Целочисленный атрибут. \en Integer attribute. + at_DoubleAttribute = 204, ///< \ru Действительный атрибут. \en Double attribute. + at_StringAttribute = 205, ///< \ru Строковый атрибут. \en String attribute. + at_GeomAttribute = 206, ///< \ru Геометрический атрибут. \en Geometric attribute. \n + at_StampRibAttribute = 207, ///< \ru Атрибут ребра жесткости листового тела. \en Attribute of reinforcement rib of sheet solid. \n + at_Int64Attribute = 208, ///< \ru Атрибут int64. \en Int64 attribute. + at_BinaryAttribute = 209, ///< \ru Бинарный атрибут. \en Binary attribute. + at_SweptFlangeAttribute = 210, ///< \ru Атрибут отбортовки листового тела. \en Swept flange attribute of a sheet solid. \n + at_CommonLast = 300, ///< \ru Обобщенные атрибуты вставлять перед этим значением. \en Common attributes should be inserted before this value. \n + + // \ru Типы связующих атрибутов. \en Types of linking attributes. + at_LinkingAttribute = 301, ///< \ru Связующий атрибут. \en Linking attribute. + at_AnchorAttribute = 302, ///< \ru Якорь. \en Anchor. \n + + // \ru Типы директивных атрибутов. \en Types of directive attributes. + at_DirectiveAttribute = 401, ///< \ru Директивный атрибут. \en Directive attribute. + at_KeepUniqueKey = 402, ///< \ru Поддерживать уникальность ключей. \en Support unique keys. \n + + // \ru Типы изделия. \en Types of product attributes. + at_ProductAttribute = 501, ///< \ru Атрибут конвертеров \en Converters attribute + at_ModelInfo = 502, ///< \ru Сведения о модели в целом. \en Information about model itself. + at_PersonOrganizationInfo = 503, ///< \ru Лицо и организация. \en Person and organization information. + at_ProductInfo = 504, ///< \ru Сведения об изделии. \en Product info. + at_STEPTextDescription = 505, ///< \ru Описание STEP. \en STEP description. + at_STEPReferenceHolder = 506, ///< \ru Обратная ссылка. \en Back reference. \n + + // \ru Типы пользовательских атрибутов. \en Types of user attributes. + at_UserAttribute = 601, ///< \ru Пользовательский атрибут. \en User attribute. + at_UserFirst = 602, ///< \ru Первый пользовательский атрибут. \en First user attribute. + at_UserLast = 900, ///< \ru Последний пользовательский атрибут. \en Last user attribute. \n + + // \ru Типы внешних (внесистемных) атрибутов. \en Types of external (off-system) attributes. + at_ExternalAttribute = 901, ///< \ru Внешний атрибут. \en External attribute. + at_ExternalAttributeImp = 902, ///< \ru Подтип - внешний атрибут \en Subtype - external attribute. + + at_FreeItem = 1000, ///< \ru Тип для прочих объектов. \en Type for the other objects. + +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Типы контейнеров атрибутов. + \en Types of attribute containers. \~ + \details \ru Типы контейнеров атрибутов наследников контейнера атрибутов. + Каждый отдельный атрибут может содержать свой контейнер атрибутов. + \en Types of attribute containers which are inheritors of attribute container. + Each separate attribute may have its attribute container. \~ + \ingroup Model_Attributes + */ +enum MbeImplicationType +{ + ace_Attribute, ///< \ru Контейнер атрибутов, содержащий другие атрибуты. \en Attribute container which contains other attributes. + ace_ModelItem, ///< \ru Контейнер атрибутов объектов геометрической модели. \en Container of geometric model objects attributes. + ace_TopItem, ///< \ru Контейнер атрибутов именованных топологических объектов. \en Container of named topological objects attributes. + ace_MeshItem, ///< \ru Контейнер атрибутов сеточных примитивов. \en Container of mesh primitives attributes. + ace_Model, ///< \ru Контейнер атрибутов геометрической модели. \en Container of geometric model attributes. + ace_AttribContainer, ///< \ru Контейнер атрибутов. \en Attribute container. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Атрибуты объекта. + \en Object attributes. \~ + \details \ru Атрибуты содержат информацию, дополняющую описание геометрической формы объекта. + Атрибут не является неотъемлемой частью объекта, а является элементом данных, которыми может быть наделен объект.\n + Атрибуты являются агентами передачи данных геометрического ядра от одного приложения другому приложению.\n + Атрибуты могут быть следующих типов.\n + Простой атрибут - атрибут несущий простую, однозначно интерпретируемую, информацию, например, цвет, признак выбора.\n + Обобщенный атрибут - атрибут стандартного типа со строковым наименованием, + например, имя, целое число, вещественное число, строка, точка, вектор, указатель.\n + С помощью таких атрибутов приложения могут обмениваться какой либо специфичной информацией + без необходимости разработки дополнительных комплексных атрибутов.\n + Комплексный атрибут - атрибут состоящий из предопределенного набора данных, + описывающих природу атрибута и его смысловую нагрузку, а так же способ его интерпретации. + Такими атрибутами могут описываться некоторые ограничения или простые зависимости а так же аннотационные объекты.\n + Директивный атрибут - атрибут определяющий предназначения объекта или действия которые необходимо с ним произвести, + например атрибут "вычитание" подразумевает что некое тело предназначено для вычитания из другого тела, + и не важно из какого. + Связующий атрибут - атрибут предназначенный для связи объекта геометрического ядра с абстрактным контейнером данных, + то есть набором данных, формат и смысловая нагрузка которых не может быть описана в рамках других атрибутов.\n + \en Attributes contain information supplementing description of object geometric shape. + Attribute is not an intrinsic part of the object, but it is an element of data which the object may contain. \n + Attributes are geometric kernel agents for transferring data from one application to another. \n + The possible types of attributes are the following. \n + Elementary attribute - an attribute which reflects simple and clearly interpreted information, for example, color or selection attribute.\n + Common attribute - attribute of standard type with string naming, + for example: name, integer value, double value, string, point, vector, pointer. \n + Applications may communicate any specific information using such attributes + without necessity of additional complex attributes developing. \n + Complex attribute - an attribute which consists of predefined data set, + which describe a nature of attribute, its semantic meaning and a way of its interpretation. + Such attributes can describe some of constraints or simple dependences and annotation objects.\n + Directive attribute - an attribute defining the purpose of object or actions which should be performed with it, + for example, an attribute "subtraction" implies that one solid is purposed for subtraction from another, + no matter from what exactly. + Linking attribute - an attribute designed for linking of geometric kernel object with abstract container of data, + i.e. a set of data, which format and semantic meaning can not be described by other attributes. \n \~ + \ingroup Model_Attributes +*/ +// --- +class MATH_CLASS MbAttribute : public MbRefItem, + public TapeBase +{ +public: + /**\ru Поведение атрибута при изменении владельца, не связанном с другими описанными действия. + \en Behavior of attribute which is not associated with other described actions when changing the owner. \~ */ + enum OnChangeOwnerAction { + chn_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnChangeOwner. \en Behavior defined by the virtual function OnChangeOwner. + chn_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. + chn_Keep, ///< \ru Сохранить атрибут, т.е. ничего с ним не делать. \en Save attribute, i.e. do not do anything with it. + chn_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). + }; + + /**\ru Поведение атрибута при перерождении объекта в другой объект. + \en Behavior of attribute when an object regenerates in other object. \~ */ + enum OnConvertOwnerAction { + cnv_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnConvertOwner. \en Behavior defined by the virtual function OnConvertOwner. + cnv_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. + cnv_Keep, ///< \ru Сохранить атрибут, т.е. ничего с ним не делать. \en Save attribute, i.e. do not do anything with it. + cnv_Copy, ///< \ru Скопировать атрибут и прицепить его копию к копии владельца. \en Copy an attribute and attach its copy to an owner copy. + cnv_Convert, ///< \ru Конвертировать атрибут и прицепить результат к копии владельца. \en Copy an attribute and attach the result to an owner copy. + cnv_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). + }; + + /**\ru Поведение атрибута при преобразовании владельца (по матрице). + \en Behaviour of attribute when transforming the owner (by the matrix). \~ */ + enum OnTransformOwnerAction { + trn_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnTransformOwner. \en Behavior defined by the virtual function OnTransformOwner. + trn_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. + trn_Keep, ///< \ru Сохранить атрибут, т.е. ничего с ним не делать. \en Save attribute, i.e. do not do anything with it. + trn_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). + }; + + /**\ru Поведение атрибута при копировании владельца. + \en Behaviour of attribute when copying the owner. \~ */ + enum OnCopyOwnerAction { + cpy_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnCopyOwner. \en Behavior defined by the virtual function OnCopyOwner. + cpy_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. + cpy_Keep, ///< \ru Сохранить атрибут, т.е. ничего с ним не делать. \en Save attribute, i.e. do not do anything with it. + cpy_Copy, ///< \ru Скопировать атрибут и прицепить его копию к копии владельца. \en Copy an attribute and attach its copy to an owner copy. + cpy_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). + }; + + /**\ru Поведение атрибута при объединении владельца с другим объектом. + \en Behaviour of attribute when merging of the owner with another object. \~ */ + enum OnMergeOwnerAction { + mrg_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnMergeOwner. \en Behavior is defined by the virtual function OnMergeOwner. + mrg_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. + mrg_Keep, ///< \ru Сохранить атрибут, т.е. ничего с ним не делать. \en Save attribute, i.e. do not do anything with it. + mrg_KeepAll, ///< \ru Передать атрибут от поглощаемого объекта поглощающему объекту без замещения. \en Transmit attribute from absorbed object to absorbing object without replacing. + mrg_KeepRep, ///< \ru Передать атрибут от поглощаемого объекта поглощающему объекту с замещением. \en Transmit attribute from absorbed object to absorbing object with replacing. + mrg_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). + }; + + /**\ru Поведение атрибута при замещении владельца с другим объектом. + \en Behavior of attribute when replacing the owner by another object. \~ */ + enum OnReplaceOwnerAction { + rep_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnReplaceOwner. \en Behavior is defined by the virtual function OnReplaceOwner. + rep_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. + rep_Keep, ///< \ru Сохранить атрибут, т.е. ничего с ним не делать. \en Save attribute, i.e. do not do anything with it. + rep_KeepAll, ///< \ru Передать атрибут от замещаемого объекта замещающему объекту без замещения. \en Transmit attribute from replaced object to substitutional object without replacing. + rep_KeepRep, ///< \ru Передать атрибут от замещаемого объекта замещающему объекту с замещением. \en Transmit attribute from replaced object to substitutional object with replacing. + rep_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). + }; + + /**\ru Поведение атрибута при разделении владельца. + \en Behavior of attribute when splitting the owner. \~ */ + enum OnSplitOwnerAction { + spl_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnSplitOwner. \en Behavior is defined by the virtual function OnSplitOwner. + spl_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. + spl_Keep, ///< \ru Сохранить атрибут, т.е. ничего с ним не делать. \en Save attribute, i.e. do not do anything with it. + spl_Copy, ///< \ru Размножить(скопировать) атрибут для каждого результата разбиения. \en Duplicate (copy) attribute for each result of splitting. + spl_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). + }; + + /**\ru Поведение атрибута при удалении владельца. + \en Behavior of attribute when deleting the owner. \~ */ + enum OnDeleteOwnerAction { + del_Self = 0, ///< \ru Поведение, определяемое виртуальной функцией OnDeleteOwner. \en Behavior defined by the virtual function OnDeleteOwner. + del_Free, ///< \ru Освободить атрибут, если это возможно, в противном случае удалить. \en Free attribute if it is possible, otherwise delete it. + del_ActCount, ///< \ru Количество элементов в перечислении (добавлять перед данным значением). \en The number of elements in enumeration (add before the given value). + }; + +private : + uint8 forChange; ///< \ru Поведение атрибута при изменении владельца. \en Behavior of attribute when changing the owner. + uint8 forConvert; ///< \ru Поведение атрибута при конвертации владельца. \en Behavior of attribute when converting the owner. + uint8 forTransform; ///< \ru Поведение атрибута при трансформировании владельца. \en Behavior of attribute when transforming the owner. + uint8 forCopy; ///< \ru Поведение атрибута при копировании владельца. \en Behavior of attribute when copying the owner. + uint8 forMerge; ///< \ru Поведение атрибута при объединении владельца. \en Behavior of attribute when merging the owner. + uint8 forReplace; ///< \ru Поведение атрибута при замене владельца. \en Behavior of attribute when replacing the owner. + uint8 forSplit; ///< \ru Поведение атрибута при разделении владельца. \en Behavior of attribute when splitting the owner. + uint8 forDelete; ///< \ru Поведение атрибута при удалении владельца. \en Behavior of attribute when deleting the owner. + bool freeable; ///< \ru Свободность атрибута. \en Attribute freeness + bool copyable; ///< \ru Разрешение копировать атрибут. \en Permission to copy attribute. + +protected : + /// \ru Конструктор без параметров для наследников. \en Constructor without parameters for inheritors. + MbAttribute(); +public : + /// \ru Деструктор. \en Destructor. + virtual ~MbAttribute(); + +public : + /** \ru \name Общие функции атрибутов + \en \name Common functions of attributes + \{ */ + /// \ru Выдать регистрационный тип (для копирования, дублирования). \en Get registrational type (for copying, duplication) + virtual MbeRefType RefType() const; + /// \ru Выдать тип контейнера атрибутов. \en Get attribute container type. + virtual MbeImplicationType ImplicationType() const; + /// \ru Выдать тип атрибута. \en Get attribute type. + virtual MbeAttributeType AttributeFamily() const = 0; + /// \ru Выдать подтип атрибута. \en Get subtype of an attribute. + virtual MbeAttributeType AttributeType() const = 0; + /// \ru Сделать копию элемента. \en Create a copy of the element. + virtual MbAttribute & Duplicate( MbRegDuplicate * iReg = NULL ) const = 0; + /** \brief \ru Определить, являются ли объекты равными. + \en Determine whether objects are equal. \~ + \details \ru Равными считаются однотипные объекты, все данные которых одинаковы (равны). + \en Objects of the same types with similar (equal) data are considered to be equal. \~ + \param[in] item - \ru Объект для сравнения. + \en Objects for comparison. \~ + \param[in] accuracy - \ru Точность сравнения. + \en The accuracy to compare. \~ + \return \ru Равны ли объекты. + \en Whether objects are equal. \~ + */ + virtual bool IsSame( const MbAttribute & item, double accuracy ) const = 0; + /// \ru Инициализировать данные по присланным. \en Initialize data. + virtual bool Init( const MbAttribute & ) = 0; + + /// \ru Проверить тип атрибута. \en Check an attribute type. + bool IsA( MbeAttributeType t ) const { return t == AttributeFamily(); } + /** \} */ + + /** \ru \name Действия над объектами геометрического ядра, влияющие на состояние атрибутов + \en \name Actions with objects of geometric kernel influencing on states of attributes. + \{ */ + /** \brief \ru Выполнить действия при изменении владельца, не связанное с другими действиями. + \en Perform actions which are not associated with other actions when changing the owner. \~ + \details \ru Действия при изменении владельца, не связанное с другими действиями. \n + Вызывается после изменения владеющего объекта при условии GetActionForChange() == chn_Self. + \en Actions which are not associated with other actions when changing the owner. \n + This function is called after changing the owning object in a case when GetActionForChange() == chn_Self. \~ */ + virtual void OnChangeOwner( const MbAttributeContainer & owner ) = 0; + + /**\ru Выполнить действия при конвертации владельца, \n + Вызывается после конвертирования владеющего объекта при условии GetActionForConvert() == cnv_Self. \n + В качестве входного параметра передается результат конвертирования объекта. + \en Perform actions when converting the owner, \n + This function is called after converting the owning object in a case when GetActionForConvert() == cnv_Self. \n + The result of object converting is passed as input parameter. \~ */ + virtual void OnConvertOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ) = 0; + + /**\ru Выполнить действия при трансформировании владельца, \n + Вызывается после трансформирования владеющего объекта при условии GetActionForTransform() == trn_Self. + В качестве входного параметра может передаваться регистратор трансформированных объектов. + \en Perform actions when transforming the owner, \n + This function is called after transforming the owning object in a case when GetActionForTransform() == trn_Self. + The registrator of transformed objects may be passed as input parameter. \~ */ + virtual void OnTransformOwner( const MbAttributeContainer & owner, const MbMatrix3D &, MbRegTransform * = NULL ) = 0; + + /**\ru Выполнить действия при перемещении владельца. \n + Вызывается после перемещения владеющего объекта при условии GetActionForTransform() == trn_Self. + В качестве входного параметра может передаваться регистратор трансформированных объектов. + \en Perform actions when moving the owner. \n + This function is called after moving the owning object in a case when GetActionForTransform() == trn_Self. + The registrator of transformed objects may be passed as input parameter. \~ */ + virtual void OnMoveOwner( const MbAttributeContainer & owner, const MbVector3D &, MbRegTransform * = NULL ) = 0; + + /**\ru Выполнить действия при вращении владельца. \n + Вызывается после вращения владеющего объекта при условии GetActionForTransform() == trn_Self. + В качестве входного параметра может передаваться регистратор трансформированных объектов. + \en Perform actions when rotating the owner. \n + This function is called after rotating the owning object in a case when GetActionForTransform() == trn_Self. + The registrator of transformed objects may be passed as input parameter. \~ */ + virtual void OnRotateOwner( const MbAttributeContainer & owner, const MbAxis3D &, double angle, MbRegTransform * = NULL ) = 0; + + /**\ru Выполнить действия при копировании владельца. \n + Вызывается после копирования владеющего объекта при условии GetActionForCopy() == cpy_Self. \n + В качестве входных параметров передаются: копия владеющего объекта и регистратор скопированных объектов. + \en Perform actions when copying the owner. \n + This function is called after copying the owning object in a case when GetActionForCopy() == cpy_Self. \n + The following objects are passed as input parameters: the owning object copy and registrator of copied objects. \~ */ + virtual void OnCopyOwner( const MbAttributeContainer & owner, MbAttributeContainer & other, MbRegDuplicate * = NULL ) = 0; + + /**\ru Выполнить действия при объединении владельца. \n + Вызывается перед слиянием владельца при условии GetActionForMerge() == mrg_Self. \n + В качестве входного параметра передается объект который будет поглощен. + \en Perform actions when merging the owner. \n + This function is called before merging the owner in a case when GetActionForMerge() == mrg_Self. \n + The object which will be absorbed is passed as input parameter. \~ */ + virtual void OnMergeOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ) = 0; + + /**\ru Выполнить действия при замене владельца. \n + Вызывается перед выполнением замены владельца при условии GetActionForReplace() == rep_Self. \n + В качестве входного параметра передается объект - заместитель. + \en Perform actions when replacing the owner. \n + This function is called before replacing the owner in a case when GetActionForReplace() == rep_Self. \n + The substitutional object is passed as input parameter. \~ */ + virtual void OnReplaceOwner( const MbAttributeContainer & owner, MbAttributeContainer & other ) = 0; + + /**\ru Выполнить действия при разделении владельца. \n + Вызывается после разбиения владеющего объекта при условии GetActionForSplit() == spl_Self. \n + В качестве входного параметра передается контейнер результатов разбиения. + \en Perform actions when splitting the owner. \n + This function is called after splitting the owning object in a case when GetActionForSplit() == spl_Self. \n + The container of splitting results is passed as input parameter. \~ */ + virtual void OnSplitOwner( const MbAttributeContainer & owner, const std::vector & others ) = 0; + + /**\ru Выполнить действия при удалении владельца. \n + Вызывается перед удалением объекта при условии GetActionForDelete() == spl_Self. + \en Perform actions when deleting the owner. \n + This function is called before deleting the owner in a case when GetActionForDelete() == spl_Self. \~ */ + virtual void OnDeleteOwner( const MbAttributeContainer & owner ) = 0; + /** \} */ + + /// \ru Выдать поведение атрибута при изменении владельца. \en Get behavior of attribute when changing the owner. + OnChangeOwnerAction GetActionForChange () const { return static_cast(forChange); } + /// \ru Выдать поведение атрибута при конвертации владельца. \en Get behavior of attribute when converting the owner. + OnConvertOwnerAction GetActionForConvert () const { return static_cast(forConvert); } + /// \ru Выдать поведение атрибута при трансформировании владельца. \en Get behavior of attribute when transforming the owner. + OnTransformOwnerAction GetActionForTransform() const { return static_cast(forTransform); } + /// \ru Выдать поведение атрибута при копировании владельца. \en Get behavior of attribute when copying the owner. + OnCopyOwnerAction GetActionForCopy () const { return static_cast(forCopy); } + /// \ru Выдать поведение атрибута при объединении владельца. \en Get behavior of attribute when merging the owner. + OnMergeOwnerAction GetActionForMerge () const { return static_cast(forMerge); } + /// \ru Выдать поведение атрибута при замене владельца. \en Get behavior of attribute when replacing the owner. + OnReplaceOwnerAction GetActionForReplace () const { return static_cast(forReplace); } + /// \ru Выдать поведение атрибута при разделении владельца. \en Get behavior of attribute when splitting the owner. + OnSplitOwnerAction GetActionForSplit () const { return static_cast(forSplit); } + /// \ru Выдать поведение атрибута при удалении владельца. \en Get behavior of attribute when deleting the owner. + OnDeleteOwnerAction GetActionForDelete () const { return static_cast(forDelete); } + + /// \ru Задать поведение атрибута при изменении владельца. \en Set behavior of attribute when changing the owner. + void SetActionForChange ( OnChangeOwnerAction a ) { forChange = (uint8)a; } + /// \ru Задать поведение атрибута при конвертации владельца. \en Set behavior of attribute when converting the owner. + void SetActionForConvert ( OnConvertOwnerAction a ) { forConvert = (uint8)a; } + /// \ru Задать поведение атрибута при трансформировании владельца. \en Set behavior of attribute when transforming the owner. + void SetActionForTransform( OnTransformOwnerAction a ) { forTransform = (uint8)a; } + /// \ru Задать поведение атрибута при копировании владельца. \en Set behavior of attribute when copying the owner. + void SetActionForCopy ( OnCopyOwnerAction a ) { forCopy = (uint8)a; } + /// \ru Задать поведение атрибута при объедении владельца. \en Set behavior of attribute when merging the owner. + void SetActionForMerge ( OnMergeOwnerAction a ) { forMerge = (uint8)a; } + /// \ru Задать поведение атрибута при замене владельца. \en Set behavior of attribute when replacing the owner. + void SetActionForReplace ( OnReplaceOwnerAction a ) { forReplace = (uint8)a; } + /// \ru Задать поведение атрибута при разбиении владельца. \en Set behavior of attribute when splitting the owner. + void SetActionForSplit ( OnSplitOwnerAction a ) { forSplit = (uint8)a; } + /// \ru Задать поведение атрибута при удалении владельца. \en Set behavior of attribute when deleting the owner. + void SetActionForDelete ( OnDeleteOwnerAction a ) { forDelete = (uint8)a; } + + /// \ru Определить поведение атрибута по другому атрибуту. \en Define behavior of an attribute by another attribute. + void InitActions ( const MbAttribute & ); + + bool CanBeFree () const { return freeable; } + bool CanBeCopied() const { return copyable; } + + void SetCanBeFree ( bool b ) { freeable = b; } + void SetCanBeCopied( bool b ) { copyable = b; } + + /// \ru Выдать свойства объекта. \en Get properties of the object. + virtual void GetProperties( MbProperties & ); + /// \ru Установить свойства объекта. \en Set properties of object. + virtual size_t SetProperties( const MbProperties & ); + /// \ru Выдать заголовок свойства объекта. \en Get a name of object property. + virtual MbePrompt GetPropertyName() = 0; + + virtual bool IsFamilyRegistrable() const; + +DECLARE_PERSISTENT_CLASS( MbAttribute ) +OBVIOUS_PRIVATE_COPY( MbAttribute ) +}; + +IMPL_PERSISTENT_OPS( MbAttribute ) + +//------------------------------------------------------------------------------ +/** \brief \ru Объект для свойств. + \en Object for properties. \~ + \details \ru Объект для свойств. \n + \en Object for properties. \n \~ + \ingroup Model_Attributes +*/ +// --- +class MATH_CLASS MbAttributeAction : public MbRefItem { +private : + uint8 & forChange; ///< \ru Поведение атрибута при изменении владельца. \en Behavior of attribute when changing the owner. + uint8 & forConvert; ///< \ru Поведение атрибута при конвертации владельца. \en Behavior of attribute when converting the owner. + uint8 & forTransform; ///< \ru Поведение атрибута при трансформировании владельца. \en Behavior of attribute when transforming the owner. + uint8 & forCopy; ///< \ru Поведение атрибута при копировании владельца. \en Behavior of attribute when copying the owner. + uint8 & forMerge; ///< \ru Поведение атрибута при объединении владельца. \en Behavior of attribute when merging the owner. + uint8 & forReplace; ///< \ru Поведение атрибута при замене владельца. \en Behavior of attribute when replacing the owner. + uint8 & forSplit; ///< \ru Поведение атрибута при разделении владельца. \en Behavior of attribute when splitting the owner. + uint8 & forDelete; ///< \ru Поведение атрибута при удалении владельца. \en Behavior of attribute when deleting the owner. + bool & freeable; ///< \ru Свободность атрибута. \en Attribute freeness + bool & copyable; ///< \ru Разрешение копировать атрибут. \en Permission to copy attribute. + +public: + /// \ru Конструктор с параметрами. \en Constructor with parameters. + MbAttributeAction( uint8 & cha, uint8 & con, uint8 & tra, uint8 & cop, uint8 & mer, uint8 & rep, uint8 & spl, uint8 & del, + bool & fre, bool & cob ) + : MbRefItem() + , forChange( cha ) + , forConvert( con ) + , forTransform( tra ) + , forCopy( cop ) + , forMerge( mer ) + , forReplace( rep ) + , forSplit( spl ) + , forDelete( del ) + , freeable( fre ) + , copyable( cob ) {} + /// \ru Деструктор. \en Destructor. + ~MbAttributeAction() {} + +public: + /// \ru Выдать свойства объекта. \en Get properties of the object. + void GetProperties( MbProperties & ); + /// \ru Установить свойства объекта. \en Set properties of object. + void SetProperties( const MbProperties & ); + +OBVIOUS_PRIVATE_COPY( MbAttributeAction ) +}; + + +//------------------------------------------------------------------------------ +// \ru Системные строки атрибутов. \en System strings of attributes. +// --- +namespace c3d // namespace C3D +{ + /// \ru Подсказка для эквидистантной грани c нулевым значением эквидистанты. \en Hint for an offset face with the null value of offset. + const_expr TCHAR c3dStr_ShellFace[] = _T( "c3d_ShellFace" ); + /// \ru Подсказка для эквидистантной грани. \en Hint for an offset face. + const_expr TCHAR c3dStr_OffsetFace[] = _T( "c3d_OffsetFace" ); + /// \ru Подсказка для вскрываемой грани. \en Hint for an open face. + const_expr TCHAR c3dStr_OpenFace[] = _T( "c3d_OpenFace" ); + /// \ru Подсказка для доп.эквидистантного смещения слипшейся грани. \en Hint for an offset of a stuck face. + const_expr TCHAR c3dStr_StuckOffset[] = _T( "c3d_StuckOffset" ); + /// \ru Подсказка для удаляемой слипшейся грани. \en Hint for a deleted stuck face. + const_expr TCHAR c3dStr_StuckDelete[] = _T( "c3d_StuckDelete" ); + + /// \ru Подсказка для расшивки граней по ребру. \en Hint for separation neighbour faces by an edge. + const_expr TCHAR c3dStr_UnstitchByEdge[] = _T( "c3d_UnstitchByEdge" ); + /// \ru Подсказка для проверки идентификатора боковой грани. \en Hint for checking flank's identifier. + const_expr TCHAR c3dStr_CheckFlankId[] = _T( "c3d_CheckFlankId" ); + /// \ru Подсказка для порядкового номера оболочки. \en Hint for shell sequence number. + const_expr TCHAR c3dStr_ShellSequenceNumber[] = _T( "c3d_ShellSequenceNumber" ); + /// \ru Подсказка для сохраняемого объекта. \en Hint for kept object. + const_expr TCHAR c3dStr_KeptObject[] = _T( "c3d_KeptObject" ); + /// \ru Подсказка для удаляемого объекта. \en Hint for deleting object. + const_expr TCHAR c3dStr_DeletingObject[] = _T( "c3d_DeletingObject" ); + /// \ru Подсказка для временного объекта. \en Hint for temporal object. + const_expr TCHAR c3dStr_TemporalObject[] = _T( "c3d_TemporalObject" ); + /// \ru Подсказка для временного маркера слитых атрибутов. \en Hint for a temporary marker of merged attributes. + const_expr TCHAR c3dStr_MergedStateMarker[] = _T( "c3d_MergedStateMarker" ); + + /**\ru Для плоской грани, сгибаемой в цилиндр - параметр u, который меньше соответствующего параметра любой точки грани, + сгибаемой в конус - угловой параметр луча, выходящего из начала координат плоскости параметров и не пересекающего контуры грани. + \en For a planar face bended in cylinder - u-parameter which is less than corresponding parameter of any point on the face, + bended in cone - angular parameter of the ray which goes out from the parameters plane origin and does not intersect contours of the face. \~*/ + const_expr TCHAR c3dStr_BendMinAnlge[] = _T( "BendMinAnlge" ); + /// \ru Для цилиндрической и конической грани параметр u, который меньше соответствующего параметра любой точки грани. \en For a cylindrical and conical face - parameter u which is less than corresponding parameter of any point on the face. + const_expr TCHAR c3dStr_UnbendMinAngle[] = _T( "UnbendMinAngle" ); + /// \ru Подсказка для продленной грани. \en Hint for extended face. + const_expr TCHAR c3dStr_ExtendedFace[] = _T( "c3d_ExtendedFace" ); + + /// \ru Подсказка для контрольного значения массы. \en Hint for the mass validation property. + const_expr TCHAR c3dStr_ValidationPropertyMassExchange[] = _T( "c3d_ValidationPropertyMassExchange" ); + /// \ru Подсказка для контрольного значения объёма. \en Hint for the volume validation property. + const_expr TCHAR c3dStr_ValidationPropertyVolumeExchange[] = _T( "c3d_ValidationPropertyVolumeExchange" ); + /// \ru Подсказка для контрольного значения площади поверхности. \en Hint for the surface area validation property. + const_expr TCHAR c3dStr_ValidationPropertySurfaceAreaExchange[] = _T( "c3d_ValidationPropertySurfaceAreaExchange" ); + /// \ru Подсказка для идентификатора элемента при обмене данными. \en Hint for the item identifier in model exchange operations. + const_expr TCHAR c3dStr_ItemIdentifierExchange[] = _T( "c3d_ItemIdentifierExchange" ); +} // namespace C3D + +#endif // __ATTRIBUTE_H diff --git a/C3d/Include/attribute_container.h b/C3d/Include/attribute_container.h index ffbd83f..ad82ea5 100644 --- a/C3d/Include/attribute_container.h +++ b/C3d/Include/attribute_container.h @@ -1,338 +1,339 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Контейнер атрибутов. - \en An attribute container. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ATTRIBUTE_CONTAINER_H -#define __ATTRIBUTE_CONTAINER_H - - -#include -#include -#include -#include -#include - - -class MATH_CLASS reader; -class MATH_CLASS writer; -class MATH_CLASS MbVector3D; -class MATH_CLASS MbAxis3D; -class MATH_CLASS MbMatrix3D; -class MATH_CLASS MbAttribute; -class MATH_CLASS MbUserAttribute; -class MATH_CLASS MbExternalAttribute; -class MATH_CLASS MbProperties; -class MbRegDuplicate; -class MbRegTransform; - - -//------------------------------------------------------------------------------ -/** \brief \ru Контейнер атрибутов. - \en An attribute container. \~ - \details \ru Контейнер атрибутов. \n - От данного класса наследуются объекты модели геометрического ядра MbItem - и топологические объекты с именем MbTopologyItem .\n - Наследники данного класса содержат атрибуты.\n - Методами данного класса выполняются действия над атрибутами объектов геометрического ядра.\n - Атрибут может влиять на состояние атрибута через его владельца, - тo есть геометрическое ядро предусматривает возможность передачи атрибутам информации об изменениях - их владельцев посредством вызовов предопределенных функций у самого атрибута.\n - Кроме передачи самой информации об изменениях происходящих с владельцем, - предусмотрена возможность определять поведение атрибута при этих изменениях путем выбора - одного из предопределенных типов поведения на каждое изменения владельца.\n - Типы действий, влияющих на состояние атрибутов.\n - Копирование, например, при создании копии тела. Действие над атрибутом производится после копирования владеющего объекта.\n - Разделение, например, разделение грани на две части при вырезании. - Действие над атрибутом производится после разбиения владеющего объекта.\n - Слияние, например, слияние граней при булевых операциях. - Действие над атрибутом производится перед выполнением слияния объектов. - Обрабатываются атрибуты всех объектов, участвующих в слиянии.\n - Изменение, не связанное с разделением или слиянием. - Действие над атрибутом производится после изменения владеющего объекта.\n - Преобразование, например, поворот или параллельный перенос. - Действие над атрибутом производится после преобразования владеющего объекта.\n - Подмена, например замена одной грани тела на другую. Действие над атрибутом производится перед выполнением замены объектов. - Обрабатываются атрибуты всех объектов, участвующих в замене.\n - Удаление объекта. Действие над атрибутом производится перед удалением объекта.\n - \en An attribute container. \n - The inheritors of this class are: objects of geometric kernel model of type MbItem - and topological objects of type MbTopologyItem.\n - Inheritors of this class contain attributes.\n - Operations with attributes of geometric kernel objects are performed by methods of this class.\n - Attribute can affect attribute state using its owner, - i.e. geometric kernel provides an opportunity for transmission to attributes the information about changes - of their owners by calling the predefined functions of the attribute.\n - In addition to transfer of information about changes occurring with owner - provided a possibility to determine the behavior of attribute with these changes by selecting of - one of the predefined types of behavior for each changing of the owner.\n - Types of actions that affect the states of attributes.\n - Copying, for example, when creating a copy of solid. Action on attribute is performed after copying of owning object.\n - Splitting. For example, splitting of a face into two parts in cutting. - Action on attribute is performed after splitting of owning object.\n - Merging. For example, merging of faces in boolean operations. - Action on attribute is performed after merging of owning object.\n - Attributes of all objects involved in merging are processed.\n - Changing which is not associated with splitting or merging. - Action on attribute is performed after changing of owning object.\n - Transformation. For example, rotation or parallel translation. - Action on attribute is performed after transformation of owning object.\n - Replacement. For example, replacement of one face of a solid to another. Action on attribute is performed after replacement of objects. - Attributes of all objects involved in replacement are processed.\n - Deletion of an object. Action on attribute is performed after deletion of an object.\n \~ - \ingroup Model_Attributes -*/ -// --- -class MATH_CLASS MbAttributeContainer -{ -typedef MultiMap AttrMap_t; - -private: - AttrMap_t attributes; ///< \ru Множество атрибутов. \en Set of attributes. - -protected: - /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. - MbAttributeContainer( const MbAttributeContainer &, MbRegDuplicate * ); -public: - /// \ru Конструктор без параметров. \en Constructor without parameters. - MbAttributeContainer(); - /// \ru Конструктор по атрибуту. \en Constructor by attribute. - MbAttributeContainer( MbAttribute & ); - /// \ru Деструктор. \en Destructor. - virtual ~MbAttributeContainer(); - -public: - - /// \ru Выдать тип контейнера атрибутов. \en Get attribute container type. - virtual MbeImplicationType ImplicationType() const { return ace_AttribContainer; } - - /** \ru \name Общие функции над атрибутами - \en \name Common functions of attributes - \{ */ - /// \ru Cдублировать атрибуты присланного объекта, свои отпустить. \en Duplicate attributes of a given object, release existing attributes. - void AttributesAssign( const MbAttributeContainer & ); - /// \ru Выдать количество объектов. \en Get the number of objects. - size_t AttributesCount() const { return attributes.Count(); } - /// \ru Удалить все атрибуты из контейнера. \en Delete all attributes from container. - void RemoveAttributes(); - - /// \ru Добавить атрибут в контейнер. \en Add attribute in container. - MbAttribute * AddAttribute( MbAttribute *, bool checkSame = true ); - /// \ru Добавить атрибут в контейнер (всегда копирует атрибут). \en Add attribute in container (always copies the attribute). - MbAttribute * AddAttribute( const MbAttribute &, bool checkSame = true ); - /// \ru Выдать атрибуты заданного семейства. \en Get attributes of a given family. - void GetAttributes( c3d::AttrVector &, MbeAttributeType aFamily, MbeAttributeType subType ) const; - /// \ru Выдать атрибуты заданного типа. \en Get attributes of a given type. - void GetAttributes( c3d::AttrVector &, MbeAttributeType aType ) const; - /// \ru Выдать атрибуты по строке описания. \en Get attributes using sample of description string. - void GetCommonAttributes( c3d::AttrVector &, const c3d::string_t & samplePrompt, MbeAttributeType subType = at_Undefined ) const; - /// \ru Выдать строковые атрибуты по строке содержания. \en Get string attributes using sample of contents of the string. - void GetStringAttributes( c3d::AttrVector &, const c3d::string_t & sampleContent ) const; - - /// \ru Выдать атрибут заданного типа, если их несколько - то первый попавшийся. \en Get an attribute of a given type, the first one is returned if there are many. - //const MbAttribute * GetAttribute( MbeAttributeType subType ) const; - /// \ru Удалить атрибут из контейнера. \en Delete an attribute from container. - bool RemoveAttribute( const MbAttribute *, bool checkAccuracySame = false, double accuracy = LENGTH_EPSILON ); - /// \ru Удалить атрибуты заданного типа. \en Delete attributes of a given type. - bool RemoveAttributes( MbeAttributeType type, MbeAttributeType subType ); - - /// \ru Выдать простой атрибут данного подтипа. \en Get a simple attribute of a given subtype. - const MbAttribute * GetSimpleAttribute( MbeAttributeType ) const; - /// \ru Выдать простой атрибут данного подтипа. \en Get a simple attribute of a given subtype. - MbAttribute * SetSimpleAttribute( MbeAttributeType ); - /// \ru Установить простой атрибут данного подтипа. \en Set a simple attribute of a given subtype. - MbAttribute * SetSimpleAttribute( MbAttribute * simpAttr ); - /// \ru Установить простой атрибут данного подтипа (всегда копирует атрибут). \en Set a simple attribute of a given subtype (always copies the attribute). - MbAttribute * SetSimpleAttribute( const MbAttribute & simpAttr ); - /// \ru Удалить простой атрибут(один и более) данного подтипа. \en Delete simple attributes (one or more) of a given subtype. - void RemoveSimpleAttribute( MbeAttributeType ); - /// \ru Отдать простой атрибут данного подтипа. \en Detach a simple attribute of a given subtype. - MbAttribute * DetachSimpleAttribute( MbeAttributeType ); - - /// \ru Выдать пользовательский атрибут данного подтипа. \en Get a user attribute of a given subtype. - void GetUserAttributes( std::vector & attrs, const MbUserAttribType & type ) const; - /// \ru Удалить пользовательский атрибут (один и более) данного подтипа. \en Delete user attributes (one or more) of a given subtype. - void RemoveUserAttributes( const MbUserAttribType & type ); - /// \ru Отдать пользовательский атрибут данного подтипа. \en Detach a user attribute of a given subtype. - void DetachUserAttributes( std::vector & attrs, const MbUserAttribType & type ); - - /// \ru Преобразовать из пользовательского в "системный" \en Convert user attribute to "system" one - static MbUserAttribute * ReduceUserAttrib ( const MbExternalAttribute & ); - /// \ru Преобразовать из "системного" в пользовательский \en Convert "system" attribute to user one - static MbExternalAttribute * AdvanceUserAttrib( const MbUserAttribute & ); - - /// \ru Выполнить действия при изменении атрибутов. \en Perform actions when changing the attributes. - void AttributesChange (); - /// \ru Выполнить действия при конвертации атрибутов. \en Perform actions when converting the attributes. - void AttributesConvert( MbAttributeContainer & other ) const; - /// \ru Выполнить действия при трансформировании атрибутов. \en Perform actions when transforming the attributes. - void AttributesTransform( const MbMatrix3D &, MbRegTransform * = NULL ); - /// \ru Выполнить действия при перемещении атрибутов. \en Perform actions when moving the attributes. - void AttributesMove ( const MbVector3D &, MbRegTransform * = NULL ); - /// \ru Выполнить действия при вращении атрибутов. \en Perform actions when rotating the attributes. - void AttributesRotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); - /// \ru Выполнить действия при копировании атрибутов. \en Perform actions when copying the attributes. - void AttributesCopy ( MbAttributeContainer & other, MbRegDuplicate * = NULL ) const; - /// \ru Выполнить действия при объединении атрибутов. \en Perform actions when merging the attributes. - void AttributesMerge ( MbAttributeContainer & other ); - /// \ru Выполнить действия при замене атрибутов. \en Perform actions when replacing the attributes. - void AttributesReplace( MbAttributeContainer & other ); - /// \ru Выполнить действия при разделении атрибутов. \en Perform actions when splitting the attributes. - void AttributesSplit ( const std::vector & others ); - /// \ru Выполнить действия при удалении атрибутов. \en Perform actions when deleting the attributes. - void AttributesDelete (); - /** \} */ - - /** \ru \name Функции простых атрибутов объекта. - \en \name Functions of object's simple attributes. - \{ */ - /// \ru Установить плотность объекта. \en Set density of an object. - void SetDensity( double ); - /// \ru Выдать плотность объекта. \en Get density of an object. - double GetDensity() const; - - /// \ru Установить визуальные свойства объекта. \en Set visual properties of the object. - void SetVisual( float a, float d, float sp, float sh, float t, float e ); - /** \brief \ru Выдать визуальные свойства объекта. - \en Get visual properties of the object. \~ - \details \ru Выдать визуальные свойства объекта. - \en Get visual properties of the object. \~ - \param[out] a - \ru Коэффициент общего фона (рассеянного освещения) - \en Coefficient of backlighting \~ - \param[out] d - \ru Коэффициент диффузного отражения - \en Coefficient of diffuse reflection \~ - \param[out] s - \ru Коэффициент зеркального отражения - \en Coefficient of specular reflection \~ - \param[out] h - \ru Блеск (показатель степени в законе зеркального отражения) - \en Shininess (index according to the law of specular reflection) \~ - \param[out] t - \ru Коэффициент непрозрачности - \en Coefficient of total reflection (opacity coefficient) \~ - \param[out] e - \ru Коэффициент излучения - \en Emissivity coefficient \~ - \return \ru true если есть такой атрибут \n false в противном случае - \en True if there is the attribute MbVisual \n otherwise false. \~ - */ - bool GetVisual( float & a, float & d, float & sp, float & sh, float & t, float & e ) const; - - /// \ru Есть ли у объекта свой цвет. \en . - - /** \brief \ru Есть ли у объекта свой цвет. - \en Whether the object is colored. \~ - \details \ru Есть ли у объекта свой цвет. - \en Whether the object is colored. \~ - \return \ru true если есть такой атрибут \n false в противном случае - \en True if there is the attribute MbColor \n otherwise false. \~ - */ - bool IsColored() const { return (GetSimpleAttribute( at_Color ) != NULL); } - /// \ru Изменить цвет объекта. \en Change color of the object. - void SetColor( uint32 ); - /// \ru Выдать цвет объекта. \en Get color of an object. - uint32 GetColor() const; - - /// \ru Установить толщину линий для отображения объекта. \en Set thickness of lines for object's representation. - void SetWidth( int ); - /// \ru Выдать толщину линий для отображения объекта. \en Get thickness of lines for object's representation. - int GetWidth() const; - - /// \ru Установить стиль линий для отображения объекта. \en Set style of lines for object's representation. - void SetStyle( int ); - /// \ru Выдать стиль линий для отображения объекта. \en Get style of lines for object's representation. - int GetStyle() const; - - /// \ru Выделить или не выделить объект. \en To allocate or not to allocate an object. - void SetSelected( bool s = true ); - /// \ru Выделен ли объект? \en Is the object selected. - bool IsSelected() const; - /// \ru Инвертировать выделение объекта. \en Invert object selection. - bool ReverseSelected(); - - /// \ru Задать: объект изменен или не изменён. \en Set: the object is changed or isn't changed. - void SetChanged( bool c = true ); - /// \ru Изменен ли объект? \en Is the object changed? - bool IsChanged() const; - - /// \ru Установить видимость. \en Set visibility. - void SetVisible( bool ); - /// \ru Видимый ли объект? \en Is the object visible? - bool IsVisible() const; - /// \ru Не видимый ли элемент? \en Is the object invisible? - bool IsInvisible() const; - /** \} */ - - /// \ru Прочитать атрибуты из потока. \en Read attributes from stream. - void AttributesRead ( reader & ); - /// \ru Записать атрибуты в поток. \en Writing attributes to stream. - void AttributesWrite( writer & ) const; - /// \ru Выдать свойства атрибутов. \en Get properties of attributes. - void GetProperties( MbProperties & ); - /// \ru Установить свойства атрибутов. \en Set properties of attributes. - void SetProperties( const MbProperties & ); - -OBVIOUS_PRIVATE_COPY( MbAttributeContainer ) -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Получить обобщенные атрибуты. - \en Get common attributes. \~ - \details \ru Получить обобщенные атрибуты. \n - \en Get common attributes. \n \~ - \param[in] attrItem - \ru Объект с атрибутами. - \en Object with attributes. \~ - \param[in] attrPrompt - \ru Подсказка атрибута для поиска. - \en Attribute prompt. \~ - \param[out] resAttrs - \ru Найденные атрибуты. - \en Found attributes. \~ - \result \ru Возвращает true, если что-то добавлено. - \en Returns 'true' if the something was got. \~ - \ingroup Model_Attributes -*/ -// --- -MATH_FUNC (bool) GetCommonAttributes( const MbAttributeContainer & attrItem, const c3d::string_t & attrPrompt, c3d::ConstAttrVector & resAttrs ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Установить обобщенные атрибуты в целевой объект из объекта-источника. - \en Set common attributes in the destination object from the source object. \~ - \details \ru Установить обобщенные атрибуты в целевой объект из объекта-источника. \n - \en Set common attributes in the destination object from the source object. \n \~ - \param[in] srcItem - \ru Объект-источник. - \en The source object. \~ - \param[in] attrType - \ru Тип атрибута. - \en Attribute type. \~ - \param[in] attrPrompt - \ru Подсказка атрибута для поиска. - \en Attribute prompt. \~ - \param[out] dstItem - \ru Целевой объект. - \en The destination object. \~ - \param[in,out] bufAttrs - \ru Буферный массив атрибутов. - \en Buffer attributes vector. \~ - \result \ru Возвращает true, если что-то добавлено. - \en Returns 'true' if the something was added. \~ - \ingroup Model_Attributes -*/ -// --- -MATH_FUNC (bool) AddCommonAttributes( const MbAttributeContainer & srcItem, MbeAttributeType attrType, const c3d::string_t & attrPrompt, - MbAttributeContainer & dstItem, c3d::AttrVector * bufAttrs = NULL ); - -//------------------------------------------------------------------------------ -/** \brief \ru Удалить обобщенные атрибуты. - \en Delete common attributes. \~ - \details \ru Удалить обобщенные атрибуты. \n - \en Delete common attributes. \n \~ - \param[in] attrItem - \ru Объект с атрибутами. - \en Object with attributes. \~ - \param[in] attrPrompt - \ru Подсказка атрибута для поиска. - \en Attribute prompt. \~ - \result \ru Возвращает true, если что-то добавлено. - \en Returns 'true' if the something was deleted. \~ - \ingroup Model_Attributes -*/ -// --- -MATH_FUNC (bool) RemoveCommonAttributes( MbAttributeContainer & attrItem, const c3d::string_t & attrPrompt ); - - -#endif // __ATTRIBUTE_CONTAINER_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Контейнер атрибутов. + \en An attribute container. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ATTRIBUTE_CONTAINER_H +#define __ATTRIBUTE_CONTAINER_H + + +#include +#include +#include +#include +#include + + +class MATH_CLASS reader; +class MATH_CLASS writer; +class MATH_CLASS MbVector3D; +class MATH_CLASS MbAxis3D; +class MATH_CLASS MbMatrix3D; +class MATH_CLASS MbAttribute; +class MATH_CLASS MbUserAttribute; +class MATH_CLASS MbExternalAttribute; +class MATH_CLASS MbProperties; +class MbRegDuplicate; +class MbRegTransform; + + +//------------------------------------------------------------------------------ +/** \brief \ru Контейнер атрибутов. + \en An attribute container. \~ + \details \ru Контейнер атрибутов. \n + От данного класса наследуются объекты модели геометрического ядра MbItem + и топологические объекты с именем MbTopologyItem .\n + Наследники данного класса содержат атрибуты.\n + Методами данного класса выполняются действия над атрибутами объектов геометрического ядра.\n + Атрибут может влиять на состояние атрибута через его владельца, + тo есть геометрическое ядро предусматривает возможность передачи атрибутам информации об изменениях + их владельцев посредством вызовов предопределенных функций у самого атрибута.\n + Кроме передачи самой информации об изменениях происходящих с владельцем, + предусмотрена возможность определять поведение атрибута при этих изменениях путем выбора + одного из предопределенных типов поведения на каждое изменения владельца.\n + Типы действий, влияющих на состояние атрибутов.\n + Копирование, например, при создании копии тела. Действие над атрибутом производится после копирования владеющего объекта.\n + Разделение, например, разделение грани на две части при вырезании. + Действие над атрибутом производится после разбиения владеющего объекта.\n + Слияние, например, слияние граней при булевых операциях. + Действие над атрибутом производится перед выполнением слияния объектов. + Обрабатываются атрибуты всех объектов, участвующих в слиянии.\n + Изменение, не связанное с разделением или слиянием. + Действие над атрибутом производится после изменения владеющего объекта.\n + Преобразование, например, поворот или параллельный перенос. + Действие над атрибутом производится после преобразования владеющего объекта.\n + Подмена, например замена одной грани тела на другую. Действие над атрибутом производится перед выполнением замены объектов. + Обрабатываются атрибуты всех объектов, участвующих в замене.\n + Удаление объекта. Действие над атрибутом производится перед удалением объекта.\n + \en An attribute container. \n + The inheritors of this class are: objects of geometric kernel model of type MbItem + and topological objects of type MbTopologyItem.\n + Inheritors of this class contain attributes.\n + Operations with attributes of geometric kernel objects are performed by methods of this class.\n + Attribute can affect attribute state using its owner, + i.e. geometric kernel provides an opportunity for transmission to attributes the information about changes + of their owners by calling the predefined functions of the attribute.\n + In addition to transfer of information about changes occurring with owner + provided a possibility to determine the behavior of attribute with these changes by selecting of + one of the predefined types of behavior for each changing of the owner.\n + Types of actions that affect the states of attributes.\n + Copying, for example, when creating a copy of solid. Action on attribute is performed after copying of owning object.\n + Splitting. For example, splitting of a face into two parts in cutting. + Action on attribute is performed after splitting of owning object.\n + Merging. For example, merging of faces in boolean operations. + Action on attribute is performed after merging of owning object.\n + Attributes of all objects involved in merging are processed.\n + Changing which is not associated with splitting or merging. + Action on attribute is performed after changing of owning object.\n + Transformation. For example, rotation or parallel translation. + Action on attribute is performed after transformation of owning object.\n + Replacement. For example, replacement of one face of a solid to another. Action on attribute is performed after replacement of objects. + Attributes of all objects involved in replacement are processed.\n + Deletion of an object. Action on attribute is performed after deletion of an object.\n \~ + \ingroup Model_Attributes +*/ +// --- +class MATH_CLASS MbAttributeContainer { +public: +typedef MultiMap AttrMap_t; +private: + AttrMap_t attributes; ///< \ru Множество атрибутов. \en Set of attributes. + +protected: + /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. + MbAttributeContainer( const MbAttributeContainer &, MbRegDuplicate * ); +public: + /// \ru Конструктор без параметров. \en Constructor without parameters. + MbAttributeContainer(); + /// \ru Конструктор по атрибуту. \en Constructor by attribute. + MbAttributeContainer( MbAttribute & ); + /// \ru Деструктор. \en Destructor. + virtual ~MbAttributeContainer(); + +public: + + /// \ru Выдать тип контейнера атрибутов. \en Get attribute container type. + virtual MbeImplicationType ImplicationType() const { return ace_AttribContainer; } + + /** \ru \name Общие функции над атрибутами + \en \name Common functions of attributes + \{ */ + /// \ru Cдублировать атрибуты присланного объекта, свои отпустить. \en Duplicate attributes of a given object, release existing attributes. + void AttributesAssign( const MbAttributeContainer & ); + /// \ru Выдать количество объектов. \en Get the number of objects. + size_t AttributesCount() const { return attributes.Count(); } + /// \ru Удалить все атрибуты из контейнера. \en Delete all attributes from container. + void RemoveAttributes( bool onDeleteOwner = false ); + + /// \ru Добавить атрибут в контейнер. \en Add attribute in container. + MbAttribute * AddAttribute( MbAttribute *, bool checkSame = true ); + /// \ru Добавить атрибут в контейнер (всегда копирует атрибут). \en Add attribute in container (always copies the attribute). + MbAttribute * AddAttribute( const MbAttribute &, bool checkSame = true ); + /// \ru Выдать атрибуты заданного семейства. \en Get attributes of a given family. + void GetAttributes( c3d::AttrVector &, MbeAttributeType aFamily, MbeAttributeType subType ) const; + /// \ru Выдать атрибуты заданного типа. \en Get attributes of a given type. + void GetAttributes( c3d::AttrVector &, MbeAttributeType aType ) const; + /// \ru Выдать атрибуты по строке описания. \en Get attributes using sample of description string. + void GetCommonAttributes( c3d::AttrVector &, const c3d::string_t & samplePrompt, MbeAttributeType subType = at_Undefined ) const; + /// \ru Выдать строковые атрибуты по строке содержания. \en Get string attributes using sample of contents of the string. + void GetStringAttributes( c3d::AttrVector &, const c3d::string_t & sampleContent ) const; + + /// \ru Выдать атрибут заданного типа, если их несколько - то первый попавшийся. \en Get an attribute of a given type, the first one is returned if there are many. + //const MbAttribute * GetAttribute( MbeAttributeType subType ) const; + /// \ru Удалить атрибут из контейнера. \en Delete an attribute from container. + bool RemoveAttribute( const MbAttribute *, bool checkAccuracySame = false, double accuracy = LENGTH_EPSILON ); + /// \ru Удалить атрибуты заданного типа. \en Delete attributes of a given type. + bool RemoveAttributes( MbeAttributeType type, MbeAttributeType subType ); + + /// \ru Выдать простой атрибут данного подтипа. \en Get a simple attribute of a given subtype. + const MbAttribute * GetSimpleAttribute( MbeAttributeType ) const; + /// \ru Выдать простой атрибут данного подтипа. \en Get a simple attribute of a given subtype. + MbAttribute * SetSimpleAttribute( MbeAttributeType ); + /// \ru Установить простой атрибут данного подтипа. \en Set a simple attribute of a given subtype. + MbAttribute * SetSimpleAttribute( MbAttribute * simpAttr ); + /// \ru Установить простой атрибут данного подтипа (всегда копирует атрибут). \en Set a simple attribute of a given subtype (always copies the attribute). + MbAttribute * SetSimpleAttribute( const MbAttribute & simpAttr ); + /// \ru Удалить простой атрибут(один и более) данного подтипа. \en Delete simple attributes (one or more) of a given subtype. + void RemoveSimpleAttribute( MbeAttributeType ); + /// \ru Отдать простой атрибут данного подтипа. \en Detach a simple attribute of a given subtype. + MbAttribute * DetachSimpleAttribute( MbeAttributeType ); + + /// \ru Выдать пользовательский атрибут данного подтипа. \en Get a user attribute of a given subtype. + void GetUserAttributes( std::vector & attrs, const MbUserAttribType & type ) const; + /// \ru Удалить пользовательский атрибут (один и более) данного подтипа. \en Delete user attributes (one or more) of a given subtype. + void RemoveUserAttributes( const MbUserAttribType & type ); + /// \ru Отдать пользовательский атрибут данного подтипа. \en Detach a user attribute of a given subtype. + void DetachUserAttributes( std::vector & attrs, const MbUserAttribType & type ); + + /// \ru Преобразовать из пользовательского в "системный" \en Convert user attribute to "system" one + static MbUserAttribute * ReduceUserAttrib ( const MbExternalAttribute & ); + /// \ru Преобразовать из "системного" в пользовательский \en Convert "system" attribute to user one + static MbExternalAttribute * AdvanceUserAttrib( const MbUserAttribute & ); + + /// \ru Выполнить действия при изменении атрибутов. \en Perform actions when changing the attributes. + void AttributesChange (); + /// \ru Выполнить действия при конвертации атрибутов. \en Perform actions when converting the attributes. + void AttributesConvert( MbAttributeContainer & other ) const; + /// \ru Выполнить действия при трансформировании атрибутов. \en Perform actions when transforming the attributes. + void AttributesTransform( const MbMatrix3D &, MbRegTransform * = NULL ); + /// \ru Выполнить действия при перемещении атрибутов. \en Perform actions when moving the attributes. + void AttributesMove ( const MbVector3D &, MbRegTransform * = NULL ); + /// \ru Выполнить действия при вращении атрибутов. \en Perform actions when rotating the attributes. + void AttributesRotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); + /// \ru Выполнить действия при копировании атрибутов. \en Perform actions when copying the attributes. + void AttributesCopy ( MbAttributeContainer & other, MbRegDuplicate * = NULL ) const; + /// \ru Выполнить действия при объединении атрибутов. \en Perform actions when merging the attributes. + void AttributesMerge ( MbAttributeContainer & other ); + /// \ru Выполнить действия при замене атрибутов. \en Perform actions when replacing the attributes. + void AttributesReplace( MbAttributeContainer & other ); + /// \ru Выполнить действия при разделении атрибутов. \en Perform actions when splitting the attributes. + void AttributesSplit ( const std::vector & others ); + /// \ru Выполнить действия при удалении атрибутов. \en Perform actions when deleting the attributes. + void AttributesDelete (); + /** \} */ + + /** \ru \name Функции простых атрибутов объекта. + \en \name Functions of object's simple attributes. + \{ */ + /// \ru Установить плотность объекта. \en Set density of an object. + void SetDensity( double ); + /// \ru Выдать плотность объекта. \en Get density of an object. + double GetDensity() const; + + /// \ru Установить визуальные свойства объекта. \en Set visual properties of the object. + void SetVisual( float a, float d, float sp, float sh, float t, float e ); + /** \brief \ru Выдать визуальные свойства объекта. + \en Get visual properties of the object. \~ + \details \ru Выдать визуальные свойства объекта. + \en Get visual properties of the object. \~ + \param[out] a - \ru Коэффициент общего фона (рассеянного освещения) + \en Coefficient of backlighting \~ + \param[out] d - \ru Коэффициент диффузного отражения + \en Coefficient of diffuse reflection \~ + \param[out] s - \ru Коэффициент зеркального отражения + \en Coefficient of specular reflection \~ + \param[out] h - \ru Блеск (показатель степени в законе зеркального отражения) + \en Shininess (index according to the law of specular reflection) \~ + \param[out] t - \ru Коэффициент непрозрачности + \en Coefficient of total reflection (opacity coefficient) \~ + \param[out] e - \ru Коэффициент излучения + \en Emissivity coefficient \~ + \return \ru true если есть такой атрибут \n false в противном случае + \en True if there is the attribute MbVisual \n otherwise false. \~ + */ + bool GetVisual( float & a, float & d, float & sp, float & sh, float & t, float & e ) const; + + /// \ru Есть ли у объекта свой цвет. \en . + + /** \brief \ru Есть ли у объекта свой цвет. + \en Whether the object is colored. \~ + \details \ru Есть ли у объекта свой цвет. + \en Whether the object is colored. \~ + \return \ru true если есть такой атрибут \n false в противном случае + \en True if there is the attribute MbColor \n otherwise false. \~ + */ + bool IsColored() const { return (GetSimpleAttribute( at_Color ) != NULL); } + /// \ru Изменить цвет объекта. \en Change color of the object. + void SetColor( uint32 ); + /// \ru Изменить цвет объекта (0-255). \en Change color of the object (0-255). + void SetColor( int R, int G, int B ); + /// \ru Выдать цвет объекта. \en Get color of an object. + uint32 GetColor() const; + + /// \ru Установить толщину линий для отображения объекта. \en Set thickness of lines for object's representation. + void SetWidth( int ); + /// \ru Выдать толщину линий для отображения объекта. \en Get thickness of lines for object's representation. + int GetWidth() const; + + /// \ru Установить стиль линий для отображения объекта. \en Set style of lines for object's representation. + void SetStyle( int ); + /// \ru Выдать стиль линий для отображения объекта. \en Get style of lines for object's representation. + int GetStyle() const; + + /// \ru Выделить или не выделить объект. \en To allocate or not to allocate an object. + void SetSelected( bool s = true ); + /// \ru Выделен ли объект? \en Is the object selected. + bool IsSelected() const; + /// \ru Инвертировать выделение объекта. \en Invert object selection. + bool ReverseSelected(); + + /// \ru Задать: объект изменен или не изменён. \en Set: the object is changed or isn't changed. + void SetChanged( bool c = true ); + /// \ru Изменен ли объект? \en Is the object changed? + bool IsChanged() const; + + /// \ru Установить видимость. \en Set visibility. + void SetVisible( bool ); + /// \ru Видимый ли объект? \en Is the object visible? + bool IsVisible() const; + /// \ru Не видимый ли элемент? \en Is the object invisible? + bool IsInvisible() const; + /** \} */ + + /// \ru Прочитать атрибуты из потока. \en Read attributes from stream. + void AttributesRead ( reader & ); + /// \ru Записать атрибуты в поток. \en Writing attributes to stream. + void AttributesWrite( writer & ) const; + /// \ru Выдать свойства атрибутов. \en Get properties of attributes. + void GetProperties( MbProperties & ); + /// \ru Установить свойства атрибутов. \en Set properties of attributes. + void SetProperties( const MbProperties & ); + +OBVIOUS_PRIVATE_COPY( MbAttributeContainer ) +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Получить обобщенные атрибуты. + \en Get common attributes. \~ + \details \ru Получить обобщенные атрибуты. \n + \en Get common attributes. \n \~ + \param[in] attrItem - \ru Объект с атрибутами. + \en Object with attributes. \~ + \param[in] attrPrompt - \ru Подсказка атрибута для поиска. + \en Attribute prompt. \~ + \param[out] resAttrs - \ru Найденные атрибуты. + \en Found attributes. \~ + \result \ru Возвращает true, если что-то добавлено. + \en Returns 'true' if the something was got. \~ + \ingroup Model_Attributes +*/ +// --- +MATH_FUNC (bool) GetCommonAttributes( const MbAttributeContainer & attrItem, const c3d::string_t & attrPrompt, c3d::ConstAttrVector & resAttrs ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Установить обобщенные атрибуты в целевой объект из объекта-источника. + \en Set common attributes in the destination object from the source object. \~ + \details \ru Установить обобщенные атрибуты в целевой объект из объекта-источника. \n + \en Set common attributes in the destination object from the source object. \n \~ + \param[in] srcItem - \ru Объект-источник. + \en The source object. \~ + \param[in] attrType - \ru Тип атрибута. + \en Attribute type. \~ + \param[in] attrPrompt - \ru Подсказка атрибута для поиска. + \en Attribute prompt. \~ + \param[out] dstItem - \ru Целевой объект. + \en The destination object. \~ + \param[in,out] bufAttrs - \ru Буферный массив атрибутов. + \en Buffer attributes vector. \~ + \result \ru Возвращает true, если что-то добавлено. + \en Returns 'true' if the something was added. \~ + \ingroup Model_Attributes +*/ +// --- +MATH_FUNC (bool) AddCommonAttributes( const MbAttributeContainer & srcItem, MbeAttributeType attrType, const c3d::string_t & attrPrompt, + MbAttributeContainer & dstItem, c3d::AttrVector * bufAttrs = NULL ); + +//------------------------------------------------------------------------------ +/** \brief \ru Удалить обобщенные атрибуты. + \en Delete common attributes. \~ + \details \ru Удалить обобщенные атрибуты. \n + \en Delete common attributes. \n \~ + \param[in] attrItem - \ru Объект с атрибутами. + \en Object with attributes. \~ + \param[in] attrPrompt - \ru Подсказка атрибута для поиска. + \en Attribute prompt. \~ + \result \ru Возвращает true, если что-то добавлено. + \en Returns 'true' if the something was deleted. \~ + \ingroup Model_Attributes +*/ +// --- +MATH_FUNC (bool) RemoveCommonAttributes( MbAttributeContainer & attrItem, const c3d::string_t & attrPrompt ); + + +#endif // __ATTRIBUTE_CONTAINER_H diff --git a/C3d/Include/cdet_bool.h b/C3d/Include/cdet_bool.h index 15fbfbb..7ebfbb6 100644 --- a/C3d/Include/cdet_bool.h +++ b/C3d/Include/cdet_bool.h @@ -1,57 +1,57 @@ -////////////////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Расчет пересечений тел посредством аппарата булевой операции. - \en Calculation of intersections between solids using the boolean operations. \~ - -*/ -////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef __CDET_BOOL_H -#define __CDET_BOOL_H - - -#include -#include -#include -#include - - -class MATH_CLASS MbSolid; -class MATH_CLASS MbSNameMaker; -class MATH_CLASS MbCurveEdge; - - -//---------------------------------------------------------------------------------------- -/** \brief \ru Расчет пересечений тел посредством аппарата булевой операции. - \en Calculation of intersections between solids using the boolean operations. \~ - \details \ru Расчет пересечений тел посредством аппарата булевой операции. - \en Calculation of intersections between solids using the boolean operations. \~ \n - \param[in] solid1 - \ru Первое тело. \en The first solid. \~ - \param[in] solid2 - \ru Второе тело. \en The second solid. \~ - \param[out] edges - \ru Ребра пересечения тел. \en Intersection edges. \~ - \param[out] intersectedFaces - \ru Пары номеров пересекшихся граней. \n - - \en The couples of indeses intersected faces of the solids, \n - \param[out] touchedFaces - \ru Пары номеров касающихся граней с противоположно направленными нормалями. - \en The couples of indeses of contacted faces with oppositely directed normals. \~ - \param[out] similarFaces - \ru Пары номеров касающихся подобных граней, которые могут быть объединены. - \en The couples of indeses of relating to similar faces that can be combined. \~ - \return \ru Код результата операции. \en Operation result code. \~ - - \warning \ru Тела будут изменены операцией! Если требуется сохранить тела без изменений, - передавайте копии, сделанные помощью MbSolid::Duplicate(). - \en The solids will be modified by this operation! To keep the body intact, - give the copies made using MbSolid::Duplicate(). \~ - - \ingroup Collision_Detection -*/ -//--- -MATH_FUNC (MbResultType) InterferenceSolids( MbSolid & solid1, MbSolid & solid2, - std::vector * edges, - c3d::IndicesPairsVector * intersectedFaces, - c3d::IndicesPairsVector * similarFaces, - c3d::IndicesPairsVector * touchedFaces ); - - -#endif // __CDET_BOOL_H - +////////////////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Расчет пересечений тел посредством аппарата булевой операции. + \en Calculation of intersections between solids using the boolean operations. \~ + +*/ +////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef __CDET_BOOL_H +#define __CDET_BOOL_H + + +#include +#include +#include +#include + + +class MATH_CLASS MbSolid; +class MATH_CLASS MbSNameMaker; +class MATH_CLASS MbCurveEdge; + + +//---------------------------------------------------------------------------------------- +/** \brief \ru Расчет пересечений тел посредством аппарата булевой операции. + \en Calculation of intersections between solids using the boolean operations. \~ + \details \ru Расчет пересечений тел посредством аппарата булевой операции. + \en Calculation of intersections between solids using the boolean operations. \~ \n + \param[in] solid1 - \ru Первое тело. \en The first solid. \~ + \param[in] solid2 - \ru Второе тело. \en The second solid. \~ + \param[out] edges - \ru Ребра пересечения тел. \en Intersection edges. \~ + \param[out] intersectedFaces - \ru Пары номеров пересекшихся граней. \n + - \en The couples of indeses intersected faces of the solids, \n + \param[out] touchedFaces - \ru Пары номеров касающихся граней с противоположно направленными нормалями. + \en The couples of indeses of contacted faces with oppositely directed normals. \~ + \param[out] similarFaces - \ru Пары номеров касающихся подобных граней, которые могут быть объединены. + \en The couples of indeses of relating to similar faces that can be combined. \~ + \return \ru Код результата операции. \en Operation result code. \~ + + \warning \ru Тела будут изменены операцией! Если требуется сохранить тела без изменений, + передавайте копии, сделанные помощью MbSolid::Duplicate(). + \en The solids will be modified by this operation! To keep the body intact, + give the copies made using MbSolid::Duplicate(). \~ + + \ingroup Collision_Detection +*/ +//--- +MATH_FUNC (MbResultType) InterferenceSolids( MbSolid & solid1, MbSolid & solid2, + std::vector * edges, + c3d::IndicesPairsVector * intersectedFaces, + c3d::IndicesPairsVector * similarFaces, + c3d::IndicesPairsVector * touchedFaces ); + + +#endif // __CDET_BOOL_H + diff --git a/C3d/Include/cdet_data.h b/C3d/Include/cdet_data.h index dcf6819..657ae38 100644 --- a/C3d/Include/cdet_data.h +++ b/C3d/Include/cdet_data.h @@ -25,8 +25,8 @@ class MbHRepSolid; //---------------------------------------------------------------------------------------- /// \ru Объект набора для контроля столкновений. \en Object of the set for collision detection. //--- -typedef MbHRepSolid * cdet_item; -typedef MbResultType cdet_result; ///< \ru Код результата контроля столкновений. \en Result code of collision queries. +typedef const MbHRepSolid * cdet_item; +typedef MbResultType cdet_result; ///< \ru Код результата контроля столкновений. \en Result code of collision queries. //---------------------------------------------------------------------------------------- // \ru Код результата контроля столкновений. \en Codes of collision detection. @@ -45,8 +45,8 @@ typedef const void * cdet_app_item; //---------------------------------------------------------------------------------------- // Constants //--- -const cdet_item CDET_NULL = NULL; ///< \ru Пустой объект набора для контроля столкновений. \en Empty object of the collision query set. -const cdet_app_item CDET_APP_NULL = NULL; ///< \ru "Нулевой" объект модели приложения. \en "Null object" of the client app. +const cdet_item CDET_NULL = C3D_NULL_PTR; ///< \ru Пустой объект набора для контроля столкновений. \en Empty object of the collision query set. +const cdet_app_item CDET_APP_NULL = C3D_NULL_PTR; ///< \ru "Нулевой" объект модели приложения. \en "Null object" of the client app. //---------------------------------------------------------------------------------------- // Base class to implement collision query details @@ -77,8 +77,8 @@ struct cdet_query const MbRefItem * refItem; const MbMatrix3D * wMatrix; geom_element() - : appItem( NULL ) - , refItem( NULL ) + : appItem( C3D_NULL_PTR ) + , refItem( C3D_NULL_PTR ) , wMatrix( &MbMatrix3D::identity ) {} }; @@ -117,7 +117,7 @@ struct cdet_query_result: public cdet_query private: static cback_res QueryFunc( cdet_query * query, message code, cback_data & ) { - C3D_ASSERT( NULL != query ); + C3D_ASSERT( C3D_NULL_PTR != query ); cdet_query_result * q = static_cast( query ); switch( code ) { @@ -140,7 +140,6 @@ private: } }; - //---------------------------------------------------------------------------------------- // The structure queries first founded collision faces //--- @@ -163,7 +162,7 @@ private: { case CDET_QUERY_STARTED: // The collision query is started for all solids of the set { - q->first = q->second = NULL; + q->first = q->second = C3D_NULL_PTR; return CBACK_VOID; } case CDET_FINISHED: // A pair of solids is finished. @@ -310,6 +309,36 @@ private: OBVIOUS_PRIVATE_COPY( cdet_collided_faces ); }; +//---------------------------------------------------------------------------------------- +// +//--- +typedef enum +{ + CDET_EXAM_EnableComponentsOnly // Test collisions between components only. + , CDET_EXAM_Enabled // Enable the pair to test collisions. + , CDET_EXAM_Disabled // Reject the collision test of the pair. +} CDET_exam_status; + +//---------------------------------------------------------------------------------------- +// +//--- +struct CDET_item_data +{ + cdet_item comp; // Descriptor of the component owning the inctance. + cdet_item inst; // Descriptor of the instance. + cdet_app_item appItem; // Application pointer for the instance or the component. + CDET_item_data() + { + comp = inst = CDET_NULL; + appItem = CDET_APP_NULL; + } +}; + +//---------------------------------------------------------------------------------------- +// +//--- +typedef CDET_exam_status (*CDET_exam_func)( cdet_query *, const CDET_item_data &, const CDET_item_data & ); + /** \} */ // Collision_Detection class TapeBase; @@ -321,23 +350,40 @@ class MbFace; // --- class MbCollisionFace { - const MbFace * mathFace; - TapeBase * partFace; + cdet_item item; // The instance to witch the face belongs. + const MbFace * mathFace; // The topological face identified in the collision detection or proximity query. + TapeBase * partFace; // The face of the application representation. public: - MbCollisionFace( const MbFace &_mathFace ) : mathFace( &_mathFace ), partFace( NULL ) {} + MbCollisionFace( const MbFace & f ) + : item( CDET_NULL ) + , mathFace( &f ) + , partFace( C3D_NULL_PTR ) + {} - const MbFace & GetMathFace() const { return *mathFace; } + const MbFace & Face() const { return *mathFace; } + cdet_item Item() const { return item; } + const MbFace & GetMathFace() const { return *mathFace; } // \ru Установка объекта модели. \en Setting an object of model. - void SetCollisionFaceObject( TapeBase * _partFace ) { partFace = _partFace; } + void SetCollisionFaceObject( TapeBase * _partFace ) { partFace = _partFace; } + // \ru Выдача объекта модели. \en Getting an object of model. TapeBase * GetCollisionFaceObject() const { return partFace; } + // \ru Задать грань и компонент-вставку, которой принадлежит. \en Set a face and its component-instance. + MbCollisionFace & SetFace( const MbFace * f, cdet_item inst ) + { + mathFace = f; + item = inst; + return *this; + } + MbCollisionFace & operator = ( const MbCollisionFace & other ) { + item = other.item; mathFace = other.mathFace; - partFace = other.partFace; //CppCheck + partFace = other.partFace; return *this; } bool operator > ( const MbCollisionFace & other ) const { return mathFace > other.mathFace; } @@ -359,9 +405,11 @@ class MATH_CLASS MbProximityParameters SPtr plane; public: - MbCartPoint thePar1, thePar2; // \ru Пара точек близости, заданная в поверхностных координатах граненй. \en The points of the proximity specified in the surface coordinates of the faces. - double theDistance; // \ru Расстояние. \en Distance. - double upperDist; // \ru Верхняя оценка для поиска минимальной дистанции. \en The upper bound of the minimal distance estimation. + //cdet_item fstItem, sndItem; // \ru Дескрипторы + MbCartPoint3D fstPnt, sndPnt; // \ru Пара точек близости, принадлежащие триангуляционным сеткам. \en The points of the proximity belonging to the triangulation grids. + MbCartPoint thePar1, thePar2; // \ru Пара точек близости, заданная в поверхностных координатах граненй. \en The points of the proximity specified in the surface coordinates of the faces. + double theDistance; // \ru Расстояние. \en Distance. + double upperDist; // \ru Верхняя оценка для поиска минимальной дистанции. \en The upper bound of the minimal distance estimation. public: MbProximityParameters(); @@ -379,12 +427,16 @@ public: const MbCollisionFace & FaceTwo() const { return *theFace2; } void SetFacePair( const MbFace &, const MbFace & ); + void SetFacePair( const MbFace *, cdet_item, const MbFace *, cdet_item ); private: MbProximityParameters( const MbProximityParameters & ); // not implemented MbProximityParameters & operator = ( const MbProximityParameters & ); // not implemented }; + + + #endif // __CDET_DATA_H // eof diff --git a/C3d/Include/cdet_utility.h b/C3d/Include/cdet_utility.h index fa39a1d..0d5e68d 100644 --- a/C3d/Include/cdet_utility.h +++ b/C3d/Include/cdet_utility.h @@ -10,13 +10,20 @@ #define __CDET_UTILITY_H #include +#include +class MtRefItem; class MbItem; class MbSolid; class MbAssembly; struct MbLumpAndFaces; class MbCollisionDetector; +/** + \addtogroup Collision_Detection + \{ +*/ + //---------------------------------------------------------------------------------------- /** \brief \ru Утилита расчета параметров пересечения и близости тел. \en Utility for calculation of intersection and proximity parameters of solids. \~ @@ -31,7 +38,6 @@ class MbCollisionDetector; an object of type MbLumpAndFaces to be added in consideration by function AddSolid will have a correct matrix of transformation to the world coordinate system in its current state, i.e. from the beginning. \~ - \ingroup Collision_Detection */ // --- class MATH_CLASS MbCollisionDetectionUtility @@ -84,12 +90,6 @@ public: public: // the functions below can be deprecated in future version. - /** - \brief \ru Добавить модель тела, как набор граней и решеток. - \en Add a solid data as a set of faces and the grids. \~ - \return \ru Индекс добавленной твердотельной модели. \en Index of added solid data. \~ - */ - size_t AddLump( const MbLumpAndFaces & ); /** \brief \ru Добавить модель тела, как набор граней и решеток. \en Add a solid data as a set of faces and the grids. \~ @@ -98,19 +98,41 @@ public: // the functions below can be deprecated in future version. cdet_item AddSolid( const MbLumpAndFaces & ); /// \ru Добавить тело с заданным положением. \en Add a solid with a given placement. cdet_item AddSolid( const MbSolid &, const MbPlacement3D &, cdet_app_item = CDET_APP_NULL ); + /** + \brief \ru Добавить новый компонент контроля соударений и параметров близости. + \en Add a new component to track collisions and proximity parameters. \~ + */ + cdet_item AddComponent( cdet_app_item ); + /** + \brief \ru Добавить новый экземпляр тела в компонент контроля соударений. + \en Add a new instance of a reused solid into the component. \~ + \param[in] compItem - \ru Компонент, которому будет принадлежать экземпляр. + \en A component to witch the instance will belong. + \param[in] solidItem - \ru Оригинальное тело, добавленное методом #AddSolid, по которому изготавливается экземпляр. + \en An original solid added by the method #AddSolid by witch the instance is made. + \param[in] place - \ru Положение, которое занимает тело экземпляра в глобальной СК. + \en The placement that the instance solid takes in global space. + \return \ru Новый экземпляр тела, зарегистрированный с аппарате контроля соударений. + \en The new solid instance registered in the detector. + \note \ru Значение compItem может быть нулевым. Значит просто вставка не будет + принадлежать ни одному компоненту. + \en The value compItem can be CDET_NULL. This just means that the + instance does not belong to any component. + */ + cdet_item AddInstance( cdet_item compItem, cdet_item solidItem, const MbPlacement3D & place ); /// \ru Удалить твердотельную модель из детектора столкновений. \en Remove a solid model from a collision detector. void RemoveSolid( cdet_item ); /// \ru Выдать количество добавленных твердотельных моделей. \en Get number of added solid models. size_t Count() const; - // Use AppItem() insead this - cdet_app_item Component( size_t solIdx ) const; /// \ru Номер твердотельной модели, зарегистрированной в детекторе. \en An index of solid model registered in the detector. size_t SolidIndex( cdet_item cItem ) const; - /// \ru Вычисление минимального расстояния между объектами (см.функцию SetDistanceComputationObjects(...)) \en Calculation of minimal distance between objects (see the function SetDistanceComputationObjects(...)) - cdet_result DistanceQuery( MbProximityParameters & minDist ) const; + /// \ru Вычисление минимального расстояния между объектами (см.функцию #SetDistanceTracking) \en Calculation of minimal distance between objects (see the function #SetDistanceTracking) + cdet_result DistanceQuery( MbProximityParameters & ) const; + /// \ru Вычисление минимального расстояния между объектами + cdet_result DistanceQuery( cdet_item, cdet_item, MbProximityParameters & ) const; /// \ru Выключить из рассмотрения все модели. \en Exclude all models from consideration. void FlushSolids(); - /// \ru Выдать иерархическое представление тела (NULL = отсутствие такового в списке). \en Get the hierarchical representation of the solid (NULL means that the solid is not in the list). + /// \ru Выдать иерархическое представление тела (CDET_NULL = отсутствие такового в списке). \en Get the hierarchical representation of the solid (CDET_NULL means that the solid is not in the list). cdet_item GetHRepSolid ( const MbLumpAndFaces & ) const; /// \ru Задать барьер для отличия касания от пересечения. \en Set the barrier for the difference between the touch and the intersection. void SetTouchTolerance( double lTol ); @@ -126,20 +148,22 @@ public: // the functions below can be deprecated in future version. // \ru Объявление конструктора копирования и оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration without implementation of the copy-constructor and assignment operator to prevent an assignment by default. OBVIOUS_PRIVATE_COPY( MbCollisionDetectionUtility ); -public: +public: /* + Deprecated and testing functions + */ + void SetCallback( CDET_exam_func ); + // Use AppItem() insead this + cdet_app_item Component( size_t solIdx ) const; // The func is deprecated. Instead, use CheckCollisions - cdet_result InterferenceDetect( void * formalPar = NULL ) const; + cdet_result InterferenceDetect( void * formalPar = C3D_NULL_PTR ) const; // The func is deprecated. Use SetDistanceTracking instead. void SetDistanceComputationObjects( const MbLumpAndFaces &, const MbLumpAndFaces & ); + // The func is deprecated. Use AddSolid/AddItem instead. + size_t AddLump( const MbLumpAndFaces & ); // For testing purposes bool IsEmpty( cdet_item ) const; // For testing purposes - cdet_item NewComponent( cdet_app_item ); - // For testing purposes - //cdet_item Component( cdet_item subItem ); - // For testing purposes - cdet_item AddInstance( cdet_item compItem, cdet_item subItem, const MbPlacement3D & ); - + const MtRefItem * _ComputeBVTree( cdet_item ); private: /* @@ -162,6 +186,30 @@ inline cdet_result MbCollisionDetectionUtility::CheckCollisions() return CheckCollisions( defaultQuery ); } +/// \ru Узел дерева объемов. \en A node of the bounding volume tree. +typedef const MtRefItem * cdet_bvt_node; +/// \ru Пустое дерево объемов. \en An empty bounding volume tree. +const cdet_bvt_node CDET_BVT_NULL = C3D_NULL_PTR; +/// \ru Пара ветвей поддерева объемов. \en A pair of branches of the bounding volume subtree. +typedef std::pair cdet_bvt_pair; + +//--------------------------------------------------------------------------------------- +/** + \brief \ru Получить левую и правую ветви поддерева объемов. + \en Get the left and the right branches of the bounding volume subtree. +*/ +//--- +MATH_FUNC(cdet_bvt_pair) BvtSubNodes( cdet_item, cdet_bvt_node ); + +//--------------------------------------------------------------------------------------- +/** \brief \ru Матрица получения ограничивающего параллелепипеда из единичного куба. + \en Transformation matrix yielding bounding parallelepiped from the unit cube. +*/ +//--- +MATH_FUNC(void) GetOrientedBox( cdet_item, cdet_bvt_node, MbMatrix3D & ); + #endif // __CDET_UTILITY_H +/** \} */ + // eof \ No newline at end of file diff --git a/C3d/Include/check_geometry.h b/C3d/Include/check_geometry.h index 65af3ec..4e9f4f6 100644 --- a/C3d/Include/check_geometry.h +++ b/C3d/Include/check_geometry.h @@ -30,7 +30,7 @@ \ingroup Algorithms_3D */ //--- -struct MATH_CLASS MbIntersectionData { +struct MATH_CLASS MbShellsIntersectionData { protected: c3d::EdgesSPtrVector edges; ///< \ru Ребра пересечения (владеет по счетчику ссылок). \en Intersection edges (owns by reference counter). c3d::IndicesVector faceIndices1; ///< \ru Номера касающихся граней первого тела. \en The numbers concerning faces of the first solid. @@ -45,28 +45,28 @@ protected: public: /// \ru Конструктор. \en Constructor. - MbIntersectionData(); + MbShellsIntersectionData(); /// \ru Конструктор по ребру. \en Constructor by an edge. - MbIntersectionData( const MbCurveEdge & ); + MbShellsIntersectionData( const MbCurveEdge & ); /// \ru Конструктор по ребрам. \en Constructor by edges. template - MbIntersectionData( const EdgesVector &, bool isSolidEdges ); + MbShellsIntersectionData( const EdgesVector &, bool isSolidEdges ); /// \ru Конструктор по ребрам. \en Constructor by edges. template - MbIntersectionData( const EdgesVector &, const FaceIndicesVector & faceNumbers1, const FaceIndicesVector & faceNumbers2 ); + MbShellsIntersectionData( const EdgesVector &, const FaceIndicesVector & faceNumbers1, const FaceIndicesVector & faceNumbers2 ); /// \ru Конструктор по ребрам. \en Constructor by edges. template - MbIntersectionData( const EdgesVector &, const c3d::IndicesPairsVector & faceNumbersPairs ); + MbShellsIntersectionData( const EdgesVector &, const c3d::IndicesPairsVector & faceNumbersPairs ); /// \ru Конструктор по телу. \en Constructor by a solid. - explicit MbIntersectionData( const MbSolid & ); + explicit MbShellsIntersectionData( const MbSolid & ); /// \ru Конструктор по точкам. \en Constructor by points. - explicit MbIntersectionData( const std::vector & ); + explicit MbShellsIntersectionData( const std::vector & ); /// \ru Конструктор по вершинам и флагу использования этих объектов, а не их копий. \en Constructor by vertices and by flag of use of these objects instead of their copies. - explicit MbIntersectionData( const c3d::ConstVerticesVector &, bool same ); + explicit MbShellsIntersectionData( const c3d::ConstVerticesVector &, bool same ); /// \ru Конструктор по вершинам и флагу использования этих объектов, а не их копий. \en Constructor by vertices and by flag of use of these objects instead of their copies. - explicit MbIntersectionData( const c3d::ConstVerticesSPtrVector &, bool same ); + explicit MbShellsIntersectionData( const c3d::ConstVerticesSPtrVector &, bool same ); /// \ru Деструктор. \en Destructor. - ~MbIntersectionData(); + ~MbShellsIntersectionData(); public: /// \ru Пересечение - есть тело. \en Intersection is a solid. @@ -105,7 +105,7 @@ public: /// \ru Получить набор точек касания. \en Get a set of touch points. const MbPointFrame * GetPointFrame() const { return pointFrame; } -OBVIOUS_PRIVATE_COPY( MbIntersectionData ) // \ru Не реализовано \en Not implemented +OBVIOUS_PRIVATE_COPY( MbShellsIntersectionData ) // \ru Не реализовано \en Not implemented }; @@ -113,7 +113,7 @@ OBVIOUS_PRIVATE_COPY( MbIntersectionData ) // \ru Не реализовано \e // \ru Конструктор по ребрам. \en Constructor by edges. //--- template -MbIntersectionData::MbIntersectionData( const EdgesVector & initEdges, bool isSolidEgdes ) +MbShellsIntersectionData::MbShellsIntersectionData( const EdgesVector & initEdges, bool isSolidEgdes ) : edges ( ) , faceIndices1 ( ) , faceIndices2 ( ) @@ -140,9 +140,9 @@ MbIntersectionData::MbIntersectionData( const EdgesVector & initEdges, bool isSo // \ru Конструктор по ребрам. \en Constructor by edges. //--- template -MbIntersectionData::MbIntersectionData( const EdgesVector & initEdges, - const FaceIndicesVector & faceInds1, - const FaceIndicesVector & faceInds2 ) +MbShellsIntersectionData::MbShellsIntersectionData( const EdgesVector & initEdges, + const FaceIndicesVector & faceInds1, + const FaceIndicesVector & faceInds2 ) : edges ( ) , faceIndices1 ( ) , faceIndices2 ( ) @@ -172,8 +172,8 @@ MbIntersectionData::MbIntersectionData( const EdgesVector & initEdges, // \ru Конструктор по ребрам. \en Constructor by edges. //--- template -MbIntersectionData::MbIntersectionData( const EdgesVector & initEdges, - const c3d::IndicesPairsVector & faceIndicesPairs ) +MbShellsIntersectionData::MbShellsIntersectionData( const EdgesVector & initEdges, + const c3d::IndicesPairsVector & faceIndicesPairs ) : edges ( ) , faceIndices1 ( ) , faceIndices2 ( ) @@ -209,7 +209,7 @@ MbIntersectionData::MbIntersectionData( const EdgesVector & initEdge // \ru Получить массив кривых пересечения. \en Get the intersection curve array. //--- template -void MbIntersectionData::GetCurves( EdgesVector & dstEdges ) const +void MbShellsIntersectionData::GetCurves( EdgesVector & dstEdges ) const { size_t addCnt = edges.size(); c3d::EdgeSPtr edge; @@ -226,7 +226,7 @@ void MbIntersectionData::GetCurves( EdgesVector & dstEdges ) const // \ru Получить номера касающихся граней первого/второго тела. \en Get numbers concerning faces of the first/second solid. //--- template -void MbIntersectionData::GetFaceNumbers( bool first, OutputIndicesVector & outputIndices ) const +void MbShellsIntersectionData::GetFaceNumbers( bool first, OutputIndicesVector & outputIndices ) const { const c3d::IndicesVector & faceIndices = first ? faceIndices1 : faceIndices2; size_t addCnt = faceIndices.size(); @@ -243,7 +243,7 @@ void MbIntersectionData::GetFaceNumbers( bool first, OutputIndicesVector & outpu // \ru Получить номера касающихся граней первого и второго тел. \en Get numbers concerning faces of the first and second solids. //--- template -void MbIntersectionData::GetFaceNumbersPairs( OutputIndicesPairsVector & outputIndicesPairs ) const +void MbShellsIntersectionData::GetFaceNumbersPairs( OutputIndicesPairsVector & outputIndicesPairs ) const { size_t addCnt = std_min( faceIndices1.size(), faceIndices2.size() ); if ( addCnt > 0 ) { @@ -272,14 +272,6 @@ void MbIntersectionData::GetFaceNumbersPairs( OutputIndicesPairsVector & outputI MATH_FUNC (bool) IsDegeneratedCurve( const MbCurve3D & curve, double eps ); -//------------------------------------------------------------------------------ -/// \ru Проверка на полное совпадение двух кривых пересечения поверхностей c метрической точностью lenEps \en Check for complete coincidence of two intersection curves of surfaces with metric tolerance lenEps -//--- -bool IsCoincidentCurves( const MbSurfaceIntersectionCurve & intCurve1, - const MbSurfaceIntersectionCurve & intCurve2, - double lenEps ); - - //------------------------------------------------------------------------------ /** \brief \ru Проверка оболочки тела на замкнутость. \en Check of solid's shell for closedness. \~ @@ -320,7 +312,7 @@ MATH_FUNC (bool) CheckSolidClosure( const MbSolid & solid ); /** \brief \ru Поиск краевых ребер замкнутой оболочки. \en Search for the boundary edges of a closed shell. \~ \details \ru Поиск краевых ребер замкнутой оболочки. \n - Краевое ребер - это ребро у которого нет ссылки на одну из смежных граней. \n + Краевое ребро - это ребро у которого нет ссылки на одну из смежных граней. \n Наличие краевых ребер замкнутой оболочки может приводит к отказу операций над оболочкой, если операцией будет затронута часть оболочки с краевыми ребрами. \n Наличие одиночных краевых ребер практически никак не влияет на правильность расчета МЦХ. diff --git a/C3d/Include/collection.h b/C3d/Include/collection.h index c77d996..57477b0 100644 --- a/C3d/Include/collection.h +++ b/C3d/Include/collection.h @@ -1,337 +1,337 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Коллекция элементов. - \en Collection of elements . \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __COLLECTION_H -#define __COLLECTION_H - - -#include -#include -#include -#include -#include - - -class MATH_CLASS MbMesh; -class MATH_CLASS MbGrid; - - -//------------------------------------------------------------------------------ -/** \brief \ru Коллекция элементов. - \en Collection of elements. \~ - \details \ru Коллекция элементов - это объект геометрической модели, наследник MbItem, являющийся - множеством элементов в трехмерном пространстве. \n - \en The collection of 3D elements is an object of geometric model (subclass MbItem) which is - the set of elements in 3D space. \n \~ - \ingroup Model_Items -*/ -// --- -class MATH_CLASS MbCollection : public MbItem { -public: - /** \brief \ru Типы коллекций 3D объектов. - \en Types of 3D object collection. \~ -*/ -enum CollectionType { - coll_PointCloud = 0, ///< \ru Облако точек. \en The point cloud. - coll_Tessellation = 1, ///< \ru Триангуляция. \en The tessellation. - coll_Elements = 2, ///< \ru Набор элементов. \en Set of elements. - coll_Segmentation = 3, ///< \ru Сегментированная полигональная сетка. \en Segmented polygonal mesh. -}; - -private: - CollectionType type; ///< \ru Тип коллекции 3D объектов. \en Type of 3D object collection. - uint32 xSize; ///< \ru Количество объектов вдоль первой координаты. \en The number of objects along the first coordinate. - uint32 ySize; ///< \ru Количество объектов вдоль второй координаты. \en The number of objects along the second coordinate. - uint32 zSize; ///< \ru Количество объектов вдоль третьей координаты. \en The number of objects along the third coordinate. - std::vector points; ///< \ru Множество точек. \en Set of points. - std::vector normals; ///< \ru Множество нормалей в точках согласовано с множеством точек. \en Set of normals at control points is synchronized with the set of points. - std::vector escorts; ///< \ru Множество значений для дополнительной информации в точках. \en The set of values for additional information of points. - std::vector triangles; ///< \ru Индексное множество треугольных пластин содержит номера элементов множества points и normals. \en Set of triangular plates contains numbers of elements of 'points' and 'normals' sets. - std::vector quadrangles; ///< \ru Индексное множество четырёхугольных пластин содержит номера элементов множества params и/или множеств points и normals. \en Set of quadrangular plates contains numbers of elements of 'params' set and/or of 'points' and 'normals' sets. - std::vector elements; ///< \ru Индексное множество объемных элементов содержит номера элементов множества points. \en Set of volume elements contains numbers of vertices of 'points' sets. - std::vector segments; ///< \ru Множество сегментов полигональной сетки. \en Set of segments of mesh. - - /** \brief \ru Габаритный куб объекта. - \en Bounding box of object. \~ - \details \ru Габаритный куб объекта рассчитывается только при запросе габарита объекта. Габаритный куб в конструкторе объекта и после модификации объекта принимает неопределенное значение. - \en Bounding box of object is calculated only at the request. Bounding box of object is undefined after object constructor and after object modifications \n \~ - */ - mutable MbCube cube; -private: - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. - MbCollection( const MbCollection & init ); - - /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. - explicit MbCollection( const MbCollection &, MbRegDuplicate * ); -public: - /// \ru Конструктор. \en Constructor. - MbCollection(); - /// \ru Конструктор. \en Constructor. - MbCollection( const MbMesh & mesh ); - - /// \ru Деструктор. \en Destructor. - virtual ~MbCollection(); - -public: - VISITING_CLASS( MbCollection ); - - // \ru Общие функции геометрического объекта \en Common functions of a geometric object - virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en A type of an object. - virtual MbeSpaceType Type() const; // \ru Групповой тип объекта. \en Group type of object. - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Создать копию. \en Create a copy. - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвинуть вдоль вектора. \en Translate along a vector. - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. - virtual bool IsSame ( const MbSpaceItem & init, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Are the objects equal? - virtual bool SetEqual ( const MbSpaceItem & init ); // \ru Сделать объекты равным. \en Make the objects equal. - virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. - virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Добавь свой габарит в куб. \en Add bounding box into a cube. - virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate bounding box in the local coordinate system. - virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. - - virtual MbProperty & CreateProperty( MbePrompt n ) const; // \ru Создать собственное свойство. \en Create a custom property. - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. - - // \ru Создать полигональный объект - упрощенную копию данного объекта. \en Create a polygonal object - a simplified copy of the given object. - virtual MbItem * CreateMesh( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * iReg ) const; - // \ru Создать полигональный объект - упрощенную копию данного объекта. \en Create a polygonal object - a simplified copy of the given object. - MbGrid * CreateGrid() const; - - // \ru Создать сетки из четырехугольных пластин наружных стенок элементов. \en Create grids by quadrangular plates of the outside walls of elements. - void CreateGridsByElements( RPArray & grids_ ) const; - - // \ru Создать угловые точки и элементы. \en Create corner points and elements. - void CreateCornerPointsAndElements( SArray & points0, SArray & elements0 ) const; - - // \ru Создать сетки из по результатам сегментации. \en Create grids by segmentation results. - void CreateGridsBySegments( RPArray & grids_ ) const; - - /** \ru \name Общие функции коллекции. - \en \name Common functions of a collection. - \{ */ - - /// \ru Выдать количество точек. \en Get count of points. - size_t PointsCount() const { return points.size(); } - /// \ru Выдать количество нормалей. \en Get the number of normals. - size_t NormalsCount() const { return normals.size(); } - /// \ru Выдать количество значений. \en Get count of values. - size_t EscortsCount() const { return escorts.size(); } - /// \ru Выдать количество треугольников. \en Get the number of triangles. - size_t TrianglesCount() const { return triangles.size(); } - /// \ru Выдать количество четырехугольников. \en Get the number of quadrangles. - size_t QuadranglesCount() const { return quadrangles.size(); } - /// \ru Выдать количество объемных элементов. \en Get the number of elements of volume. - size_t ElementsCount() const { return elements.size(); } - /// \ru Выдать количество сегментов. \en Get the number of segments of mesh. - size_t SegmentsCount() const { return segments.size(); } - /// \ru Выдать количество триангуляций. \en Get the number of triangulations. - //size_t GridsCount() const { return grids.size(); } - ptrdiff_t PointsMaxIndex() const { ptrdiff_t c = points.size(); return ( c - 1 ); } - /// \ru Выдать количество нормалей минус 1 (максимальный индекс). \en Get the number of normals minus one (maximal index). - ptrdiff_t NormalsMaxIndex() const { ptrdiff_t c = normals.size(); return ( c - 1 ); } - - /// \ru Добавить в коллекцию точку и нормаль в точке. \en Add a point and normal at the point to collection. - void AddPoint ( const MbCartPoint3D & p3D, const MbVector3D & n3D ) { points.push_back(p3D); normals.push_back(n3D); cube.SetEmpty(); } - /// \ru Добавить в коллекцию точку. \en Add a point to collection. - void AddPoint ( const MbCartPoint3D & p3D ) { points.push_back(p3D); cube.SetEmpty(); } - /// \ru Добавить в коллекцию нормаль. \en Add a normal to collection. - void AddNormal( const MbVector3D & n3D ) { normals.push_back(n3D) ; } - /// \ru Добавить в коллекцию точки. \en Add points to collection. - void AddPoints ( const std::vector & pnts ) { points.insert(points.end(), pnts.begin(), pnts.end()); cube.SetEmpty(); } - /// \ru Добавить в коллекцию нормали. \en Add normals to collection. - void AddNormals( const SArray & nrms ) { normals.insert(normals.end(), nrms.begin(), nrms.end()); cube.SetEmpty(); } - /// \ru Добавить в коллекцию данных. \en Add scores to collection. - void AddEscorts( const std::vector & scores ) { escorts.insert(escorts.end(), scores.begin(), scores.end()); } - - /// \ru Добавить треугольник. \en Add a triangle. - void AddTriangle ( const MbTriangle & triangle ) { triangles.push_back( triangle ); } - /// \ru Добавить треугольник с заданными номерами вершин. \en Add a triangle by the given indices of vertices - void AddTriangle ( uint j0, uint j1, uint j2, bool o ) { MbTriangle t(j0,j1,j2,o); triangles.push_back( t ); } - /// \ru Добавить четырёхугольник. \en Add a quadrangle. - void AddQuadrangle( const MbQuadrangle & quadrangle ) { quadrangles.push_back( quadrangle ); } - /// \ru Добавить четырёхугольник с заданными номерами вершин. \en Add a quadrangle by the given indices of vertices. - void AddQuadrangle( uint j0, uint j1, uint j2, uint j3, bool o ) { MbQuadrangle t(j0,j1,j2,j3,o); quadrangles.push_back( t ); } - /// \ru Добавить объемный элемент. \en Add an element. - void AddElement( const MbElement & element ) { elements.push_back(element); } - /// \ru Добавить объемный элемент. \en Add an element. - void AddElement( uint j0, uint j1, uint j2, uint j3, uint j4, uint j5, uint j6, uint j7 ) { - MbElement t( j0,j1,j2,j3,j4,j5,j6,j7 ); elements.push_back( t ); } - void AddSegment( const MbGridSegment & segment ) { segments.push_back( segment ); } - void AddSegment( const std::vector & initFaces ) { MbGridSegment seg( initFaces ); segments.push_back( seg ); } - /// \ru Добавить полигон. \en Add a polygon. - //void AddGrid( MbExactGrid & grd ) { grids.push_back( &grd ); } - - /// \ru Выдать индексы точек в массиве points для i-го треугольника (связанного или несвязанного). \en Get indices of points in 'points' array for i-th triangle (adjacent or non-adjacent). - bool GetTrianglePointIndex ( size_t i, uint & ind0, uint & ind1, uint & ind2 ) const; - /// \ru Выдать индексы точек в массиве points для i-го четырехугольника (связанного или несвязанного). \en Get indices of points in 'points' array for i-th quadrangle (adjacent or non-adjacent). - bool GetQuadranglePointIndex( size_t i, uint & ind0, uint & ind1, uint & ind2, uint & ind3 ) const; - /// \ru Выдать для треугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th triangle in general numbering (with strips). - bool GetTrianglePoints ( size_t i, MbCartPoint3D &p0, MbCartPoint3D &p1, MbCartPoint3D &p2 ) const; - /// \ru Выдать для треугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th triangle in general numbering (with strips). - bool GetTriangleNormals ( size_t i, MbVector3D &n0, MbVector3D &n1, MbVector3D &n2 ) const; - - /// \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th quadrangle in general numbering (with strips). - bool GetQuadranglePoints ( size_t i, MbCartPoint3D &p0, MbCartPoint3D &p1, MbCartPoint3D &p2, MbCartPoint3D &p3 ) const; - /// \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th quadrangle in general numbering (with strips). - bool GetQuadrangleNormals( size_t i, MbVector3D &n0, MbVector3D &n1, MbVector3D &n2, MbVector3D &n3 ) const; - - /// \ru Удалить точки. \en Delete points. - void PointsRemove() { points.clear(); - #ifdef STANDARD_C11 - points.shrink_to_fit(); - #endif - cube.SetEmpty(); } - /// \ru Удалить точку с заданным номером. \en Delete point by the given index. - void PointRemove ( size_t i ) { if ( i < points.size() ) points.erase( points.begin() + i ); cube.SetEmpty(); } - /// \ru Удалить нормаль с заданным номером. \en Delete normal by the given index. - void NormalRemove( size_t i ) { if ( i < normals.size() ) normals.erase( normals.begin() + i ); } - - /// \ru Установить тип объекта. \en Set type. - void SetType( CollectionType t ) { type = t; } - /// \ru Выдать тип объекта. \en Get type. - CollectionType GetType() const { return type; } - /// \ru Установить количество объектов вдоль первой координаты. \en Set the number of objects along the first coordinate. - void SetXSize( uint32 n ) { xSize = n; } - /// \ru Установить количество объектов вдоль второй координаты. \en Set the number of objects along the cecond coordinate. - void SetYSize( uint32 n ) { ySize = n; } - /// \ru Установить количество объектов вдоль третьей координаты. \en Set the number of objects along the third coordinate. - void SetZSize( uint32 n ) { zSize = n; } - /// \ru Выдать количество объектов вдоль первой координаты. \en Get the number of objects along the first coordinate. - uint32 GetXSize() const { return xSize; } - /// \ru Выдать количество объектов вдоль второй координаты. \en Get the number of objects along the cecond coordinate. - uint32 GetYSize() const { return ySize; } - /// \ru Выдать количество объектов вдоль третьей координаты. \en Get the number of objects along the third coordinate. - uint32 GetZSize() const { return zSize; } - - /// \ru Выдать точку по её номеру. \en Get point by its index. - void GetPoint ( size_t i, MbCartPoint3D & p ) const { p = points[i]; } - /// \ru Выдать множество точек. \en Get set of points. - const std::vector & GetPoints ( ) const { return points; } - /// \ru Выдать нормаль по её номеру. \en Get normal by its index. - void GetNormal( size_t i, MbVector3D & n ) const { n = normals[i]; } - /// \ru Выдать множество нормалей. \en Get set of normals. - const std::vector & GetNormals( ) const { return normals; } - /// \ru Выдать точку по её номеру. \en Get point by its index. - double GetEscort( size_t i ) const { return escorts[i]; } - /// \ru Выдать элемент по его номеру. \en Get element by its index. - void GetElement( size_t i, MbElement & elem ) const { elem = elements[i]; } - /// \ru Выдать индексы точек в массиве points для i-го объемного элемента. \en Get indices of points in 'points' array for i-th element. - bool GetElementIndex( size_t i, uint & ind0, uint & ind1, uint & ind2, uint & ind3, uint & ind4, uint & ind5, uint & ind6, uint & ind7 ) const; - /// \ru Выдать для элемента с номером i точки вершин. \en Get points of vertices for i-th element. - bool GetElementPoints ( size_t i, MbCartPoint3D &p0, MbCartPoint3D &p1, MbCartPoint3D &p2, MbCartPoint3D &p3, - MbCartPoint3D &p4, MbCartPoint3D &p5, MbCartPoint3D &p6, MbCartPoint3D &p7 ) const; - /// \ru Выдать сегмент по его номеру. \en Get segment by its index. - void GetSegment( size_t i, MbGridSegment & seg ) const { seg = segments[i]; } - /// \ru Выдать точку с заданным номером. \en Get point by the given index. - const MbCartPoint3D & GetPoint ( size_t i ) const { return points[i]; } - /// \ru Выдать нормаль с заданным номером. \en Get normal by the given index. - const MbVector3D & GetNormal( size_t i ) const { return ( (normals.size() == 1) ? normals[0] : normals[i] ); } - /// \ru Выдать треугольник с номером i. \en Get i-th triangle. - const MbTriangle & GetTriangle ( size_t i ) const { return triangles[i]; } - /// \ru Выдать четырёхугольник с номером i. \en Get i-th quadrangle. - const MbQuadrangle & GetQuadrangle( size_t i ) const { return quadrangles[i]; } - /// \ru Выдать четырёхугольник с номером i. \en Get i-th quadrangle. - const MbElement & GetElement ( size_t i ) const { return elements[i]; } - /// \ru Выдать сегмент по его номеру. \en Get segment by its index. - const MbGridSegment & GetSegment( size_t i ) const { return segments[i]; } - /// \ru Выдать полигон с номером i. \en Get i-th polygon. - //const MbExactGrid & GetGrid ( size_t i ) const { return *grids[i]; } - - /// \ru Удалить все xтреугольники. \en Delete all triangles. - void TrianglesDelete() { triangles.clear(); } - /// \ru Удалить все четырехугольники. \en Delete all quadrangles. - void QuadranglesDelete() { quadrangles.clear(); } - /// \ru Удалить все объемные элементы. \en Delete all elements. - void ElementsDelete() { elements.clear(); } - /// \ru Удалить все сегменты. \en Delete all segments. - void SegmentsDelete() { segments.clear(); } - /// \ru Удалить все nhbfyuekzwbb. \en Delete all triangulations. - //void GridsDelete(); - - /// \ru Зарезервировать память для контейнеров. \en Reserve memory for some containers. - void ReservePointsNormals( size_t n ) { points.reserve( points.size() + n ); normals.reserve( normals.size() + n ); } - /// \ru Зарезервировать память для контейнера точек. \en Reserve memory for container of points. - void PointsReserve ( size_t n ) { points.reserve( points.size() + n ); } - /// \ru Зарезервировать память для контейнера нормалей. \en Reserve memory for container of normals. - void NormalsReserve ( size_t n ) { normals.reserve( normals.size() + n ); } - /// \ru Зарезервировать память для контейнера параметров. \en Reserve memory for container of elements. - /// \ru Зарезервировать память для контейнера параметров. \en Reserve memory for container of elements. - void EscordsReserve ( size_t n ) { escorts.reserve( escorts.size() + n ); } - /// \ru Зарезервировать память для контейнера треугольников. \en Reserve memory for container of triangles. - void TrianglesReserve ( size_t n ) { triangles.reserve( triangles.size() + n ); } - /// \ru Зарезервировать память для контейнера четырехугольников. \en Reserve memory for container of quadrangles. - void QuadranglesReserve( size_t n ) { quadrangles.reserve( quadrangles.size() + n ); } - /// \ru Зарезервировать память для контейнера элементов. \en Reserve memory for container of elements. - void ElementsReserve ( size_t n ) { elements.reserve( elements.size() + n ); } - /// \ru Зарезервировать память для контейнера сегментов. \en Reserve memory for container of segments. - void SegmentsReserve ( size_t n ) { segments.reserve( segments.size() + n ); } - /// \ru Зарезервировать память для контейнера полигонов. \en Reserve memory for container of grids. - //void GridReserve ( size_t n ) { grids.reserve( grids.size() + n ); } - - /// \ru Удалить всю триангуляцию без освобождения памяти, занятую контейнерами. \en Delete all triangulation without freeing the memory occupied by containers. - void Flush() { points.clear(); normals.clear(); escorts.clear(); - triangles.clear(); quadrangles.clear(); elements.clear(); segments.clear(); //grids.clear(); - cube.SetEmpty(); } - /// \ru Удалить всю триангуляцию и освободить память. \en Delete all triangulation and free the memory. - void HardFlush() { points.clear(); normals.clear(); escorts.clear(); - triangles.clear(); quadrangles.clear(); elements.clear(); segments.clear(); //grids.clear(); - #ifdef STANDARD_C11 - points.shrink_to_fit(); normals.shrink_to_fit(); escorts.shrink_to_fit(); - triangles.shrink_to_fit(); quadrangles.shrink_to_fit(); elements.shrink_to_fit(); segments.shrink_to_fit(); //grids.shrink_to_fit(); - #endif - cube.SetEmpty(); } - /// \ru Освободить лишнюю память. \en Free the unnecessary memory. - void Adjust() { - #ifdef STANDARD_C11 - points.shrink_to_fit(); normals.shrink_to_fit(); escorts.shrink_to_fit(); - triangles.shrink_to_fit(); quadrangles.shrink_to_fit(); elements.shrink_to_fit(); segments.shrink_to_fit(); //grids.shrink_to_fit(); - #endif - } - - /// \ru Инициализировать объект. \en Initialize object. - void Init( const MbCollection & init ); - /// \ru Инициализировать объект. \en Initialize object. - void Init( const MbGrid & init ); - /// \ru Инициализировать объект. \en Initialize object. - void Init( const MbMesh & init ); - - // \ru Выдать контейнер треугольников. \en Get the container of triangles. - template - void GetTriangles( TrianglesVector & tVector ) const { - tVector.reserve( tVector.size() + triangles.size() ); - for ( size_t i = 0, iCount = triangles.size(); i < iCount; i++ ) - tVector.push_back( triangles[i] ); - } - // \ru Выдать контейнер четырёхугольников. \en Get the container of quadrangles. - template - void GetQuadrangles( QuadranglesVector & qVector ) const { - qVector.reserve( qVector.size() + quadrangles.size() ); - for ( size_t i = 0, iCount = quadrangles.size(); i < iCount; i++ ) - qVector.push_back( quadrangles[i] ); - } - - /// \ru Преобразовать четырёхугольники в треугольники. \en Convert quadrangles to triangles. - void ConvertQuadranglesToTriangles(); - /// \ru Преобразовать все объекты в треугольники и уравнять число точек и нормалей. \en Convert all objects to triangles and equalize count of points and count of normals. - void ConvertAllToTriangles(); - /// \ru Удалить дублирующие с заданной точностью друг друга точки. \en Remove redundant points with a given tolerance (duplicates). - bool RemoveRedundantPoints( bool deleteNormals, double epsilon = LENGTH_EPSILON ); - - /** \} */ - private: - /// \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - MbCollection & operator = ( const MbCollection & ); - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCollection ) -}; - -IMPL_PERSISTENT_OPS( MbCollection ) - -#endif // __COLLECTION_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Коллекция элементов. + \en Collection of elements . \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __COLLECTION_H +#define __COLLECTION_H + + +#include +#include +#include +#include +#include + + +class MATH_CLASS MbMesh; +class MATH_CLASS MbGrid; + + +//------------------------------------------------------------------------------ +/** \brief \ru Коллекция элементов. + \en Collection of elements. \~ + \details \ru Коллекция элементов - это объект геометрической модели, наследник MbItem, являющийся + множеством элементов в трехмерном пространстве. \n + \en The collection of 3D elements is an object of geometric model (subclass MbItem) which is + the set of elements in 3D space. \n \~ + \ingroup Model_Items +*/ +// --- +class MATH_CLASS MbCollection : public MbItem { +public: + /** \brief \ru Типы коллекций 3D объектов. + \en Types of 3D object collection. \~ +*/ +enum CollectionType { + coll_PointCloud = 0, ///< \ru Облако точек. \en The point cloud. + coll_Tessellation = 1, ///< \ru Триангуляция. \en The tessellation. + coll_Elements = 2, ///< \ru Набор элементов. \en Set of elements. + coll_Segmentation = 3, ///< \ru Сегментированная полигональная сетка. \en Segmented polygonal mesh. +}; + +private: + CollectionType type; ///< \ru Тип коллекции 3D объектов. \en Type of 3D object collection. + uint32 xSize; ///< \ru Количество объектов вдоль первой координаты. \en The number of objects along the first coordinate. + uint32 ySize; ///< \ru Количество объектов вдоль второй координаты. \en The number of objects along the second coordinate. + uint32 zSize; ///< \ru Количество объектов вдоль третьей координаты. \en The number of objects along the third coordinate. + std::vector points; ///< \ru Множество точек. \en Set of points. + std::vector normals; ///< \ru Множество нормалей в точках согласовано с множеством точек. \en Set of normals at control points is synchronized with the set of points. + std::vector escorts; ///< \ru Множество значений для дополнительной информации в точках. \en The set of values for additional information of points. + std::vector triangles; ///< \ru Индексное множество треугольных пластин содержит номера элементов множества points и normals. \en Set of triangular plates contains numbers of elements of 'points' and 'normals' sets. + std::vector quadrangles; ///< \ru Индексное множество четырёхугольных пластин содержит номера элементов множества params и/или множеств points и normals. \en Set of quadrangular plates contains numbers of elements of 'params' set and/or of 'points' and 'normals' sets. + std::vector elements; ///< \ru Индексное множество объемных элементов содержит номера элементов множества points. \en Set of volume elements contains numbers of vertices of 'points' sets. + std::vector segments; ///< \ru Множество сегментов полигональной сетки. \en Set of segments of mesh. + + /** \brief \ru Габаритный куб объекта. + \en Bounding box of object. \~ + \details \ru Габаритный куб объекта рассчитывается только при запросе габарита объекта. Габаритный куб в конструкторе объекта и после модификации объекта принимает неопределенное значение. + \en Bounding box of object is calculated only at the request. Bounding box of object is undefined after object constructor and after object modifications \n \~ + */ + mutable MbCube cube; +private: + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. + MbCollection( const MbCollection & init ); + + /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. + explicit MbCollection( const MbCollection &, MbRegDuplicate * ); +public: + /// \ru Конструктор. \en Constructor. + MbCollection(); + /// \ru Конструктор. \en Constructor. + MbCollection( const MbMesh & mesh ); + + /// \ru Деструктор. \en Destructor. + virtual ~MbCollection(); + +public: + VISITING_CLASS( MbCollection ); + + // \ru Общие функции геометрического объекта \en Common functions of a geometric object + virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en A type of an object. + virtual MbeSpaceType Type() const; // \ru Групповой тип объекта. \en Group type of object. + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Создать копию. \en Create a copy. + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвинуть вдоль вектора. \en Translate along a vector. + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. + virtual bool IsSame ( const MbSpaceItem & init, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Are the objects equal? + virtual bool SetEqual ( const MbSpaceItem & init ); // \ru Сделать объекты равным. \en Make the objects equal. + virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. + virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Добавь свой габарит в куб. \en Add bounding box into a cube. + virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate bounding box in the local coordinate system. + virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. + + virtual MbProperty & CreateProperty( MbePrompt n ) const; // \ru Создать собственное свойство. \en Create a custom property. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. + + // \ru Создать полигональный объект - упрощенную копию данного объекта. \en Create a polygonal object - a simplified copy of the given object. + virtual MbItem * CreateMesh( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * iReg ) const; + // \ru Создать полигональный объект - упрощенную копию данного объекта. \en Create a polygonal object - a simplified copy of the given object. + MbGrid * CreateGrid() const; + + // \ru Создать сетки из четырехугольных пластин наружных стенок элементов. \en Create grids by quadrangular plates of the outside walls of elements. + void CreateGridsByElements( RPArray & grids_ ) const; + + // \ru Создать угловые точки и элементы. \en Create corner points and elements. + void CreateCornerPointsAndElements( SArray & points0, SArray & elements0 ) const; + + // \ru Создать сетки из по результатам сегментации. \en Create grids by segmentation results. + void CreateGridsBySegments( RPArray & grids_ ) const; + + /** \ru \name Общие функции коллекции. + \en \name Common functions of a collection. + \{ */ + + /// \ru Выдать количество точек. \en Get count of points. + size_t PointsCount() const { return points.size(); } + /// \ru Выдать количество нормалей. \en Get the number of normals. + size_t NormalsCount() const { return normals.size(); } + /// \ru Выдать количество значений. \en Get count of values. + size_t EscortsCount() const { return escorts.size(); } + /// \ru Выдать количество треугольников. \en Get the number of triangles. + size_t TrianglesCount() const { return triangles.size(); } + /// \ru Выдать количество четырехугольников. \en Get the number of quadrangles. + size_t QuadranglesCount() const { return quadrangles.size(); } + /// \ru Выдать количество объемных элементов. \en Get the number of elements of volume. + size_t ElementsCount() const { return elements.size(); } + /// \ru Выдать количество сегментов. \en Get the number of segments of mesh. + size_t SegmentsCount() const { return segments.size(); } + /// \ru Выдать количество триангуляций. \en Get the number of triangulations. + //size_t GridsCount() const { return grids.size(); } + ptrdiff_t PointsMaxIndex() const { ptrdiff_t c = points.size(); return ( c - 1 ); } + /// \ru Выдать количество нормалей минус 1 (максимальный индекс). \en Get the number of normals minus one (maximal index). + ptrdiff_t NormalsMaxIndex() const { ptrdiff_t c = normals.size(); return ( c - 1 ); } + + /// \ru Добавить в коллекцию точку и нормаль в точке. \en Add a point and normal at the point to collection. + void AddPoint ( const MbCartPoint3D & p3D, const MbVector3D & n3D ) { points.push_back(p3D); normals.push_back(n3D); cube.SetEmpty(); } + /// \ru Добавить в коллекцию точку. \en Add a point to collection. + void AddPoint ( const MbCartPoint3D & p3D ) { points.push_back(p3D); cube.SetEmpty(); } + /// \ru Добавить в коллекцию нормаль. \en Add a normal to collection. + void AddNormal( const MbVector3D & n3D ) { normals.push_back(n3D) ; } + /// \ru Добавить в коллекцию точки. \en Add points to collection. + void AddPoints ( const std::vector & pnts ) { points.insert(points.end(), pnts.begin(), pnts.end()); cube.SetEmpty(); } + /// \ru Добавить в коллекцию нормали. \en Add normals to collection. + void AddNormals( const SArray & nrms ) { normals.insert(normals.end(), nrms.begin(), nrms.end()); cube.SetEmpty(); } + /// \ru Добавить в коллекцию данных. \en Add scores to collection. + void AddEscorts( const std::vector & scores ) { escorts.insert(escorts.end(), scores.begin(), scores.end()); } + + /// \ru Добавить треугольник. \en Add a triangle. + void AddTriangle ( const MbTriangle & triangle ) { triangles.push_back( triangle ); } + /// \ru Добавить треугольник с заданными номерами вершин. \en Add a triangle by the given indices of vertices + void AddTriangle ( uint j0, uint j1, uint j2, bool o ) { MbTriangle t(j0,j1,j2,o); triangles.push_back( t ); } + /// \ru Добавить четырёхугольник. \en Add a quadrangle. + void AddQuadrangle( const MbQuadrangle & quadrangle ) { quadrangles.push_back( quadrangle ); } + /// \ru Добавить четырёхугольник с заданными номерами вершин. \en Add a quadrangle by the given indices of vertices. + void AddQuadrangle( uint j0, uint j1, uint j2, uint j3, bool o ) { MbQuadrangle t(j0,j1,j2,j3,o); quadrangles.push_back( t ); } + /// \ru Добавить объемный элемент. \en Add an element. + void AddElement( const MbElement & element ) { elements.push_back(element); } + /// \ru Добавить объемный элемент. \en Add an element. + void AddElement( uint j0, uint j1, uint j2, uint j3, uint j4, uint j5, uint j6, uint j7 ) { + MbElement t( j0,j1,j2,j3,j4,j5,j6,j7 ); elements.push_back( t ); } + void AddSegment( const MbGridSegment & segment ) { segments.push_back( segment ); } + void AddSegment( const std::vector & initFaces ) { MbGridSegment seg( initFaces ); segments.push_back( seg ); } + /// \ru Добавить полигон. \en Add a polygon. + //void AddGrid( MbExactGrid & grd ) { grids.push_back( &grd ); } + + /// \ru Выдать индексы точек в массиве points для i-го треугольника (связанного или несвязанного). \en Get indices of points in 'points' array for i-th triangle (adjacent or non-adjacent). + bool GetTrianglePointIndex ( size_t i, uint & ind0, uint & ind1, uint & ind2 ) const; + /// \ru Выдать индексы точек в массиве points для i-го четырехугольника (связанного или несвязанного). \en Get indices of points in 'points' array for i-th quadrangle (adjacent or non-adjacent). + bool GetQuadranglePointIndex( size_t i, uint & ind0, uint & ind1, uint & ind2, uint & ind3 ) const; + /// \ru Выдать для треугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th triangle in general numbering (with strips). + bool GetTrianglePoints ( size_t i, MbCartPoint3D &p0, MbCartPoint3D &p1, MbCartPoint3D &p2 ) const; + /// \ru Выдать для треугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th triangle in general numbering (with strips). + bool GetTriangleNormals ( size_t i, MbVector3D &n0, MbVector3D &n1, MbVector3D &n2 ) const; + + /// \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th quadrangle in general numbering (with strips). + bool GetQuadranglePoints ( size_t i, MbCartPoint3D &p0, MbCartPoint3D &p1, MbCartPoint3D &p2, MbCartPoint3D &p3 ) const; + /// \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th quadrangle in general numbering (with strips). + bool GetQuadrangleNormals( size_t i, MbVector3D &n0, MbVector3D &n1, MbVector3D &n2, MbVector3D &n3 ) const; + + /// \ru Удалить точки. \en Delete points. + void PointsRemove() { points.clear(); + #ifdef C3D_STANDARD_CXX_11_PARTIAL + points.shrink_to_fit(); + #endif + cube.SetEmpty(); } + /// \ru Удалить точку с заданным номером. \en Delete point by the given index. + void PointRemove ( size_t i ) { if ( i < points.size() ) points.erase( points.begin() + i ); cube.SetEmpty(); } + /// \ru Удалить нормаль с заданным номером. \en Delete normal by the given index. + void NormalRemove( size_t i ) { if ( i < normals.size() ) normals.erase( normals.begin() + i ); } + + /// \ru Установить тип объекта. \en Set type. + void SetType( CollectionType t ) { type = t; } + /// \ru Выдать тип объекта. \en Get type. + CollectionType GetType() const { return type; } + /// \ru Установить количество объектов вдоль первой координаты. \en Set the number of objects along the first coordinate. + void SetXSize( uint32 n ) { xSize = n; } + /// \ru Установить количество объектов вдоль второй координаты. \en Set the number of objects along the cecond coordinate. + void SetYSize( uint32 n ) { ySize = n; } + /// \ru Установить количество объектов вдоль третьей координаты. \en Set the number of objects along the third coordinate. + void SetZSize( uint32 n ) { zSize = n; } + /// \ru Выдать количество объектов вдоль первой координаты. \en Get the number of objects along the first coordinate. + uint32 GetXSize() const { return xSize; } + /// \ru Выдать количество объектов вдоль второй координаты. \en Get the number of objects along the cecond coordinate. + uint32 GetYSize() const { return ySize; } + /// \ru Выдать количество объектов вдоль третьей координаты. \en Get the number of objects along the third coordinate. + uint32 GetZSize() const { return zSize; } + + /// \ru Выдать точку по её номеру. \en Get point by its index. + void GetPoint ( size_t i, MbCartPoint3D & p ) const { p = points[i]; } + /// \ru Выдать множество точек. \en Get set of points. + const std::vector & GetPoints ( ) const { return points; } + /// \ru Выдать нормаль по её номеру. \en Get normal by its index. + void GetNormal( size_t i, MbVector3D & n ) const { n = normals[i]; } + /// \ru Выдать множество нормалей. \en Get set of normals. + const std::vector & GetNormals( ) const { return normals; } + /// \ru Выдать точку по её номеру. \en Get point by its index. + double GetEscort( size_t i ) const { return escorts[i]; } + /// \ru Выдать элемент по его номеру. \en Get element by its index. + void GetElement( size_t i, MbElement & elem ) const { elem = elements[i]; } + /// \ru Выдать индексы точек в массиве points для i-го объемного элемента. \en Get indices of points in 'points' array for i-th element. + bool GetElementIndex( size_t i, uint & ind0, uint & ind1, uint & ind2, uint & ind3, uint & ind4, uint & ind5, uint & ind6, uint & ind7 ) const; + /// \ru Выдать для элемента с номером i точки вершин. \en Get points of vertices for i-th element. + bool GetElementPoints ( size_t i, MbCartPoint3D &p0, MbCartPoint3D &p1, MbCartPoint3D &p2, MbCartPoint3D &p3, + MbCartPoint3D &p4, MbCartPoint3D &p5, MbCartPoint3D &p6, MbCartPoint3D &p7 ) const; + /// \ru Выдать сегмент по его номеру. \en Get segment by its index. + void GetSegment( size_t i, MbGridSegment & seg ) const { seg = segments[i]; } + /// \ru Выдать точку с заданным номером. \en Get point by the given index. + const MbCartPoint3D & GetPoint ( size_t i ) const { return points[i]; } + /// \ru Выдать нормаль с заданным номером. \en Get normal by the given index. + const MbVector3D & GetNormal( size_t i ) const { return ( (normals.size() == 1) ? normals[0] : normals[i] ); } + /// \ru Выдать треугольник с номером i. \en Get i-th triangle. + const MbTriangle & GetTriangle ( size_t i ) const { return triangles[i]; } + /// \ru Выдать четырёхугольник с номером i. \en Get i-th quadrangle. + const MbQuadrangle & GetQuadrangle( size_t i ) const { return quadrangles[i]; } + /// \ru Выдать четырёхугольник с номером i. \en Get i-th quadrangle. + const MbElement & GetElement ( size_t i ) const { return elements[i]; } + /// \ru Выдать сегмент по его номеру. \en Get segment by its index. + const MbGridSegment & GetSegment( size_t i ) const { return segments[i]; } + /// \ru Выдать полигон с номером i. \en Get i-th polygon. + //const MbExactGrid & GetGrid ( size_t i ) const { return *grids[i]; } + + /// \ru Удалить все xтреугольники. \en Delete all triangles. + void TrianglesDelete() { triangles.clear(); } + /// \ru Удалить все четырехугольники. \en Delete all quadrangles. + void QuadranglesDelete() { quadrangles.clear(); } + /// \ru Удалить все объемные элементы. \en Delete all elements. + void ElementsDelete() { elements.clear(); } + /// \ru Удалить все сегменты. \en Delete all segments. + void SegmentsDelete() { segments.clear(); } + /// \ru Удалить все nhbfyuekzwbb. \en Delete all triangulations. + //void GridsDelete(); + + /// \ru Зарезервировать память для контейнеров. \en Reserve memory for some containers. + void ReservePointsNormals( size_t n ) { points.reserve( points.size() + n ); normals.reserve( normals.size() + n ); } + /// \ru Зарезервировать память для контейнера точек. \en Reserve memory for container of points. + void PointsReserve ( size_t n ) { points.reserve( points.size() + n ); } + /// \ru Зарезервировать память для контейнера нормалей. \en Reserve memory for container of normals. + void NormalsReserve ( size_t n ) { normals.reserve( normals.size() + n ); } + /// \ru Зарезервировать память для контейнера параметров. \en Reserve memory for container of elements. + /// \ru Зарезервировать память для контейнера параметров. \en Reserve memory for container of elements. + void EscordsReserve ( size_t n ) { escorts.reserve( escorts.size() + n ); } + /// \ru Зарезервировать память для контейнера треугольников. \en Reserve memory for container of triangles. + void TrianglesReserve ( size_t n ) { triangles.reserve( triangles.size() + n ); } + /// \ru Зарезервировать память для контейнера четырехугольников. \en Reserve memory for container of quadrangles. + void QuadranglesReserve( size_t n ) { quadrangles.reserve( quadrangles.size() + n ); } + /// \ru Зарезервировать память для контейнера элементов. \en Reserve memory for container of elements. + void ElementsReserve ( size_t n ) { elements.reserve( elements.size() + n ); } + /// \ru Зарезервировать память для контейнера сегментов. \en Reserve memory for container of segments. + void SegmentsReserve ( size_t n ) { segments.reserve( segments.size() + n ); } + /// \ru Зарезервировать память для контейнера полигонов. \en Reserve memory for container of grids. + //void GridReserve ( size_t n ) { grids.reserve( grids.size() + n ); } + + /// \ru Удалить всю триангуляцию без освобождения памяти, занятую контейнерами. \en Delete all triangulation without freeing the memory occupied by containers. + void Flush() { points.clear(); normals.clear(); escorts.clear(); + triangles.clear(); quadrangles.clear(); elements.clear(); segments.clear(); //grids.clear(); + cube.SetEmpty(); } + /// \ru Удалить всю триангуляцию и освободить память. \en Delete all triangulation and free the memory. + void HardFlush() { points.clear(); normals.clear(); escorts.clear(); + triangles.clear(); quadrangles.clear(); elements.clear(); segments.clear(); //grids.clear(); + #ifdef C3D_STANDARD_CXX_11_PARTIAL + points.shrink_to_fit(); normals.shrink_to_fit(); escorts.shrink_to_fit(); + triangles.shrink_to_fit(); quadrangles.shrink_to_fit(); elements.shrink_to_fit(); segments.shrink_to_fit(); //grids.shrink_to_fit(); + #endif + cube.SetEmpty(); } + /// \ru Освободить лишнюю память. \en Free the unnecessary memory. + void Adjust() { + #ifdef C3D_STANDARD_CXX_11_PARTIAL + points.shrink_to_fit(); normals.shrink_to_fit(); escorts.shrink_to_fit(); + triangles.shrink_to_fit(); quadrangles.shrink_to_fit(); elements.shrink_to_fit(); segments.shrink_to_fit(); //grids.shrink_to_fit(); + #endif + } + + /// \ru Инициализировать объект. \en Initialize object. + void Init( const MbCollection & init ); + /// \ru Инициализировать объект. \en Initialize object. + void Init( const MbGrid & init ); + /// \ru Инициализировать объект. \en Initialize object. + void Init( const MbMesh & init ); + + // \ru Выдать контейнер треугольников. \en Get the container of triangles. + template + void GetTriangles( TrianglesVector & tVector ) const { + tVector.reserve( tVector.size() + triangles.size() ); + for ( size_t i = 0, iCount = triangles.size(); i < iCount; i++ ) + tVector.push_back( triangles[i] ); + } + // \ru Выдать контейнер четырёхугольников. \en Get the container of quadrangles. + template + void GetQuadrangles( QuadranglesVector & qVector ) const { + qVector.reserve( qVector.size() + quadrangles.size() ); + for ( size_t i = 0, iCount = quadrangles.size(); i < iCount; i++ ) + qVector.push_back( quadrangles[i] ); + } + + /// \ru Преобразовать четырёхугольники в треугольники. \en Convert quadrangles to triangles. + void ConvertQuadranglesToTriangles(); + /// \ru Преобразовать все объекты в треугольники и уравнять число точек и нормалей. \en Convert all objects to triangles and equalize count of points and count of normals. + void ConvertAllToTriangles(); + /// \ru Удалить дублирующие с заданной точностью друг друга точки. \en Remove redundant points with a given tolerance (duplicates). + bool RemoveRedundantPoints( bool deleteNormals, double epsilon = LENGTH_EPSILON ); + + /** \} */ + private: + /// \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + MbCollection & operator = ( const MbCollection & ); + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCollection ) +}; + +IMPL_PERSISTENT_OPS( MbCollection ) + +#endif // __COLLECTION_H diff --git a/C3d/Include/comanager.h b/C3d/Include/comanager.h index f493bf7..c96969e 100644 --- a/C3d/Include/comanager.h +++ b/C3d/Include/comanager.h @@ -1,59 +1,59 @@ -////////////////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Модуль: COMANAGER - \en Module: COMANAGER. \~ - \details \ru Цель: Менеджер геометрических ограничений для MbModel - \en Target: Geometric constraints manager for MbModel \~ - -*/ -////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef __COMANAGER_H -#define __COMANAGER_H -// -#include -#include -// constraints -#include "gce_api.h" - - - -class GcFormerImpl; -class MbConstraint; - - -////////////////////////////////////////////////////////////////////////////////////////// -// -/// \ru Менеджер для взаимодействия с решателем \en Manager of interactions with the solver -// -////////////////////////////////////////////////////////////////////////////////////////// - -class MATH_CLASS ConstraintManager2D -{ - GCE_system m_gcSolver; - GcFormerImpl & m_gcFormer; - -public: - ConstraintManager2D(); - ~ConstraintManager2D(); - -public: - /// \ru Добавить ограничение в решатель \en Add a constraint to the solver - bool AddConstraint( const MbConstraint & ); - /// \ru Рассчитать систему ограничений \en Compute a system of constraints - bool Evaluate(); - /// \ru Применить решение \en Apply the solution - void ApplySolution(); - /// \ru Очистить весь контекст решателя \en Clear the whole context of the solver - void Clear(); - -private: - ConstraintManager2D( const ConstraintManager2D & ); - ConstraintManager2D & operator = ( const ConstraintManager2D & ); -}; - -#endif // __COMANAGER_H - - +////////////////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Модуль: COMANAGER + \en Module: COMANAGER. \~ + \details \ru Цель: Менеджер геометрических ограничений для MbModel + \en Target: Geometric constraints manager for MbModel \~ + +*/ +////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef __COMANAGER_H +#define __COMANAGER_H +// +#include +#include +// constraints +#include "gce_api.h" + + + +class GcFormerImpl; +class MbConstraint; + + +////////////////////////////////////////////////////////////////////////////////////////// +// +/// \ru Менеджер для взаимодействия с решателем \en Manager of interactions with the solver +// +////////////////////////////////////////////////////////////////////////////////////////// + +class MATH_CLASS ConstraintManager2D +{ + GCE_system m_gcSolver; + GcFormerImpl & m_gcFormer; + +public: + ConstraintManager2D(); + ~ConstraintManager2D(); + +public: + /// \ru Добавить ограничение в решатель \en Add a constraint to the solver + bool AddConstraint( const MbConstraint & ); + /// \ru Рассчитать систему ограничений \en Compute a system of constraints + bool Evaluate(); + /// \ru Применить решение \en Apply the solution + void ApplySolution(); + /// \ru Очистить весь контекст решателя \en Clear the whole context of the solver + void Clear(); + +private: + ConstraintManager2D( const ConstraintManager2D & ); + ConstraintManager2D & operator = ( const ConstraintManager2D & ); +}; + +#endif // __COMANAGER_H + + // eof \ No newline at end of file diff --git a/C3d/Include/constraint.h b/C3d/Include/constraint.h index d096cee..b15f24f 100644 --- a/C3d/Include/constraint.h +++ b/C3d/Include/constraint.h @@ -1,229 +1,237 @@ -////////////////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Геометрическое ограничение. - \en Geometric constraint. \~ -*/ -////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef __CONSTRAINT_H -#define __CONSTRAINT_H - -#include -#include -#include -#include -#include - -struct CNodeIterator; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Аргумент геометрического ограничения. - \en An argument of geometric constraint. \~ - \details \ru Аргумент ограничений со связанным элементом, указателем на геометрический - объект, содержащий элемент, и указателем на сборку, содержащую объект. - \en An argument of constraints with connected element, the geometric object - containing the element, and the assembly containing the object.\~ - \ingroup Model_Items -*/ -//--- -class MATH_CLASS MtGeomArgument -{ -private: - SPtr propItem; ///< \ru Элемент объекта, непосредственно выбранный для связи. \en The element of geom object which constraints are connected to. - SimpleName propName; ///< \ru Имя связываемого элемента. \en Name of a connected element. - uint32 hash; ///< \ru Имя объекта сборки или подсборки, содержащего объект связи с ограничением. \en Hash code of the path from the root to the item. - SPtr item; ///< \ru Объект сборки или подсборки, содержащий объект связи с ограничением. \en Assembly object that hosts geom entity connected to constraint. - const MbAssembly * root; ///< \ru Сборка, содержащая объект с ограничением. \en The assembly that hosts geom object with entity connected to constraint. - -public: - static const MtGeomArgument null; ///< \ru Пустой аргумент. \en An empty argument. - -public: - MtGeomArgument( const MbRefItem * p, const MbItem * h ); - MtGeomArgument( const MtGeomArgument & ); - MtGeomArgument() : propItem( NULL ), propName( UNDEFINED_SNAME ) - , hash( UNDEFINED_SNAME ), item( NULL ), root( NULL ) {} - -public: - /** \brief \ru Получить непосредственный объект сборки, содержащий ссылочный объект. - \en Get immediate object of the assembly containing the reference object. - \param trans - \ru Матрица ссылочного объекта в системе координат непосредственного объекта сборки. - - \en Matrix from the reference object to the sub-item of the assembly. \~ - */ - const MbItem * SubItemOf( const MbAssembly *, MbMatrix3D & trans ) const; - /// \ru Объект геометрической модели, владеющий аргументом. \en Geometry model object which is a host of an argument. - const MbItem * HostItem() const { return item; } - /// \ru Аргумент ограничения, заданный в ЛСК хозяина. \en Geometric constraint argument given in the host's LCS. - const MbRefItem * PropItem() const { return propItem; } - /// \ru Выдать значение геометрии аргумента, заданное в ЛСК хозяина. \en Get geometric value of argument given in the host LCS. - MtGeomVariant PropGeom() const; - /// \ru Выдать хэш-имя объекта. \en Get a hash name of the object. - SimpleName PropName() const; - /// \ru Равны ли объекты? \en Are objects equal? \~ - bool IsSame( const MtGeomArgument & r ) const { - return ( (propItem == r.propItem) && (propName == r.propName) && (item == r.item) && (hash == r.hash) ); - } - /// \ru Равны ли ссылкпи на объект модели? \en Are the references to the model object equal? \~ - bool IsSameItemReference( const MtGeomArgument & r ) const { // MtItemReference - return ( (hash == r.hash) && (item == r.item) && (root == r.root) ); - } - /// \ru Оператор равенства объектов. \en Objects equality operator. \~ - bool operator == ( const MtGeomArgument & ) const; - /// \ru Оператор копирования. \en Copy operator. \~ - MtGeomArgument & operator = ( const MtGeomArgument & ); - -KNOWN_OBJECTS_RW_REF_OPERATORS( MtGeomArgument ) // Serializing into a file format -}; // MtGeomArgument - - -//---------------------------------------------------------------------------------------- -/** \brief \ru Геометрическое ограничение. - \en Geometric constraint. \~ - \details \ru Этот класс представляет все виды ограничений, включая геометрические и - размерные отношения между объектами модели. - \en This class represents all kinds of constraints of assembly, including - geometrical and dimensional relationships between the model objects. \~ - \ingroup Model_Items -*/ -//--- -class MATH_CLASS MtGeomConstraint -{ -private: - const ItConstraintItem * m_cItem; ///< \ru Указатель на реализацию геометрического ограничения. \en Pointer to geometric constraint implementation. \~ - std::vector m_arguments; ///< \ru Аргументы геометрического ограничения. \en The arguments of geometric constraint. \~ - SPtr m_mesh; ///< \ru Объект для демонстрации геометрического ограничения. \en The draw object of geometric constraint. \~ - -public: - MtGeomConstraint( const MtGeomConstraint & ); - ~MtGeomConstraint(); - -public: - /// \ru Возвращает true, если ограничение не действительно. \en Return true if the constraint is invalid. - bool IsNull() const { return m_cItem == NULL; } - /// \ru Тип сопряжения (геометрического ограничения). \en Type of geometric constraint. - MtMateType ConstraintType() const; - /// \ru Текущее значение размера. \en Current value of the dimension. - double DimValue() const; - /// \ru Создать полигональный объект для отображения геометрических ограничений. \en Create a polygonal object for visualization. - bool CreateMesh( const MbAssembly & assem, const MbStepData & stepData, const MbFormNote & note, double meshUnit, uint32 color ); - /// \ru Выдать указатель на объект для демонстрации геометрического ограничения. \en Get a pointer to draw object of geometric constraint. - const MbItem * GetMesh() const { return m_mesh.get(); } - - // \ru Объявление оператора присваивания. \en Declaration of the assignment operator. - MtGeomConstraint & operator = (const MtGeomConstraint & arg ); - -protected: - friend class MbConstraintSystem; - friend class MtConstraintIter; - /// \ru Выдать указатель на реализацию геометрического ограничения. \en Get a pointer to geometric constraint implementation. \~ - const ItConstraintItem * ConstraintItem() const { return m_cItem; } - - MtGeomConstraint( const ItConstraintItem * cItem, const MtGeomArgument & a1, const MtGeomArgument & a2 ); - MtGeomConstraint( const MbConstraintSystem &, const ItConstraintItem * cItem ); -}; // MtGeomConstraint - - -//---------------------------------------------------------------------------------------- -/// \ru Итератор обходящий ограничения сборки. \en Iterator traversing assembly constraints. -//--- -class MATH_CLASS MtConstraintIter -{ -private: - CNodeIterator * m_cIter; - const MbConstraintSystem * m_gcSystem; - -public: - MtConstraintIter(); - MtConstraintIter( const MtConstraintIter & ); - MtConstraintIter & operator = ( const MtConstraintIter & ); - ~MtConstraintIter(); - -public: - MtGeomConstraint Get() const; - MtConstraintIter & Set( const MbConstraintSystem *, CNodeIterator & ); - const MtConstraintIter & Next(); - bool EqualTo( const MtConstraintIter & ) const; - -public: - //operator CNodeIterator& () { return *impl; } - MtGeomConstraint operator*() const { return Get(); } - // prefix operator - const MtConstraintIter & operator++() { return Next(); } - bool operator ==( const MtConstraintIter & iter ) const { return EqualTo( iter ); } - bool operator !=( const MtConstraintIter & iter ) const { return !EqualTo( iter ); } - -}; // MtConstraintIter - - -//---------------------------------------------------------------------------------------- -/// \ru Обработчик события, связанные с решением сборки. \en The event handles related to solving the assembly. -//--- -struct MATH_CLASS ItAssemblyReactor -{ -public: - /// \ru Захватить сборкой объект для дальнейшей работы. \en Capture the reactor instance by the assembly for further work. - virtual void Capture( const MbAssembly * ) = 0; - /// \ru Отпустить сборкой объект, прекратить работать с ним. \en Release this instance by the assembly, stop working with it. - virtual void Release() = 0; - /// \ru Геометрический решатель не пытался удовлетворить ограничение. \en This called when geometric solver failed to try for constraint satisfaction. - virtual void EvaluationFailed( const MbAssembly * ) const {} - /// \ru Геометрический решатель нашел новую позицию под-объекта сборки. \en The geometric solver found a new position of a constrained sub-object belonging the assembly. - virtual void PositionChanged( const MbAssembly *, const MbItem * /*subItem*/ ) const {} - -protected: - ~ItAssemblyReactor() {} -}; // ItAssemblyReactor - - -//---------------------------------------------------------------------------------------- -/** \brief \ru Интерфейс для системы ограничения импорта сборки из приложения. - \en The user defined interface for import constraint system of an assembly from CAD application. -*/ -//--- -struct MATH_CLASS ItAssemblyImportData -{ -protected: - ~ItAssemblyImportData() {} - -public: - /// \ru Импорт системы ограничений сборки. \en Import a constraint system of the assembly. - virtual bool ImportCSystem( const MbAssembly &, GCM_system & ) const = 0; - /// \ru Получить дескриптор элемента сборки в системе ограничений. \en Get a descriptor of assembly sub-item which used in the constraint system. - virtual MtGeomId GeomId( const MbAssembly &, const MbItem * ) const = 0; - /// \ru Получить объект модели, являющийся аргументом геометрического ограничения. \en Get the model object that is the argument of the geometric constraint. - virtual MtGeomArgument GeomSubItem( const MtArgument & ) const { return MtGeomArgument(); } - /// \ru Получить объект модели, являющийся аргументом геометрического ограничения. \en Get the model object that is the argument of the geometric constraint. - virtual MtGeomArgument GeomSubItem( MtGeomId ) const { return MtGeomArgument(); } -}; //ItAssemblyImportData - -//---------------------------------------------------------------------------------------- -/// \ru Оператор равенства объектов. \en Objects equality operator. \~ -//--- -inline bool MtGeomArgument::operator == ( const MtGeomArgument & r ) const -{ - if ( propItem != r.propItem ) - return false; - if ( propName != r.propName ) - return false; - if ( (item == r.item) && (hash == r.hash) ) - return true; - if ( (hash == r.hash) && (root == r.root) ) - return true; - return false; -} - -//---------------------------------------------------------------------------------------- -/// \ru Оператор копирования. \en Copy operator. \~ -//--- -inline MtGeomArgument & MtGeomArgument::operator = ( const MtGeomArgument & arg ) -{ - propItem = arg.propItem; - propName = arg.propName; - hash = arg.hash; - item = arg.item; - root = arg.root; - return *this; -} - -#endif // __CONSTRAINT_H +////////////////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Геометрическое ограничение. + \en Geometric constraint. \~ +*/ +////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef __CONSTRAINT_H +#define __CONSTRAINT_H + +#include +#include +#include +#include +#include + +struct CNodeIterator; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Аргумент геометрического ограничения. + \en An argument of geometric constraint. \~ + \details \ru Аргумент ограничений со связанным элементом, указателем на геометрический + объект, содержащий элемент, и указателем на сборку, содержащую объект. + \en An argument of constraints with connected element, the geometric object + containing the element, and the assembly containing the object.\~ + \ingroup Model_Items +*/ +//--- +class MATH_CLASS MtGeomArgument +{ +private: + SPtr propItem; ///< \ru Элемент объекта, непосредственно выбранный для связи. \en The element of geom object which constraints are connected to. + SimpleName propName; ///< \ru Имя связываемого элемента. \en Name of a connected element. + SimpleName hash; ///< \ru Имя объекта сборки или подсборки, содержащего объект связи с ограничением. \en Hash code of the path from the root to the item. + SPtr item; ///< \ru Объект сборки или подсборки, содержащий объект связи с ограничением. \en Assembly object that hosts geom entity connected to constraint. + const MbAssembly * root; ///< \ru Сборка, содержащая объект с ограничением. \en The assembly that hosts geom object with entity connected to constraint. + +public: + static const MtGeomArgument null; ///< \ru Пустой аргумент. \en An empty argument. + +public: + MtGeomArgument( const MbRefItem * p, const MbItem * h ); + MtGeomArgument( const MtGeomArgument & ); + MtGeomArgument() : propItem( NULL ), propName( c3d::UNDEFINED_SNAME ) + , hash( c3d::UNDEFINED_SNAME ), item( NULL ), root( NULL ) {} + +public: + /** \brief \ru Получить непосредственный объект сборки, содержащий ссылочный объект. + \en Get immediate object of the assembly containing the reference object. + \param trans - \ru Матрица ссылочного объекта в системе координат непосредственного объекта сборки. + - \en Matrix from the reference object to the sub-item of the assembly. \~ + */ + const MbItem * SubItemOf( const MbAssembly *, MbMatrix3D & trans ) const; + /// \ru Объект геометрической модели, владеющий аргументом. \en Geometry model object which is a host of an argument. + const MbItem * HostItem() const { return item; } + /// \ru Аргумент ограничения, заданный в ЛСК хозяина. \en Geometric constraint argument given in the host's LCS. + const MbRefItem * PropItem() const { return propItem; } + ///< \ru Выдать имя связываемого элемента. \en Get name of a connected element. + SimpleName GetPropName() const { return propName; } + ///< \ru Выдать имя объекта сборки или подсборки, содержащего объект связи с ограничением. \en Get hash code of the path from the root to the item. + SimpleName GetHash() const { return hash; } + ///< \ru Выдать сборку, содержащую объект с ограничением. \en Get assembly that hosts geom object with entity connected to constraint. + const MbAssembly * GetRoot() const { return root; } + /// \ru Выдать значение геометрии аргумента, заданное в ЛСК хозяина. \en Get geometric value of argument given in the host LCS. + MtGeomVariant PropGeom() const; + /// \ru Выдать хэш-имя объекта. \en Get a hash name of the object. + SimpleName PropName() const; + /// \ru Равны ли объекты? \en Are objects equal? \~ + bool IsSame( const MtGeomArgument & r ) const { + return ( (propItem == r.propItem) && (propName == r.propName) && (item == r.item) && (hash == r.hash) ); + } + /// \ru Равны ли ссылкпи на объект модели? \en Are the references to the model object equal? \~ + bool IsSameItemReference( const MtGeomArgument & r ) const { // MtItemReference + return ( (hash == r.hash) && (item == r.item) && (root == r.root) ); + } + /// \ru Оператор равенства объектов. \en Objects equality operator. \~ + bool operator == ( const MtGeomArgument & ) const; + /// \ru Оператор копирования. \en Copy operator. \~ + MtGeomArgument & operator = ( const MtGeomArgument & ); + +KNOWN_OBJECTS_RW_REF_OPERATORS( MtGeomArgument ) // Serializing into a file format +}; // MtGeomArgument + + +//---------------------------------------------------------------------------------------- +/** \brief \ru Геометрическое ограничение. + \en Geometric constraint. \~ + \details \ru Этот класс представляет все виды ограничений, включая геометрические и + размерные отношения между объектами модели. + \en This class represents all kinds of constraints of assembly, including + geometrical and dimensional relationships between the model objects. \~ + \ingroup Model_Items +*/ +//--- +class MATH_CLASS MtGeomConstraint +{ +private: + const ItConstraintItem * m_cItem; ///< \ru Указатель на реализацию геометрического ограничения. \en Pointer to geometric constraint implementation. \~ + std::vector m_arguments; ///< \ru Аргументы геометрического ограничения. \en The arguments of geometric constraint. \~ + SPtr m_mesh; ///< \ru Объект для демонстрации геометрического ограничения. \en The draw object of geometric constraint. \~ + +public: + MtGeomConstraint( const MtGeomConstraint & ); + ~MtGeomConstraint(); + +public: + /// \ru Возвращает true, если ограничение не действительно. \en Return true if the constraint is invalid. + bool IsNull() const { return m_cItem == NULL; } + /// \ru Тип сопряжения (геометрического ограничения). \en Type of geometric constraint. + MtMateType ConstraintType() const; + /// \ru Текущее значение размера. \en Current value of the dimension. + double DimValue() const; + /// \ru Создать полигональный объект для отображения геометрических ограничений. \en Create a polygonal object for visualization. + bool CreateMesh( const MbAssembly & assem, const MbStepData & stepData, const MbFormNote & note, double meshUnit, uint32 color ); + /// \ru Выдать указатель на реализацию геометрического ограничения. \en Get a pointer to geometric constraint implementation. \~ + const ItConstraintItem * ConstraintItem() const { return m_cItem; } + /// \ru Выдать аргументы геометрического ограничения. \en Get arguments of geometric constraint. \~ + const std::vector & GeomArguments() const { return m_arguments; } + /// \ru Выдать указатель на объект для демонстрации геометрического ограничения. \en Get a pointer to draw object of geometric constraint. \~ + const MbItem * GetMesh() const { return m_mesh.get(); } + + // \ru Объявление оператора присваивания. \en Declaration of the assignment operator. + MtGeomConstraint & operator = (const MtGeomConstraint & arg ); + +protected: + friend class MbConstraintSystem; + friend class MtConstraintIter; + + MtGeomConstraint( const ItConstraintItem * cItem, const MtGeomArgument & a1, const MtGeomArgument & a2 ); + MtGeomConstraint( const MbConstraintSystem &, const ItConstraintItem * cItem ); +}; // MtGeomConstraint + + +//---------------------------------------------------------------------------------------- +/// \ru Итератор обходящий ограничения сборки. \en Iterator traversing assembly constraints. +//--- +class MATH_CLASS MtConstraintIter +{ +private: + CNodeIterator * m_cIter; + const MbConstraintSystem * m_gcSystem; + +public: + MtConstraintIter(); + MtConstraintIter( const MtConstraintIter & ); + MtConstraintIter & operator = ( const MtConstraintIter & ); + ~MtConstraintIter(); + +public: + MtGeomConstraint Get() const; + MtConstraintIter & Set( const MbConstraintSystem *, CNodeIterator & ); + const MtConstraintIter & Next(); + bool EqualTo( const MtConstraintIter & ) const; + +public: + //operator CNodeIterator& () { return *impl; } + MtGeomConstraint operator*() const { return Get(); } + // prefix operator + const MtConstraintIter & operator++() { return Next(); } + bool operator ==( const MtConstraintIter & iter ) const { return EqualTo( iter ); } + bool operator !=( const MtConstraintIter & iter ) const { return !EqualTo( iter ); } + +}; // MtConstraintIter + + +//---------------------------------------------------------------------------------------- +/// \ru Обработчик события, связанные с решением сборки. \en The event handles related to solving the assembly. +//--- +struct MATH_CLASS ItAssemblyReactor +{ +public: + /// \ru Захватить сборкой объект для дальнейшей работы. \en Capture the reactor instance by the assembly for further work. + virtual void Capture( const MbAssembly * ) = 0; + /// \ru Отпустить сборкой объект, прекратить работать с ним. \en Release this instance by the assembly, stop working with it. + virtual void Release() = 0; + /// \ru Геометрический решатель не пытался удовлетворить ограничение. \en This called when geometric solver failed to try for constraint satisfaction. + virtual void EvaluationFailed( const MbAssembly * ) const {} + /// \ru Геометрический решатель нашел новую позицию под-объекта сборки. \en The geometric solver found a new position of a constrained sub-object belonging the assembly. + virtual void PositionChanged( const MbAssembly *, const MbItem * /*subItem*/ ) const {} + +protected: + ~ItAssemblyReactor() {} +}; // ItAssemblyReactor + + +//---------------------------------------------------------------------------------------- +/** \brief \ru Интерфейс для системы ограничения импорта сборки из приложения. + \en The user defined interface for import constraint system of an assembly from CAD application. +*/ +//--- +struct MATH_CLASS ItAssemblyImportData +{ +protected: + ~ItAssemblyImportData() {} + +public: + /// \ru Импорт системы ограничений сборки. \en Import a constraint system of the assembly. + virtual bool ImportCSystem( const MbAssembly &, GCM_system & ) const = 0; + /// \ru Получить дескриптор элемента сборки в системе ограничений. \en Get a descriptor of assembly sub-item which used in the constraint system. + virtual MtGeomId GeomId( const MbAssembly &, const MbItem * ) const = 0; + /// \ru Получить объект модели, являющийся аргументом геометрического ограничения. \en Get the model object that is the argument of the geometric constraint. + virtual MtGeomArgument GeomSubItem( const MtArgument & ) const { return MtGeomArgument(); } + /// \ru Получить объект модели, являющийся аргументом геометрического ограничения. \en Get the model object that is the argument of the geometric constraint. + virtual MtGeomArgument GeomSubItem( MtGeomId ) const { return MtGeomArgument(); } +}; //ItAssemblyImportData + +//---------------------------------------------------------------------------------------- +/// \ru Оператор равенства объектов. \en Objects equality operator. \~ +//--- +inline bool MtGeomArgument::operator == ( const MtGeomArgument & r ) const +{ + if ( propItem != r.propItem ) + return false; + if ( propName != r.propName ) + return false; + if ( (item == r.item) && (hash == r.hash) ) + return true; + if ( (hash == r.hash) && (root == r.root) ) + return true; + return false; +} + +//---------------------------------------------------------------------------------------- +/// \ru Оператор копирования. \en Copy operator. \~ +//--- +inline MtGeomArgument & MtGeomArgument::operator = ( const MtGeomArgument & arg ) +{ + propItem = arg.propItem; + propName = arg.propName; + hash = arg.hash; + item = arg.item; + root = arg.root; + return *this; +} + +#endif // __CONSTRAINT_H diff --git a/C3d/Include/constraint_item.h b/C3d/Include/constraint_item.h index 916663d..5f48735 100644 --- a/C3d/Include/constraint_item.h +++ b/C3d/Include/constraint_item.h @@ -1,255 +1,255 @@ -////////////////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Двухмерные геометрические ограничения для объектов C3D-модели - \en 2D-constraints between geometric objects of C3D-model. - \~ - \attention \ru Данный файл содержит типы данных и вызовы, предназначенные для тестирования - и отладки, поэтому могут быть изменены или удалены из API C3D Kernel без - предупреждения. Для применения функциональности решателя двухмерных ограничений - рекомендуется реализация собственного модуля встраивания в приложение на - основе интерфейсов gce_api.h и gce_types.h. - - \en This file contains data types and calls for testing and debugging, so they - can be modified or removed from the C3D Kernel API without notice. To use - the 2D constraint solver, it is recommended to implement a custom embedding module - based on th interfaces gce_api.h and gce_types.h. - \~ -*/ -////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef __CONSTRAINT_ITEM_H -#define __CONSTRAINT_ITEM_H - - -#include -#include -#include -#include -#include -#include - - -//---------------------------------------------------------------------------------------- -/// \ru Кодировка геометрического примитива \en Geometric primitive encoding -//--- -struct GeomCode -{ - enum Type - { - // \ru Словарь примитивов плоскости (соответствует словарю решателя; типы геометрических подмножеств плоскости) \en Plane primitive dictionary (corresponds to the solver dictionary; types of geometric subsets of plane) - NULL_GEOM, ///< \ru пустое геометрическое множество \en empty geometric set - POINT, ///< \ru Точка: элемент плоскости \en Point: element of the plane - PROPER_POINT = POINT, ///< \ru Контрольная точка по индексу \en Control point by index - LINE, ///< \ru Прямая \en Line - CIRCLE, - ELLIPSE, - SPLINE, - PARAMETRIC, ///< \ru Неподвижная параметрическая кривая \en Fixed parametric curve - - // \ru Дифференциация подтипов точки \en Differentiation of subtypes of the point - FIRST_END, ///< \ru Начальная точка кривой \en Start point of a curve - SECOND_END, ///< \ru Конечная точка кривой \en End point of a curve - MIDDLE_POINT, ///< \ru Средняя точка кривой \en Middle point of a curve - CENTRE_POINT, ///< \ru Центральная точка эллипса \en Central point of an ellipse - SPLINE_POINT, ///< \ru Контрольная точка сплайна по индексу \en Control point of a spline by index - Q1_POINT, ///< \ru Квадрантная точка на 3 ч \en Quadrant point at 3 o'clock - Q2_POINT, ///< \ru Квадрантная точка на 12 ч \en Quadrant point at 12 o'clock - Q3_POINT, ///< \ru Квадрантная точка на 9 ч \en Quadrant point at 9 o'clock - Q4_POINT, ///< \ru Квадрантная точка на 6 ч \en Quadrant point at 6 o'clock - - // \ru Размеры \en Sizes - /* - LINEAR_DIM, - ANGULAR_DIM, - */ - }; - Type type; ///< \ru Тип геометрии объекта модели (из словаря типов, поддерживаемых решателем) \en Type of geometry of a model object (from dictionary of types supported by the solver) - size_t index; ///< \ru Номер примитива для данного объекта модели (кодируется в индивидуальных адаптерах) \en Number of a primitive for a given object of the model (encoded in individual adapters) - - GeomCode( GeomCode::Type t ) : type(t),index(0) {} - bool operator != ( const GeomCode & g ) const { return type != g.type || index != g.index; } - GeomCode & operator = ( const Type & gType ) { type = gType; index = 0; return *this; } -}; - - -////////////////////////////////////////////////////////////////////////////////////////// -// -/// \ru Аргумент ограничения (или геометрический примитив решателя) \en Argument of constraint (or geometric primitive of the solver) -/**\ru Этот тип: - 1) Кодирует информацию о геометрии, которая является аргументом для ограничений; - 2) соответствует одному из примитивных типов словаря решателя - - Словарь примитивов решателя: точка, прямая, окружность, эллипс, сплайн. - \en This type: - 1) Encodes the information about the geometry which is the argument for constraints; - 2) Corresponds to one of primitive types of the solver dictionary - - The solver primitives dictionary: point, line, circle, ellipse, spline. \~ - -*/ -////////////////////////////////////////////////////////////////////////////////////////// - -class MATH_CLASS GcArgument { -public: - typedef MbRefItem * ParObject; ///< \ru Ассоциированный тип: владелец примитива, которого он содержит, как свою составную часть \en Associated type: primitives owner which contain the primitive as a component - -public: - GeomCode m_geom; ///< \ru Тип геометрии объекта модели (из словаря типов, поддерживаемых решателем) \en Type of geometry of a model object (from dictionary of types supported by the solver) - c3d::RefItemSPtr m_item; ///< \ru Объект модели \en The model object - -public: - GcArgument() : m_geom(GeomCode::NULL_GEOM), m_item() {} - GcArgument( GeomCode type, MbRefItem & item ) : m_geom(type), m_item(&item) {} - GcArgument( const GcArgument & ag ) : m_geom(ag.m_geom), m_item(ag.m_item) {} - GcArgument & operator = ( const GcArgument & g ) { m_geom = g.m_geom; m_item = g.m_item; return *this; } - bool operator != ( const GcArgument & g ) const { return m_item.get() != g.m_item.get() && m_geom != g.m_geom; } -}; - -//---------------------------------------------------------------------------------------- -// -//--- -inline GcArgument::ParObject Owner( GcArgument & g ) { return g.m_item; } -inline GcArgument::ParObject Owner( const GcArgument & g ) { return g.m_item; } - -//---------------------------------------------------------------------------------------- -/// \ru Параметры размерного ограничения \en Parameters of size constraint -//--- -struct DimParameters -{ - double dimValue; - double dirAngle; ///< \ru Угол для направленного размера \en Angle for a directed size -}; - - -////////////////////////////////////////////////////////////////////////////////////////// -// -/// \ru Элементарное ограничение \en Elementary constraint -/**\ru Элементарное ограничение соответствует типам ограничений из словаря решателя и не более того. - Ограничения более сложных типов описываются набором классов MbConstraint. - \en Elementary constraint corresponds to the types of constraints from the dictionary of the solver and nothing more. - Constraints of more complex types are described by a set of classes MbConstraint. \~ -*/ -////////////////////////////////////////////////////////////////////////////////////////// - -class MATH_CLASS MbConstraint -{ -public: - typedef GcArgument Argument; ///< \ru Тип аргумента ограничения \en Type of argument of the constraint - typedef std::vector arg_list; - typedef arg_list::const_iterator arg_iter; - typedef std::pair arg_iter_pair; - static const Argument null_arg; - -private: - constraint_type m_type; ///< \ru Тип ограничения из словаря решателя \en Type of the argument of the solver dictionary - arg_list m_args; ///< \ru Аргументы ограничения (примитивы из словаря решателя) \en Arguments of the constraint (primitives from the solver dictionary) - DimParameters m_pars; ///< \ru Параметры размера (можно считать тоже аргументом, но представлен другим типом) \en Parameters of size (it can be considered as an argument but it is represented by another type) - -public: - MbConstraint( constraint_type, const Argument &, const Argument & ); // \ru Бинарное ограничение \en Binary constraint - MbConstraint( const MbConstraint & ); - -public: - const Argument & GetGeom( size_t nb ) const { --nb; return nb < m_args.size() ? m_args[nb] : null_arg; } - constraint_type Type() const { return m_type; } - arg_iter_pair Arguments() const { return arg_iter_pair( m_args.begin(), m_args.end() ); } - - /* - bool operator < ( const MbConstraint & c ) const; - bool operator == ( const MbConstraint & c ) const; - */ - -public: - MbConstraint & operator = ( const MbConstraint & ); -}; - - -//---------------------------------------------------------------------------------------- -/// \ru Формирователь модели в решателе \en Generator of a model in the solver -//--- -/* struct GcFormer -{ - // \ru Геометрические объекты \en Geometric objects - virtual bool Point( const GcArgument & ) = 0; - virtual bool Line( const GcArgument & ) = 0; - virtual bool LineSeg( const GcArgument &, const GcArgument & ) = 0; - virtual bool Circle( const MbRefItem &, const MbCartPoint &, double ) = 0; - virtual bool Circle( const GcArgument & ) = 0; - - // \ru Геометрические ограничения \en Geometric constraints - virtual bool Coincidence( const GcArgument &, const GcArgument & ) = 0; - virtual bool Incidence( const GcArgument &, const GcArgument & ) = 0; - virtual bool Vertical( const GcArgument &, const GcArgument & ) = 0; - virtual bool Horizontal( const GcArgument &, const GcArgument & ) = 0; - - // \ru Размерные ограничения \en Dimensional constraints - virtual bool LinearDimension( const MbConstraint & ) = 0; -}; -*/ - - -////////////////////////////////////////////////////////////////////////////////////////// -// -/// \ru Ограничение модели \en Model constraints -// -////////////////////////////////////////////////////////////////////////////////////////// - -class MbConstraintItem: public TapeBase - , public MtRefItem -{ - MbConstraint m_arg; - -public: - MbConstraintItem(); - MbConstraintItem( const MbConstraint & c ) : MtRefItem(), m_arg(c) {} - constraint_type GceType() const { return m_arg.Type(); } - const MbConstraint & GceConstraint() const { return m_arg; } - - virtual ClassDescriptor GetClassDescriptor( const VersionContainer & ) const - { - C3D_ASSERT_UNCONDITIONAL( false ); // Неполная реализация класса - return ClassDescriptor( ::pureName(typeid(*this).name()), Math::MathID() ); - } -}; - - -////////////////////////////////////////////////////////////////////////////////////////// -// -/// \ru Размерное ограничение \en Dimensional constraint -// -////////////////////////////////////////////////////////////////////////////////////////// - -class MbDimensional: public MbConstraintItem -{ - MbCartPoint legendPos; ///< \ru Положение размерной надписи в ЛСК размера \en Position of a dimension legend in LCS of the dimension - -private: - /// \ru Выдать ЛСК размера \en Get LCS of the dimension - virtual void GetPlacement( MbPlacement & ) const; -}; - -////////////////////////////////////////////////////////////////////////////////////////// -// -/// \ru Система геометрических ограничений \en Geometric constraints system -// -////////////////////////////////////////////////////////////////////////////////////////// - -class MATH_CLASS MbConstraintSystem2D -{ - typedef SPtr ConstraintPtr; - std::vector myConstraints; - -public: - MbConstraintSystem2D(); - ~MbConstraintSystem2D(); - -public: - void AddConstraint( SPtr ); -}; - - -#endif // __CONSTRAINT_ITEM_H - - +////////////////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Двухмерные геометрические ограничения для объектов C3D-модели + \en 2D-constraints between geometric objects of C3D-model. + \~ + \attention \ru Данный файл содержит типы данных и вызовы, предназначенные для тестирования + и отладки, поэтому могут быть изменены или удалены из API C3D Kernel без + предупреждения. Для применения функциональности решателя двухмерных ограничений + рекомендуется реализация собственного модуля встраивания в приложение на + основе интерфейсов gce_api.h и gce_types.h. + + \en This file contains data types and calls for testing and debugging, so they + can be modified or removed from the C3D Kernel API without notice. To use + the 2D constraint solver, it is recommended to implement a custom embedding module + based on th interfaces gce_api.h and gce_types.h. + \~ +*/ +////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef __CONSTRAINT_ITEM_H +#define __CONSTRAINT_ITEM_H + + +#include +#include +#include +#include +#include +#include + + +//---------------------------------------------------------------------------------------- +/// \ru Кодировка геометрического примитива \en Geometric primitive encoding +//--- +struct GeomCode +{ + enum Type + { + // \ru Словарь примитивов плоскости (соответствует словарю решателя; типы геометрических подмножеств плоскости) \en Plane primitive dictionary (corresponds to the solver dictionary; types of geometric subsets of plane) + NULL_GEOM, ///< \ru пустое геометрическое множество \en empty geometric set + POINT, ///< \ru Точка: элемент плоскости \en Point: element of the plane + PROPER_POINT = POINT, ///< \ru Контрольная точка по индексу \en Control point by index + LINE, ///< \ru Прямая \en Line + CIRCLE, + ELLIPSE, + SPLINE, + PARAMETRIC, ///< \ru Неподвижная параметрическая кривая \en Fixed parametric curve + + // \ru Дифференциация подтипов точки \en Differentiation of subtypes of the point + FIRST_END, ///< \ru Начальная точка кривой \en Start point of a curve + SECOND_END, ///< \ru Конечная точка кривой \en End point of a curve + MIDDLE_POINT, ///< \ru Средняя точка кривой \en Middle point of a curve + CENTRE_POINT, ///< \ru Центральная точка эллипса \en Central point of an ellipse + SPLINE_POINT, ///< \ru Контрольная точка сплайна по индексу \en Control point of a spline by index + Q1_POINT, ///< \ru Квадрантная точка на 3 ч \en Quadrant point at 3 o'clock + Q2_POINT, ///< \ru Квадрантная точка на 12 ч \en Quadrant point at 12 o'clock + Q3_POINT, ///< \ru Квадрантная точка на 9 ч \en Quadrant point at 9 o'clock + Q4_POINT, ///< \ru Квадрантная точка на 6 ч \en Quadrant point at 6 o'clock + + // \ru Размеры \en Sizes + /* + LINEAR_DIM, + ANGULAR_DIM, + */ + }; + Type type; ///< \ru Тип геометрии объекта модели (из словаря типов, поддерживаемых решателем) \en Type of geometry of a model object (from dictionary of types supported by the solver) + size_t index; ///< \ru Номер примитива для данного объекта модели (кодируется в индивидуальных адаптерах) \en Number of a primitive for a given object of the model (encoded in individual adapters) + + GeomCode( GeomCode::Type t ) : type(t),index(0) {} + bool operator != ( const GeomCode & g ) const { return type != g.type || index != g.index; } + GeomCode & operator = ( const Type & gType ) { type = gType; index = 0; return *this; } +}; + + +////////////////////////////////////////////////////////////////////////////////////////// +// +/// \ru Аргумент ограничения (или геометрический примитив решателя) \en Argument of constraint (or geometric primitive of the solver) +/**\ru Этот тип: + 1) Кодирует информацию о геометрии, которая является аргументом для ограничений; + 2) соответствует одному из примитивных типов словаря решателя + + Словарь примитивов решателя: точка, прямая, окружность, эллипс, сплайн. + \en This type: + 1) Encodes the information about the geometry which is the argument for constraints; + 2) Corresponds to one of primitive types of the solver dictionary + + The solver primitives dictionary: point, line, circle, ellipse, spline. \~ + +*/ +////////////////////////////////////////////////////////////////////////////////////////// + +class MATH_CLASS GcArgument { +public: + typedef MbRefItem * ParObject; ///< \ru Ассоциированный тип: владелец примитива, которого он содержит, как свою составную часть \en Associated type: primitives owner which contain the primitive as a component + +public: + GeomCode m_geom; ///< \ru Тип геометрии объекта модели (из словаря типов, поддерживаемых решателем) \en Type of geometry of a model object (from dictionary of types supported by the solver) + c3d::RefItemSPtr m_item; ///< \ru Объект модели \en The model object + +public: + GcArgument() : m_geom(GeomCode::NULL_GEOM), m_item() {} + GcArgument( GeomCode type, MbRefItem & item ) : m_geom(type), m_item(&item) {} + GcArgument( const GcArgument & ag ) : m_geom(ag.m_geom), m_item(ag.m_item) {} + GcArgument & operator = ( const GcArgument & g ) { m_geom = g.m_geom; m_item = g.m_item; return *this; } + bool operator != ( const GcArgument & g ) const { return m_item.get() != g.m_item.get() && m_geom != g.m_geom; } +}; + +//---------------------------------------------------------------------------------------- +// +//--- +inline GcArgument::ParObject Owner( GcArgument & g ) { return g.m_item; } +inline GcArgument::ParObject Owner( const GcArgument & g ) { return g.m_item; } + +//---------------------------------------------------------------------------------------- +/// \ru Параметры размерного ограничения \en Parameters of size constraint +//--- +struct DimParameters +{ + double dimValue; + double dirAngle; ///< \ru Угол для направленного размера \en Angle for a directed size +}; + + +////////////////////////////////////////////////////////////////////////////////////////// +// +/// \ru Элементарное ограничение \en Elementary constraint +/**\ru Элементарное ограничение соответствует типам ограничений из словаря решателя и не более того. + Ограничения более сложных типов описываются набором классов MbConstraint. + \en Elementary constraint corresponds to the types of constraints from the dictionary of the solver and nothing more. + Constraints of more complex types are described by a set of classes MbConstraint. \~ +*/ +////////////////////////////////////////////////////////////////////////////////////////// + +class MATH_CLASS MbConstraint +{ +public: + typedef GcArgument Argument; ///< \ru Тип аргумента ограничения \en Type of argument of the constraint + typedef std::vector arg_list; + typedef arg_list::const_iterator arg_iter; + typedef std::pair arg_iter_pair; + static const Argument null_arg; + +private: + constraint_type m_type; ///< \ru Тип ограничения из словаря решателя \en Type of the argument of the solver dictionary + arg_list m_args; ///< \ru Аргументы ограничения (примитивы из словаря решателя) \en Arguments of the constraint (primitives from the solver dictionary) + DimParameters m_pars; ///< \ru Параметры размера (можно считать тоже аргументом, но представлен другим типом) \en Parameters of size (it can be considered as an argument but it is represented by another type) + +public: + MbConstraint( constraint_type, const Argument &, const Argument & ); // \ru Бинарное ограничение \en Binary constraint + MbConstraint( const MbConstraint & ); + +public: + const Argument & GetGeom( size_t nb ) const { --nb; return nb < m_args.size() ? m_args[nb] : null_arg; } + constraint_type Type() const { return m_type; } + arg_iter_pair Arguments() const { return arg_iter_pair( m_args.begin(), m_args.end() ); } + + /* + bool operator < ( const MbConstraint & c ) const; + bool operator == ( const MbConstraint & c ) const; + */ + +public: + MbConstraint & operator = ( const MbConstraint & ); +}; + + +//---------------------------------------------------------------------------------------- +/// \ru Формирователь модели в решателе \en Generator of a model in the solver +//--- +/* struct GcFormer +{ + // \ru Геометрические объекты \en Geometric objects + virtual bool Point( const GcArgument & ) = 0; + virtual bool Line( const GcArgument & ) = 0; + virtual bool LineSeg( const GcArgument &, const GcArgument & ) = 0; + virtual bool Circle( const MbRefItem &, const MbCartPoint &, double ) = 0; + virtual bool Circle( const GcArgument & ) = 0; + + // \ru Геометрические ограничения \en Geometric constraints + virtual bool Coincidence( const GcArgument &, const GcArgument & ) = 0; + virtual bool Incidence( const GcArgument &, const GcArgument & ) = 0; + virtual bool Vertical( const GcArgument &, const GcArgument & ) = 0; + virtual bool Horizontal( const GcArgument &, const GcArgument & ) = 0; + + // \ru Размерные ограничения \en Dimensional constraints + virtual bool LinearDimension( const MbConstraint & ) = 0; +}; +*/ + + +////////////////////////////////////////////////////////////////////////////////////////// +// +/// \ru Ограничение модели \en Model constraints +// +////////////////////////////////////////////////////////////////////////////////////////// + +class MbConstraintItem: public TapeBase + , public MtRefItem +{ + MbConstraint m_arg; + +public: + MbConstraintItem(); + MbConstraintItem( const MbConstraint & c ) : MtRefItem(), m_arg(c) {} + constraint_type GceType() const { return m_arg.Type(); } + const MbConstraint & GceConstraint() const { return m_arg; } + + virtual ClassDescriptor GetClassDescriptor( const VersionContainer & ) const + { + C3D_ASSERT_UNCONDITIONAL( false ); // Неполная реализация класса + return ClassDescriptor( ::pureName(typeid(*this).name()), Math::MathID() ); + } +}; + + +////////////////////////////////////////////////////////////////////////////////////////// +// +/// \ru Размерное ограничение \en Dimensional constraint +// +////////////////////////////////////////////////////////////////////////////////////////// + +class MbDimensional: public MbConstraintItem +{ + MbCartPoint legendPos; ///< \ru Положение размерной надписи в ЛСК размера \en Position of a dimension legend in LCS of the dimension + +private: + /// \ru Выдать ЛСК размера \en Get LCS of the dimension + virtual void GetPlacement( MbPlacement & ) const; +}; + +////////////////////////////////////////////////////////////////////////////////////////// +// +/// \ru Система геометрических ограничений \en Geometric constraints system +// +////////////////////////////////////////////////////////////////////////////////////////// + +class MATH_CLASS MbConstraintSystem2D +{ + typedef SPtr ConstraintPtr; + std::vector myConstraints; + +public: + MbConstraintSystem2D(); + ~MbConstraintSystem2D(); + +public: + void AddConstraint( SPtr ); +}; + + +#endif // __CONSTRAINT_ITEM_H + + // eof \ No newline at end of file diff --git a/C3d/Include/contour_graph.h b/C3d/Include/contour_graph.h index 993e2bf..2fb9573 100644 --- a/C3d/Include/contour_graph.h +++ b/C3d/Include/contour_graph.h @@ -370,7 +370,7 @@ public: \param[in] m - \ru Направление обхода.\n Имеет значение знак числа m:\n если m > 0, то обход против часовой стрелки,\n - если m < 0, то по часовой стрелки. + если m < 0, то по часовой стрелке. \en The traversal direction.\n Has a value of sign of number m:\n if m > 0, then traversal is counterclockwise,\n diff --git a/C3d/Include/conv_annotation_item.h b/C3d/Include/conv_annotation_item.h index 999dfb1..c3c8dbf 100644 --- a/C3d/Include/conv_annotation_item.h +++ b/C3d/Include/conv_annotation_item.h @@ -9,16 +9,18 @@ #ifndef __CONV_ANNOTATION_ITEM_H #define __CONV_ANNOTATION_ITEM_H - +#include #include -#include #include -#include -#include -#include +#include + #include #include +class MbLineSegment3D; +class MbArc3D; +class MbItem; +class MbPlaneItem; //------------------------------------------------------------------------------ /** \brief \ru Тип элемента аннотации. @@ -182,7 +184,7 @@ enum MbeDefinedDimensionSymbol { dds_RegardlessOfFeatureSize, ///< \ru . \en . dds_Straightness, ///< \ru Допуск прямолинейности. \en Straightness. dds_Symmetry, ///< \ru Допуск симметричности. \en .Symmetry - dds_TotlaRunout, ///< \ru Допуск полного радиального (либо торцевого) биения. \en TotlaRunout. + dds_TotlaRunout, ///< \ru Допуск полного радиального (либо торцевого) биения. \en Full radial (or face) runout tolerance. }; @@ -393,7 +395,7 @@ struct MaTerminatorSymbol { \en Curve and terminators. \~ */ class CONV_CLASS MaDecoratedCurve : public MbRefItem { - c3d::SpaceCurveSPtr curve; + SPtr curve; std::vector< MaTerminatorSymbol > terminators; MbeDecoratedCurveRole curveType; public: @@ -401,7 +403,7 @@ public: MaDecoratedCurve( const MaDecoratedCurve& ); ///< \ru Конструктор копирования. \en Copy constructor. const MaDecoratedCurve& operator= ( const MaDecoratedCurve& ); ///< \ru Оператор присваивания. \en Assignment operator. - c3d::SpaceCurveSPtr GetCurve() const; ///< \ru Получить кривую. \en Get curve. + SPtr GetCurve() const; ///< \ru Получить кривую. \en Get curve. bool CurveEmpty() const; ///< \ru Пуста ли кривая. \en If curve is empty. void SetCurve( MbCurve3D* crv ); ///< \ru Задать кривую. \en Set curve. size_t TerminatorsCount() const; ///< \ru Получить число законцовок. \en Set number of terminators. @@ -409,6 +411,7 @@ public: void AddTerminator( const MaTerminatorSymbol& term ); ///< \ru Добавить законцовку. \en Add terminator. bool IsA( MbeDecoratedCurveRole ) const; ///< \ru Проверка типа кривой. \en Check curve type. + MbeDecoratedCurveRole IsA() const; ///< \ru Проверка типа кривой. \en Check curve type. void DuplicateCurve( const MbMatrix3D& transform ); ///< \ru Заменить кривую на преобразованный по матрице дубликат. \en Replace curve by transformed replica. }; @@ -450,6 +453,15 @@ public: /// \ru Добавить геометрический визуальный аннотационный элемент. \en Add the geometric visual annotation element of the kernel. void AddGeometricAnnotationElement( const MbItem& ); + /// \ru Добавить собственные геометрические визуальные аннотационный элементы в контейнер. \en Add own geometric visual annotation elements to container. + void AddAnnotationGeometryTo( std::vector< SPtr >& addTo ) const; + + /// \ru Число текстовых элементов. \en Count of text items. + size_t TextItemsCount() const; + + /// \ru Получить текстовый элемент с указанным индексом. \en Get specified text item. + SPtr TextItem( size_t ) const; + /// \ru Задать аннотационные объекты ядра. \en Set the annotation objects of the kernel. template< typename In > void SetAnnotationGeometry( In first, In last ); @@ -498,6 +510,27 @@ protected: typedef SPtr AnnotationSPtr; +/** \brief \ru Контейнер объектов аннотации. +\en Container of annotation objects. \~ +\ingroup Exchange_Base +*/ +typedef std::vector vector_of_annotation; +typedef std::vector AnnotationSptrVector; + + +/** \brief \ru Ассоциация наборов аннотационных объектов элементам со счётчиком ссылок. +\en Association of sets of annotation objects with elements with reference counter. \~ +\ingroup Exchange_Base +*/ +typedef std::map< SPtr, AnnotationSptrVector > map_of_visual_items; + + +/** \brief \ru Контейнер текстовых блоков. +\en Container of text blocks. \~ +\ingroup Exchange_Base +*/ +typedef std::vector< SPtr > vector_of_text; + //------------------------------------------------------------------------------ /** \brief \ru Размер - родоначальник классов для размеров различных типов. @@ -524,7 +557,7 @@ public: virtual Mae_AnnotationType Type() const; /// \ru Получить размерную кривую. \en Get the dimensional curve. - MbCurve3D* GetDimensionCurve(); + MbCurve3D* GetDimensionCurve() const; /// \ru Задать номинал. \en Set a value. void SetValue( double v ); @@ -551,9 +584,9 @@ public: */ bool AddTerminator( const MaTerminatorSymbol& init ); /// \ru Получить первый законцовочный символ. \en Get the first terminator. - bool GetFirstTerminator( MaTerminatorSymbol& first ); + bool GetFirstTerminator( MaTerminatorSymbol& first ) const; /// \ru Получить второй законцовочный символ. \en Get the second terminator. - bool GetSecondTerminator( MaTerminatorSymbol& second ); + bool GetSecondTerminator( MaTerminatorSymbol& second ) const; void InitValueTerminators( const MaDimension& init ); protected: @@ -595,9 +628,9 @@ public: const MbRefItem * GetBindTarget(); /// \ru Получить проекционную кривую к базовому объекту привязки. \en Get projection curve to the base binding object. - MbLineSegment3D* GetProjectionBase(); + MbLineSegment3D* GetProjectionBase() const; /// \ru Получить проекционную кривую ко второму объекту привязки. \en Get the projection curve to the second binding object. - MbLineSegment3D* GetProjectionTarget(); + MbLineSegment3D* GetProjectionTarget() const; /// \ru Задать кривую, вдоль которой провдится измерение. \en Set the curve the measurement is performed along. void SetPath( MbCurve3D* inPath ); @@ -645,9 +678,9 @@ public: const MbRefItem * GetBindTarget(); /// \ru Получить проекционную кривую к базовому объекту привязки. \en Get projection curve to the base binding object. - MbLineSegment3D * GetProjectionBase(); + MbLineSegment3D * GetProjectionBase() const; /// \ru Получить проекционную кривую ко второму объекту привязки. \en Get the projection curve to the second binding object. - MbLineSegment3D * GetProjectionTarget(); + MbLineSegment3D * GetProjectionTarget() const; /// \ru Если заданы проекционные кривые и если они не параллельны, получить точку пересечения или скрещивания. Метод работает и за пределеми параметрической области. \en If the projection curves are specified and if they are not parallel, get the point of intersection or crossing. The method works outside the bounds of a parametric region too. bool NearestBetweenProjections( MbCartPoint3D& pnt ); /// \ru Создать дубликат и трансформировать его согласно матрице. \en Create a replica then transform it. @@ -684,7 +717,7 @@ public: /// \ru Получить базовый объект привязки. \en Get the base binding object. const MbRefItem * GetBindBase(); /// \ru Получить проекционную кривую к базовому объекту привязки. \en Get projection curve to the base binding object. - MbLineSegment3D * GetProjectionBase(); + MbLineSegment3D * GetProjectionBase() const; /// \ru Создать дубликат и трансформировать его согласно матрице. \en Create a replica then transform it. virtual SPtr ShallowDuplicateTransform( const MbMatrix3D& ); @@ -723,9 +756,9 @@ public: const MbRefItem * GetBindBase(); /// \ru Получить проекционную кривую к базовому объекту привязки. \en Get projection curve to the base binding object. - MbLineSegment3D * GetProjectionBase(); + MbLineSegment3D * GetProjectionBase() const; /// \ru Получить вторую проекционную кривую к объекту привязки. \en Get the first projection curve to the binding object. - MbLineSegment3D * GetProjectionTarget(); + MbLineSegment3D * GetProjectionTarget() const; /// \ru Создать дубликат и трансформировать его согласно матрице. \en Create a replica then transform it. virtual SPtr ShallowDuplicateTransform( const MbMatrix3D& ); diff --git a/C3d/Include/conv_binary_object.h b/C3d/Include/conv_binary_object.h new file mode 100644 index 0000000..16290a0 --- /dev/null +++ b/C3d/Include/conv_binary_object.h @@ -0,0 +1,24 @@ +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __BINOBJ_H +#define __BINOBJ_H + + +//------------------------------------------------------------------------------ +// бинарный объект +// --- +struct BinaryObj { + + void * p; + void * pSort; + + BinaryObj( void *otherId, void *otherP ) : p( otherId ), pSort( otherP ) {} + + bool operator == ( const BinaryObj &o ) const { return pSort == o.pSort; } + bool operator < ( const BinaryObj &o ) const { return pSort < o.pSort; } +}; + + +#endif \ No newline at end of file diff --git a/C3d/Include/conv_error_result.h b/C3d/Include/conv_exchange_settings.h similarity index 50% rename from C3d/Include/conv_error_result.h rename to C3d/Include/conv_exchange_settings.h index e8ad6ae..d0c1ab9 100644 --- a/C3d/Include/conv_error_result.h +++ b/C3d/Include/conv_exchange_settings.h @@ -1,25 +1,24 @@ //////////////////////////////////////////////////////////////////////////////// /** \file - \brief \ru Перечисления, используемые при импорте и экспорте. - \en Enumerations for import/export operations.\~ - \details \ru Определены перечисления, определяющие результат конвертирования, - разрешение на чтение и запись различных объектов и передаваемых черезх конвертер строк. - \en Converting result, objects and properties filters, special strings - of enumerations are defined.\~ + \brief \ru Настройки импорта и экспорта. + \en Settings of import and export procedure. \~ + \details \ru Интерфейс настроек и предопределённая реализация ConvConvertorProperty3D. + \en Interface of settings and pre-defined implementation ConvConvertorProperty3D. \~ */ //////////////////////////////////////////////////////////////////////////////// -#ifndef __CONV_ERROR_RESULT_H -#define __CONV_ERROR_RESULT_H - - -#include +#ifndef __CONV_MODEL_PROPERTIES_H +#define __CONV_MODEL_PROPERTIES_H +#include +#include +#include +#include //------------------------------------------------------------------------------ /** \brief \ru Константы единиц измерения. - \en Length units constants.\~ +\en Length units constants.\~ \ingroup Data_Interface */ // --- @@ -37,7 +36,7 @@ //------------------------------------------------------------------------------ /** \brief \ru Прикладной протокол. - \en Applied protocol.\~ +\en Applied protocol.\~ \ingroup Data_Interface */ // --- @@ -58,51 +57,24 @@ enum MbeImpExpFormat { //------------------------------------------------------------------------------ -/** \brief \ru Обменный формат модели. - \en Model exchange format.\~ +/** \brief \ru Индексы строк, передаваемых через конвертер. +\en Indices of strings, transmitted through converter.\~ \ingroup Data_Interface */ // --- -enum MbeModelExchangeFormat { - mxf_autodetect, ///< \ru Интерпретировать содержимое по расширению файла. \en File extension defines format. - mxf_ACIS, ///< \ru Интерпретировать содержимое как ACIS (.sat). \en Read data from buffer as ACIS (.sat). - mxf_IGES, ///< \ru Интерпретировать содержимое как IGES (.igs или .iges). \en Read data from buffer as IGES (.igs or .iges). - mxf_JT, ///< \ru Интерпретировать содержимое как JT (.jt). \en Read data from buffer as JT (.jt). - mxf_Parasolid, ///< \ru Интерпретировать содержимое как Parasolid (.x_t, .x_b, .xmt_txt, .xmp_txt, .xmt_bin или .xmp_bin ). \en Read data from buffer as Parasolid (.x_t, .x_b, .xmt_txt, .xmp_txt, .xmt_bin or .xmp_bin ). - mxf_STEP, ///< \ru Интерпретировать содержимое как STEP (.stp или .step). \en Read data from buffer as STEP (.stp or .step). - mxf_STL, ///< \ru Интерпретировать содержимое как STL (.stl). \en Read data from buffer as STL (.stl). - mxf_VRML, ///< \ru Интерпретировать содержимое как VRML (.wrl). \en Read data from buffer as VRML (.wrl). - mxf_GRDECL, ///< \ru Интерпретировать содержимое как GRDECL (.grdecl). \en Read data from buffer as GRDECL (.grdecl). - mxf_ASCIIPoint, ///< \ru Интерпретировать содержимое как облако точек в ASCII (.txt, .asc или .xyz). \en Read data from buffer as ASCII point cloud (.txt, .asc or .xyz). - mxf_C3D, ///< \ru Интерпретировать содержимое как C3D (.c3d). \en Read data from buffer as C3D (.c3d). -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Результат конвертирования. - \en Result of converting operation. -\ingroup Data_Interface -*/ -// --- -enum MbeConvResType { - cnv_Success = 0, ///< \ru Успешное завершение. \en Success. - cnv_Error, ///< \ru Ошибка в процессе конвертирования. \en Error. - cnv_UserCanceled, ///< \ru Процесс прерван пользователем. \en Process interrupted by user. - cnv_NoBody, ///< \ru Не найдено тел. \en No solids found. - cnv_NoObjects, ///< \ru Не найдено объектов. \en No objects found. - cnv_FileOpenError, ///< \ru Ошибка открытия файла. \en File open error. - cnv_FileWriteError, ///< \ru Ошибка записи файла. \en File write error. - cnv_FileDeleteError, ///< \ru Ошибка удаления файла. \en Could not delete file. - cnv_ImpossibleReadAssembly,///< \ru Не поддерживает работу со сборками. \en Assemblies are not supported. - cnv_LicenseNotFound, ///< \ru Ошибка получения лицензии. \en License check failure. - cnv_NotEnoughMemory, ///< \ru Недостаточно памяти. \en Not enough memory. - cnv_UnknownExtension ///< \ru Неизвестное расширение файла. \en Unknown file extenstion. +enum MbeConverterStrings { + cvs_BEGIN = 0, ///< \ru Для удобства перебора. \en For lookup only. + cvs_STEPAuthor, ///< \ru Автор для конвертера STEP. \en Author of the document, in STEP. + cvs_STEPOrganization, ///< \ru Организация для конвертера STEP. \en The organization, the author is related with, in STEP. + cvs_STEPComment, ///< \ru Комментарий файла формата STEP. \en Annotation, in STEP. + cvs_CAD_NAME, ///< \ru Название САПР при экспорте. \en CAD Name for export. + cvs_END ///< \ru Для удобства перебора. \en For lookup only. }; //------------------------------------------------------------------------------ /** \brief \ru Индексы, управляющие разрешением на чтение или запись объектов. - \en Indeces, which filter imported/exported objects or properties.\~ +\en Indeces, which filter imported/exported objects or properties.\~ \ingroup Data_Interface */ // --- @@ -137,59 +109,17 @@ enum MbeIOPermiss { iop_wAssociated, ///< \ru Разрешение на запись ассоциированной геометрии (резьбы и др). \en Export associated geometry (threads etc). iop_rDensity, ///< \ru Разрешение на чтение единиц плотности. \en Import density units. iop_wDensity, ///< \ru Разрешение на запись единиц плотности. \en Export density units. + iop_rValidationProperties, ///< \ru Разрешение на чтение контрольных параметров - объёма, площади поверхности, центра масс. \en Import validation properties - volume, surface area, centroid. + iop_wValidationProperties, ///< \ru Разрешение на запись контрольных параметров - объёма, площади поверхности, центра масс. \en Export validation properties - volume, surface area, centroid. iop_rStyle, ///< \ru Разрешение на чтение элементов оформления (цвет, начертание, и т.п.). \en Import appearance. iop_wStyle, ///< \ru Разрешение на запись элементов оформления (цвет, начертание, и т.п.). \en Export appearance. iop_END }; -//------------------------------------------------------------------------------ -/** \brief \ru Индексы строк, передаваемых через конвертер. - \en Indeхes of strings, transmitted through converter.\~ -\ingroup Data_Interface -*/ -// --- -enum MbeConverterStrings { - cvs_BEGIN = 0, ///< \ru Для удобства перебора. \en For lookup only. - cvs_STEPAuthor, ///< \ru Автор для конвертера STEP. \en Author of the document, in STEP. - cvs_STEPOrganization, ///< \ru Организация для конвертера STEP. \en The organization, the author is related with, in STEP. - cvs_STEPComment, ///< \ru Комментарий файла формата STEP. \en Annotation, in STEP. - cvs_END ///< \ru Для удобства перебора. \en For lookup only. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Ключи строк, соответствующих названию специальных атрибутов. - \en Keys of the strings, which mark special attributes.\~ -\ingroup Data_Interface -*/ -// --- -enum ePromtAttributeKey { - pac_GConverterInternalIsDummy, ///< \ru Является ли элемент пустышкой.\~ - pac_GeneralIsAssembly, ///< \ru Является ли элемент сборкой. \en Is item assembly.\~ - pac_GeneralFileName, ///< \ru Имя файла. \en File name.\~ - pac_STEPHeader, ///< \ru Заголовок STEP. \en STEP header.\~ - pac_STEPProduct, ///< \ru Изделие STEP. \en STEP product.\~ - pac_STEPPersonOrganization, ///< \ru Лицо и организация STEP. \en STEP person and organization.\~ - pac_STEPAssignedRole ///< \ru Назначенная роль STEP. \en The role, assigned to the person.\~ -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Представление текста при экспорте. - \en Representation of exported text.\~ -\ingroup Data_Exchange -*/ -// --- -enum eTextForm { - exf_TextOnly, ///< \ru Только текст. \en Text only. - exf_GeometryOnly, ///< \ru Только геометрия. \en Geometry only. -}; - - //------------------------------------------------------------------------------ /** \brief \ru Тип сообщения об ошибке при выводе в лог. - \en Type of a log message.\~ +\en Type of a log message.\~ \ingroup Data_Exchange */ // --- @@ -204,7 +134,7 @@ enum eMsgType { //------------------------------------------------------------------------------ /** \brief \ru Код подробного сообщения об ошибке при выводе в лог. - \en The key of a detailed log message.\~ +\en The key of a detailed log message.\~ \ingroup Data_Interface */ // --- @@ -288,8 +218,10 @@ enum eMsgDetail { emd_WarningUndefinedAxisDirection, ///< \ru Не определено направление оси. \en Axis direction is not defined. emd_WarningDegeneratedItemWasSkipped, ///< \ru Проигнорирован (пропущен) вырожденный объект. \en Degenerate object was missed. emd_WarningFloatParceFailureDefaultUsed, ///< \ru Ошибка разпознавания числа с плавающей точкой, подставлено значение по умолчанию. \en Floating point value couldn't be parced; default value was used. + emd_WarningSameShapeEdgeTwiceInLoop, ///< \ru В цикле дважды встречается одинаковое ребро. \en Edge based on same curves twice enters a loop. emd_WarningIncorrectFaceWasNotAddedToShell, ///< \ru Некорректная грань не была добавлена в оболочку. \en Incorrect face was not added to shell. emd_WarningBoundsNotConnectedWithSeams, ///< \ru Границы замкнутой грани не стыкуются со швами. \en Bounds of periodic face not connected with seams. + emd_WarningIntCurveWasReplacedBySegment, ///< \ru Кривая пересечения была заменена отрезком. \en Intersection curve was replaced by segment. emd_MessageWeightsFilled, ///< \ru Веса заданы. \en Weights are set. @@ -303,12 +235,15 @@ enum eMsgDetail { emd_WarningSTEPEdgeCurveByVertices, ///< \ru Кривая ребра скорректирована с учётом координат вершин. \en Edge curve corrected in accordance with vertices. ( by BUG_73871 ) emd_MessageSTEPFlagChangedToF, ///< \ru Произведена замена флага на .F.. \en Flag was set as .F. in STEP. emd_MessageSTEPFlagChangedToT, ///< \ru Произведена замена флага на .T.. \en Flag was set as .T. in STEP. + emd_WarningBooleanUndefined, ///< \ru Булево значение не определено. \en Boolean value not defined. emd_WarningACISUnsupportedInterpoleCurveType, ///< \ru Данный подтип ACIS интерполяционной кривой не поддерживается. \en Interpolation curve type is not supported by SAT converter. emd_WarningACISUnsupportedParametricCurveType, ///< \ru Данный подтип ACIS параметрической кривой не поддерживается. \en Parametric curve type is not supported by SAT converter. emd_ErrorACISUnsupportedVersion, ///< \ru Данная версия ACIS NT не поддерживается. \en Th version of file is not supported by SAT converter. emd_WarningACISCannotImportEntityId, ///< \ru Не удалось импортировать объект с данным Id. \en Cannot import this object by SAT converter. emd_WarningACISIncorrectIntAttribute, ///< \ru Некорректный целочисленный атрибут. \en Incorrect integer attribute. + emd_WarningVRMLGridDuplicatesInMeshes, ///< \ru Присутствуют дубликаты объектов в сетках. \en There are grid duplicates in meshes. + emd_WarningACISLawIntCurveIsNotCreated, ///< \ru Кривая по закону не создана. \en Law intersection curve is not created. emd_ErrorIGESIncorrectExternalReference, ///< \ru Неверное имя внешней ссылки. \en Invalid external reference in IGES. @@ -321,54 +256,247 @@ enum eMsgDetail { //------------------------------------------------------------------------------ -/** \brief \ru Идентификаторы сообщений индикатора прогресса выполнения конвертации данных. - \en Identifiers of the execution progress indicator messages converters data exchange \~ -\ingroup Data_Exchange +/** \brief \ru Интерфейс свойств конвертера. +\en Interface of converter's properties. \~ +\details \ru Интерфейс свойств конвертера реализует выдачу имени документа и других сведений о нём, таких как автор, +и управление режимами работы - сшивкой поверхностей с возможностью создания твёрдых +тел, фильтрацией объектов, формирование журнала трансляции. +\en Interface of converter's properties realizes getting the document's name and other information about it, such as the author, +and management of modes of operations - stitching of surfaces with possibility of solids creation, +objects filtration, generation of translation journal. \~ +\ingroup Exchange_Interface */ -//--- -enum MbeProgBarId_Converters { - pbarId_Cnv_Beg = pbarId_PointsSurface_End + 1, +class IConvertorProperty3D { +public : + virtual ~IConvertorProperty3D() {} - pbarId_Cnv_Parse_Data, // \ru Синтаксический анализ... \en Syntactic analysis... - pbarId_Cnv_Create_Objects, // \ru Создание объектов... \en Creation of objects... - pbarId_Cnv_Process_Surfaces, // \ru Обработка поверхностей... \en Surfaces processing... - pbarId_Cnv_Process_Annotation,// \ru Обработка аннотации... \en Annotation processing... - pbarId_Cnv_Create_Model, // \ru Создание модели... \en Creation of model... - pbarId_Cnv_Write_Model, // \ru Запись модели... \en Writing of model... +public: + /// \ru Получить имя документа. \en Get document's name. + virtual const std::string GetDocumentName () const = 0; //{ return std::string( GetDocName().get_str() ); }; + /// \ru Получить имя файла для конвертирования. \en Get file name for converting. + virtual const c3d::path_string FullFilePath () const = 0 ;//{ return c3d::path_string( GetFileName().c_str() ); }; + /// \ru Является ли файл текстовым. \en Whether the file is a text file. + virtual bool IsFileAscii () const = 0; + /// \ru Получить версию формата при экспорте. \en Get the version of format for export. + virtual long int GetFormatVersion () const { return EXPORT_DEFAULT; }; + /// \ru Задать формат для экспорта \en Set format for export + DEPRECATE_DECLARE virtual MbeImpExpFormat GetFormat () const { return ief_STEP203; } + /// \ru Следует ли экспортировать только поверхности ( введено для работы конвертера IGES ). \en Whether to export only surfaces (introduced for work with converter IGES ). + virtual bool IsOutOnlySurfaces() const = 0; + /// \ru Является ли экспортируемый документ сборкой. \en Whether the document for export is an assembly. + virtual bool IsAssembling () const = 0; + /// \ru Получить значение разрешения на импорт экспорт объектов определенного типа. \en Get the value of permission for import-export of objects of a certain type. + virtual bool GetIoPermission( MbeIOPermiss nPermission ) const = 0; + /// \ru Получить значения разрешений на импорт экспорт объектов определенных типов. \en Get values of permission for import-export of objects of certain types. + virtual void GetIoPermissions( std::vector& ioPermissions ) const = 0; + /// \ru Установить разрешение на импорт экспорт объектов определенного типа. \en Set permission for import-export of objects of a certain type. + virtual void SetIoPermission( MbeIOPermiss nPermission, bool set ) = 0; + /// \ru Получить значение специфичной строки для конвертера. \en Get the value of a certain string for the converter. + virtual bool GetPropertyString ( MbeConverterStrings nString, std::string & propertyString ) const = 0; + /// \ru Установить значение специфичной строки для конвертера. \en Set the value of a certain string for the converter. + virtual void SetPropertyString ( MbeConverterStrings nString, const std::string & propertyString ) = 0; + /// \ru Представление текста в аннотационных объектах. \en Text representation in annotation objects. + virtual eTextForm GetAnnotationTextRepresentation () const { return exf_TextOnly; } + /// \ru Следует ли компоненты экспортировать в разные файлы (если позволяет формат). \en Export components into separate files ( if provided in format). + virtual bool ExportComponentsSeparately() const { return false; } + /// \ru Получить ЛСК, относительно которой позиционирована модель. \en Get the location, the model is placed in. + virtual MbPlacement3D GetOriginLocation() const = 0; + /// \ru Заменять ли принудительно СК компонент на правые. \en Replace components' placements to right-oriented. + virtual bool ReplaceLocationsToRight() const = 0; + /** \brief \ru Сшивать ли поверхности автоматически. + \en If surfaces should be stitched automatically. \~ + \return \ru true - Сшивать поверхности автоматически, false - Спросить пользователя, сшивать ли поверхности. + \en true - Stitch surfaces automatically, false - Ask user first time. \~ + \param[out] stitchPrecision - \ru Точность сшивки. + \en Stitch precision. \~ + */ + virtual bool EnableAutoStitch( double& /*stitchPrecision*/ ) const = 0; - pbarId_Cnv_End, -}; + /** \brief \ru Получить множитель единиц длины по отношению к миллиметру. + \en Get the factor of the length units to millimeters. \~ + \details \ru При импорте, если единицы измерения не заданы явно с помощью средств, предоставляемых обменным форматом, + все размеры (координаты точек, радиусы) умножаются на возвращаемое значение. При экспорте либо с помощью + средств, предоставляемых обменным форматом, задаются единицы измерения, либо все размеры модели (координаты + точек, радиусы) умножаются на возвращаемое значение. + \en During the import all spatial objects (coordinate values, radiuses) are multiplied by the returned value, + unless the scale factor comes from the exchange file. During the export the exchange format facilities are + used to specify the length units or all spatial objects (coordinate values, radiuses) are multiplied by the + returned value. \~ + */ + virtual double LengthUnitsFactor() const { return LENGTH_UNIT_MM; } + + + /** \brief \ru Получить дополнительный множитель единиц длины по отношению к миллиметру в модели приложения. + \en Get addifional factor of the length units to millimeters in the application model. \~ + \details \ru При импорте из всех форматов за исключением JT, если единицы измерения, в том числе и заданные + явно с помощью средств, предоставляемых обменным форматом, все размеры (координаты точек, радиусы) умножаются + на возвращаемое значение. При экспорте либо с помощью средств, предоставляемых обменным форматом, задаются + единицы измерения, либо все размеры модели (координаты точек, радиусы) умножаются на возвращаемое значение. + \en During the import from all formats except for JT all spatial objects (coordinate values, radiuses) are + multiplied by the returned value, even if the scale factor comes from the exchange file. During the export the + exchange format facilities are used to specify the length units or all spatial objects (coordinate values, + radiuses) are multiplied by the returned value. \~ + */ + virtual double AppLengthUnitsFactor() const { return LENGTH_UNIT_MM; } + + /** \brief \ru Сделать запись в журнал конвертирования. + \en Make a record in the converter report. \~ + \param[in] id - \ru Идентификатор элемента внутри файла стороннего формата. + \en Identifier of an element inside the file of a foreign format. \~ + \param[in] msgType - \ru Тип сообщения. + \en Message type. \~ + \param[in] msgText - \ru Код сообщения. + \en Message code. \~ + */ + virtual void LogReport( ptrdiff_t id, eMsgType msgType, eMsgDetail msgText ) = 0; + + // /** \brief \ru Следует ли показывать сообщения и диалоги пользователю. \en Whether to show messages and dialog to the user. \~ + // \details \ru Обеспечивает работу через API. \en Provide possibility for work via API. \~ + // \return \ru true - обычная работа, false - через API. \en true - ordinary work, false - via API. \~ + // */ + virtual bool CanShowMessages() const = 0; + /// \ru Дать данные вычисления триангуляции (для конвертера JT, STL и VRML). \en Get data for step calculation during triangulation (for JT, STL, VRML only). + virtual MbStepData TesselationParameters() const { return MbStepData(); } + /// \ru Дать данные вычисления триангуляции уровня детализации (для конвертера JT). \en Get data for step calculation during triangulation of LOD0 (for JTonly). + virtual MbStepData LOD0TesselationParameters() const { return TesselationParameters(); } + /// \ru Флаг сохранения совпадающих точек швов. (для конвертера STL и VRML). \en Whether to keep coincident points of seams (for STL, VRML only). + virtual bool DualSeams() const { return true; } + /// \ru Флаг сохранения совпадающих точек швов. (для конвертера STL и VRML). \en Whether to keep coincident points of seams (for STL, VRML only). + virtual void DualSeams( bool ) {} + /// \ru Проводить ли аудит траснляции. \en Whether to audit the translation. + virtual bool TotalAudit() { return false; } + + /// \ru Выполнять ли слияние подобных граней. \en Whether to join similar faces. + virtual bool JoinSimilarFaces() const { return true; } + /// \ru Добавлять ли удаленные грани в качестве оболочек. \en Whether to add removed faces as shells. + virtual bool AddRemovedFacesAsShells() const { return false; } +}; // IConvertorProperty3D //------------------------------------------------------------------------------ -/** \brief \ru Идентификаторы сообщений индикатора прогресса выполнения триангуляции при выполнении конвертации данных. - \en Identifiers of the execution progress indicator messages triangulation. \~ -\ingroup Data_Exchange +/** \brief \ru Предопределённая реализация интерфейса свойств конвертера. + \en Pre-defined implementation of converter's properties. \~ +\ingroup Exchange_Interface */ -//--- -enum MbeProgBarId_Triangulation { - pbarId_Triangulation_Beg = pbarId_Cnv_End + 1, +class CONV_CLASS ConvConvertorProperty3D : public IConvertorProperty3D { +public: + std::string docName; ///< \ru Имя документа. \en Document name. + c3d::path_string fileName; ///< \ru Имя файла. \en File name. + bool fileASCII; ///< \ru Экспортировать ли в текстовый файл (если формат поддерживает двоичный). \en Export to text file (if format supports binary one). + long int formatVersion; /// \ru Версия формата при экспорте. \en The version of format for export. + bool exportIGESTopology; ///< \ru Экспортировать ли топологию в IGES. \en Export topology items into IGES. + std::vector ioPermissions; ///< \ru Фильтр объектов по типам. \en Type objects filter. + std::map propertyStrings; ///< \ru Особые значения сведений о документе. \en Specific values of documents properties. + eTextForm annotTextReprSTEP; ///< \ru Представление текста элементов аннотации. \en Text representation in annotation items. + MbPlacement3D originLocation; ///< \ru ЛСК документа. \en Own placement of the document. + bool replaceLocationsToRight; ///< \ru Следует ли принудительно преобразовывать ЛСК объектов к правым (для форматов, допускающих левые). \en Force replacement of locations to right ones. + bool enableAutostitch; ///< \ru Сшивать ли поверхности автоматически. \en Automatically stitch surfaces into shells. + double autostitchPrecision; ///< \ru Точность сшивки. \en Stitch precision. + bool showMessages; ///< \ru Отображать ли сообщения. \en Invoke messages show. + MbStepData tesseleationStepData; ///< \ru Параметры триангуляции при экспорте в STL и VRML. \en Tessellation parameters for export into STL and VRML. + MbStepData LOD0StepData; ///< \ru Параметры триангуляции при экспорте в JT. \en Tessellation parameters for export into JT. + bool dualSeams; ///< \ru Признак сдваивания швов при экспорте в STL и VRML. \en Make dual seams when export into STL and VRML. + bool joinSimilarFaces; ///< \ru Выполнять ли слияние подобных граней. \en Whether to join similar faces. + bool addRemovedFacesAsShells; ///< \ru Добавлять ли удаленные грани в качестве отдельных оболочек. \en Whether to add removed faces as shells. + double lengthUnitsFactor; ///< \ru Единицы длины модели. \en Length units of the model. + double appUnitsFactor; ///< \ru Единицы длины модели пользовательского приложения. \en Length units of the model used in user application. + bool auditEnabled; + + /// \ru Сведения о сообщениях конвертера. \en Converter message data. + struct LogRecord { + ptrdiff_t id; ///< \ru Идентификатор записи. \en Record id. + eMsgType msgType; ///< \ru Тип сообщения. \en Message type. + eMsgDetail msgText; ///< \ru Код сообщения. \en Message code. + }; + + std::vector< LogRecord > logRecords; ///< \ru Сообщения конвертера. \en Converter messages. - pbarId_Calc_Triangulation, // \ru Расчет триангуляции \en Calculating of triangulation +public: - pbarId_Triangulation_End, -}; + ConvConvertorProperty3D(); ///< \ru Конструктор. \en Constructor. + + /// \ru Получить имя документа. \en Get document's name. + virtual const std::string GetDocumentName () const { return docName; }; + /// \ru Получить имя файла для конвертирования. \en Get file name for converting. + virtual const c3d::path_string FullFilePath () const { return fileName; }; + /// \ru Является ли файл текстовым. \en Whether the file is a text file. + virtual bool IsFileAscii () const; + /// \ru Получить версию формата при экспорте. \en Get the version of format for export. + virtual long int GetFormatVersion () const; + /// \ru Следует ли экспортировать только поверхности ( введено для работы конвертера IGES ). \en Whether to export only surfaces (introduced for work with converter IGES ). + virtual bool IsOutOnlySurfaces() const; + /// \ru Является ли экспортируемый документ сборкой. \en Whether the document for export is an assembly. + virtual bool IsAssembling () const { return true; }; + /// \ru Получить значение разрешения на импорт экспорт объектов определенного типа. \en Get the value of permission for import-export of objects of a certain type. + virtual bool GetIoPermission( MbeIOPermiss nPermission ) const; + /// \ru Получить значения разрешений на импорт экспорт объектов определенных типов. \en Get values of permission for import-export of objects of certain types. + virtual void GetIoPermissions( std::vector& ioPermissions ) const; + /// \ru Установить разрешение на импорт экспорт объектов определенного типа. \en Set permission for import-export of objects of a certain type. + virtual void SetIoPermission( MbeIOPermiss nPermission, bool isSet ); + /// \ru Получить значение специфичной строки для конвертера. \en Get the value of a certain string for the converter. + virtual bool GetPropertyString ( MbeConverterStrings nString, std::string & propertyString ) const; + /// \ru Установить значение специфичной строки для конвертера. \en Set the value of a certain string for the converter. + virtual void SetPropertyString ( MbeConverterStrings nString, const std::string & propertyString ); + /// \ru Представление текста в аннотационных объектах. \en Text representation in annotation objects. + virtual eTextForm GetAnnotationTextRepresentation () const; + /// \ru Следует ли компоненты экспортировать в разные файлы (если позволяет формат). \en Export components into separate files ( if provided in format). + virtual bool ExportComponentsSeparately() const; + /// \ru Получить ЛСК, относительно которой позиционирована модель. \en Get the location, the model is placed in. + virtual MbPlacement3D GetOriginLocation() const; + /// \ru Заменять ли принудительно СК компонент на правые. \en Replace components' placements to right-oriented. + virtual bool ReplaceLocationsToRight() const; + /** \brief \ru Сшивать ли поверхности автоматически. + \en If surfaces should be stitched automatically. \~ + \return \ru true - Сшивать поверхности автоматически, false - Спросить пользователя, сшивать ли поверхности. + \en true - Stitch surfaces automatically, false - Ask user first time. \~ + \param[out] stitchPrecision - \ru Точность сшивки. + \en Stitch precision. \~ + */ virtual bool EnableAutoStitch( double& /*stitchPrecision*/ ) const; + + /// \ru Получить множитель единиц длины по отношению к миллиметру. \en Get the factor of the length units to millimeters. + virtual double LengthUnitsFactor() const; + + /** \brief \ru Получить множитель единиц длины по отношению к миллиметру в модели приложения. + \en Get the factor of the length units to millimeters in the application model. \~ + */ + virtual double AppLengthUnitsFactor() const; + + /** \brief \ru Сделать запись в журнал конвертирования. + \en Make a record in the converter report. \~ + \param[in] id - \ru Идентификатор элемента внутри файла стороннего формата. + \en Identifier of an element inside the file of a foreign format. \~ + \param[in] msgType - \ru Тип сообщения. + \en Message type. \~ + \param[in] msgText - \ru Код сообщения. + \en Message code. \~ + */ + virtual void LogReport( ptrdiff_t id, eMsgType msgType, eMsgDetail msgText ); + +// /** \brief \ru Следует ли показывать сообщения и диалоги пользователю. \en Whether to show messages and dialog to the user. \~ +// \details \ru Обеспечивает работу через API. \en Provide possibility for work via API. \~ +// \return \ru true - обычная работа, false - через API. \en true - ordinary work, false - via API. \~ +// */ + virtual bool CanShowMessages() const; + + /// \ru Дать данные вычисления триангуляции (для конвертера STL и VRML). \en Get data for step calculation during triangulation (for STL, VRML only). + virtual MbStepData TesselationParameters() const; + /// \ru Дать данные вычисления триангуляции уровня детализации (для конвертера JT). \en Get data for step calculation during triangulation of LOD0 (for JTonly). + virtual MbStepData LOD0TesselationParameters() const; + /// \ru Получить флаг сохранения совпадающих точек швов. (для конвертера STL и VRML). \en Whether to keep coincident points of seams (for STL, VRML only). + virtual bool DualSeams() const; + /// \ru Задать флаг сохранения совпадающих точек швов. (для конвертера STL и VRML). \en Whether to keep coincident points of seams (for STL, VRML only). + virtual void DualSeams( bool ); + /// \ru Проводить ли аудит траснляции. \en Whether to audit the translation. + virtual bool TotalAudit(); + /// \ru Выполнять ли слияние подобных граней. \en Whether to join similar faces. + virtual bool JoinSimilarFaces() const { return joinSimilarFaces; } + /// \ru Добавлять ли удаленные грани в качестве оболочек. \en Whether to add removed faces as shells. + virtual bool AddRemovedFacesAsShells() const { return addRemovedFacesAsShells; } + + OBVIOUS_PRIVATE_COPY( ConvConvertorProperty3D ) + +}; // IConvertorProperty3D -//------------------------------------------------------------------------------ -/** \brief \ru Идентификаторы сообщений индикатора прогресса выполнения расчёта - масс-инерционные характеристики детали или сборки при выполнении конвертации данных. - \en Identifiers of the execution progress indicator messages of mass-inertial properties of assembly or a detail. \~ -\ingroup Data_Exchange -*/ -//--- -enum MbeProgBarId_MassInertiaProperties { - pbarId_MassInertiaProperties_Beg = pbarId_Triangulation_End + 1, - pbarId_Calc_MassInertiaProperties, // \ru Расчет масс-инерционных характеристик \en Mass-inertial properties calculation - - pbarId_MassInertiaProperties_End, -}; - - -#endif // __CONV_ERROR_RESULT_H \ No newline at end of file +#endif // __CONV_MODEL_PROPERTIES_H diff --git a/C3d/Include/conv_model_document.h b/C3d/Include/conv_model_document.h new file mode 100644 index 0000000..b58bca4 --- /dev/null +++ b/C3d/Include/conv_model_document.h @@ -0,0 +1,575 @@ +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Сущности конвертерной модели: документ, деталь, сборка, вставка. + \en Entities of converter-compatible model: document, part, assembly, instance. \~ + \details \ru Интерфейсы сущностей и предопределённая реализация модельного документа C3dModelDocument. + \en Interfaces of entities and pre-defined implementation of model document C3dModelDocument. \~ +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CONV_MODEL_DOCUMENT_H +#define __CONV_MODEL_DOCUMENT_H + +#include +#include +#include +#include +#include +#include +#include + + + +class MbPlacement3D; +class MbName; + +class MbAttributeContainer; + +class ItModelAssembly; +class ItModelPart; +class ItModelInstance; + +class IProgressIndicator; + +typedef SPtr ModelAssemblyPtr; +typedef SPtr ModelPartPtr; +typedef SPtr ModelInstancePtr; + + + +//------------------------------------------------------------------------------ +/** \brief \ru Интерфейс документа модели сборки или детали. +\en Interface of document of an assembly model or a part model. \~ +\ingroup Exchange_Interface +*/ +// --- +class ItModelDocument : public MbRefItem +{ +public: + /// \ru Это сборка? \en Is it an assembly? + virtual bool IsAssembly() const = 0; + /// \ru Это ни сборка, ни деталь? \en Is it neither an assembly nor a part? + virtual bool IsEmpty() const = 0; + + /** \brief \ru Прообраз новой интерфейсной функции - задать модель ЛСК, относительно которой позиционируется модель. + \en Prototype of a new interface function - get the placement the model is defined in. \~ + */ + //virtual MbPlacement3D GetOriginLocation() const = 0; + + /** \brief \ru Прообраз новой интерфейсной функции - задать модель для наполнения. + \en Prototype of a new interface function - set a model to fill. \~ + */ + virtual void SetContent( MbItem* /*content*/) = 0; + + /** \brief \ru Прообраз новой интерфейсной функции - получить наполнение. + \en Prototype of a new interface function - get the filling. \~ + */ + virtual MbItem * GetContent() /*{ return NULL; }*/ = 0; + + /** \brief \ru Создать документ с новой сборкой при импорте. + \en Create a document with a new assembly while importing. \~ + \details \ru Увеличить счётчик ссылок результирующего документа на 1. + \en Increase the reference counter of the resultant document by 1. \~ + \param[in] fileName - \ru Имя сборки. + \en Assembly name. \~ + \param[in] solids - \ru Тела, добавляемые в сборку. + \en Solids to add into the assembly. \~ + \return \ru Экземпляр сборки, если операция прошла успешно, NULL в противном случае. + \en Instance of an assembly if the operation succeeded, NULL - otherwise. \~ + */ + virtual ModelAssemblyPtr CreateAssembly( const c3d::ItemsSPtrVector & componentItems, const c3d::string_t& fileName ) = 0; + + + /** \brief \ru Создать документ с новой деталью при импорте. + \en Create a document with a new part while importing. \~ + \details \ru Увеличить счётчик ссылок результирующего документа на 1. + \en Increase the reference counter of the resultant document by 1. \~ + \param[in] solids - \ru Тела, добавляемые в деталь. + \en Solids to add into a part. \~ + \param[in] fileName - \ru Имя детали. + \en A part name. \~ + \return \ru Экземпляр детали, если операция прошла успешно, NULL в противном случае. + \en Instance of the part if the operation succeeded, NULL - otherwise. \~ + */ + virtual ModelPartPtr CreatePart( const c3d::ItemsSPtrVector & componentItems, const c3d::string_t& fileName ) = 0; + + /** \brief \ru Получить сборку для экспорта. + \en Get an assembly for export. \~ + \details \ru Увеличить счётчик ссылок результирующей сборки на 1. + \en Increase the reference counter of the resultant assembly by 1. \~ + \return \ru Экземпляр сборки, если операция прошла успешно, NULL в противном случае. + \en Instance of an assembly if the operation succeeded, NULL - otherwise. \~ + */ + virtual ModelAssemblyPtr GetInstanceAssembly( ) = 0; + + + /** \brief \ru Получить деталь для экспорта. + \en Get the detail for export. \~ + \details \ru Увеличить счётчик ссылок результирующей детали на 1. + \en Increase the reference counter of the resultant part by 1. \~ + \return \ru Экземпляр детали, если операция прошла успешно, NULL в противном случае. + \en Instance of the part if the operation succeeded, NULL - otherwise. \~ + */ + virtual ModelPartPtr GetInstancePart( ) = 0; + + /** \brief \ru Завершить импорт и сохранить документ. + \en Complete the import and save the document. \~ + \return \ru true, если операция прошла успешно, false в противном случае. + \en true if the operation succeeded, false - otherwise. \~ + \param[in] \ru indicator Объект для отображения хода процесса. + \en indicator An object indicating a process progress. \~ + */ + virtual bool FinishImport( IProgressIndicator * indicator ) = 0; + + /** \brief \ru Получить элементы аннотации, соответствующие элементам геометрической модели. + \en Get elements of annotation, corresponding items of geometric model. \~ + \param[in] eTextForm - \ru Форма представления текста. + \en Text representation form. \~ + \return \ru Контейнер объектов аннотации. + \en Vector of annotation objects. \~ + */ + virtual map_of_visual_items GetAnnotationItems( eTextForm ) const = 0; + + /// \ru Задать размеры. \en Set sizes. + virtual void SetAnnotationItems( const map_of_visual_items& ) = 0; + + /// \ru Открыть документ. \en Open a document. + virtual void OpenDocument() = 0; + +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Формирователь геометрического представления текста. +\en Generator of text element's geometry shape. \~ +\ingroup Exchange_Interface +*/ +// --- +class CONV_CLASS C3DSymbolToItem : public MbRefItem { +public: + virtual SPtr TextToItem( const MaTextItem*, const MbPlacement3D& location ) const; + virtual SPtr TerminatorToItem( const MaTerminatorSymbol*, const MbPlacement3D& location ) const; + + virtual ~C3DSymbolToItem(); +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Формирователь геометрического представления PMI. +\en Generator of PMI's geometry shape. \~ +\ingroup Exchange_Interface +*/ +// --- +class CONV_CLASS C3DPmiToItem : public MbRefItem { + SPtr symToItem; +public: + C3DPmiToItem( SPtr = SPtr() ); + virtual SPtr operator() ( const MaAnnotationItem* ) const; + + virtual ~C3DPmiToItem(); +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Реализация документа модели, формирующая регулярную структуру. +\en Implementation of model document which has regular structure. \~ +\ingroup Exchange_Interface +*/ +// --- +class CONV_CLASS C3dModelDocument: public ItModelDocument { + + ModelPartPtr part; ///< \ru Представление в виде детали. \en Representation as detail. + ModelAssemblyPtr assembly; ///< \ru Представление в виде сборки. \en Representation as assembly. + map_of_visual_items visualItems; ///< \ru Элементы аннотации. \en Annotation items. + c3d::ItemSPtr rawContent; ///< \ru Передаваемый модельный элемент. \en Converted model item. + SPtr pmiToItem; ///< \ru Включены ли элементы аннотации непосредственно в модельный элемент. \en Model item contains PMI. +public: + + C3dModelDocument( SPtr pmiToContent = SPtr() ); ///< \ru Конструктор. \en Conscructor. + + virtual ~C3dModelDocument(); ///< \ru Деструктор. \en Descructor. + + // Является ли сборкой. + virtual bool IsAssembly() const; + // Пуст ли. + virtual bool IsEmpty() const; + // Задать модель напрямую. + virtual void SetContent( MbItem* /*content*/); + // Выдать модель напрямую. + virtual MbItem * GetContent(); + // Создать сборку. + virtual ModelAssemblyPtr CreateAssembly( const c3d::ItemsSPtrVector & componentItems, const c3d::string_t& fileName ); + // Создать деталь. + virtual ModelPartPtr CreatePart( const c3d::ItemsSPtrVector & componentItems, const c3d::string_t& fileName ); + // Выдать сборку. + virtual ModelAssemblyPtr GetInstanceAssembly( ); + // Выдать деталь. + virtual ModelPartPtr GetInstancePart( ); + // Завершить импорт. + virtual bool FinishImport( IProgressIndicator * ); + // Выдать элементы аннотации. + virtual map_of_visual_items GetAnnotationItems( eTextForm ) const; + // Задать элементы аннотации. + virtual void SetAnnotationItems( const map_of_visual_items& vi ); + // Открыть документ. + virtual void OpenDocument(); + + /// \ru Включены ли PMI в элемент модели. \en If PMI is included into model item. + SPtrPmiInContent() const; + + /// \ru Зарегистрировать элемент аннотации. \en Register annotation object. + void RegisterAnnotation( c3d::ItemSPtr component, const AnnotationSptrVector& annotation, const AnnotationSptrVector& requirements ); +}; + + +typedef C3dModelDocument RegularModelDocument; +typedef C3dModelDocument ConvModelDocument; + + +//------------------------------------------------------------------------------ +/** \brief \ru Интерфейс свойств вставки, подсборки или детали. +\en Interface of properties of an instance, a subassembly or a part. \~ +\ingroup Exchange_Interface +*/ +// --- +class ItModelInstanceProperties : public MbRefItem +{ +public: + + /// \ru Атрибуты. \en Attributes. + + /// \ru Задать атрибуты. \en Set attributes. + virtual bool SetAttributes( const c3d::AttrSPtrVector& /*attributes*/ ) = 0; + + /// \ru Получить атрибуты. \en Get attributes. + virtual c3d::AttrSPtrVector GetAttributes( ) const = 0;// { return c3d::AttrSPtrVector(); } + + + /// \ru Технические требования. \en Technical requirements. + + /// \ru Получить технические требования. \en Get technical requirements. + virtual void GetRequirements( AnnotationSptrVector &, eTextForm ) const = 0; + + /// \ru Задать технические требования. \en Set technical requirements. + virtual void SetRequirements( const AnnotationSptrVector & ) = 0; + + /// \ru Наименование. \en Name. + + /// \ru Задать имя документа. \en Set document's name. + DEPRECATE_DECLARE virtual bool SetName( const std::string& /*name*/ ) { return false; }; + /// \ru Получить имя документа. \en Get document's name. + DEPRECATE_DECLARE virtual std::string Name() const { return std::string(); }; + + /// \ru Обозначение. \en Marking. + + /// \ru Задать обозначение документа. \en Set document marking. + DEPRECATE_DECLARE virtual bool SetMarking( const std::string& /*name*/ ) { return false; }; + /// \ru Получить обозначение документа. \en Get document marking. + DEPRECATE_DECLARE virtual std::string Marking() const { return std::string(); }; + + /// \ru Автор. \en Author. + + /// \ru Задать имя автора. \en Set author's name. + DEPRECATE_DECLARE virtual bool SetAuthor( const std::string& /*name*/ ) { return false; }; + /// \ru Получить имя автора. \en Get author's name. + DEPRECATE_DECLARE virtual std::string Author() const { return std::string(); }; + + /// \ru Организация. \en Organization. + + /// \ru Задать имя автора. \en Set author's name. + DEPRECATE_DECLARE virtual bool SetOrganization( const std::string& /*name*/ ) { return false; }; + /// \ru Получить имя автора. \en Get author's name. + DEPRECATE_DECLARE virtual std::string Organization() const { return std::string(); }; + + /// \ru Комментарий. \en Comment. + + /// \ru Задать комментарии. \en Set the comments. + DEPRECATE_DECLARE virtual bool SetComments( const std::vector< std::string > & /*comments*/ ) { return false; }; + /// \ru Получить следующий комментарий. \en Get the next comment. + DEPRECATE_DECLARE virtual std::vector< std::string > GetComments( ) const { return std::vector< std::string >(); }; + + /// \ru Цвет сборки, детали или вставки. \en Color of an assembly, a part or an instance. + + /// \ru Задать цветовые свойства. \en Set color properties. + DEPRECATE_DECLARE virtual bool SetColor( const MbAttributeContainer & ) { return false; }; + /// \ru Получить цветовые свойства. \en Get color properties. + DEPRECATE_DECLARE virtual bool GetColor( MbAttributeContainer & ) const { return false; }; + + /// \ru Цвет тела. \en Solid color. + + /// \ru Задать цветовые свойства оболочки. \en Set color properties of a shell. + DEPRECATE_DECLARE virtual bool SetColor( const MbAttributeContainer &, size_t ) { return false; }; + + /// \ru Цвет грани. \en Face color. + + /// \ru Задать цветовые свойства грани \en Set color properties of a face. + DEPRECATE_DECLARE virtual bool SetColor( const MbAttributeContainer &, const MbName & ) { return false; }; + /// \ru Получить цветовые свойства грани. \en Get color properties of a face. + DEPRECATE_DECLARE virtual bool GetColor( MbAttributeContainer &, const MbName & ) const { return false; }; +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Интерфейс вставки компоненты. +\en Interface of the component instance. \~ +\ingroup Exchange_Interface +*/ +// --- +class ItModelInstance : public ItModelInstanceProperties +{ +public: + // \ru Выдать идентификатор сборки или детали \en Get identifier of an assembly or a part + virtual void * GetId() = 0; + /// \ru Выдать расположение этой вставки в координатах родителя. \en Get the placement of this instance in parent's coordinates. + virtual bool GetPlacement( MbPlacement3D & ) const = 0; + /// \ru Это сборка? \en Is it an assembly? + virtual bool IsAssembly() const = 0; + /// \ru Это ни сборка, ни деталь? \en Is it neither an assembly nor a part? + virtual bool IsEmpty() const = 0; + + /** \brief \ru Создать пустую сборку при импорте и увеличить счётчик ссылок на 1. + \en Create an empty assembly while importing and increase the reference counter by 1. \~ + \param[in] place - \ru ЛСК сборки в родительской модели. + \en LCS of the assembly in the parent's model. \~ + \param[in] fileName - \ru Имя сборки. + \en Assembly name. \~ + \return \ru Экземпляр сборки, если операция прошла успешно, NULL в противном случае. + \en Instance of an assembly if the operation succeeded, NULL - otherwise. \~ + */ + virtual ModelAssemblyPtr CreateAssembly( const MbPlacement3D &place, const c3d::ItemsSPtrVector & componentItems, const c3d::string_t& fileName ) = 0; + + /** \brief \ru Создать деталь при импорте. + \en Create a part while importing. \~ + \details \ru Увеличить счётчик ссылок детали на 1. + \en Increase the reference counter of a part by 1. \~ + \param[in] place - \ru ЛСК детали. + \en LCS of a part. \~ + \param[in] solids - \ru Тела, включаемые в деталь. + \en Solids included in the part. \~ + \param[in] fileName - \ru Название детали. + \en Solid's name. \~ + \return \ru Экземпляр детали, если операция прошла успешно, NULL в противном случае. + \en Instance of the part if the operation succeeded, NULL - otherwise. \~ + */ + virtual ModelPartPtr CreatePart( const MbPlacement3D &place, const c3d::ItemsSPtrVector & componentItems, const c3d::string_t& fileName ) = 0; + + /** \brief \ru Получить сборку для экспорта. + \en Get an assembly for export. \~ + \return \ru Экземпляр сборки, если операция прошла успешно, NULL в противном случае. + \en Instance of an assembly if the operation succeeded, NULL - otherwise. \~ + */ + virtual ModelAssemblyPtr GetInstanceAssembly( ) = 0; + + + /** \brief \ru Получить деталь для экспорта. + \en Get the detail for export. \~ + \return \ru Экземпляр детали, если операция прошла успешно, NULL в противном случае. + \en Instance of the part if the operation succeeded, NULL - otherwise. \~ + */ + virtual ModelPartPtr GetInstancePart( ) = 0; + + /** \brief \ru Создать подсборку при импорте, и её вставку. + \en Create a subassembly and its instance while importing. \~ + \param[in] place - \ru ЛСК сборки в родительской модели. + \en LCS of the assembly in the parent's model. \~ + \param[in] existing - \ru Сборка, подлежащая вставке. + \en An assembly to insert. \~ + \return \ru true, если операция прошла успешно, false в противном случае. + \en true if the operation succeeded, false - otherwise. \~ + */ + virtual bool SetAssembly( const MbPlacement3D & place, const ItModelAssembly * existing ) = 0; + + /** \brief \ru Создать деталь при импорте, и её вставку. + \en Create a part while importing and its instance. \~ + \param[in] place - \ru ЛСК детали в родительской модели. + \en LCS of a part in the parent's model. \~ + \param[in] existing - \ru Деталь, подлежащая вставке. + \en Detail to insert. \~ + \return \ru true, если операция прошла успешно, false в противном случае. + \en true if the operation succeeded, false - otherwise. \~ + */ + virtual bool SetPart( const MbPlacement3D & place, const ItModelPart * existing ) = 0; + +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Тип объектов, которые необходимо выдать для экспорта или добавить при импорте. +\en Type of objects to be returned for export or to be added while importing. \~ +\ingroup Data_Interface +*/ +// --- +enum MbeGettingItemType { + git_Item = 0, ///< \ru Получить элементы всех типов. \en Get items of all types. + git_Solid, ///< \ru Получить тела. \en Get solids. + git_Surface, ///< \ru Получить поверхности. \en Get surfaces. + git_WireFrame, ///< \ru Получить проволочные каркасы. \en Get wire frames. + git_PlaneInstance, ///< \ru Получить вставки плоских объектов (эскизы). \en Get plane instances (drafts). + git_PointFrame, ///< \ru Получить точечные каркасы. \en Get point frames. + git_AssociatedGeometry ///< \ru Получить ассоциированные геометрические объекты (резьбы). \en Get associated geometry objects (threads). +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Интерфейс сборки. +\en Interface of the assembly. \~ +\details \ru Экземпляр должен порождаться в методах CreateAssembly реализаций +интерфейсов ItModelDocument и ItModelAInstance. Собственные элементы детали +должны передаваться как параметры конструктора. \~ \en The object should be +created in the CreateAssembly method of the implementations of the +ItModelDocument and ItModelInstance interfaces. Own Items of the detail should +be arguments of the constructor. +\ingroup Exchange_Interface +*/ +// --- +class ItModelAssembly : public ItModelInstanceProperties +{ +public: + /** \brief \ru Получить имя файла сборки без пути и расширения для экспорта. + \en Get the file name of an assembly without the path and the extension for export. \~ + \return \ru Имя файла сборки. + \en An assembly file name. \~ + */ + virtual c3d::path_string PureFileName() const = 0; + + /** \brief \ru Получить пустой интерфейс вставки для создания подсборки или детали при импорте. + \en Get an empty interface of the insertion for creation of subassembly or a part while importing. \~ + \details \ru Увеличить счётчик ссылок на 1. + \en Increase the reference counter by 1. \~ + \return \ru Интерфейс вставки, если операция прошла успешно или NULL в противном случае. + \en Interface of the instance if the operation succeeded and NULL otherwise. \~ + */ + virtual ModelInstancePtr PrepareInstance() = 0; + + /** \brief \ru Получить интерфейс следующей вставки для создания подсборки или детали при экспорте. + \en Get the interface of the next insertion for creation of a subassembly or a part while exporting. \~ + \return \ru Интерфейс вставки, если операция прошла успешно или NULL в противном случае. + \en Interface of the insertion if the operation succeeded and NULL otherwise. \~ + */ + virtual ModelInstancePtr NextInstance( bool includeInvisible ) = 0; + + /// \ru Выдать ЛСК, общую для элементов компонента. \en Get the placement, which all the items of the component use for transformation. + virtual bool GetPlacement( MbPlacement3D & ) const { return false; }; + + /** \brief \ru Получить объекты из корня сборки при экспорте. + \en Get objects from the assembly root while exporting. \~ + \param[out] items - \ru Наполняемый массив (состоит из объектов классов MbSolid, MbCurve3D, MbCartPoint3D). + \en Array to fill (consist of objects of classes MbSolid, MbCurve3D, MbCartPoint3D). \~ + \param[in] includeInvisible - \ru Если true, то выдаются все тела, включая невидимые, если false - только видимые. + \en If true, then all the solids are returned, including invisible ones, if false - only visible ones. \~ + */ + virtual void GetItems( c3d::ItemsSPtrVector & items, MbeGettingItemType itemType, bool includeInvisible ) const = 0; + + /** \brief \ru Добавить объекты в корень сборки при импорте. + \en Add objects to the assembly root while importing. \~ + \param[in] items - \ru Объекты, добавляемые в модель (тела, кривые и точки). + \en Objects to add to the model (solids, curves and points). \~ + */ + virtual void AddItems( const c3d::ItemsSPtrVector & items ) = 0; + + /** \brief \ru Получить элементы аннотации из сборки. + \en Get elements of annotation from the assembly. \~ + \param[in] eTextForm - \ru Форма представления текста. + \en Text representation form. \~ + \param[in] includeInvisible - \ru Если true, то выдаются все объекты аннотации, включая невидимые, если false - только видимые. + \en If true, all the annotation objects are returned, including invisible ones, if false - only visible ones. \~ + \return \ru Контейнер объектов аннотации. + \en Vector of annotation objects. \~ + */ + virtual AnnotationSptrVector GetAnnotationItems( eTextForm, bool ) const { return AnnotationSptrVector(); }; // Реализация будет удалена после того, как она будет осуществлена на стороне 3D + virtual AnnotationSptrVector GetAnnotationItems( eTextForm ) const { return AnnotationSptrVector(); }; // Будет удалена после её реализации на стороне 3D + + /** \brief \ru Задать элементы аннотации в сборке. + \en Set elements of annotation in the assembly. \~ + \param[in] sourceDim - \ru Элементы аннотации + \en Elements of annotation. \~ + */ + virtual void SetAnnotationItems( const AnnotationSptrVector & ) = 0; + +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Интерфейс детали. +\en Interface of a part. \~ +\details \ru Экземпляр должен порождаться в методах CreatePart реализаций +интерфейсов ItModelDocument и ItModelAInstance. Собственные элементы детали +должны передаваться как параметры конструктора. \~ \en The object should be +created in the CreatePart method of the implementations of the +ItModelDocument and ItModelInstance interfaces. Own Items of the detail should +be arguments of the constructor. +\ingroup Exchange_Interface +*/ +// --- +class ItModelPart : public ItModelInstanceProperties +{ +public: + /** \brief \ru Получить имя файла детали без пути и расширения для экспорта. + \en Get the file name of a part without the path and extension for export. \~ + \return \ru Имя файла детали. + \en A part file name. \~ + */ + virtual c3d::path_string PureFileName() const = 0; + + /** \brief \ru Получить пустой интерфейс вставки для создания подсборки или детали при импорте. + \en Get an empty interface of the insertion for creation of subassembly or a part while importing. \~ + \details \ru Увеличить счётчик ссылок на 1. + \en Increase the reference counter by 1. \~ + \return \ru Интерфейс вставки, если операция прошла успешно или NULL в противном случае. + \en Interface of the instance if the operation succeeded and NULL otherwise. \~ + */ + virtual ModelInstancePtr PrepareInstance() = 0; + + /** \brief \ru Получить интерфейс следующей вставки для создания подсборки или детали при экспорте. + \en Get the interface of the next insertion for creation of a subassembly or a part while exporting. \~ + \return \ru Интерфейс вставки, если операция прошла успешно или NULL в противном случае. + \en Interface of the insertion if the operation succeeded and NULL otherwise. \~ + */ + virtual ModelInstancePtr NextInstance( bool includeInvisible ) = 0; + + /// \ru Выдать ЛСК, общую для элементов компонента. \en Get the placement, which all the items of the component use for transformation. + virtual bool GetPlacement( MbPlacement3D & ) const { return false; }; + + /** \brief \ru Получить объекты из детали при экспорте. + \en Get objects from the part while exporting. \~ + \param[out] items - \ru Наполняемый массив (состоит из объектов классов MbSolid, MbWireFrame, MbPointFrame). + \en Array to fill (consists of objects of classes MbSolid, MbWireFrame, MbPointFrame). \~ + \param[in] itemType - \ru Тип объектов, которыми нужно наполнить массив. + \en Type of objects the array should be filled with. \~ + \param[in] includeInvisible - \ru Если true, то выдаются все тела, включая невидимые, если false - только видимые. + \en If true, all the solids are returned, including invisible ones, if false - only visible ones. \~ + */ + virtual void GetItems( c3d::ItemsSPtrVector & items, MbeGettingItemType itemType, bool includeInvisible ) const = 0; + + /** \brief \ru Добавить объекты в деталь при импорте. + \en Add objects to a part while importing. \~ + \param[in] items - \ru Объекты, добавляемые в модель (кривые и точки). + \en Objects to be added to the model (curves and points). \~ + */ + virtual void AddItems( const c3d::ItemsSPtrVector & items ) = 0; + + /** \brief \ru Получить элементы аннотации из детали. + \en Get elements of annotation from the detail. \~ + \param[in] eTextForm - \ru Форма представления текста. + \en Text representation form. \~ + \param[in] includeInvisible - \ru Если true, то выдаются все объекты аннотации, включая невидимые, если false - только видимые. + \en If true, all the annotation objects are returned, including invisible ones, if false - only visible ones. \~ + \return \ru Контейнер объектов аннотации. + \en Vector of annotation objects. \~ + */ + virtual AnnotationSptrVector GetAnnotationItems( eTextForm, bool ) const { return AnnotationSptrVector(); }; // Реализация будет удалена после того, как она будет осуществлена на стороне 3D + virtual AnnotationSptrVector GetAnnotationItems( eTextForm ) const { return AnnotationSptrVector(); }; // Будет удалена после её реализации на стороне 3D + + + /** \brief \ru Задать элементы аннотации в детали. + \en Set elements of annotation in the part. \~ + \param[in] sourceDim - \ru Элементы аннотации + \en Elements of annotation. \~ + */ + virtual void SetAnnotationItems( const AnnotationSptrVector & ) = 0; + +}; + + +#endif // __CONV_MODEL_DOCUMENT_H \ No newline at end of file diff --git a/C3d/Include/conv_i_converter.h b/C3d/Include/conv_model_exchange.h similarity index 65% rename from C3d/Include/conv_i_converter.h rename to C3d/Include/conv_model_exchange.h index d899345..b1d4550 100644 --- a/C3d/Include/conv_i_converter.h +++ b/C3d/Include/conv_model_exchange.h @@ -1,154 +1,367 @@ //////////////////////////////////////////////////////////////////////////////// /** \file - \brief \ru Интерфейсы конвертера. - \en Interfaces of the converter. \~ - + \brief \ru Общий интерфейс конвертера. + \en Common API of the converter. \~ + \details \ru Функции чтения и записи в буфер и файл с автоопределением формата по + расширению файла, класс-конвертер с методами для каждого формата и возможностью + подключения плагина, функции для работы с каждым форматом. + \en Functions for export and import from buffer and file using file's extension + for format detection, class of converter for format-specific methods and API for + plugin, format-specific import and export functions. \~ */ //////////////////////////////////////////////////////////////////////////////// #ifndef __CONV_I_CONVERTER_H #define __CONV_I_CONVERTER_H - -#include #include -#include -#include -#include -#include - - -class IProgressIndicator; -struct IScaleRequestor; -class ItModelDocument; -class MATH_CLASS MbRefItem; -class MATH_CLASS MbPlacement3D; -class MATH_CLASS MbModel; +#include +#include +class IProgressIndicator; +struct IScaleRequestor; +class ItModelDocument; +class IConvertorProperty3D; /** \addtogroup Exchange_Interface \{ */ +//------------------------------------------------------------------------------ +/** \brief \ru Обменный формат модели. +\en Model exchange format.\~ +\ingroup Data_Interface +*/ +// --- +enum MbeModelExchangeFormat { + mxf_autodetect, ///< \ru Интерпретировать содержимое по расширению файла. \en File extension defines format. + mxf_ACIS, ///< \ru Интерпретировать содержимое как ACIS (.sat). \en Read data from buffer as ACIS (.sat). + mxf_IGES, ///< \ru Интерпретировать содержимое как IGES (.igs или .iges). \en Read data from buffer as IGES (.igs or .iges). + mxf_JT, ///< \ru Интерпретировать содержимое как JT (.jt). \en Read data from buffer as JT (.jt). + mxf_Parasolid, ///< \ru Интерпретировать содержимое как Parasolid (.x_t, .x_b, .xmt_txt, .xmp_txt, .xmt_bin или .xmp_bin ). \en Read data from buffer as Parasolid (.x_t, .x_b, .xmt_txt, .xmp_txt, .xmt_bin or .xmp_bin ). + mxf_STEP, ///< \ru Интерпретировать содержимое как STEP (.stp или .step). \en Read data from buffer as STEP (.stp or .step). + mxf_STL, ///< \ru Интерпретировать содержимое как STL (.stl). \en Read data from buffer as STL (.stl). + mxf_VRML, ///< \ru Интерпретировать содержимое как VRML (.wrl). \en Read data from buffer as VRML (.wrl). + mxf_GRDECL, ///< \ru Интерпретировать содержимое как GRDECL (.grdecl). \en Read data from buffer as GRDECL (.grdecl). + mxf_ASCIIPoint, ///< \ru Интерпретировать содержимое как облако точек в ASCII (.txt, .asc или .xyz). \en Read data from buffer as ASCII point cloud (.txt, .asc or .xyz). + mxf_C3D, ///< \ru Интерпретировать содержимое как C3D (.c3d). \en Read data from buffer as C3D (.c3d). +}; + //------------------------------------------------------------------------------ -/** \brief \ru Интерфейс свойств конвертера. - \en Interface of converter's properties. \~ - \details \ru Интерфейс свойств конвертера реализует выдачу имени документа и других сведений о нём, таких как автор, - и управление режимами работы - сшивкой поверхностей с возможностью создания твёрдых - тел, фильтрацией объектов, формирование журнала трансляции. - \en Interface of converter's properties realizes getting the document's name and other information about it, such as the author, - and management of modes of operations - stitching of surfaces with possibility of solids creation, - objects filtration, generation of translation journal. \~ -\ingroup Exchange_Interface +/** \brief \ru Результат конвертирования. +\en Result of converting operation. +\ingroup Data_Interface */ -class IConvertorProperty3D { -public : - virtual ~IConvertorProperty3D() {} +// --- +enum MbeConvResType { + cnv_Success = 0, ///< \ru Успешное завершение. \en Success. + cnv_Error, ///< \ru Ошибка в процессе конвертирования. \en Error. + cnv_UserCanceled, ///< \ru Процесс прерван пользователем. \en Process interrupted by user. + cnv_NoBody, ///< \ru Не найдено тел. \en No solids found. + cnv_NoObjects, ///< \ru Не найдено объектов. \en No objects found. + cnv_FileOpenError, ///< \ru Ошибка открытия файла. \en File open error. + cnv_FileWriteError, ///< \ru Ошибка записи файла. \en File write error. + cnv_FileDeleteError, ///< \ru Ошибка удаления файла. \en Could not delete file. + cnv_ImpossibleReadAssembly,///< \ru Не поддерживает работу со сборками. \en Assemblies are not supported. + cnv_LicenseNotFound, ///< \ru Ошибка получения лицензии. \en License check failure. + cnv_NotEnoughMemory, ///< \ru Недостаточно памяти. \en Not enough memory. + cnv_UnknownExtension ///< \ru Неизвестное расширение файла. \en Unknown file extenstion. +}; -public: - /// \ru Получить имя документа. \en Get document's name. - virtual const std::string GetDocumentName () const = 0; //{ return std::string( GetDocName().get_str() ); }; - /// \ru Получить имя файла для конвертирования. \en Get file name for converting. - virtual const c3d::path_string FullFilePath () const = 0 ;//{ return c3d::path_string( GetFileName().c_str() ); }; - /// \ru Является ли файл текстовым. \en Whether the file is a text file. - virtual bool IsFileAscii () const = 0; - /// \ru Получить версию формата при экспорте. \en Get the version of format for export. - virtual long int GetFormatVersion () const { return EXPORT_DEFAULT; }; - /// \ru Задать формат для экспорта \en Set format for export - DEPRECATE_DECLARE virtual MbeImpExpFormat GetFormat () const { return ief_STEP203; } - /// \ru Следует ли экспортировать только поверхности ( введено для работы конвертера IGES ). \en Whether to export only surfaces (introduced for work with converter IGES ). - virtual bool IsOutOnlySurfaces() const = 0; - /// \ru Является ли экспортируемый документ сборкой. \en Whether the document for export is an assembly. - virtual bool IsAssembling () const = 0; - /// \ru Получить значение разрешения на импорт экспорт объектов определенного типа. \en Get the value of permission for import-export of objects of a certain type. - virtual bool GetIoPermission( MbeIOPermiss nPermission ) const = 0; - /// \ru Получить значения разрешений на импорт экспорт объектов определенных типов. \en Get values of permission for import-export of objects of certain types. - virtual void GetIoPermissions( std::vector& ioPermissions ) const = 0; - /// \ru Установить разрешение на импорт экспорт объектов определенного типа. \en Set permission for import-export of objects of a certain type. - virtual void SetIoPermission( MbeIOPermiss nPermission, bool set ) = 0; - /// \ru Получить значение специфичной строки для конвертера. \en Get the value of a certain string for the converter. - virtual bool GetPropertyString ( MbeConverterStrings nString, std::string & propertyString ) const = 0; - /// \ru Установить значение специфичной строки для конвертера. \en Set the value of a certain string for the converter. - virtual void SetPropertyString ( MbeConverterStrings nString, const std::string & propertyString ) = 0; - /// \ru Представление текста в аннотационных объектах. \en Text representation in annotation objects. - virtual eTextForm GetAnnotationTextRepresentation () const { return exf_TextOnly; } - /// \ru Следует ли компоненты экспортировать в разные файлы (если позволяет формат). \en Export components into separate files ( if provided in format). - virtual bool ExportComponentsSeparately() const { return false; } - /// \ru Получить ЛСК, относительно которой позиционирована модель. \en Get the location, the model is placed in. - virtual MbPlacement3D GetOriginLocation() const = 0; - /// \ru Заменять ли принудительно СК компонент на правые. \en Replace components' placements to right-oriented. - virtual bool ReplaceLocationsToRight() const = 0; - /** \brief \ru Сшивать ли поверхности автоматически. - \en If surfaces should be stitched automatically. \~ - \return \ru true - Сшивать поверхности автоматически, false - Спросить пользователя, сшивать ли поверхности. - \en true - Stitch surfaces automatically, false - Ask user first time. \~ - \param[out] stitchPrecision - \ru Точность сшивки. - \en Stitch precision. \~ + +namespace c3d { + + class C3DExchangeBuffer; + + /** \brief \ru Прочитать файл обменного формата в модель. + \en Read a file of an exchange format into model. \~ + \details \ru Если свойства конвертера заданы, аргумент fileName игнорируется, а имя файла берётся из свойств конвертера. + В противном случае импорт идёт с умолчательными параметрами, соответствующими реализации ConvConvertorProperty3D. \~ + \en The fileName argument is not used if converter properties are defined obviously, file path comes from the FullFilePath + method. Otherwise default parameters corresponding ConvConvertorProperty3D implementation are used for import. + \param[out] model - \ru Модель. + \en The model. \~ + \param[in] filePath - \ru Путь файла. + \en File path. \~ + \param[in] prop - \ru Реализация интерфейса свойств конвертера. + \en Implementation of converter's properties interface. \~ + \param[in] indicator - \ru Индикатор хода процесса. + \en The process progress indicator. \~ + \return \ru Код завершения операции. + \en Code of the operation termination. \~ + \ingroup Exchange_Interface */ - virtual bool EnableAutoStitch( double& /*stitchPrecision*/ ) const = 0; + CONV_FUNC (MbeConvResType) ImportFromFile( MbModel & model, + const path_string & fileName, + IConvertorProperty3D * prop = C3D_NULL_PTR, + IProgressIndicator * indicator = C3D_NULL_PTR ); - /** \brief \ru Получить множитель единиц длины по отношению к миллиметру. - \en Get the factor of the length units to millimeters. \~ - \details \ru При импорте, если единицы измерения не заданы явно с помощью средств, предоставляемых обменным форматом, - все размеры (координаты точек, радиусы) умножаются на возвращаемое значение. При экспорте либо с помощью - средств, предоставляемых обменным форматом, задаются единицы измерения, либо все размеры модели (координаты - точек, радиусы) умножаются на возвращаемое значение. - \en During the import all spatial objects (coordinate values, radiuses) are multiplied by the returned value, - unless the scale factor comes from the exchange file. During the export the exchange format facilities are - used to specify the length units or all spatial objects (coordinate values, radiuses) are multiplied by the - returned value. \~ + + /** \brief \ru Прочитать файл обменного формата в элемент. + \en Read a file of an exchange format into element. \~ + \details \ru Если свойства конвертера заданы, аргумент fileName игнорируется, а имя файла берётся из свойств конвертера. + В противном случае импорт идёт с умолчательными параметрами, соответствующими реализации ConvConvertorProperty3D. \~ + \en The fileName argument is not used if converter properties are defined obviously, file path comes from the FullFilePath + method. Otherwise default parameters corresponding ConvConvertorProperty3D implementation are used for import. + \param[out] item - \ru Замещаемый элемент. + \en The element to replace. \~ + \param[in] filePath - \ru Путь файла. + \en File path. \~ + \param[in] prop - \ru Реализация интерфейса свойств конвертера. + \en Implementation of converter's properties interface. \~ + \param[in] indicator - \ru Индикатор хода процесса. + \en The process progress indicator. \~ + \return \ru Код завершения операции. + \en Code of the operation termination. \~ + \ingroup Exchange_Interface */ - virtual double LengthUnitsFactor() const { return LENGTH_UNIT_MM; } + CONV_FUNC (MbeConvResType) ImportFromFile( c3d::ItemSPtr& item, + const path_string& filePath, + IConvertorProperty3D* prop = C3D_NULL_PTR, + IProgressIndicator* indicator = C3D_NULL_PTR ); - - /** \brief \ru Получить дополнительный множитель единиц длины по отношению к миллиметру в модели приложения. - \en Get addifional factor of the length units to millimeters in the application model. \~ - \details \ru При импорте из всех форматов за исключением JT, если единицы измерения, в том числе и заданные - явно с помощью средств, предоставляемых обменным форматом, все размеры (координаты точек, радиусы) умножаются - на возвращаемое значение. При экспорте либо с помощью средств, предоставляемых обменным форматом, задаются - единицы измерения, либо все размеры модели (координаты точек, радиусы) умножаются на возвращаемое значение. - \en During the import from all formats except for JT all spatial objects (coordinate values, radiuses) are - multiplied by the returned value, even if the scale factor comes from the exchange file. During the export the - exchange format facilities are used to specify the length units or all spatial objects (coordinate values, - radiuses) are multiplied by the returned value. \~ + /** \brief \ru Прочитать файл обменного формата в модель. + \en Read a file of an exchange format into model. \~ + \details \ru Если свойства конвертера заданы, аргумент fileName игнорируется, а имя файла берётся из свойств конвертера. + В противном случае импорт идёт с умолчательными параметрами, соответствующими реализации ConvConvertorProperty3D. \~ + \en The fileName argument is not used if converter properties are defined obviously, file path comes from the FullFilePath + method. Otherwise default parameters corresponding ConvConvertorProperty3D implementation are used for import. + \param[out] mDoc - \ru Модельный документ. + \en The model. \~ + \param[in] filePath - \ru Путь файла. + \en File path. \~ + \param[in] prop - \ru Реализация интерфейса свойств конвертера. + \en Implementation of converter's properties interface. \~ + \param[in] indicator - \ru Индикатор хода процесса. + \en The process progress indicator. \~ + \return \ru Код завершения операции. + \en Code of the operation termination. \~ + \ingroup Exchange_Interface */ - virtual double AppLengthUnitsFactor() const { return LENGTH_UNIT_MM; } + CONV_FUNC (MbeConvResType) ImportFromFile( ItModelDocument & mDoc, + const path_string & filePath, + IConvertorProperty3D * prop = C3D_NULL_PTR, + IProgressIndicator * indicator = C3D_NULL_PTR ); - /** \brief \ru Сделать запись в журнал конвертирования. - \en Make a record in the converter report. \~ - \param[in] id - \ru Идентификатор элемента внутри файла стороннего формата. - \en Identifier of an element inside the file of a foreign format. \~ - \param[in] msgType - \ru Тип сообщения. - \en Message type. \~ - \param[in] msgText - \ru Код сообщения. - \en Message code. \~ + /** \brief \ru Записать модель в файл обменного формата. + \en Write the model into an exchange format file. \~ + \details \ru Если свойства конвертера заданы, аргумент fileName игнорируется, а имя файла берётся из свойств конвертера. + В противном случае экспорт идёт с умолчательными параметрами, соответствующими реализации ConvConvertorProperty3D. \~ + \en The fileName argument is not used if converter properties are defined obviously, file path comes from the FullFilePath + method. Otherwise default parameters corresponding ConvConvertorProperty3D implementation are used for export. + \param[out] model - \ru Модель. + \en The model. \~ + \param[in] filePath - \ru Путь файла. + \en File path. \~ + \param[in] prop - \ru Реализация интерфейса свойств конвертера. + \en Implementation of converter's properties interface. \~ + \param[in] indicator - \ru Индикатор хода процесса. + \en The process progress indicator. \~ + \return \ru Код завершения операции. + \en Code of the operation termination. \~ + \ingroup Exchange_Interface */ - virtual void LogReport( ptrdiff_t id, eMsgType msgType, eMsgDetail msgText ) = 0; + CONV_FUNC (MbeConvResType) ExportIntoFile( MbModel & model, + const path_string & filePath, + IConvertorProperty3D * prop = C3D_NULL_PTR, + IProgressIndicator * indicator = C3D_NULL_PTR ); -// /** \brief \ru Следует ли показывать сообщения и диалоги пользователю. \en Whether to show messages and dialog to the user. \~ -// \details \ru Обеспечивает работу через API. \en Provide possibility for work via API. \~ -// \return \ru true - обычная работа, false - через API. \en true - ordinary work, false - via API. \~ -// */ - virtual bool CanShowMessages() const = 0; - /// \ru Дать данные вычисления триангуляции (для конвертера JT, STL и VRML). \en Get data for step calculation during triangulation (for JT, STL, VRML only). - virtual MbStepData TesselationParameters() const { return MbStepData(); } - /// \ru Дать данные вычисления триангуляции уровня детализации (для конвертера JT). \en Get data for step calculation during triangulation of LOD0 (for JTonly). - virtual MbStepData LOD0TesselationParameters() const { return TesselationParameters(); } - /// \ru Флаг сохранения совпадающих точек швов. (для конвертера STL и VRML). \en Whether to keep coincident points of seams (for STL, VRML only). - virtual bool DualSeams() const { return true; } - /// \ru Флаг сохранения совпадающих точек швов. (для конвертера STL и VRML). \en Whether to keep coincident points of seams (for STL, VRML only). - virtual void DualSeams( bool ) {} - /// \ru Проводить ли аудит траснляции. \en Whether to audit the translation. - virtual bool TotalAudit() { return false; } + /** \brief \ru Записать модель в файл обменного формата. + \en Write the model into an exchange format file. \~ + \details \ru Если свойства конвертера заданы, аргумент fileName игнорируется, а имя файла берётся из свойств конвертера. + В противном случае экспорт идёт с умолчательными параметрами, соответствующими реализации ConvConvertorProperty3D. \~ + \en The fileName argument is not used if converter properties are defined obviously, file path comes from the FullFilePath + method. Otherwise default parameters corresponding ConvConvertorProperty3D implementation are used for export. + \param[out] model - \ru Экспортируемый лемент. + \en The exported element. \~ + \param[in] filePath - \ru Путь файла. + \en File path. \~ + \param[in] prop - \ru Реализация интерфейса свойств конвертера. + \en Implementation of converter's properties interface. \~ + \param[in] indicator - \ru Индикатор хода процесса. + \en The process progress indicator. \~ + \return \ru Код завершения операции. + \en Code of the operation termination. \~ + \ingroup Exchange_Interface + */ + CONV_FUNC (MbeConvResType ) ExportIntoFile( MbItem& item, + const path_string& filePath, + IConvertorProperty3D* prop = C3D_NULL_PTR, + IProgressIndicator* indicator = C3D_NULL_PTR ); - /// \ru Выполнять ли слияние подобных граней. \en Whether to join similar faces. - virtual bool JoinSimilarFaces() const { return true; } - /// \ru Добавлять ли удаленные грани в качестве оболочек. \en Whether to add removed faces as shells. - virtual bool AddRemovedFacesAsShells() const { return false; } -}; // IConvertorProperty3D + /** \brief \ru Записать модельный документ в файл обменного формата. + \en Write the model into an exchange format file. \~ + \details \ru Если свойства конвертера заданы, аргумент fileName игнорируется, а имя файла берётся из свойств конвертера. + В противном случае экспорт идёт с умолчательными параметрами, соответствующими реализации ConvConvertorProperty3D. \~ + \en The fileName argument is not used if converter properties are defined obviously, file path comes from the FullFilePath + method. Otherwise default parameters corresponding ConvConvertorProperty3D implementation are used for export. + \param[in] mDoc - \ru Экспортируемый модельный документ. + \en The exported model document. \~ + \param[in] filePath - \ru Путь файла. + \en File path. \~ + \param[in] prop - \ru Реализация интерфейса свойств конвертера. + \en Implementation of converter's properties interface. \~ + \param[in] indicator - \ru Индикатор хода процесса. + \en The process progress indicator. \~ + \return \ru Код завершения операции. + \en Code of the operation termination. \~ + \ingroup Exchange_Interface + */ + CONV_FUNC (MbeConvResType ) ExportIntoFile( ItModelDocument& mDoc, + const path_string& filePath, + IConvertorProperty3D* prop = C3D_NULL_PTR, + IProgressIndicator* indicator = C3D_NULL_PTR ); + + + /** \brief \ru Импортировать данные из буфера в модель. + \en Import data from buffer into model. \~ + \param[out] model - \ru Модель. + \en The model. \~ + \param[in] buffer - \ru Буфер. + \en Buffer. \~ + \param[in] modelFormat - \ru Формат модели. + \en Model format. \~ + \param[in] prop - \ru Реализация интерфейса свойств конвертера. + \en Implementation of converter's properties interface. \~ + \param[in] indicator - \ru Индикатор хода процесса. + \en The process progress indicator. \~ + \return \ru Код завершения операции. + \en Code of the operation termination. \~ + \ingroup Exchange_Interface + */ + CONV_FUNC (MbeConvResType) ImportFromBuffer( MbModel & model, + const C3DExchangeBuffer& buffer, + MbeModelExchangeFormat modelFormat, + IConvertorProperty3D * prop = C3D_NULL_PTR, + IProgressIndicator * indicator = C3D_NULL_PTR ); + + + /** \brief \ru Импортировать данные из буфера в модель. + \en Import data from buffer into model. \~ + \param[out] item - \ru Замещаемый элемент. + \en The item to replace. \~ + \param[in] buffer - \ru Буфер. + \en Buffer. \~ + \param[in] modelFormat - \ru Формат модели. + \en Model format. \~ + \param[in] prop - \ru Реализация интерфейса свойств конвертера. + \en Implementation of converter's properties interface. \~ + \param[in] indicator - \ru Индикатор хода процесса. + \en The process progress indicator. \~ + \return \ru Код завершения операции. + \en Code of the operation termination. \~ + \ingroup Exchange_Interface + */ + CONV_FUNC(MbeConvResType) ImportFromBuffer( c3d::ItemSPtr& item, + const C3DExchangeBuffer& buffer, + MbeModelExchangeFormat modelFormat, + IConvertorProperty3D* prop = C3D_NULL_PTR, + IProgressIndicator* indicator = C3D_NULL_PTR ); + + + /** \brief \ru Экспортировать модель в буфер. + \en Export model into buffer. \~ + \param[in] model - \ru Модель. + \en The model. \~ + \param[in] modelFormat - \ru Формат модели. + \en Model format. \~ + \param[out] buffer - \ru Буфер. + \en Buffer. \~ + \param[in] prop - \ru Реализация интерфейса свойств конвертера. + \en Implementation of converter's properties interface. \~ + \param[in] indicator - \ru Индикатор хода процесса. + \en The process progress indicator. \~ + \return \ru Код завершения операции. + \en Code of the operation termination. \~ + \ingroup Exchange_Interface + */ + CONV_FUNC (MbeConvResType) ExportIntoBuffer( MbModel & model, + MbeModelExchangeFormat modelFormat, + C3DExchangeBuffer& buffer, + IConvertorProperty3D * prop = C3D_NULL_PTR, + IProgressIndicator * indicator = C3D_NULL_PTR ); + + + /** \brief \ru Экспортировать модель в буфер. + \en Export model into buffer. \~ + \param[in] item - \ru Экспортируемый элемент. + \en The item to export. \~ + \param[in] modelFormat - \ru Формат модели. + \en Model format. \~ + \param[out] buffer - \ru Буфер. + \en Buffer. \~ + \param[in] prop - \ru Реализация интерфейса свойств конвертера. + \en Implementation of converter's properties interface. \~ + \param[in] indicator - \ru Индикатор хода процесса. + \en The process progress indicator. \~ + \return \ru Код завершения операции. + \en Code of the operation termination. \~ + \ingroup Exchange_Interface + */ + CONV_FUNC(MbeConvResType) ExportIntoBuffer( MbItem& item, + MbeModelExchangeFormat modelFormat, + C3DExchangeBuffer& buffer, + IConvertorProperty3D* prop = C3D_NULL_PTR, + IProgressIndicator* indicator = C3D_NULL_PTR ); + + + //------------------------------------------------------------------------------ + /** \brief \ru Буфер для обмена. + \en Memory buffer for data exchange. \~ + \details \ru Обеспечивает обмен данными через оперативную память с контролем выделения и освобождения. + \en Prvides data exchange with memory allocation and deallocation control. \~ + \ingroup Exchange_Interface + */ + class C3DExchangeBuffer { + char* data; ///< \ru Адрес буфера. \en Buffer address. + size_t count; ///< \ru Число байт. \en Bytes count. + public: + + // \ru Конструктор. \en Constructor. + C3DExchangeBuffer() + : data( C3D_NULL_PTR ) + , count( 0 ) { + } + + // \ru Деструктор. \en Destructor. + ~C3DExchangeBuffer() { + Clear(); + } + + // \ru Очистить. \en Clear. + inline void Clear() { + delete[] data; + count = 0; + } + + // \ru Инициализировать буфер. \en Initialize buffer. + inline void Init( const char* init, size_t size ) { + Clear(); + data = new char[size]; + count = size; + ::memcpy( data, init, count ); + } + + // \ru Инициализировать буфер. \en Initialize buffer. + inline void Swap( char*& init, size_t& size ) { + std::swap( init, data ); + std::swap( size, count ); + } + + // \ru Получить адрес буфера. \en Get buffer address. + inline const char* Data() const { + return data; + } + + // \ru Получить число байт. \en Get count of bytes. + inline size_t Count() const { + return count; + } + }; +}; //------------------------------------------------------------------------------ @@ -500,6 +713,42 @@ public: */ virtual MbeConvResType ASCIIPointCloudWrite ( IConvertorProperty3D & prop, ItModelDocument & idoc, IProgressIndicator * indicator = 0, MbRefItem * qeuryStitch = 0 ) = 0; + + /** \brief \ru Загрузить плагин получения данных для построения модели. + \en Load plugin for getting information necessary to build model. \~ + \param[in] pluginName - \ru Имя подключаемого файла. + \en Name of the file to link. \~ + \return \ru Код завершения операции. + \en Code of the operation termination. \~ + \ingroup ASCII_Exchange + */ + virtual MbeConvResType LoadForeignReader( const c3d::path_string& pluginName ) = 0; + + + /** \brief \ru Отключить загруженный плагин получения данных для построения модели. + \en Release the loaded plugin for getting information necessary to build model. \~ + \return \ru Код завершения операции. + \en Code of the operation termination. \~ + \ingroup ASCII_Exchange + */ + virtual MbeConvResType ReleaseForeignReader() = 0; + + /** \brief \ru Прочитать файл с использованием плагина. + \en Read a file using plugin. \~ + \param[in] path - \ru ПУть к файлу, который нужно прочитать. + \en Path of the file to read. \~ + \param[in] idoc - \ru Реализация интерфейса документа. + \en Implementation of document interface. \~ + \param[in] prop - \ru Реализация интерфейса свойств конвертера. + \en Implementation of converter's properties interface. \~ + \param[in] indicator - \ru Индикатор хода процесса. + \en The process progress indicator. \~ + \return \ru Код завершения операции. + \en Code of the operation termination. \~ + \ingroup ASCII_Exchange + */ + virtual MbeConvResType ImportForeign( const c3d::path_string& path, ItModelDocument & idoc, IConvertorProperty3D * prop = 0, IProgressIndicator * indicator = 0 ) = 0; + }; // IConvertor3D @@ -511,6 +760,13 @@ public: CONV_FUNC (IConvertor3D *) GetConvertor3D(); +//------------------------------------------------------------------------------ +/** \brief \ru Освободить интерфейс конвертера. + \en Release the converter interface. \~ +\ingroup Exchange_Interface +*/ +CONV_FUNC( void ) ReleaseConvertor3D( IConvertor3D* ); + /** \brief \ru Прочитать файл формата SAT. \en Read a file of SAT format. \~ @@ -773,75 +1029,6 @@ CONV_FUNC (MbeConvResType ) ASCIIPointCloudWrite( IConvertorProperty3D & prop, I namespace c3d { - - /** \brief \ru Прочитать файл обменного формата в модель. - \en Read a file of an exchange format into model. \~ - \details \ru Если свойства конвертера заданы, аргумент fileName игнорируется, а имя файла берётся из свойств конвертера. - В противном случае импорт идёт с умолчательными параметрами, соответствующими реализации ConvConvertorProperty3D. \~ - \en The fileName argument is not used if converter properties are defined obviously, file path comes from the FullFilePath - method. Otherwise default parameters corresponding ConvConvertorProperty3D implementation are used for import. - \param[out] model - \ru Модель. - \en The model. \~ - \param[in] filePath - \ru Путь файла. - \en File path. \~ - \param[in] prop - \ru Реализация интерфейса свойств конвертера. - \en Implementation of converter's properties interface. \~ - \param[in] indicator - \ru Индикатор хода процесса. - \en The process progress indicator. \~ - \return \ru Код завершения операции. - \en Code of the operation termination. \~ - \ingroup Exchange_Interface - */ - CONV_FUNC (MbeConvResType) ImportFromFile( MbModel & model, - const path_string & fileName, - IConvertorProperty3D * prop = 0, - IProgressIndicator * indicator = 0 ); - - /** \brief \ru Прочитать файл обменного формата в модель. - \en Read a file of an exchange format into model. \~ - \details \ru Если свойства конвертера заданы, аргумент fileName игнорируется, а имя файла берётся из свойств конвертера. - В противном случае импорт идёт с умолчательными параметрами, соответствующими реализации ConvConvertorProperty3D. \~ - \en The fileName argument is not used if converter properties are defined obviously, file path comes from the FullFilePath - method. Otherwise default parameters corresponding ConvConvertorProperty3D implementation are used for import. - \param[out] mDoc - \ru Модельный документ. - \en The model. \~ - \param[in] filePath - \ru Путь файла. - \en File path. \~ - \param[in] prop - \ru Реализация интерфейса свойств конвертера. - \en Implementation of converter's properties interface. \~ - \param[in] indicator - \ru Индикатор хода процесса. - \en The process progress indicator. \~ - \return \ru Код завершения операции. - \en Code of the operation termination. \~ - \ingroup Exchange_Interface - */ - CONV_FUNC (MbeConvResType) ImportFromFile( ItModelDocument & mDoc, - const path_string & filePath, - IConvertorProperty3D * prop, - IProgressIndicator * indicator ); - - /** \brief \ru Записать модель в файл обменного формата. - \en Write the model into an exchange format file. \~ - \details \ru Если свойства конвертера заданы, аргумент fileName игнорируется, а имя файла берётся из свойств конвертера. - В противном случае экспорт идёт с умолчательными параметрами, соответствующими реализации ConvConvertorProperty3D. \~ - \en The fileName argument is not used if converter properties are defined obviously, file path comes from the FullFilePath - method. Otherwise default parameters corresponding ConvConvertorProperty3D implementation are used for export. - \param[out] model - \ru Модель. - \en The model. \~ - \param[in] filePath - \ru Путь файла. - \en File path. \~ - \param[in] prop - \ru Реализация интерфейса свойств конвертера. - \en Implementation of converter's properties interface. \~ - \param[in] indicator - \ru Индикатор хода процесса. - \en The process progress indicator. \~ - \return \ru Код завершения операции. - \en Code of the operation termination. \~ - \ingroup Exchange_Interface - */ - CONV_FUNC (MbeConvResType) ExportIntoFile( MbModel & model, - const path_string & filePath, - IConvertorProperty3D * prop = 0, - IProgressIndicator * indicator = 0 ); /** \brief \ru Импортировать данные из буфера в модель. \en Import data from buffer into model. \~ @@ -861,12 +1048,36 @@ namespace c3d { \en Code of the operation termination. \~ \ingroup Exchange_Interface */ - CONV_FUNC (MbeConvResType) ImportFromBuffer( MbModel & model, - const char * data, - size_t length, - MbeModelExchangeFormat modelFormat, - IConvertorProperty3D * prop = 0, - IProgressIndicator * indicator = 0 ); + DEPRECATE_DECLARE CONV_FUNC (MbeConvResType) ImportFromBuffer( MbModel & model, + const char* data, + size_t length, + MbeModelExchangeFormat modelFormat, + IConvertorProperty3D * prop = 0, + IProgressIndicator * indicator = 0 ); + + /** \brief \ru Импортировать данные из буфера в модель. + \en Import data from buffer into model. \~ + \param[out] item - \ru Замещаемый элемент. + \en The item to replace. \~ + \param[in] data - \ru Буфер. + \en Buffer. \~ + \param[in] length - \ru Размер буфера. + \en Buffer size. \~ + \param[in] modelFormat - \ru Формат модели. + \en Model format. \~ + \param[in] prop - \ru Реализация интерфейса свойств конвертера. + \en Implementation of converter's properties interface. \~ + \param[in] indicator - \ru Индикатор хода процесса. + \en The process progress indicator. \~ + \return \ru Код завершения операции. + \en Code of the operation termination. \~ + \ingroup Exchange_Interface + */ + DEPRECATE_DECLARE CONV_FUNC(MbeConvResType) ImportFromBuffer( c3d::ItemSPtr& item, + const char* data, + size_t length, + MbeModelExchangeFormat modelFormat, + IConvertorProperty3D* prop = NULL, IProgressIndicator* indicator = NULL); /** \brief \ru Экспортировать модель в буфер. \en Export model into buffer. \~ @@ -886,13 +1097,38 @@ namespace c3d { \en Code of the operation termination. \~ \ingroup Exchange_Interface */ - CONV_FUNC (MbeConvResType) ExportIntoBuffer( MbModel & model, - MbeModelExchangeFormat modelFormat, - char *& data, - size_t & length, - IConvertorProperty3D * prop = 0, - IProgressIndicator * indicator = 0 ); -}; + DEPRECATE_DECLARE CONV_FUNC (MbeConvResType) ExportIntoBuffer( MbModel & model, + MbeModelExchangeFormat modelFormat, + char*& data, + size_t& length, + IConvertorProperty3D * prop = 0, + IProgressIndicator * indicator = 0 ); + + + /** \brief \ru Экспортировать модель в буфер. + \en Export model into buffer. \~ + \param[in] item - \ru Экспортируемый элемент. + \en The item to export. \~ + \param[in] modelFormat - \ru Формат модели. + \en Model format. \~ + \param[out] data - \ru Буфер. + \en Buffer. \~ + \param[out] length - \ru Размер буфера. + \en Buffer size. \~ + \param[in] prop - \ru Реализация интерфейса свойств конвертера. + \en Implementation of converter's properties interface. \~ + \param[in] indicator - \ru Индикатор хода процесса. + \en The process progress indicator. \~ + \return \ru Код завершения операции. + \en Code of the operation termination. \~ + \ingroup Exchange_Interface + */ + DEPRECATE_DECLARE CONV_FUNC(MbeConvResType) ExportIntoBuffer( MbItem& item, MbeModelExchangeFormat modelFormat, + char*& data, + size_t& length, + IConvertorProperty3D* prop = NULL, IProgressIndicator* indicator = NULL); + +} /** \} */ diff --git a/C3d/Include/conv_model_properties.h b/C3d/Include/conv_model_properties.h deleted file mode 100644 index 39b2f00..0000000 --- a/C3d/Include/conv_model_properties.h +++ /dev/null @@ -1,712 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Интерфейсы, используемые при импорте и экспорте. - \en Interfaces used for import and export. \~ -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CONV_MODEL_PROPERTIES_H -#define __CONV_MODEL_PROPERTIES_H - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbPlacement3D; -class MATH_CLASS MbItem; -class MATH_CLASS MbName; -class ItModelAssembly; -class ItModelPart; - - -/** \brief \ru Контейнер объектов аннотации. - \en Container of annotation objects. \~ -\ingroup Exchange_Base -*/ -typedef std::vector vector_of_annotation; - - -/** \brief \ru Ассоциация наборов аннотационных объектов элементам со счётчиком ссылок. - \en Association of sets of annotation objects with elements with reference counter. \~ -\ingroup Exchange_Base -*/ -typedef std::map< SPtr, vector_of_annotation > map_of_visual_items; - - -/** \brief \ru Контейнер текстовых блоков. - \en Container of text blocks. \~ -\ingroup Exchange_Base -*/ -typedef std::vector< SPtr > vector_of_text; - - -//------------------------------------------------------------------------------ -/** \brief \ru Типы линий, передаваемых через конвертер. - \en Types of lines passed via converter. \~ -\ingroup Data_Interface -*/ -// --- -enum MbeLineFontPattern { - lfp_BEGIN = 0, ///< \ru Для удобства перебора. \en For the convenient search. - lfp_STEPcontinuous, ///< \ru Непрерывная в конвертерах STEP и IGES. \en Continuous line in STEP and IGES (Solid) converters. - lfp_STEPchain, ///< \ru Штрих-пунктирная в конвертерах STEP и IGES. \en Chain line( dash-dotted) in STEP and IGES converters. - lfp_STEPchainDoubleDash, ///< \ru Штриховая с двумя пунктирами в конвертерах STEP и IGES. \en Dash-double-dot line in STEP and IGES (Phantom) converter. - lfp_STEPdashed, ///< \ru Штриховая в конвертерах STEP и IGES. \en Dash line in STEP and IGES converters. - lfp_STEPdotted, ///< \ru Пунктирная в конвертерах STEP и IGES. \en Dotted line in STEP and IGES converters. - lfp_END ///< \ru Для удобства перебора. \en For search -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Отображение точек, передаваемых через конвертер. - \en Representation of points passed via converter. \~ -\ingroup Data_Interface -*/ -// --- -enum MbeDotMarkerSymbol { - dms_BEGIN = 0, ///< \ru Для удобства перебора. \en For the convenient search. - dms_STEPdot, ///< \ru Точка. \en A point. - dms_STEPx, ///< \ru Косой крест. \en x - cross. - dms_STEPplus, ///< \ru Прямой крест. \en Plus. - dms_STEPasterisk, ///< \ru Звёздочка. \en Asterisk. - dms_STEPring, ///< \ru Кольцо. \en Ring. - dms_STEPsquare, ///< \ru Квадрат. \en Square. - dms_STEPtriangle, ///< \ru Треугольник. \en Triangle. - dms_END ///< \ru Для удобства перебора. \en For the convenient search. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Тип объектов, которые необходимо выдать для экспорта или добавить при импорте. - \en Type of objects to be returned for export or to be added while importing. \~ -\ingroup Data_Interface -*/ -// --- -enum MbeGettingItemType { - git_Item = 0, ///< \ru Получить элементы всех типов. \en Get items of all types. - git_Solid, ///< \ru Получить тела. \en Get solids. - git_Surface, ///< \ru Получить поверхности. \en Get surfaces. - git_WireFrame, ///< \ru Получить проволочные каркасы. \en Get wire frames. - git_PlaneInstance, ///< \ru Получить вставки плоских объектов (эскизы). \en Get plane instances (drafts). - git_PointFrame, ///< \ru Получить точечные каркасы. \en Get point frames. - git_AssociatedGeometry ///< \ru Получить ассоциированные геометрические объекты (резьбы). \en Get associated geometry objects (threads). -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Интерфейс свойств вставки, подсборки или детали. - \en Interface of properties of an instance, a subassembly or a part. \~ -\ingroup Exchange_Interface -*/ -// --- -class ItModelInstanceProperties : public MbRefItem -{ -public: - - /// \ru Атрибуты. \en Attributes. - - /// \ru Задать атрибуты. \en Set attributes. - virtual bool SetAttributes( const c3d::AttrSPtrVector& /*attributes*/ ) = 0; - - /// \ru Получить атрибуты. \en Get attributes. - virtual c3d::AttrSPtrVector GetAttributes( ) const = 0;// { return c3d::AttrSPtrVector(); } - - - /// \ru Технические требования. \en Technical requirements. - - /// \ru Получить технические требования. \en Get technical requirements. - virtual void GetRequirements( vector_of_annotation &, eTextForm ) const = 0; - - /// \ru Задать технические требования. \en Set technical requirements. - virtual void SetRequirements( const vector_of_annotation & ) = 0; - - /// \ru Наименование. \en Name. - - /// \ru Задать имя документа. \en Set document's name. - DEPRECATE_DECLARE virtual bool SetName( const std::string& /*name*/ ) { return false; }; - /// \ru Получить имя документа. \en Get document's name. - DEPRECATE_DECLARE virtual std::string Name() const { return std::string(); }; - - /// \ru Обозначение. \en Marking. - - /// \ru Задать обозначение документа. \en Set document marking. - DEPRECATE_DECLARE virtual bool SetMarking( const std::string& /*name*/ ) { return false; }; - /// \ru Получить обозначение документа. \en Get document marking. - DEPRECATE_DECLARE virtual std::string Marking() const { return std::string(); }; - - /// \ru Автор. \en Author. - - /// \ru Задать имя автора. \en Set author's name. - DEPRECATE_DECLARE virtual bool SetAuthor( const std::string& /*name*/ ) { return false; }; - /// \ru Получить имя автора. \en Get author's name. - DEPRECATE_DECLARE virtual std::string Author() const { return std::string(); }; - - /// \ru Организация. \en Organization. - - /// \ru Задать имя автора. \en Set author's name. - DEPRECATE_DECLARE virtual bool SetOrganization( const std::string& /*name*/ ) { return false; }; - /// \ru Получить имя автора. \en Get author's name. - DEPRECATE_DECLARE virtual std::string Organization() const { return std::string(); }; - - /// \ru Комментарий. \en Comment. - - /// \ru Задать комментарии. \en Set the comments. - DEPRECATE_DECLARE virtual bool SetComments( const std::vector< std::string > & /*comments*/ ) { return false; }; - /// \ru Получить следующий комментарий. \en Get the next comment. - DEPRECATE_DECLARE virtual std::vector< std::string > GetComments( ) const { return std::vector< std::string >(); }; - - /// \ru Цвет сборки, детали или вставки. \en Color of an assembly, a part or an instance. - - /// \ru Задать цветовые свойства. \en Set color properties. - DEPRECATE_DECLARE virtual bool SetColor( const MbAttributeContainer & ) { return false; }; - /// \ru Получить цветовые свойства. \en Get color properties. - DEPRECATE_DECLARE virtual bool GetColor( MbAttributeContainer & ) const { return false; }; - - /// \ru Цвет тела. \en Solid color. - - /// \ru Задать цветовые свойства оболочки. \en Set color properties of a shell. - DEPRECATE_DECLARE virtual bool SetColor( const MbAttributeContainer &, size_t ) { return false; }; - - /// \ru Цвет грани. \en Face color. - - /// \ru Задать цветовые свойства грани \en Set color properties of a face. - DEPRECATE_DECLARE virtual bool SetColor( const MbAttributeContainer &, const MbName & ) { return false; }; - /// \ru Получить цветовые свойства грани. \en Get color properties of a face. - DEPRECATE_DECLARE virtual bool GetColor( MbAttributeContainer &, const MbName & ) const { return false; }; -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Интерфейс вставки компоненты. - \en Interface of the component instance. \~ -\ingroup Exchange_Interface -*/ -// --- -class ItModelInstance : public ItModelInstanceProperties -{ -public: - // \ru Выдать идентификатор сборки или детали \en Get identifier of an assembly or a part - virtual void * GetId() = 0; - /// \ru Выдать расположение этой вставки в координатах родителя. \en Get the placement of this instance in parent's coordinates. - virtual bool GetPlacement( MbPlacement3D & ) const = 0; - /// \ru Это сборка? \en Is it an assembly? - virtual bool IsAssembly() const = 0; - /// \ru Это ни сборка, ни деталь? \en Is it neither an assembly nor a part? - virtual bool IsEmpty() const = 0; - - /** \brief \ru Создать пустую сборку при импорте и увеличить счётчик ссылок на 1. - \en Create an empty assembly while importing and increase the reference counter by 1. \~ - \param[in] place - \ru ЛСК сборки в родительской модели. - \en LCS of the assembly in the parent's model. \~ - \param[in] fileName - \ru Имя сборки. - \en Assembly name. \~ - \return \ru Экземпляр сборки, если операция прошла успешно, NULL в противном случае. - \en Instance of an assembly if the operation succeeded, NULL - otherwise. \~ - */ - virtual SPtr CreateAssembly( const MbPlacement3D &place, const std::vector< SPtr > & componentItems, const c3d::string_t& fileName ) = 0; - - /** \brief \ru Создать деталь при импорте. - \en Create a part while importing. \~ - \details \ru Увеличить счётчик ссылок детали на 1. - \en Increase the reference counter of a part by 1. \~ - \param[in] place - \ru ЛСК детали. - \en LCS of a part. \~ - \param[in] solids - \ru Тела, включаемые в деталь. - \en Solids included in the part. \~ - \param[in] fileName - \ru Название детали. - \en Solid's name. \~ - \return \ru Экземпляр детали, если операция прошла успешно, NULL в противном случае. - \en Instance of the part if the operation succeeded, NULL - otherwise. \~ - */ - virtual SPtr CreatePart( const MbPlacement3D &place, const std::vector< SPtr > & componentItems, const c3d::string_t& fileName ) = 0; - - /** \brief \ru Получить сборку для экспорта. - \en Get an assembly for export. \~ - \return \ru Экземпляр сборки, если операция прошла успешно, NULL в противном случае. - \en Instance of an assembly if the operation succeeded, NULL - otherwise. \~ - */ - virtual SPtr GetInstanceAssembly( ) = 0; - - - /** \brief \ru Получить деталь для экспорта. - \en Get the detail for export. \~ - \return \ru Экземпляр детали, если операция прошла успешно, NULL в противном случае. - \en Instance of the part if the operation succeeded, NULL - otherwise. \~ - */ - virtual SPtr GetInstancePart( ) = 0; - - /** \brief \ru Создать подсборку при импорте, и её вставку. - \en Create a subassembly and its instance while importing. \~ - \param[in] place - \ru ЛСК сборки в родительской модели. - \en LCS of the assembly in the parent's model. \~ - \param[in] existing - \ru Сборка, подлежащая вставке. - \en An assembly to insert. \~ - \return \ru true, если операция прошла успешно, false в противном случае. - \en true if the operation succeeded, false - otherwise. \~ - */ - virtual bool SetAssembly( const MbPlacement3D & place, const ItModelAssembly * existing ) = 0; - - /** \brief \ru Создать деталь при импорте, и её вставку. - \en Create a part while importing and its instance. \~ - \param[in] place - \ru ЛСК детали в родительской модели. - \en LCS of a part in the parent's model. \~ - \param[in] existing - \ru Деталь, подлежащая вставке. - \en Detail to insert. \~ - \return \ru true, если операция прошла успешно, false в противном случае. - \en true if the operation succeeded, false - otherwise. \~ - */ - virtual bool SetPart( const MbPlacement3D & place, const ItModelPart * existing ) = 0; - -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Интерфейс сборки. - \en Interface of the assembly. \~ - \details \ru Экземпляр должен порождаться в методах CreateAssembly реализаций - интерфейсов ItModelDocument и ItModelAInstance. Собственные элементы детали - должны передаваться как параметры конструктора. \~ \en The object should be - created in the CreateAssembly method of the implementations of the - ItModelDocument and ItModelInstance interfaces. Own Items of the detail should - be arguments of the constructor. -\ingroup Exchange_Interface -*/ -// --- -class ItModelAssembly : public ItModelInstanceProperties -{ -public: - /** \brief \ru Получить имя файла сборки без пути и расширения для экспорта. - \en Get the file name of an assembly without the path and the extension for export. \~ - \return \ru Имя файла сборки. - \en An assembly file name. \~ - */ - virtual c3d::path_string PureFileName() const = 0; - - /** \brief \ru Получить пустой интерфейс вставки для создания подсборки или детали при импорте. - \en Get an empty interface of the insertion for creation of subassembly or a part while importing. \~ - \details \ru Увеличить счётчик ссылок на 1. - \en Increase the reference counter by 1. \~ - \return \ru Интерфейс вставки, если операция прошла успешно или NULL в противном случае. - \en Interface of the instance if the operation succeeded and NULL otherwise. \~ - */ - virtual SPtr PrepareInstance() = 0; - - /** \brief \ru Получить интерфейс следующей вставки для создания подсборки или детали при экспорте. - \en Get the interface of the next insertion for creation of a subassembly or a part while exporting. \~ - \return \ru Интерфейс вставки, если операция прошла успешно или NULL в противном случае. - \en Interface of the insertion if the operation succeeded and NULL otherwise. \~ - */ - virtual SPtr NextInstance( bool includeInvisible ) = 0; - - /// \ru Выдать ЛСК, общую для элементов компонента. \en Get the placement, which all the items of the component use for transformation. - virtual bool GetPlacement( MbPlacement3D & ) const { return false; }; - - /** \brief \ru Получить объекты из корня сборки при экспорте. - \en Get objects from the assembly root while exporting. \~ - \param[out] items - \ru Наполняемый массив (состоит из объектов классов MbSolid, MbCurve3D, MbCartPoint3D). - \en Array to fill (consist of objects of classes MbSolid, MbCurve3D, MbCartPoint3D). \~ - \param[in] includeInvisible - \ru Если true, то выдаются все тела, включая невидимые, если false - только видимые. - \en If true, then all the solids are returned, including invisible ones, if false - only visible ones. \~ - */ - virtual void GetItems( std::vector< SPtr > & items, MbeGettingItemType itemType, bool includeInvisible ) const = 0; - - /** \brief \ru Добавить объекты в корень сборки при импорте. - \en Add objects to the assembly root while importing. \~ - \param[in] items - \ru Объекты, добавляемые в модель (тела, кривые и точки). - \en Objects to add to the model (solids, curves and points). \~ - */ - virtual void AddItems( const std::vector< SPtr > & items ) = 0; - - /** \brief \ru Получить элементы аннотации из сборки. - \en Get elements of annotation from the assembly. \~ - \param[in] eTextForm - \ru Форма представления текста. - \en Text representation form. \~ - \param[in] includeInvisible - \ru Если true, то выдаются все объекты аннотации, включая невидимые, если false - только видимые. - \en If true, all the annotation objects are returned, including invisible ones, if false - only visible ones. \~ - \return \ru Контейнер объектов аннотации. - \en Vector of annotation objects. \~ - */ - virtual vector_of_annotation GetAnnotationItems( eTextForm, bool ) const { return vector_of_annotation(); }; // Реализация будет удалена после того, как она будет осуществлена на стороне 3D - virtual vector_of_annotation GetAnnotationItems( eTextForm ) const { return vector_of_annotation(); }; // Будет удалена после её реализации на стороне 3D - - /** \brief \ru Задать элементы аннотации в сборке. - \en Set elements of annotation in the assembly. \~ - \param[in] sourceDim - \ru Элементы аннотации - \en Elements of annotation. \~ - */ - virtual void SetAnnotationItems( const vector_of_annotation & ) = 0; - -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Интерфейс детали. - \en Interface of a part. \~ - \details \ru Экземпляр должен порождаться в методах CreatePart реализаций - интерфейсов ItModelDocument и ItModelAInstance. Собственные элементы детали - должны передаваться как параметры конструктора. \~ \en The object should be - created in the CreatePart method of the implementations of the - ItModelDocument and ItModelInstance interfaces. Own Items of the detail should - be arguments of the constructor. -\ingroup Exchange_Interface -*/ -// --- -class ItModelPart : public ItModelInstanceProperties -{ -public: - /** \brief \ru Получить имя файла детали без пути и расширения для экспорта. - \en Get the file name of a part without the path and extension for export. \~ - \return \ru Имя файла детали. - \en A part file name. \~ - */ - virtual c3d::path_string PureFileName() const = 0; - - /** \brief \ru Получить пустой интерфейс вставки для создания подсборки или детали при импорте. - \en Get an empty interface of the insertion for creation of subassembly or a part while importing. \~ - \details \ru Увеличить счётчик ссылок на 1. - \en Increase the reference counter by 1. \~ - \return \ru Интерфейс вставки, если операция прошла успешно или NULL в противном случае. - \en Interface of the instance if the operation succeeded and NULL otherwise. \~ - */ - virtual SPtr PrepareInstance() = 0; - - /** \brief \ru Получить интерфейс следующей вставки для создания подсборки или детали при экспорте. - \en Get the interface of the next insertion for creation of a subassembly or a part while exporting. \~ - \return \ru Интерфейс вставки, если операция прошла успешно или NULL в противном случае. - \en Interface of the insertion if the operation succeeded and NULL otherwise. \~ - */ - virtual SPtr NextInstance( bool includeInvisible ) = 0; - - /// \ru Выдать ЛСК, общую для элементов компонента. \en Get the placement, which all the items of the component use for transformation. - virtual bool GetPlacement( MbPlacement3D & ) const { return false; }; - - /** \brief \ru Получить объекты из детали при экспорте. - \en Get objects from the part while exporting. \~ - \param[out] items - \ru Наполняемый массив (состоит из объектов классов MbSolid, MbWireFrame, MbPointFrame). - \en Array to fill (consists of objects of classes MbSolid, MbWireFrame, MbPointFrame). \~ - \param[in] itemType - \ru Тип объектов, которыми нужно наполнить массив. - \en Type of objects the array should be filled with. \~ - \param[in] includeInvisible - \ru Если true, то выдаются все тела, включая невидимые, если false - только видимые. - \en If true, all the solids are returned, including invisible ones, if false - only visible ones. \~ - */ - virtual void GetItems( std::vector< SPtr > & items, MbeGettingItemType itemType, bool includeInvisible ) const = 0; - - /** \brief \ru Добавить объекты в деталь при импорте. - \en Add objects to a part while importing. \~ - \param[in] items - \ru Объекты, добавляемые в модель (кривые и точки). - \en Objects to be added to the model (curves and points). \~ - */ - virtual void AddItems( const std::vector< SPtr > & items ) = 0; - - /** \brief \ru Получить элементы аннотации из детали. - \en Get elements of annotation from the detail. \~ - \param[in] eTextForm - \ru Форма представления текста. - \en Text representation form. \~ - \param[in] includeInvisible - \ru Если true, то выдаются все объекты аннотации, включая невидимые, если false - только видимые. - \en If true, all the annotation objects are returned, including invisible ones, if false - only visible ones. \~ - \return \ru Контейнер объектов аннотации. - \en Vector of annotation objects. \~ - */ - virtual vector_of_annotation GetAnnotationItems( eTextForm, bool ) const { return vector_of_annotation(); }; // Реализация будет удалена после того, как она будет осуществлена на стороне 3D - virtual vector_of_annotation GetAnnotationItems( eTextForm ) const { return vector_of_annotation(); }; // Будет удалена после её реализации на стороне 3D - - - /** \brief \ru Задать элементы аннотации в детали. - \en Set elements of annotation in the part. \~ - \param[in] sourceDim - \ru Элементы аннотации - \en Elements of annotation. \~ - */ - virtual void SetAnnotationItems( const vector_of_annotation & ) = 0; - -}; - -//------------------------------------------------------------------------------ -/** \brief \ru Интерфейс документа модели сборки или детали. - \en Interface of document of an assembly model or a part model. \~ -\ingroup Exchange_Interface -*/ -// --- -class ItModelDocument : public MbRefItem -{ -public: - /// \ru Это сборка? \en Is it an assembly? - virtual bool IsAssembly() const = 0; - /// \ru Это ни сборка, ни деталь? \en Is it neither an assembly nor a part? - virtual bool IsEmpty() const = 0; - - /** \brief \ru Прообраз новой интерфейсной функции - задать модель ЛСК, относительно которой позиционируется модель. - \en Prototype of a new interface function - get the placement the model is defined in. \~ - */ - //virtual MbPlacement3D GetOriginLocation() const = 0; - - /** \brief \ru Прообраз новой интерфейсной функции - задать модель для наполнения. - \en Prototype of a new interface function - set a model to fill. \~ - */ - virtual void SetContent( MbItem* /*content*/) = 0; - - /** \brief \ru Прообраз новой интерфейсной функции - получить наполнение. - \en Prototype of a new interface function - get the filling. \~ - */ - virtual MbItem * GetContent() /*{ return NULL; }*/ = 0; - - /** \brief \ru Создать документ с новой сборкой при импорте. - \en Create a document with a new assembly while importing. \~ - \details \ru Увеличить счётчик ссылок результирующего документа на 1. - \en Increase the reference counter of the resultant document by 1. \~ - \param[in] fileName - \ru Имя сборки. - \en Assembly name. \~ - \param[in] solids - \ru Тела, добавляемые в сборку. - \en Solids to add into the assembly. \~ - \return \ru Экземпляр сборки, если операция прошла успешно, NULL в противном случае. - \en Instance of an assembly if the operation succeeded, NULL - otherwise. \~ - */ - virtual SPtr CreateAssembly( const std::vector< SPtr > & componentItems, const c3d::string_t& fileName ) = 0; - - - /** \brief \ru Создать документ с новой деталью при импорте. - \en Create a document with a new part while importing. \~ - \details \ru Увеличить счётчик ссылок результирующего документа на 1. - \en Increase the reference counter of the resultant document by 1. \~ - \param[in] solids - \ru Тела, добавляемые в деталь. - \en Solids to add into a part. \~ - \param[in] fileName - \ru Имя детали. - \en A part name. \~ - \return \ru Экземпляр детали, если операция прошла успешно, NULL в противном случае. - \en Instance of the part if the operation succeeded, NULL - otherwise. \~ - */ - virtual SPtr CreatePart( const std::vector< SPtr > & componentItems, const c3d::string_t& fileName ) = 0; - - /** \brief \ru Получить сборку для экспорта. - \en Get an assembly for export. \~ - \details \ru Увеличить счётчик ссылок результирующей сборки на 1. - \en Increase the reference counter of the resultant assembly by 1. \~ - \return \ru Экземпляр сборки, если операция прошла успешно, NULL в противном случае. - \en Instance of an assembly if the operation succeeded, NULL - otherwise. \~ - */ - virtual SPtr GetInstanceAssembly( ) = 0; - - - /** \brief \ru Получить деталь для экспорта. - \en Get the detail for export. \~ - \details \ru Увеличить счётчик ссылок результирующей детали на 1. - \en Increase the reference counter of the resultant part by 1. \~ - \return \ru Экземпляр детали, если операция прошла успешно, NULL в противном случае. - \en Instance of the part if the operation succeeded, NULL - otherwise. \~ - */ - virtual SPtr GetInstancePart( ) = 0; - - /** \brief \ru Завершить импорт и сохранить документ. - \en Complete the import and save the document. \~ - \return \ru true, если операция прошла успешно, false в противном случае. - \en true if the operation succeeded, false - otherwise. \~ - \param[in] \ru indicator Объект для отображения хода процесса. - \en indicator An object indicating a process progress. \~ - */ - virtual bool FinishImport( IProgressIndicator * indicator ) = 0; - - /** \brief \ru Получить элементы аннотации, соответствующие элементам геометрической модели. - \en Get elements of annotation, corresponding items of geometric model. \~ - \param[in] eTextForm - \ru Форма представления текста. - \en Text representation form. \~ - \return \ru Контейнер объектов аннотации. - \en Vector of annotation objects. \~ - */ - virtual map_of_visual_items GetAnnotationItems( eTextForm ) const = 0; - - /// \ru Задать размеры. \en Set sizes. - virtual void SetAnnotationItems( const map_of_visual_items& ) = 0; - - /// \ru Открыть документ. \en Open a document. - virtual void OpenDocument() = 0; - -}; - - - -//------------------------------------------------------------------------------ -/** \brief \ru Реализация документа модели, формирующая регулярную структуру. - \en Implementation of model document which has regular structure. \~ -\ingroup Exchange_Interface -*/ -// --- -class CONV_CLASS C3dModelDocument: public ItModelDocument { - - SPtr part; ///< \ru Представление в виде детали. \en Representation as detail. - SPtr assembly; ///< \ru Представление в виде сборки. \en Representation as assembly. - map_of_visual_items visualItems; ///< \ru Элементы аннотации. \en Annotation items. - c3d::ItemSPtr rawContent; -public: - - virtual ~C3dModelDocument(); ///< \ru Деструктор. \en Descructor. - - // Является ли сборкой. - virtual bool IsAssembly() const; - // Пуст ли. - virtual bool IsEmpty() const; - // Задать модель напрямую. - virtual void SetContent( MbItem* /*content*/); - // Выдать модель напрямую. - virtual MbItem * GetContent(); - // Создать сборку. - virtual SPtr CreateAssembly( const std::vector< SPtr > & componentItems, const c3d::string_t& fileName ); - // Создать деталь. - virtual SPtr CreatePart( const std::vector< SPtr > & componentItems, const c3d::string_t& fileName ); - // Выдать сборку. - virtual SPtr GetInstanceAssembly( ); - // Выдать деталь. - virtual SPtr GetInstancePart( ); - // Завершить импорт. - virtual bool FinishImport( IProgressIndicator * ); - // Выдать элементы аннотации. - virtual map_of_visual_items GetAnnotationItems( eTextForm ) const; - // Задать элементы аннотации. - virtual void SetAnnotationItems( const map_of_visual_items& vi ); - // Открыть документ. - virtual void OpenDocument(); - - /// \ru Зарегистрировать элемент аннотации. \en Register annotation object. - void RegisterAnnotation( c3d::ItemSPtr component, const vector_of_annotation& annotation, const vector_of_annotation& requirements ); -}; - - -typedef C3dModelDocument RegularModelDocument; -typedef C3dModelDocument ConvModelDocument; - - -//------------------------------------------------------------------------------ -/** \brief \ru Упрощенная реализация интерфейса свойств конвертера. - \en Simple implementation of converter's properties. \~ -\ingroup Exchange_Interface -*/ -class CONV_CLASS ConvConvertorProperty3D : public IConvertorProperty3D { -public: - std::string docName; ///< \ru Имя документа. \en Document name. - c3d::path_string fileName; ///< \ru Имя файла. \en File name. - bool fileASCII; ///< \ru Экспортировать ли в текстовый файл (если формат поддерживает двоичный). \en Export to text file (if format supports binary one). - long int formatVersion; /// \ru Версия формата при экспорте. \en The version of format for export. - bool exportIGESTopology; ///< \ru Экспортировать ли топологию в IGES. \en Export topology items into IGES. - std::vector ioPermissions; ///< \ru Фильтр объектов по типам. \en Type objects filter. - std::map propertyStrings; ///< \ru Особые значения сведений о документе. \en Specific values of documents properties. - eTextForm annotTextReprSTEP; ///< \ru Представление текста элементов аннотации. \en Text representation in annotation items. - MbPlacement3D originLocation; ///< \ru ЛСК документа. \en Own placement of the document. - bool replaceLocationsToRight; ///< \ru Следует ли принудительно преобразовывать ЛСК объектов к правым (для форматов, допускающих левые). \en Force replacement of locations to right ones. - bool enableAutostitch; ///< \ru Сшивать ли поверхности автоматически. \en Automatically stitch surfaces into shells. - double autostitchPrecision; ///< \ru Точность сшивки. \en Stitch precision. - bool showMessages; ///< \ru Отображать ли сообщения. \en Invoke messages show. - MbStepData tesseleationStepData; ///< \ru Параметры триангуляции при экспорте в STL и VRML. \en Tessellation parameters for export into STL and VRML. - MbStepData LOD0StepData; ///< \ru Параметры триангуляции при экспорте в JT. \en Tessellation parameters for export into JT. - bool dualSeams; ///< \ru Признак сдваивания швов при экспорте в STL и VRML. \en Make dual seams when export into STL and VRML. - bool joinSimilarFaces; ///< \ru Выполнять ли слияние подобных граней. \en Whether to join similar faces. - bool addRemovedFacesAsShells; ///< \ru Добавлять ли удаленные грани в качестве отдельных оболочек. \en Whether to add removed faces as shells. - double lengthUnitsFactor; ///< \ru Единицы длины модели. \en Length units of the model. - double appUnitsFactor; ///< \ru Единицы длины модели пользовательского приложения. \en Length units of the model used in user application. - bool auditEnabled; - - /// \ru Сведения о сообщениях конвертера. \en Converter message data. - struct LogRecord { - ptrdiff_t id; ///< \ru Идентификатор записи. \en Record id. - eMsgType msgType; ///< \ru Тип сообщения. \en Message type. - eMsgDetail msgText; ///< \ru Код сообщения. \en Message code. - }; - - std::vector< LogRecord > logRecords; ///< \ru Сообщения конвертера. \en Converter messages. - -public: - - ConvConvertorProperty3D(); ///< \ru Конструктор. \en Constructor. - - /// \ru Получить имя документа. \en Get document's name. - virtual const std::string GetDocumentName () const { return docName; }; - /// \ru Получить имя файла для конвертирования. \en Get file name for converting. - virtual const c3d::path_string FullFilePath () const { return fileName; }; - /// \ru Является ли файл текстовым. \en Whether the file is a text file. - virtual bool IsFileAscii () const; - /// \ru Получить версию формата при экспорте. \en Get the version of format for export. - virtual long int GetFormatVersion () const; - /// \ru Следует ли экспортировать только поверхности ( введено для работы конвертера IGES ). \en Whether to export only surfaces (introduced for work with converter IGES ). - virtual bool IsOutOnlySurfaces() const; - /// \ru Является ли экспортируемый документ сборкой. \en Whether the document for export is an assembly. - virtual bool IsAssembling () const { return true; }; - /// \ru Получить значение разрешения на импорт экспорт объектов определенного типа. \en Get the value of permission for import-export of objects of a certain type. - virtual bool GetIoPermission( MbeIOPermiss nPermission ) const; - /// \ru Получить значения разрешений на импорт экспорт объектов определенных типов. \en Get values of permission for import-export of objects of certain types. - virtual void GetIoPermissions( std::vector& ioPermissions ) const; - /// \ru Установить разрешение на импорт экспорт объектов определенного типа. \en Set permission for import-export of objects of a certain type. - virtual void SetIoPermission( MbeIOPermiss nPermission, bool isSet ); - /// \ru Получить значение специфичной строки для конвертера. \en Get the value of a certain string for the converter. - virtual bool GetPropertyString ( MbeConverterStrings nString, std::string & propertyString ) const; - /// \ru Установить значение специфичной строки для конвертера. \en Set the value of a certain string for the converter. - virtual void SetPropertyString ( MbeConverterStrings nString, const std::string & propertyString ); - /// \ru Представление текста в аннотационных объектах. \en Text representation in annotation objects. - virtual eTextForm GetAnnotationTextRepresentation () const; - /// \ru Следует ли компоненты экспортировать в разные файлы (если позволяет формат). \en Export components into separate files ( if provided in format). - virtual bool ExportComponentsSeparately() const; - /// \ru Получить ЛСК, относительно которой позиционирована модель. \en Get the location, the model is placed in. - virtual MbPlacement3D GetOriginLocation() const; - /// \ru Заменять ли принудительно СК компонент на правые. \en Replace components' placements to right-oriented. - virtual bool ReplaceLocationsToRight() const; - /** \brief \ru Сшивать ли поверхности автоматически. - \en If surfaces should be stitched automatically. \~ - \return \ru true - Сшивать поверхности автоматически, false - Спросить пользователя, сшивать ли поверхности. - \en true - Stitch surfaces automatically, false - Ask user first time. \~ - \param[out] stitchPrecision - \ru Точность сшивки. - \en Stitch precision. \~ - */ virtual bool EnableAutoStitch( double& /*stitchPrecision*/ ) const; - - /// \ru Получить множитель единиц длины по отношению к миллиметру. \en Get the factor of the length units to millimeters. - virtual double LengthUnitsFactor() const; - - /** \brief \ru Получить множитель единиц длины по отношению к миллиметру в модели приложения. - \en Get the factor of the length units to millimeters in the application model. \~ - */ - virtual double AppLengthUnitsFactor() const; - - /** \brief \ru Сделать запись в журнал конвертирования. - \en Make a record in the converter report. \~ - \param[in] id - \ru Идентификатор элемента внутри файла стороннего формата. - \en Identifier of an element inside the file of a foreign format. \~ - \param[in] msgType - \ru Тип сообщения. - \en Message type. \~ - \param[in] msgText - \ru Код сообщения. - \en Message code. \~ - */ - virtual void LogReport( ptrdiff_t id, eMsgType msgType, eMsgDetail msgText ); - -// /** \brief \ru Следует ли показывать сообщения и диалоги пользователю. \en Whether to show messages and dialog to the user. \~ -// \details \ru Обеспечивает работу через API. \en Provide possibility for work via API. \~ -// \return \ru true - обычная работа, false - через API. \en true - ordinary work, false - via API. \~ -// */ - virtual bool CanShowMessages() const; - - /// \ru Дать данные вычисления триангуляции (для конвертера STL и VRML). \en Get data for step calculation during triangulation (for STL, VRML only). - virtual MbStepData TesselationParameters() const; - /// \ru Дать данные вычисления триангуляции уровня детализации (для конвертера JT). \en Get data for step calculation during triangulation of LOD0 (for JTonly). - virtual MbStepData LOD0TesselationParameters() const; - /// \ru Получить флаг сохранения совпадающих точек швов. (для конвертера STL и VRML). \en Whether to keep coincident points of seams (for STL, VRML only). - virtual bool DualSeams() const; - /// \ru Задать флаг сохранения совпадающих точек швов. (для конвертера STL и VRML). \en Whether to keep coincident points of seams (for STL, VRML only). - virtual void DualSeams( bool ); - /// \ru Проводить ли аудит траснляции. \en Whether to audit the translation. - virtual bool TotalAudit(); - /// \ru Выполнять ли слияние подобных граней. \en Whether to join similar faces. - virtual bool JoinSimilarFaces() const { return joinSimilarFaces; } - /// \ru Добавлять ли удаленные грани в качестве оболочек. \en Whether to add removed faces as shells. - virtual bool AddRemovedFacesAsShells() const { return addRemovedFacesAsShells; } - - OBVIOUS_PRIVATE_COPY( ConvConvertorProperty3D ) - -}; // IConvertorProperty3D - - - -#endif // __CONV_MODEL_PROPERTIES_H diff --git a/C3d/Include/conv_predefined.h b/C3d/Include/conv_predefined.h new file mode 100644 index 0000000..bec7f66 --- /dev/null +++ b/C3d/Include/conv_predefined.h @@ -0,0 +1,151 @@ +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Перечисления, используемые при импорте и экспорте. + \en Enumerations for import/export operations.\~ + \details \ru Определены перечисления, определяющие результат конвертирования, + разрешение на чтение и запись различных объектов и передаваемых черезх конвертер строк. + \en Converting result, objects and properties filters, special strings + of enumerations are defined.\~ +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CONV_ERROR_RESULT_H +#define __CONV_ERROR_RESULT_H + + +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Представление текста при экспорте. +\en Representation of exported text.\~ +\ingroup Data_Exchange +*/ +// --- +enum eTextForm { + exf_TextOnly, ///< \ru Только текст. \en Text only. + exf_GeometryOnly, ///< \ru Только геометрия. \en Geometry only. +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Предопределённые ключи атрибутов для передачи контрольных параметров. +\en Predefined key of attributes used for validation properties' exchange.\~ +\ingroup Data_Interface +*/ +// --- +/// \ru Объём. \en Volume. +#define C3D_CAD_VALIDATION_PROPERTY_VOLUME c3d::c3dStr_ValidationPropertyVolumeExchange +/// \ru Площать поверхости. \en Surface area. +#define C3D_CAD_VALIDATION_PROPERTY_AREA c3d::c3dStr_ValidationPropertySurfaceAreaExchange +/// \ru Масса. \en Mass. +#define C3D_CAD_VALIDATION_PROPERTY_MASS c3d::c3dStr_ValidationPropertyMassExchange +/// \ru Идентификатор элемента. \en Item Identifier. +#define C3D_CAD_ITEM_IDENTIFIER c3d::c3dStr_ItemIdentifierExchange + + +//------------------------------------------------------------------------------ +/** \brief \ru Типы линий, передаваемых через конвертер. +\en Types of lines passed via converter. \~ +\ingroup Data_Interface +*/ +// --- +enum MbeLineFontPattern { + lfp_BEGIN = 0, ///< \ru Для удобства перебора. \en For the convenient search. + lfp_STEPcontinuous, ///< \ru Непрерывная в конвертерах STEP и IGES. \en Continuous line in STEP and IGES (Solid) converters. + lfp_STEPchain, ///< \ru Штрих-пунктирная в конвертерах STEP и IGES. \en Chain line( dash-dotted) in STEP and IGES converters. + lfp_STEPchainDoubleDash, ///< \ru Штриховая с двумя пунктирами в конвертерах STEP и IGES. \en Dash-double-dot line in STEP and IGES (Phantom) converter. + lfp_STEPdashed, ///< \ru Штриховая в конвертерах STEP и IGES. \en Dash line in STEP and IGES converters. + lfp_STEPdotted, ///< \ru Пунктирная в конвертерах STEP и IGES. \en Dotted line in STEP and IGES converters. + lfp_END ///< \ru Для удобства перебора. \en For search +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Отображение точек, передаваемых через конвертер. +\en Representation of points passed via converter. \~ +\ingroup Data_Interface +*/ +// --- +enum MbeDotMarkerSymbol { + dms_BEGIN = 0, ///< \ru Для удобства перебора. \en For the convenient search. + dms_STEPdot, ///< \ru Точка. \en A point. + dms_STEPx, ///< \ru Косой крест. \en x - cross. + dms_STEPplus, ///< \ru Прямой крест. \en Plus. + dms_STEPasterisk, ///< \ru Звёздочка. \en Asterisk. + dms_STEPring, ///< \ru Кольцо. \en Ring. + dms_STEPsquare, ///< \ru Квадрат. \en Square. + dms_STEPtriangle, ///< \ru Треугольник. \en Triangle. + dms_END ///< \ru Для удобства перебора. \en For the convenient search. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Ключи строк, соответствующих названию специальных атрибутов. + \en Keys of the strings, which mark special attributes.\~ +\ingroup Data_Interface +*/ +// --- +enum ePromtAttributeKey { + pac_GConverterInternalIsDummy, ///< \ru Является ли элемент пустышкой.\~ + pac_GeneralIsAssembly, ///< \ru Является ли элемент сборкой. \en Is item assembly.\~ + pac_GeneralFileName, ///< \ru Имя файла. \en File name.\~ + pac_STEPHeader, ///< \ru Заголовок STEP. \en STEP header.\~ + pac_STEPProduct, ///< \ru Изделие STEP. \en STEP product.\~ + pac_STEPPersonOrganization, ///< \ru Лицо и организация STEP. \en STEP person and organization.\~ + pac_STEPAssignedRole ///< \ru Назначенная роль STEP. \en The role, assigned to the person.\~ +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Идентификаторы сообщений индикатора прогресса выполнения конвертации данных. + \en Identifiers of the execution progress indicator messages converters data exchange \~ +\ingroup Data_Exchange +*/ +//--- +enum MbeProgBarId_Converters { + pbarId_Cnv_Beg = pbarId_PointsSurface_End + 1, + + pbarId_Cnv_Parse_Data, // \ru Синтаксический анализ... \en Syntactic analysis... + pbarId_Cnv_Create_Objects, // \ru Создание объектов... \en Creation of objects... + pbarId_Cnv_Process_Surfaces, // \ru Обработка поверхностей... \en Surfaces processing... + pbarId_Cnv_Process_Annotation,// \ru Обработка аннотации... \en Annotation processing... + pbarId_Cnv_Create_Model, // \ru Создание модели... \en Creation of model... + pbarId_Cnv_Write_Model, // \ru Запись модели... \en Writing of model... + + pbarId_Cnv_End, +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Идентификаторы сообщений индикатора прогресса выполнения триангуляции при выполнении конвертации данных. + \en Identifiers of the execution progress indicator messages triangulation. \~ +\ingroup Data_Exchange +*/ +//--- +enum MbeProgBarId_Triangulation { + pbarId_Triangulation_Beg = pbarId_Cnv_End + 1, + + pbarId_Calc_Triangulation, // \ru Расчет триангуляции \en Calculating of triangulation + + pbarId_Triangulation_End, +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Идентификаторы сообщений индикатора прогресса выполнения расчёта + масс-инерционные характеристики детали или сборки при выполнении конвертации данных. + \en Identifiers of the execution progress indicator messages of mass-inertial properties of assembly or a detail. \~ +\ingroup Data_Exchange +*/ +//--- +enum MbeProgBarId_MassInertiaProperties { + pbarId_MassInertiaProperties_Beg = pbarId_Triangulation_End + 1, + + pbarId_Calc_MassInertiaProperties, // \ru Расчет масс-инерционных характеристик \en Mass-inertial properties calculation + + pbarId_MassInertiaProperties_End, +}; + + +#endif // __CONV_ERROR_RESULT_H \ No newline at end of file diff --git a/C3d/Include/conv_topo_mesh.h b/C3d/Include/conv_topo_mesh.h new file mode 100644 index 0000000..d5e2ad9 --- /dev/null +++ b/C3d/Include/conv_topo_mesh.h @@ -0,0 +1,90 @@ +//////////////////////////////////////////////////////////////////////////////// +/** +\file +\brief Преобразователь сетки к форме, сохраняющей связи граней и полигонов. +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CONV_TOPO_MESH_H +#define __CONV_TOPO_MESH_H + +#include + +#include +#include + +#include +#include + +class MbMesh; + +namespace JTC { + + class TopoMesh; + class TopoGrid; + class TopoLoop; + class TopoVertex; + class MeshVertex; + class MeshPolygon; + + typedef SPtr TopoMeshPtr; + typedef SPtr TopoGridPtr; + typedef SPtr TopoLoopPtr; + typedef SPtr TopoVertexPtr; + typedef SPtr MeshVertexPtr; + typedef SPtr MeshPolygonPtr; + + typedef std::vector RawTopoGridVector; + typedef std::vector TopoGridVector; + typedef std::vector TopoLoopVector; + typedef std::vector TopoVertexVector; + typedef std::vector MeshVertexVector; + typedef std::vector MeshPolygonVector; + + + //------------------------------------------------------------------------------ + // Сетка с топологической информацией + // --- + class CONV_CLASS TopoMesh : public MbRefItem { + SPtr mesh; + TopoGridVector grids; + MeshVertexVector ownPoints; + MeshPolygonVector ownFacePolygons; + std::map< size_t, std::vector > degeneratedTriangles; + std::vector boundaryPoints; + double metricTolerance; + public: + TopoMesh(); // Конструктор + + virtual ~TopoMesh(); //Деструктор + + bool Init( const MbMesh& mesh, bool enableDiagnostics = false ); // Инициализировать + + const MbMesh* GetMesh() const; // Получить сетку + + size_t MeshPolygonsCount() const; // Число полигонов + + MeshPolygonPtr Polygon( size_t index ) const; // Получить полигон + + size_t MeshVerticisCount() const; // Число вершин + + MeshVertexPtr Vertex( size_t index ) const; // Получить вершину + + std::map< size_t, std::vector > GetDegeneratedTriangles() const; // Получить вырожденные треуголники + + std::vector GetBoundaryPoints() const; // Получить граничные точки сетки + + void Reset(); // Сбросить все данные + + size_t NextBoundaryVertex( size_t indexBoundaryVertex, const std::vector& allBoundary ) const; // Получить следующую в цепочке граничную вершину. + + bool InitVoidBoundFrom( std::vector& freeBoundaryVerticis ); // Сформировать внешнюю границу начиная с указанной вершины. + + double MetricTolerance() const; // Получить точность задания расстояния. + + OBVIOUS_PRIVATE_COPY( TopoMesh ) + }; + +}; + +#endif // !__CONV_TOPO_MESH_H diff --git a/C3d/Include/cr_attribute_provider.h b/C3d/Include/cr_attribute_provider.h index a242ca2..086e2e3 100644 --- a/C3d/Include/cr_attribute_provider.h +++ b/C3d/Include/cr_attribute_provider.h @@ -1,153 +1,153 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Поставщик атрибутов для топологических объектов. - \en Topological objects attributes provider. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_ATTRIBURE_PROVIDER_H -#define __CR_ATTRIBURE_PROVIDER_H - - -#include -#include -#include -#include - - -class MATH_CLASS MbNamedAttributeContainer; - - -//------------------------------------------------------------------------------ -/** \brief \ru Поставщик атрибутов для топологических объектов. - \en Topological objects attributes provider. \~ - \details \ru Поставщик атрибутов для топологических объектов создаёт атрибуты для журнала построений. \n - \en Topological objects attributes provider creates attributes for history tree. \n \~ - \ingroup Model_Creators - */ -class MATH_CLASS MbAttributeProvider : public MbCreator -{ -private: - struct NamedAttrCondDuplicator - { - public: - MbAttributeProvider & target_; - NamedAttrCondDuplicator( MbAttributeProvider & target ) : target_(target) {} - void operator () ( MbNamedAttributeContainer * source ); - private: - void operator = ( const NamedAttrCondDuplicator & ); - }; - - struct NamedAttrCondComparer - { - public: - MbName target_; - NamedAttrCondComparer( const MbName & target ) : target_(target) {} - bool operator () ( MbNamedAttributeContainer * source ); - private: - void operator = ( const NamedAttrCondComparer & ); - }; - - struct NamedAttrCondSetter - { - public: - MbFaceShell & target_; - NamedAttrCondSetter( MbFaceShell & target ) : target_(target) {} - void operator () ( MbNamedAttributeContainer * source ); - private: - void operator = ( const NamedAttrCondSetter & ); - }; - - typedef std::vector::iterator ContIter; - -private: - std::vector attrConts; // \ru Передаваемые атрибуты \en Attributes to pass - -public: - MbAttributeProvider( const MbSNameMaker & n ); - ~MbAttributeProvider(); - - virtual MbeCreatorType IsA() const; // \ru Выдать тип элемента. \en Get an element type. - virtual void Transform( const MbMatrix3D & matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. - virtual void Move( const MbVector3D & to, MbRegTransform * = NULL ); // \ru Сдвинуть вдоль вектора. \en Move along a vector. - virtual void Rotate( const MbAxis3D & axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. - virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & ) const; // \ru Определить, являются ли объекты подобными. \en Determine whether the objects are similar. - virtual bool SetEqual( const MbCreator & ); // \ru Сделать равным. \en Make equal. - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию. \en Create a copy. - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта. \en Set properties of the object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - - virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, - RPArray * items = NULL ); // \ru Построение \en Construction. - - // \ru Добавить отдельный атрибут (забрать во владение) \en Add a separate attribute. - void AddAttribute( const MbName & name, MbAttribute * attr ); - // \ru Добавить контейнер атрибутов (забрать во владение) \en Add an attribute container. - void AddNamedCont( MbNamedAttributeContainer * attr ); - // \ru Добавить контейнер атрибутов (сделать себе копию) \en Add an attribute container (make a copy). - void AddNamedCont( MbNamedAttributeContainer & attr ); - -protected: - MbAttributeProvider( const MbAttributeProvider & ); - void operator = ( const MbAttributeProvider & ); // \ru Не реализовано \en Not implemented - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbAttributeProvider ) -}; - -IMPL_PERSISTENT_OPS( MbAttributeProvider ) - - -//------------------------------------------------------------------------------ -/** \brief \ru Контейнер атрибутов. - \en Attribute container. \~ - \details \ru Контейнер атрибутов для одного топологического объекта. \n - \en An attribute container for a topological object. \n \~ - \ingroup Model_Attributes - */ -class MATH_CLASS MbNamedAttributeContainer -{ - typedef c3d::AttrVector::iterator AttrIter; - -private: - MbName target; // \ru Имя топологического объекта, которому будут отданы хранимые атрибуты. \en Name of the topological object the stored attributes will be passed to. - c3d::AttrVector attributes; // \ru Передаваемые атрибуты. \en Attributes to pass. - -public: - MbNamedAttributeContainer( const MbName & ); - virtual ~MbNamedAttributeContainer(); - -public: - /// \ru Записать полученные атрибуты. \en Save the received attributes. - void ReceiveAttributes ( c3d::AttrVector & attrs ); - /// \ru Скопировать атрибуты. \en Copy attributes. - void DuplicateAttributes( c3d::AttrVector & attrs, MbRegDuplicate * iReg = NULL ) const; - /// \ru Дать количество атрибутов. \en Get the attributes count. - size_t AttributesCount() const { return attributes.size(); } - /// \ru Добавить атрибут. \en Add an attribute. - void AddAttribute( MbAttribute & ) ; - const MbAttribute * _GetAttribute( size_t k ) const { return attributes[k]; } - -public: - /// \ru Дать имя топологического объекта. \en Get the topological object name. - const MbName & GetName() const { return target; } - - /// \ru Читать из потока. \en Read from stream. - void ReadAttrCont ( reader & ); - /// \ru Записать в поток. \en Write to stream. - void WriteAttrCont( writer & ) const; - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. - -protected: - MbNamedAttributeContainer( const MbNamedAttributeContainer & ); - void operator = ( const MbNamedAttributeContainer & ); // \ru Не реализовано \en Not implemented -}; - - -#endif // __CR_ATTRIBURE_PROVIDER_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Поставщик атрибутов для топологических объектов. + \en Topological objects attributes provider. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_ATTRIBURE_PROVIDER_H +#define __CR_ATTRIBURE_PROVIDER_H + + +#include +#include +#include +#include + + +class MATH_CLASS MbNamedAttributeContainer; + + +//------------------------------------------------------------------------------ +/** \brief \ru Поставщик атрибутов для топологических объектов. + \en Topological objects attributes provider. \~ + \details \ru Поставщик атрибутов для топологических объектов создаёт атрибуты для журнала построений. \n + \en Topological objects attributes provider creates attributes for history tree. \n \~ + \ingroup Model_Creators + */ +class MATH_CLASS MbAttributeProvider : public MbCreator +{ +private: + struct NamedAttrCondDuplicator + { + public: + MbAttributeProvider & target_; + NamedAttrCondDuplicator( MbAttributeProvider & target ) : target_(target) {} + void operator () ( MbNamedAttributeContainer * source ); + private: + void operator = ( const NamedAttrCondDuplicator & ); + }; + + struct NamedAttrCondComparer + { + public: + MbName target_; + NamedAttrCondComparer( const MbName & target ) : target_(target) {} + bool operator () ( MbNamedAttributeContainer * source ); + private: + void operator = ( const NamedAttrCondComparer & ); + }; + + struct NamedAttrCondSetter + { + public: + MbFaceShell & target_; + NamedAttrCondSetter( MbFaceShell & target ) : target_(target) {} + void operator () ( MbNamedAttributeContainer * source ); + private: + void operator = ( const NamedAttrCondSetter & ); + }; + + typedef std::vector::iterator ContIter; + +private: + std::vector attrConts; // \ru Передаваемые атрибуты \en Attributes to pass + +public: + MbAttributeProvider( const MbSNameMaker & n ); + ~MbAttributeProvider(); + + virtual MbeCreatorType IsA() const; // \ru Выдать тип элемента. \en Get an element type. + virtual void Transform( const MbMatrix3D & matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. + virtual void Move( const MbVector3D & to, MbRegTransform * = NULL ); // \ru Сдвинуть вдоль вектора. \en Move along a vector. + virtual void Rotate( const MbAxis3D & axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. + virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & ) const; // \ru Определить, являются ли объекты подобными. \en Determine whether the objects are similar. + virtual bool SetEqual( const MbCreator & ); // \ru Сделать равным. \en Make equal. + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию. \en Create a copy. + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта. \en Set properties of the object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + + virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, + RPArray * items = NULL ); // \ru Построение \en Construction. + + // \ru Добавить отдельный атрибут (забрать во владение) \en Add a separate attribute. + void AddAttribute( const MbName & name, MbAttribute * attr ); + // \ru Добавить контейнер атрибутов (забрать во владение) \en Add an attribute container. + void AddNamedCont( MbNamedAttributeContainer * attr ); + // \ru Добавить контейнер атрибутов (сделать себе копию) \en Add an attribute container (make a copy). + void AddNamedCont( MbNamedAttributeContainer & attr ); + +protected: + MbAttributeProvider( const MbAttributeProvider & ); + void operator = ( const MbAttributeProvider & ); // \ru Не реализовано \en Not implemented + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbAttributeProvider ) +}; + +IMPL_PERSISTENT_OPS( MbAttributeProvider ) + + +//------------------------------------------------------------------------------ +/** \brief \ru Контейнер атрибутов. + \en Attribute container. \~ + \details \ru Контейнер атрибутов для одного топологического объекта. \n + \en An attribute container for a topological object. \n \~ + \ingroup Model_Attributes + */ +class MATH_CLASS MbNamedAttributeContainer +{ + typedef c3d::AttrVector::iterator AttrIter; + +private: + MbName target; // \ru Имя топологического объекта, которому будут отданы хранимые атрибуты. \en Name of the topological object the stored attributes will be passed to. + c3d::AttrVector attributes; // \ru Передаваемые атрибуты. \en Attributes to pass. + +public: + MbNamedAttributeContainer( const MbName & ); + virtual ~MbNamedAttributeContainer(); + +public: + /// \ru Записать полученные атрибуты. \en Save the received attributes. + void ReceiveAttributes ( c3d::AttrVector & attrs ); + /// \ru Скопировать атрибуты. \en Copy attributes. + void DuplicateAttributes( c3d::AttrVector & attrs, MbRegDuplicate * iReg = NULL ) const; + /// \ru Дать количество атрибутов. \en Get the attributes count. + size_t AttributesCount() const { return attributes.size(); } + /// \ru Добавить атрибут. \en Add an attribute. + void AddAttribute( MbAttribute & ) ; + const MbAttribute * _GetAttribute( size_t k ) const { return attributes[k]; } + +public: + /// \ru Дать имя топологического объекта. \en Get the topological object name. + const MbName & GetName() const { return target; } + + /// \ru Читать из потока. \en Read from stream. + void ReadAttrCont ( reader & ); + /// \ru Записать в поток. \en Write to stream. + void WriteAttrCont( writer & ) const; + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. + +protected: + MbNamedAttributeContainer( const MbNamedAttributeContainer & ); + void operator = ( const MbNamedAttributeContainer & ); // \ru Не реализовано \en Not implemented +}; + + +#endif // __CR_ATTRIBURE_PROVIDER_H diff --git a/C3d/Include/cr_connecting_curve.h b/C3d/Include/cr_connecting_curve.h index 58e90f9..f9c196b 100644 --- a/C3d/Include/cr_connecting_curve.h +++ b/C3d/Include/cr_connecting_curve.h @@ -1,182 +1,196 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Строитель кривой сопряжения двух кривых. - \en Constructor of curve connecting two curves. -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_CONNECTING_CURVE_H -#define __CR_CONNECTING_CURVE_H - - -#include - - -class MATH_CLASS MbCartPoint; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbSurface; -class MATH_CLASS MbElementarySurface; -class MATH_CLASS MbEdge; - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель кривой сопряжения двух кривых. - \en Constructor of curve connecting two curves. \~ - \details \ru Строитель кривой сопряжения двух кривых.\n - \en Constructor of curve connecting two curves.\n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbConnectingCurveCreator : public MbCreator { -private: - MbCurve3D * curve1; ///< \ru Первая скругляемая кривая \en The first curve to connect - MbCurve3D * curve2; ///< \ru Вторая скругляемая кривая \en The second curve to connect - double init1; ///< \ru Исходное приближение параметра первой скругляемой кривой (для ft_Fillet и ft_OnSurface) \en The initial approximation of parameter of the first curve to be connected (for ft_Fillet and ft_OnSurface) - double init2; ///< \ru Исходное приближение параметра второй скругляемой кривой (для ft_Fillet и ft_OnSurface) \en The initial approximation of parameter of the second curve to be connected (for ft_Fillet and ft_OnSurface) - double param1; ///< \ru Параметр точки стыковки первой скругляемой кривой (кроме ft_Double) \en Connection point parameter of the first curve (except ft_Double) - double param2; ///< \ru Параметр точки стыковки второй скругляемой кривой (кроме ft_Double) \en Connection point parameter of the second curve (except ft_Double) - double radius1; ///< \ru Исходное приближение радиуса (кроме ft_Bridge, для ft_Double - радиус скругления первого участка, для ft_Spline - tension) \en The initial approximation of radius (except ft_Bridge, for ft_Double - the first segment fillet radius, for ft_Spline - tension) - double radius2; ///< \ru Результат расчета радиуса (кроме ft_Bridge, для ft_Double - радиус скругления второго участка, для ft_Spline - tension) \en The radius calculation result (except ft_Bridge, for ft_Double - the second segment fillet radius, for ft_Spline - tension) - bool sense1; ///< \ru Совпадение направления кривой скругления и первой кривой (кроме ft_Spline, для ft_Double - начало/конец кривой) \en Coincidence of the connecting curve direction and the first curve (except ft_Spline, for ft_Double - start/end point of the curve) - bool sense2; ///< \ru Совпадение направления кривой скругления и второй кривой (кроме ft_Spline, для ft_Double - начало/конец кривой) \en Coincidence of the connecting curve direction and the second curve (except ft_Spline, for ft_Double - start/end point of the curve) - MbeMatingType mating1; ///< \ru Тип сопряжения с первой кривой (для ft_Spline) \en Type of mating with the first curve (for ft_Spline) - MbeMatingType mating2; ///< \ru Тип сопряжения со второй кривой (для ft_Spline) \en Type of mating with the second curve (for ft_Spline) - MbeConnectingType type; ///< \ru Тип скругления (обычное или на поверхности) \en Connection type (ordinary or on a surface) - -protected: - MbConnectingCurveCreator( const MbConnectingCurveCreator & , MbRegDuplicate * iReg ); // \ru Конструктор копирования \en Copy-constructor - MbConnectingCurveCreator( const MbConnectingCurveCreator & ); // \ru Не реализовано \en Not implemented - MbConnectingCurveCreator(); // \ru Не реализовано \en Not implemented - -public: - MbConnectingCurveCreator( const MbSNameMaker & n, - const MbCurve3D & c1, double t1, double p1, double r1, bool s1, MbeMatingType m1, - const MbCurve3D & c2, double t2, double p2, double r2, bool s2, MbeMatingType m2, MbeConnectingType t ); - -public : - virtual ~MbConnectingCurveCreator(); - - // \ru Общие функции строителя \en The common functions of the creator - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element - virtual MbCreator & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Сделать копию \en Create a copy - - virtual bool IsSame ( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal - - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - - virtual MbePrompt GetPropertyName(); // \ru Дать имя свойства объекта \en Get the object property name - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - // \ru Построить кривую по журналу построения \en Create a curve from the history tree - virtual bool CreateSpaceCurve( MbWireFrame *&, MbeCopyMode, RPArray * items = NULL ); - - /** \} */ - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. - void operator = ( const MbConnectingCurveCreator & ); - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbConnectingCurveCreator ) -}; - -IMPL_PERSISTENT_OPS( MbConnectingCurveCreator ) - -//------------------------------------------------------------------------------ -/** \brief \ru Создание строителя скругления двух кривых. - \en Create two curves fillet constructor. \~ - \details \ru Создание строителя скругления двух кривых.\n - \en Create two curves fillet constructor.\n \~ - \param[in] curve1 - \ru Кривая 1. - \en Curve 1. \~ - \param[in] curve2 - \ru Кривая 2. - \en Curve 2. \~ - \result \ru Возвращает строитель. - \en Returns the constructor. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbCreator *) CreateFilletEdge( const MbCurve3D & curve1, double & t1, - const MbCurve3D & curve2, double & t2, - double & radius, bool sense, - MbeConnectingType type, - const MbSNameMaker & names, - MbResultType & res, - bool & unchanged, // \ru Для ft_Fillet и ft_OnSurface \en For ft_Fillet and ft_OnSurface - MbElementarySurface *& surface, // \ru Для ft_Fillet и ft_OnSurface \en For ft_Fillet and ft_OnSurface - MbEdge *& edge ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создание строителя сопряжения двух кривых сплайном. - \en Create constructor of two curves connection by a spline. \~ - \details \ru Создание строителя сопряжения двух кривых сплайном.\n - \en Create constructor of two curves connection by a spline.\n \~ - \param[in] curve1 - \ru Кривая 1. - \en Curve 1. \~ - \param[in] curve2 - \ru Кривая 2. - \en Curve 2. \~ - \result \ru Возвращает строитель. - \en Returns the constructor. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbCreator *) CreateSplineEdge( const MbCurve3D & curve1, double t1, MbeMatingType mating1, - const MbCurve3D & curve2, double t2, MbeMatingType mating2, - double tension1, double tension2, - const MbSNameMaker & names, - MbResultType & res, - MbEdge *& edge ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создание строителя сопряжения концов двух кривых составной кривой плавного соединения. - \en Create a constructor of conjugation of two curves end points by a composite curve of smooth connection. \~ - \details \ru Создание строителя сопряжения концов двух кривых составной кривой плавного соединения.\n - \en Create a constructor of conjugation of two curves end points by a composite curve of smooth connection.\n \~ - \param[in] curve1 - \ru Кривая 1. - \en Curve 1. \~ - \param[in] curve2 - \ru Кривая 2. - \en Curve 2. \~ - \result \ru Возвращает строитель. - \en Returns the constructor. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbCreator *) CreateConnectingEdge( const MbCurve3D & curve1, bool isBegin1, double radius1, - const MbCurve3D & curve2, bool isBegin2, double radius2, - const MbSNameMaker & names, - MbResultType & res, - MbEdge *& edge ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Cоздание строителя сопряжения двух кривых кубическим сплайном Эрмита (кривой-мостиком). - \en Create a constructor of two curves conjugation by a cubic Hermite spline (transition curve). \~ - \details \ru Cоздание строителя сопряжения двух кривых кубическим сплайном Эрмита (кривой-мостиком).\n - \en Create a constructor of two curves conjugation by a cubic Hermite spline (transition curve).\n \~ - \param[in] curve1 - \ru Кривая 1. - \en Curve 1. \~ - \param[in] curve2 - \ru Кривая 2. - \en Curve 2. \~ - \result \ru Возвращает строитель. - \en Returns the constructor. \~ - \ingroup Curve3D_Modeling -*/ -// --- -MATH_FUNC (MbCreator *) CreateBridgeEdge( const MbCurve3D & curve1, double t1, bool sense1, - const MbCurve3D & curve2, double t2, bool sense2, - const MbSNameMaker & names, - MbResultType & res, - MbEdge *& edge ); - - +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Строитель кривой сопряжения двух кривых. + \en Constructor of curve connecting two curves. +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_CONNECTING_CURVE_H +#define __CR_CONNECTING_CURVE_H + + +#include + + +class MATH_CLASS MbCartPoint; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbSurface; +class MATH_CLASS MbElementarySurface; +class MATH_CLASS MbEdge; + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель кривой сопряжения двух кривых. + \en Constructor of curve connecting two curves. \~ + \details \ru Строитель кривой сопряжения двух кривых.\n + \en Constructor of curve connecting two curves.\n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbConnectingCurveCreator : public MbCreator { +private: + MbCurve3D * curve1; ///< \ru Первая скругляемая кривая \en The first curve to connect + MbCurve3D * curve2; ///< \ru Вторая скругляемая кривая \en The second curve to connect + double init1; ///< \ru Исходное приближение параметра первой скругляемой кривой (для ft_Fillet и ft_OnSurface) \en The initial approximation of parameter of the first curve to be connected (for ft_Fillet and ft_OnSurface) + double init2; ///< \ru Исходное приближение параметра второй скругляемой кривой (для ft_Fillet и ft_OnSurface) \en The initial approximation of parameter of the second curve to be connected (for ft_Fillet and ft_OnSurface) + double param1; ///< \ru Параметр точки стыковки первой скругляемой кривой (кроме ft_Double) \en Connection point parameter of the first curve (except ft_Double) + double param2; ///< \ru Параметр точки стыковки второй скругляемой кривой (кроме ft_Double) \en Connection point parameter of the second curve (except ft_Double) + double radius1; ///< \ru Исходное приближение радиуса (кроме ft_Bridge, для ft_Double - радиус скругления первого участка, для ft_Spline - tension) \en The initial approximation of radius (except ft_Bridge, for ft_Double - the first segment fillet radius, for ft_Spline - tension) + double radius2; ///< \ru Результат расчета радиуса (кроме ft_Bridge, для ft_Double - радиус скругления второго участка, для ft_Spline - tension) \en The radius calculation result (except ft_Bridge, for ft_Double - the second segment fillet radius, for ft_Spline - tension) + bool sense1; ///< \ru Совпадение направления кривой скругления и первой кривой (кроме ft_Spline, для ft_Double - начало/конец кривой) \en Coincidence of the connecting curve direction and the first curve (except ft_Spline, for ft_Double - start/end point of the curve) + bool sense2; ///< \ru Совпадение направления кривой скругления и второй кривой (кроме ft_Spline, для ft_Double - начало/конец кривой) \en Coincidence of the connecting curve direction and the second curve (except ft_Spline, for ft_Double - start/end point of the curve) + MbeMatingType mating1; ///< \ru Тип сопряжения с первой кривой (для ft_Spline) \en Type of mating with the first curve (for ft_Spline) + MbeMatingType mating2; ///< \ru Тип сопряжения со второй кривой (для ft_Spline) \en Type of mating with the second curve (for ft_Spline) + MbeConnectingType type; ///< \ru Тип скругления (обычное или на поверхности) \en Connection type (ordinary or on a surface) + +protected: + MbConnectingCurveCreator( const MbConnectingCurveCreator & , MbRegDuplicate * iReg ); // \ru Конструктор копирования \en Copy-constructor + MbConnectingCurveCreator( const MbConnectingCurveCreator & ); // \ru Не реализовано \en Not implemented + MbConnectingCurveCreator(); // \ru Не реализовано \en Not implemented + +public: + MbConnectingCurveCreator( const MbSNameMaker & n, + const MbCurve3D & c1, double t1, double p1, double r1, bool s1, MbeMatingType m1, + const MbCurve3D & c2, double t2, double p2, double r2, bool s2, MbeMatingType m2, MbeConnectingType t ); + +public : + virtual ~MbConnectingCurveCreator(); + + // \ru Общие функции строителя \en The common functions of the creator + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element + virtual MbCreator & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Сделать копию \en Create a copy + + virtual bool IsSame ( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + + virtual MbePrompt GetPropertyName(); // \ru Дать имя свойства объекта \en Get the object property name + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + // \ru Построить кривую по журналу построения \en Create a curve from the history tree + virtual bool CreateSpaceCurve( MbWireFrame *&, MbeCopyMode, RPArray * items = NULL ); + + /** \} */ + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. + void operator = ( const MbConnectingCurveCreator & ); + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbConnectingCurveCreator ) +}; + +IMPL_PERSISTENT_OPS( MbConnectingCurveCreator ) + +//------------------------------------------------------------------------------ +/** \brief \ru Создание строителя скругления двух кривых. + \en Create two curves fillet constructor. \~ + \details \ru Создание строителя скругления двух кривых.\n + \en Create two curves fillet constructor.\n \~ + \param[in] curve1 - \ru Кривая 1. + \en Curve 1. \~ + \param[in/out] t1 - \ru Параметр точки на кривой 1 соединения с кривой соединения. + \en A point parameter on curve 1 of connection with fillet curve. \~ + \param[in] curve2 - \ru Кривая 2. + \en Curve 2. \~ + \param[in/out] t2 - \ru Параметр точки на кривой 2 соединения с кривой соединения. + \en A point parameter on curve 2 of connection with fillet curve. \~ + \param[in/out] radius - \ru Радиус дуги или цилиндра. + \en The radius of an arc or a cylinder. \~ + \param[in] type - \ru Тип скругления. + \en The fillet type. \~ + \param[in] names - \ru Именователь построенного ребра. + \en An object defining the edges names. \~ + \param[out] res - \ru Код результата операции. + \en Operation result code. \~ + \param[out] surface - \ru Поверхность, которая будет создана и на которой базируется соединительная кривая, (может быть возращён NULL). + \en A surface on which the fillet curve is based on, it will be created by the method (can be NULL). \~ + \result \ru Возвращает строитель. + \en Returns the constructor. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbCreator *) CreateFilletEdge( const MbCurve3D & curve1, double & t1, + const MbCurve3D & curve2, double & t2, + double & radius, bool sense, + MbeConnectingType type, + const MbSNameMaker & names, + MbResultType & res, + bool & unchanged, // \ru Для ft_Fillet и ft_OnSurface \en For ft_Fillet and ft_OnSurface + MbElementarySurface *& surface, // \ru Для ft_Fillet и ft_OnSurface \en For ft_Fillet and ft_OnSurface + MbEdge *& edge ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создание строителя сопряжения двух кривых сплайном. + \en Create constructor of two curves connection by a spline. \~ + \details \ru Создание строителя сопряжения двух кривых сплайном.\n + \en Create constructor of two curves connection by a spline.\n \~ + \param[in] curve1 - \ru Кривая 1. + \en Curve 1. \~ + \param[in] curve2 - \ru Кривая 2. + \en Curve 2. \~ + \result \ru Возвращает строитель. + \en Returns the constructor. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbCreator *) CreateSplineEdge( const MbCurve3D & curve1, double t1, MbeMatingType mating1, + const MbCurve3D & curve2, double t2, MbeMatingType mating2, + double tension1, double tension2, + const MbSNameMaker & names, + MbResultType & res, + MbEdge *& edge ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создание строителя сопряжения концов двух кривых составной кривой плавного соединения. + \en Create a constructor of conjugation of two curves end points by a composite curve of smooth connection. \~ + \details \ru Создание строителя сопряжения концов двух кривых составной кривой плавного соединения.\n + \en Create a constructor of conjugation of two curves end points by a composite curve of smooth connection.\n \~ + \param[in] curve1 - \ru Кривая 1. + \en Curve 1. \~ + \param[in] curve2 - \ru Кривая 2. + \en Curve 2. \~ + \result \ru Возвращает строитель. + \en Returns the constructor. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbCreator *) CreateConnectingEdge( const MbCurve3D & curve1, bool isBegin1, double radius1, + const MbCurve3D & curve2, bool isBegin2, double radius2, + const MbSNameMaker & names, + MbResultType & res, + MbEdge *& edge ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Cоздание строителя сопряжения двух кривых кубическим сплайном Эрмита (кривой-мостиком). + \en Create a constructor of two curves conjugation by a cubic Hermite spline (transition curve). \~ + \details \ru Cоздание строителя сопряжения двух кривых кубическим сплайном Эрмита (кривой-мостиком).\n + \en Create a constructor of two curves conjugation by a cubic Hermite spline (transition curve).\n \~ + \param[in] curve1 - \ru Кривая 1. + \en Curve 1. \~ + \param[in] curve2 - \ru Кривая 2. + \en Curve 2. \~ + \result \ru Возвращает строитель. + \en Returns the constructor. \~ + \ingroup Curve3D_Modeling +*/ +// --- +MATH_FUNC (MbCreator *) CreateBridgeEdge( const MbCurve3D & curve1, double t1, bool sense1, + const MbCurve3D & curve2, double t2, bool sense2, + const MbSNameMaker & names, + MbResultType & res, + MbEdge *& edge ); + + #endif // __CR_CONNECTING_CURVE_H \ No newline at end of file diff --git a/C3d/Include/cr_duplication_solid.h b/C3d/Include/cr_duplication_solid.h index 0207d16..5a81df9 100644 --- a/C3d/Include/cr_duplication_solid.h +++ b/C3d/Include/cr_duplication_solid.h @@ -1,103 +1,104 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Строитель размноженого набора граней. - \en Constructor of duplication face sets . \~ -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef CR_ELEMENTARY_SOLID_H -#define CR_ELEMENTARY_SOLID_H - - -#include -#include - - -class MATH_CLASS MbFaceShell; -class MbRegTransform; -class MbRegDuplicate; - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель размноженого набора граней. - \en Constructor of duplication face sets . \~ - \details \ru Строитель выполняет размножение тела согласно параметрам и объединяет копии в одно тело\n - \en Creator makes duplication of face sets accordind to parameters and unite its into a single face set\~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbDuplicationSolid : public MbCreator { -protected: - DuplicationValues * parameters; ///< \ru Параметры размножения. \en Parameters of duplication. - -public: - /// \ru Конструктор по параметрам. \en Constructor by parameters. - MbDuplicationSolid( const DuplicationValues & p, const MbSNameMaker & n ); -private: - MbDuplicationSolid( const MbDuplicationSolid & init, MbRegDuplicate *ireg ); - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. - MbDuplicationSolid( const MbDuplicationSolid & init ); -public: - virtual~MbDuplicationSolid(); - - /** \ru \name Общие функции строителя оболочки. - \en \name Common functions of the shell creator. - \{ */ - /// \ru Получить регистрационный тип (для копирования, дублирования). \en Get the registration type (for copying, duplication). - virtual MbeCreatorType IsA() const; - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const ; // \ru сделать копию \en create a copy - virtual void Transform( const MbMatrix3D & matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move( const MbVector3D & to, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate( const MbAxis3D & axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - - virtual void GetProperties( MbProperties & properties ); // \ru выдать свойства объекта \en get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru записать свойства объекта \en set properties of the object - virtual MbePrompt GetPropertyName(); // \ru выдать заголовок свойства объекта \en get a name of object property - - virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & ) const; // \ru являются ли объекты подобными \en whether the objects are similar - virtual bool SetEqual ( const MbCreator & ); // \ru сделать равным \en make equal - - virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, - RPArray * items = NULL ); // \ru Построение \en Construction - /** \} */ - -private : -// \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - void operator = ( const MbDuplicationSolid & ); - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbDuplicationSolid ) -}; // MbDuplicationSolid - -IMPL_PERSISTENT_OPS( MbDuplicationSolid ) - -//------------------------------------------------------------------------------ -/** \brief \ru Создать оболочку размножения исходной оболочки. - \en Create a shell of duplication of original shell. \~ - \details \ru По данной оболочке и параметрам размножения построить оболочку как результат объединения копий.\n - Одновременно с построением оболочки функция создаёт её строитель. \n - \en For a given shell and duplication parameters construct a shell as a result of a union of copies. \n - The function simultaneously constructs the shell and creates its constructor.\~ - \param[in] solid - \ru Исходная оболочка. - \en Original face set. \~ - \param[in] params - \ru Параметры размножения. - \en Parameters of duplication. \~ - \param[in] operNames - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[out] duplSolid - \ru Построенный набор граней. - \en Constructed set of faces. \~ - \param[out] res - \ru Код результата операции. - \en Operation result code. \~ - \result \ru Возвращает строитель, если операция была выполнена успешно. - \en Returns the constructor if the operation has been successfully performed. \~ - \ingroup Solid_Modeling -*/ -MATH_FUNC (MbCreator *) CreateDuplication( const MbFaceShell & solid, - const DuplicationValues & params, - const MbSNameMaker & operNames, - MbResultType & res, - MbFaceShell *& shell ); - - -#endif // CR_ELEMENTARY_SOLID_H \ No newline at end of file +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Строитель размноженного набора граней. + \en Constructor of duplication face sets . \~ +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef CR_DUPLICATION_SOLID_H +#define CR_DUPLICATION_SOLID_H + + +#include +#include + + +class MATH_CLASS MbFaceShell; +class MbRegTransform; +class MbRegDuplicate; + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель размноженного набора граней. + \en Constructor of duplication face sets . \~ + \details \ru Строитель выполняет размножение тела согласно параметрам и объединяет копии в одно тело\n + \en Creator makes duplication of face sets according to parameters and unite its into a single face set\~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbDuplicationSolid : public MbCreator { +protected: + DuplicationValues * parameters; ///< \ru Параметры размножения. \en Parameters of duplication. + +public: + /// \ru Конструктор по параметрам. \en Constructor by parameters. + MbDuplicationSolid( const DuplicationValues &, const MbSNameMaker & ); +private: + MbDuplicationSolid( const MbDuplicationSolid &, MbRegDuplicate * ); + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. + MbDuplicationSolid( const MbDuplicationSolid & ); +public: + virtual~MbDuplicationSolid(); + + /** \ru \name Общие функции строителя оболочки. + \en \name Common functions of the shell creator. + \{ */ + /// \ru Получить регистрационный тип (для копирования, дублирования). \en Get the registration type (for copying, duplication). + virtual MbeCreatorType IsA() const; + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const ; // \ru сделать копию \en create a copy + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + + virtual void GetProperties( MbProperties & ); // \ru выдать свойства объекта \en get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru записать свойства объекта \en set properties of the object + virtual MbePrompt GetPropertyName(); // \ru выдать заголовок свойства объекта \en get a name of object property + + virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & ) const; // \ru являются ли объекты подобными \en whether the objects are similar + virtual bool SetEqual ( const MbCreator & ); // \ru сделать равным \en make equal + + virtual bool CreateShell( MbFaceShell *&, MbeCopyMode sameShell, + RPArray * items = NULL ); // \ru Построение \en Construction + /** \} */ + +private : +// \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + void operator = ( const MbDuplicationSolid & ); + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbDuplicationSolid ) +}; // MbDuplicationSolid + +IMPL_PERSISTENT_OPS( MbDuplicationSolid ) + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать оболочку размножения исходной оболочки. + \en Create a shell of duplication of original shell. \~ + \details \ru По данной оболочке и параметрам размножения построить оболочку как результат объединения копий.\n + Одновременно с построением оболочки функция создаёт её строитель. \n + \en For a given shell and duplication parameters construct a shell as a result of a union of copies. \n + The function simultaneously constructs the shell and creates its constructor.\~ + \param[in] solid - \ru Исходная оболочка. + \en Original face set. \~ + \param[in] params - \ru Параметры размножения. + \en Parameters of duplication. \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[out] duplSolid - \ru Построенный набор граней. + \en Constructed set of faces. \~ + \param[out] res - \ru Код результата операции. + \en Operation result code. \~ + \result \ru Возвращает строитель, если операция была выполнена успешно. + \en Returns the constructor if the operation has been successfully performed. \~ + \ingroup Solid_Modeling +*/ +MATH_FUNC (MbCreator *) CreateDuplication( const MbFaceShell & solid, + const DuplicationValues & params, + const MbSNameMaker & operNames, + MbResultType & res, + MbFaceShell *& shell ); + + +#endif // CR_DUPLICATION_SOLID_H \ No newline at end of file diff --git a/C3d/Include/cr_elementary_solid.h b/C3d/Include/cr_elementary_solid.h index 1879a6f..43f630c 100644 --- a/C3d/Include/cr_elementary_solid.h +++ b/C3d/Include/cr_elementary_solid.h @@ -48,12 +48,21 @@ class MATH_CLASS MbElementarySolid : public MbCreator { protected : SArray points; ///< \ru Опорные точки оболочки тела. \en Support points of a solid shell. ElementaryShellType type; ///< \ru Тип тела. \en Type of a solid. - + MbPlacement3D position; ///< \ru Локальная система координат тела. \en Local coordinate system оf a solid. + double radius; ///< \ru Радиус основания тела. \en Radius of the base of the solid. + double minorRadius; ///< \ru Малый радиус основания тела. \en Small radius of the base of the solid. + double height; ///< \ru Высота тела. \en Height of a solid. + double length; ///< \ru Длина тела. \en Length of a solid. + double minorLength; ///< \ru Малая длина тела. \en Small length of a solid. + double width; ///< \ru Ширина тела. \en Width of a solid. + double angle; ///< \ru Угол между осью position.axisZ и боковой образующей. \en Angle between position.axisZ axis and lateral generatrix. + double ratio; ///< \ru Коэффициент растяжения. \en Stretch factor. public : /** \brief \ru Конструктор. \en Constructor. \~ - \details \ru Конструктор по точкам и типу тела. - \en Constructor by points and a type of a solid. \~ + \details \ru Конструктор по точкам и типу тела. При этом, по массиву точек тела и его типу наполняются соответствующие его параметры. + \en Constructor by points and type of solid. + At the same time, the corresponding parameters are filled in by the array of points of the body and its type. \~ \param[in] pnts - \ru Опорные точки. \n pnts[0] определяет начало локальной системы координат. \n @@ -89,17 +98,8 @@ public : \en An object defining names generation in the operation. \~ */ template - MbElementarySolid( const Points & pnts, ElementaryShellType t, const MbSNameMaker & n ) - : MbCreator( n ) - , points ( ) - , type ( t ) - { - size_t cnt = pnts.size(); - points.reserve( cnt ); - for ( size_t k = 0; k < cnt; ++k ) { - points.push_back( pnts[k] ); - } - } + MbElementarySolid( const Points & pnts, ElementaryShellType t, const MbSNameMaker & n ); + private : MbElementarySolid( const MbElementarySolid &, MbRegDuplicate * iReg ); // \ru Конструктор копирования с регистратором \en Copy-constructor with the registrator @@ -183,7 +183,7 @@ IMPL_PERSISTENT_OPS( MbElementarySolid ) */ // --- MATH_FUNC (MbCreator *) CreateElementary( const SArray & points, - ElementaryShellType t, + const ElementaryShellType t, const MbSNameMaker & n, MbResultType & res, MbFaceShell *& shell ); diff --git a/C3d/Include/cr_extension_shell.h b/C3d/Include/cr_extension_shell.h index 01a0f35..a906d9e 100644 --- a/C3d/Include/cr_extension_shell.h +++ b/C3d/Include/cr_extension_shell.h @@ -1,123 +1,121 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Построение удлинённой грани оболочки. - \en Construction of an extended face of a shell. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_EXTENSION_SHELL_H -#define __CR_EXTENSION_SHELL_H - - -#include -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель удлинённой грани оболочки. - \en Constructor of an extended face of a shell. \~ - \details \ru Строитель удлинённой грани оболочки. Удлинение может быть выполнено следующими способами. - Может быть ублинена на заданное расстояние указанная грань. - К указанной грани может быть добавлена гладко стыкующаяся с ней грань. - К указанной грани может быть добавлена грань, полученная выдавливанием крайнего ребра в заданном направлении. - \en Constructor of an extended face of a shell. Extension can be performed in the following ways: - The specified faces can be extended on the given distance. - A smoothly connected face can be added to the given face. - A face obtained by extrusion of boundary edge in the given direction can be added to the specified face. \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbExtensionShell : public MbCreator { -protected : - MbItemIndex faceIndex; ///< \ru Идентификатор удлиняемой грани в оболочке. \en Identifier of a shell face to extend. - SArray edgeIndexes; ///< \ru Идентификаторы ребер в грани. \en Identifier of edges in the face. - ExtensionValues parameters; ///< \ru Параметры построения удлинённой оболочки. \en Parameters of the extended shell construction. - -public : - MbExtensionShell( const MbItemIndex & fInd, const SArray & inds, - const ExtensionValues & p, const MbSNameMaker & n ); -private : - MbExtensionShell( const MbExtensionShell &, MbRegDuplicate * ireg ); -public : - virtual ~MbExtensionShell(); - - // \ru Общие функции математического объекта \en Common functions of the mathematical object - - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy - virtual void Transform( const MbMatrix3D & matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move( const MbVector3D & to, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate( const MbAxis3D & axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property - - virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal - - // \ru Общие функции твердого тела \en Common functions of solid solid - - virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, - RPArray * items = NULL ); // \ru Построение \en Construction - - // \ru Дать параметры. \en Get the parameters. - void GetParameters( ExtensionValues & params ) const { params = parameters; } - // \ru Установить параметры. \en Set the parameters. - void SetParameters( const ExtensionValues & params ) { parameters = params; } - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbExtensionShell ) -OBVIOUS_PRIVATE_COPY( MbExtensionShell ) -}; // MbExtensionShell - -IMPL_PERSISTENT_OPS( MbExtensionShell ) - -//------------------------------------------------------------------------------ -/** \brief \ru Построить удлинённую грань оболочки. - \en Construct the extended face of a shell. \~ - \details \ru Построить удлинённую грань оболочки. Удлинение может быть выполнено следующими способами. - Может быть ублинена на заданное расстояние указанная грань. - К указанной грани может быть добавлена гладко стыкующаяся с ней грань. - К указанной грани может быть добавлена грань, полученная выдавливанием крайнего ребра в заданном направлении. - Одновременно с построением оболочки функция создаёт её строитель.\n - \en Construct the extended face of a shell. Extension can be performed in the following ways: - The specified faces can be extended on the given distance. - A smoothly connected face can be added to the given face. - A face obtained by extrusion of a boundary edge in the given direction can be added to the specified face. - The function simultaneously creates the shell and its constructor.\n \~ - \param[in] solid - \ru Исходная оболочка. - \en The initial shell. \~ - \param[in] sameShell - \ru Режим копирования исходной оболочки. - \en Mode of copying the initial shell. \~ - \param[in] face - \ru Удлиняемая грагнь. - \en Face to extend. \~ - \param[in] edges - \ru Крайние рёбра удлиняемой грани. - \en Boundary edges of a face to extend. \~ - \param[in] parameters - \ru Параметры построения. - \en Parameters of a shell creation. \~ - \param[in] operNames - \ru Именователь граней. - \en An object for naming faces. \~ - \param[out] res - \ru Код результата операции. - \en Operation result code. \~ - \param[out] shell - \ru Построенная оболочка. - \en The resultant shell. \~ - \result \ru Возвращает строитель оболочки. - \en Returns the shell constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateExtensionShell( MbFaceShell * solid, - MbeCopyMode sameShell, - MbFace & face, - const RPArray & edges, - const ExtensionValues & parameters, - const MbSNameMaker & operNames, - MbResultType & res, - MbFaceShell *& shell ); - - -#endif // __CR_EXTENSION_SHELL_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Построение удлинённой грани оболочки. + \en Construction of an extended face of a shell. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_EXTENSION_SHELL_H +#define __CR_EXTENSION_SHELL_H + + +#include +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель удлинённой грани оболочки. + \en Constructor of an extended face of a shell. \~ + \details \ru Строитель удлинённой грани оболочки. Удлинение может быть выполнено следующими способами. + Может быть удлинена на заданное расстояние указанная грань. + К указанной грани может быть добавлена гладко стыкующаяся с ней грань. + К указанной грани может быть добавлена грань, полученная выдавливанием крайнего ребра в заданном направлении. + \en Constructor of an extended face of a shell. Extension can be performed in the following ways: + The specified faces can be extended on the given distance. + A smoothly connected face can be added to the given face. + A face obtained by extrusion of boundary edge in the given direction can be added to the specified face. \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbExtensionShell : public MbCreator { +protected : + std::vector facesIndex; ///< \ru Идентификаторы удлиняемых граней в оболочке. \en Identifier of a shell faces to extend. + ExtensionValues parameters; ///< \ru Параметры построения удлинённой оболочки. \en Parameters of the extended shell construction. + +public : + MbExtensionShell( const std::vector & fInd, + const ExtensionValues & p, const MbSNameMaker & n ); +private : + MbExtensionShell( const MbExtensionShell &, MbRegDuplicate * ireg ); +public : + virtual ~MbExtensionShell(); + + // \ru Общие функции математического объекта \en Common functions of the mathematical object + + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy + virtual void Transform( const MbMatrix3D & matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move( const MbVector3D & to, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate( const MbAxis3D & axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property + + virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + + // \ru Общие функции твердого тела \en Common functions of solid solid + + virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, + RPArray * items = NULL ); // \ru Построение \en Construction + + // \ru Дать параметры. \en Get the parameters. + void GetParameters( ExtensionValues & params ) const { params = parameters; } + // \ru Установить параметры. \en Set the parameters. + void SetParameters( const ExtensionValues & params ) { parameters = params; } + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbExtensionShell ) +OBVIOUS_PRIVATE_COPY( MbExtensionShell ) +}; // MbExtensionShell + + +IMPL_PERSISTENT_OPS( MbExtensionShell ) + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить удлинённую грань оболочки. + \en Construct the extended face of a shell. \~ + \details \ru Построить удлинённую грань оболочки. Удлинение может быть выполнено следующими способами. + Может быть удлинена на заданное расстояние указанная грань. + К указанной грани может быть добавлена гладко стыкующаяся с ней грань. + К указанной грани может быть добавлена грань, полученная выдавливанием крайнего ребра в заданном направлении. + Одновременно с построением оболочки функция создаёт её строитель.\n + \en Construct the extended face of a shell. An extension can be performed in the following ways: + The specified faces can be extended on the given distance. + A smoothly connected face can be added to the given face. + A face obtained by extrusion of a boundary edge in the given direction can be added to the specified face. + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] solid - \ru Исходная оболочка. + \en The initial shell. \~ + \param[in] sameShell - \ru Режим копирования исходной оболочки. + \en Mode of copying the initial shell. \~ + \param[in] edges - \ru Множество краевых ребер, через которые выполняется продление. + \en An array of boundary edges through which to extend the face. \~ + \param[in] parameters - \ru Параметры построения. + \en Parameters of a shell creation. \~ + \param[in] operNames - \ru Именователь граней. + \en An object for naming faces. \~ + \param[out] res - \ru Код результата операции. + \en Operation result code. \~ + \param[out] shell - \ru Построенная оболочка. + \en The resultant shell. \~ + \result \ru Возвращает строитель оболочки. + \en Returns the shell constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateExtensionShell( MbFaceShell * solid, + MbeCopyMode sameShell, + const RPArray & edges, + const ExtensionValues & parameters, + const MbSNameMaker & operNames, + MbResultType & res, + MbFaceShell *& shell ); + + +#endif // __CR_EXTENSION_SHELL_H diff --git a/C3d/Include/cr_fillet_solid.h b/C3d/Include/cr_fillet_solid.h index 90bbb6a..b12681a 100644 --- a/C3d/Include/cr_fillet_solid.h +++ b/C3d/Include/cr_fillet_solid.h @@ -36,6 +36,7 @@ struct MATH_CLASS MbEdgeFunction; class MATH_CLASS MbFilletSolid : public MbSmoothSolid { public : RPArray functions; ///< \ru Функции изменения радиусов сопряжения. \en Functions of changing conjugation radii. + RPArray slideways; ///< \ru Опорные кривые сопряжения. \en Supporting curves of conjugation. SArray boundaries; ///< \ru Номера граней для обрезки краёв скругления / фаски. \en Indices of faces for trimming the fillet / chamfer boundaries. SArray vertices; ///< \ru Номера скругляемых вершин. \en Indices of vertices to fillet. CornerValues cornerData; ///< \ru Параметры скругления вершин. \en Parameters of vertices fillet. @@ -43,6 +44,7 @@ public : public : MbFilletSolid( SArray & inds, RPArray & funcs, + RPArray & slids, SArray & bounds, SArray & verts, const SmoothValues & params, @@ -68,6 +70,7 @@ public : virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. virtual bool IsSame( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar ( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar virtual bool SetEqual( const MbCreator & init ); // \ru Сделать равным. \en Make equal. // \ru Общие функции твердого тела \en Common functions of solid @@ -85,6 +88,7 @@ private : IMPL_PERSISTENT_OPS( MbFilletSolid ) + //------------------------------------------------------------------------------ /** \brief \ru Создать оболочку со cкруглением ребeр. \en Create a shell with edges fillet. \~ diff --git a/C3d/Include/cr_intersection_curve.h b/C3d/Include/cr_intersection_curve.h index dc2e656..b9eacc6 100644 --- a/C3d/Include/cr_intersection_curve.h +++ b/C3d/Include/cr_intersection_curve.h @@ -1,73 +1,73 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Строитель кривой пересечения. - \en Intersection curve constructor. -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_INTERSECTION_CURVE_H -#define __CR_INTERSECTION_CURVE_H - - -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель кривой пересечения. - \en Intersection curve constructor. \~ - \details \ru Строитель кривой пересечения.\n - \en Intersection curve constructor.\n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbIntCurveCreator : public MbCreator { -private: - RPArray creators1; // \ru Журнал построения первой оболочки. \en The first shell history tree. - RPArray creators2; // \ru Журнал построения второй оболочки. \en The second shell history tree. - -protected: - MbIntCurveCreator( const MbIntCurveCreator &, MbRegDuplicate * iReg ); // \ru Конструктор копирования \en Copy-constructor - MbIntCurveCreator( const MbIntCurveCreator & ); // \ru Не реализовано \en Not implemented - MbIntCurveCreator(); // \ru Не реализовано \en Not implemented -public: - MbIntCurveCreator( const RPArray & creators1, bool same1, - const RPArray & creators2, bool same2, - const MbSNameMaker & snMaker ); -public: - virtual ~MbIntCurveCreator(); - - // \ru Общие функции строителя. \en The common functions of the creator. - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element - virtual MbCreator & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Сделать копию \en Create a copy - - virtual bool IsSame ( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal - - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - - virtual MbePrompt GetPropertyName(); // \ru Дать имя свойства объекта \en Get the object property name - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - // \ru Построить кривую по журналу построения \en Create a curve from the history tree - virtual bool CreateWireFrame( MbWireFrame *&, MbeCopyMode, RPArray * items = NULL ); - - /** \} */ - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. - void operator = ( const MbIntCurveCreator & ); // \ru Не реализовано!!! \en Not implemented!!! - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbIntCurveCreator ) -}; - -IMPL_PERSISTENT_OPS( MbIntCurveCreator ) - -#endif // __CR_INTERSECTION_CURVE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Строитель кривой пересечения. + \en Intersection curve constructor. +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_INTERSECTION_CURVE_H +#define __CR_INTERSECTION_CURVE_H + + +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель кривой пересечения. + \en Intersection curve constructor. \~ + \details \ru Строитель кривой пересечения.\n + \en Intersection curve constructor.\n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbIntCurveCreator : public MbCreator { +private: + RPArray creators1; // \ru Журнал построения первой оболочки. \en The first shell history tree. + RPArray creators2; // \ru Журнал построения второй оболочки. \en The second shell history tree. + +protected: + MbIntCurveCreator( const MbIntCurveCreator &, MbRegDuplicate * iReg ); // \ru Конструктор копирования \en Copy-constructor + MbIntCurveCreator( const MbIntCurveCreator & ); // \ru Не реализовано \en Not implemented + MbIntCurveCreator(); // \ru Не реализовано \en Not implemented +public: + MbIntCurveCreator( const RPArray & creators1, bool same1, + const RPArray & creators2, bool same2, + const MbSNameMaker & snMaker ); +public: + virtual ~MbIntCurveCreator(); + + // \ru Общие функции строителя. \en The common functions of the creator. + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element + virtual MbCreator & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Сделать копию \en Create a copy + + virtual bool IsSame ( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + + virtual MbePrompt GetPropertyName(); // \ru Дать имя свойства объекта \en Get the object property name + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + // \ru Построить кривую по журналу построения \en Create a curve from the history tree + virtual bool CreateWireFrame( MbWireFrame *&, MbeCopyMode, RPArray * items = NULL ); + + /** \} */ + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. + void operator = ( const MbIntCurveCreator & ); // \ru Не реализовано!!! \en Not implemented!!! + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbIntCurveCreator ) +}; + +IMPL_PERSISTENT_OPS( MbIntCurveCreator ) + +#endif // __CR_INTERSECTION_CURVE_H diff --git a/C3d/Include/cr_join_shell.h b/C3d/Include/cr_join_shell.h index dd25c8b..b8a3999 100644 --- a/C3d/Include/cr_join_shell.h +++ b/C3d/Include/cr_join_shell.h @@ -1,239 +1,239 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Построение оболочки соединения. - \en Construction of a join shell. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_JOIN_SHELL_H -#define __CR_JOIN_SHELL_H - - -#include -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель оболочки соединения. - \en Constructor of a join shell. \~ - \details \ru Строитель оболочки, соединяющей две грани по двум кривым на них. \n - \en Constructor of a shell joining two faces by two curves on them. \n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbJoinShell : public MbCreator { -protected: - MbCurve3D * curve1; ///< \ru Первая образующая кривая. \en The first generating curve. - MbCurve3D * curve2; ///< \ru Вторая образующая кривая. \en The second generating curve. - JoinSurfaceValues parameters; ///< \ru Параметры поверхности соединения. \en Parameters of a join surface. -public : - MbJoinShell( MbCurve3D & c1, MbCurve3D & c2, const JoinSurfaceValues & p, const MbSNameMaker & n ); -private : - MbJoinShell( const MbJoinShell & init, MbRegDuplicate * ireg ); -public : - virtual ~MbJoinShell(); - - // \ru Общие функции математического объекта \en Common functions of the mathematical object - - virtual MbeCreatorType IsA () const; ///< \ru Тип элемента \en Element type - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; ///< \ru Сделать копию \en Make a copy - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); ///< \ru Преобразовать элемент согласно матрице \en Transform an element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); ///< \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); ///< \ru Поворот вокруг оси \en Rotation about an axis - - virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными. \en Whether the objects are similar - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal - - virtual MbePrompt GetPropertyName (); // \ru Выдать заголовок свойства объекта \en Get a name of object property - virtual void GetProperties ( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties ( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - // \ru Общие функции твердого тела \en Common functions of solid solid - - virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, - RPArray * items = NULL ); ///< \ru Построение \en Construction - - - // \ru Дать параметры. \en Get the parameters. - void GetParameters( JoinSurfaceValues & params ) const { params = parameters; } - // \ru Установить параметры. \en Set the parameters. - void SetParameters( const JoinSurfaceValues & params ) { parameters = params; } - - const MbCurve3D & GetCurve( ptrdiff_t num ) const; - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbJoinShell ) -OBVIOUS_PRIVATE_COPY( MbJoinShell ) -}; - -IMPL_PERSISTENT_OPS( MbJoinShell ) - -//------------------------------------------------------------------------------ -/* \brief \ru Проверить необходимость модификации второй кривой. - \en Check if a modification of the second curve is necessary. \~ - \details \ru Проверить необходимость модификации второй кривой для построения оболочки соединения на этих кривых. \n - \en Check whether a modification of the second curve is necessary for construction of a join shell on these curves. \n \~ - \param[in] curve1 - \ru Первая кривая. - \en The first curve. \~ - \param[in] curve2 - \ru Вторая кривая. - \en The second curve. \~ - \param[out] isInverted1 - \ru Была ли первая кривая инвертирована. - \en Whether the first curve was inverted. \~ - \param[out] isShifted1 - \ru Было ли смещено начало второй кривой. - \en Whether the beginning of the first curve was shifted. \~ - \param[in] version - \ru Версия построения. - \en The version of construction. \~ - \result \ru Возвращает построенную оболочку. - \en Returns the constructed shell. \~ - \ingroup Model_Creators -*/ -//--- -void CheckJoinedShellCurve( const MbCurve3D & curve1, - const MbCurve3D & curve2, - bool & isInverted1, - bool & isShifted1, - VERSION version ); - - -//------------------------------------------------------------------------------ -/* \brief \ru Построить кривую по набору рёбер. - \en Construct a curve given a set of edges. \~ - \details \ru Построить кривую по набору рёбер для поверхности соединения. - \en Construct a curve given a set of edges for a join surface. \~ - \param[in] edges - \ru Набор ребер. - \en A set of edges. \~ - \param[in] orients - \ru Ориентация рёбер набора. - \en Orientation of edges from the set. \~ - \param[in] matr - \ru Матрица преобразования рёбер набора. - \en Transformation matrix of edges from the set. \~ - \param[out] res - \ru Код результата построения. - \en Construction result code. \~ - \result \ru Возвращает построенную кривую. - \en Returns the constructed curve. \~ - \ingroup Model_Creators -*/ -//--- -MbCurve3D * CreateJoinedShellCurve( const RPArray & edges, - const SArray & orients, - const MbMatrix3D & matr, - MbResultType & res ); - - -//------------------------------------------------------------------------------ -/* \brief \ru Построить оболочку соединения. - \en Construct a join shell. \~ - \details \ru Построить оболочку, соединяющую две грани по двум кривым на них. \n - \en Construct a shell joining two faces by two curves on them. \n \~ - \param[in] curve1 - \ru Кривая на первой соединяемой поверхности. - \en A curve on the first surface to join. \~ - \param[in] curve2 - \ru Кривая на второй соединяемой поверхности. - \en A curve on the second surface to join. \~ - \param[in] parameters - \ru Параметры построения. - \en Parameters of a shell creation. \~ - \param[in] names - \ru Именователь граней. - \en An object for naming faces. \~ - \param[in] isPhantom - \ru Режим создания фантома. - \en Create in the phantom mode. \~ - \param[out] res - \ru Код результата построения. - \en Construction result code. \~ - \result \ru Возвращает построенную оболочку. - \en Returns the constructed shell. \~ - \ingroup Model_Creators -*/ -// --- -MbFaceShell * MakeJoinShell( MbSurfaceCurve & curve1, - MbSurfaceCurve & curve2, - JoinSurfaceValues & parameters, - const MbSNameMaker & names, - bool isPhantom, - MbResultType & res ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить оболочку соединения. - \en Construct a join shell. \~ - \details \ru Построить оболочку, соединяющую две грани по двум кривым на них. - Одновременно с построением оболочки функция создаёт её строитель.\n - \en Construct a shell joining two faces by two curves on them. - The function simultaneously creates the shell and its constructor.\n \~ - \param[in] curve1 - \ru Кривая на первой соединяемой поверхности. - \en A curve on the first surface to join. \~ - \param[in] curve2 - \ru Кривая на второй соединяемой поверхности. - \en A curve on the second surface to join. \~ - \param[in] parameters - \ru Параметры построения. - \en Parameters of a shell creation. \~ - \param[in] names - \ru Именователь граней. - \en An object for naming faces. \~ - \param[out] res - \ru Код результата построения. - \en Construction result code. \~ - \param[out] shell - \ru Построенная оболочка. - \en The resultant shell. \~ - \result \ru Возвращает строитель оболочки. - \en Returns the shell constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateJoinShell( MbSurfaceCurve & curve1, - MbSurfaceCurve & curve2, - JoinSurfaceValues & parameters, - const MbSNameMaker & names, - MbResultType & res, - MbFaceShell *& shell ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить оболочку соединения. - \en Construct a join shell. \~ - \details \ru Построить оболочку соединения по двум наборам ребер. - Рёбра двух наборов определяют набор граней соединения, каждая из которых побстроена по двум кривым. - Одновременно с построением оболочки функция создаёт её строитель.\n - \en Construct a shell of join given two sets of edges. - Edges of two sets define a set of join faces each of which is constructed by two curves. - The function simultaneously creates the shell and its constructor.\n \~ - \param[in] edges1 - \ru Первый набор ребер. - \en The first set of edges. \~ - \param[in] orients1 - \ru Ориентация рёбер первого набора. - \en Orientation of edges from the first set. \~ - \param[in] edges2 - \ru Второй набор ребер. - \en The second set of edges. \~ - \param[in] orients2 - \ru Ориентация рёбер второго набора. - \en Orientation of edges of the second set. \~ - \param[in] matr1 - \ru Матрица преобразования рёбер первого набора. - \en Transformation matrix of edges from the first set. \~ - \param[in] matr2 - \ru Матрица преобразования рёбер второго набора. - \en Transformation matrix of edges from the second set. \~ - \param[in] parameters - \ru Параметры построения. - \en Parameters of a shell creation. \~ - \param[in] names - \ru Именователь граней. - \en An object for naming faces. \~ - \param[out] res - \ru Код результата операции. - \en Operation result code. \~ - \param[out] shell - \ru Построенная оболочка. - \en The resultant shell. \~ - \param[in] isPhantom - \ru Режим создания фантома. - \en Create in the phantom mode. \~ - \result \ru Возвращает строитель оболочки. - \en Returns the shell constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateJoinShell( const RPArray & edges1, - const SArray & orients1, - const RPArray & edges2, - const SArray & orients2, - const MbMatrix3D & matr1, - const MbMatrix3D & matr2, - JoinSurfaceValues & parameters, - const MbSNameMaker & names, - MbResultType & res, - MbFaceShell *& shell, - bool isPhantom ); - - -#endif // __CR_JOIN_SHELL_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Построение оболочки соединения. + \en Construction of a join shell. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_JOIN_SHELL_H +#define __CR_JOIN_SHELL_H + + +#include +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель оболочки соединения. + \en Constructor of a join shell. \~ + \details \ru Строитель оболочки, соединяющей две грани по двум кривым на них. \n + \en Constructor of a shell joining two faces by two curves on them. \n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbJoinShell : public MbCreator { +protected: + MbCurve3D * curve1; ///< \ru Первая образующая кривая. \en The first generating curve. + MbCurve3D * curve2; ///< \ru Вторая образующая кривая. \en The second generating curve. + JoinSurfaceValues parameters; ///< \ru Параметры поверхности соединения. \en Parameters of a join surface. +public : + MbJoinShell( MbCurve3D & c1, MbCurve3D & c2, const JoinSurfaceValues & p, const MbSNameMaker & n ); +private : + MbJoinShell( const MbJoinShell & init, MbRegDuplicate * ireg ); +public : + virtual ~MbJoinShell(); + + // \ru Общие функции математического объекта \en Common functions of the mathematical object + + virtual MbeCreatorType IsA () const; ///< \ru Тип элемента \en Element type + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; ///< \ru Сделать копию \en Make a copy + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); ///< \ru Преобразовать элемент согласно матрице \en Transform an element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); ///< \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); ///< \ru Поворот вокруг оси \en Rotation about an axis + + virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными. \en Whether the objects are similar + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + + virtual MbePrompt GetPropertyName (); // \ru Выдать заголовок свойства объекта \en Get a name of object property + virtual void GetProperties ( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties ( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + // \ru Общие функции твердого тела \en Common functions of solid solid + + virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, + RPArray * items = NULL ); ///< \ru Построение \en Construction + + + // \ru Дать параметры. \en Get the parameters. + void GetParameters( JoinSurfaceValues & params ) const { params = parameters; } + // \ru Установить параметры. \en Set the parameters. + void SetParameters( const JoinSurfaceValues & params ) { parameters = params; } + + const MbCurve3D & GetCurve( ptrdiff_t num ) const; + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbJoinShell ) +OBVIOUS_PRIVATE_COPY( MbJoinShell ) +}; + +IMPL_PERSISTENT_OPS( MbJoinShell ) + +//------------------------------------------------------------------------------ +/* \brief \ru Проверить необходимость модификации второй кривой. + \en Check if a modification of the second curve is necessary. \~ + \details \ru Проверить необходимость модификации второй кривой для построения оболочки соединения на этих кривых. \n + \en Check whether a modification of the second curve is necessary for construction of a join shell on these curves. \n \~ + \param[in] curve1 - \ru Первая кривая. + \en The first curve. \~ + \param[in] curve2 - \ru Вторая кривая. + \en The second curve. \~ + \param[out] isInverted1 - \ru Была ли первая кривая инвертирована. + \en Whether the first curve was inverted. \~ + \param[out] isShifted1 - \ru Было ли смещено начало второй кривой. + \en Whether the beginning of the first curve was shifted. \~ + \param[in] version - \ru Версия построения. + \en The version of construction. \~ + \result \ru Возвращает построенную оболочку. + \en Returns the constructed shell. \~ + \ingroup Model_Creators +*/ +//--- +void CheckJoinedShellCurve( const MbCurve3D & curve1, + const MbCurve3D & curve2, + bool & isInverted1, + bool & isShifted1, + VERSION version ); + + +//------------------------------------------------------------------------------ +/* \brief \ru Построить кривую по набору рёбер. + \en Construct a curve given a set of edges. \~ + \details \ru Построить кривую по набору рёбер для поверхности соединения. + \en Construct a curve given a set of edges for a join surface. \~ + \param[in] edges - \ru Набор ребер. + \en A set of edges. \~ + \param[in] orients - \ru Ориентация рёбер набора. + \en Orientation of edges from the set. \~ + \param[in] matr - \ru Матрица преобразования рёбер набора. + \en Transformation matrix of edges from the set. \~ + \param[out] res - \ru Код результата построения. + \en Construction result code. \~ + \result \ru Возвращает построенную кривую. + \en Returns the constructed curve. \~ + \ingroup Model_Creators +*/ +//--- +MbCurve3D * CreateJoinedShellCurve( const RPArray & edges, + const SArray & orients, + const MbMatrix3D & matr, + MbResultType & res ); + + +//------------------------------------------------------------------------------ +/* \brief \ru Построить оболочку соединения. + \en Construct a join shell. \~ + \details \ru Построить оболочку, соединяющую две грани по двум кривым на них. \n + \en Construct a shell joining two faces by two curves on them. \n \~ + \param[in] curve1 - \ru Кривая на первой соединяемой поверхности. + \en A curve on the first surface to join. \~ + \param[in] curve2 - \ru Кривая на второй соединяемой поверхности. + \en A curve on the second surface to join. \~ + \param[in] parameters - \ru Параметры построения. + \en Parameters of a shell creation. \~ + \param[in] names - \ru Именователь граней. + \en An object for naming faces. \~ + \param[in] isPhantom - \ru Режим создания фантома. + \en Create in the phantom mode. \~ + \param[out] res - \ru Код результата построения. + \en Construction result code. \~ + \result \ru Возвращает построенную оболочку. + \en Returns the constructed shell. \~ + \ingroup Model_Creators +*/ +// --- +MbFaceShell * MakeJoinShell( MbSurfaceCurve & curve1, + MbSurfaceCurve & curve2, + JoinSurfaceValues & parameters, + const MbSNameMaker & names, + bool isPhantom, + MbResultType & res ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить оболочку соединения. + \en Construct a join shell. \~ + \details \ru Построить оболочку, соединяющую две грани по двум кривым на них. + Одновременно с построением оболочки функция создаёт её строитель.\n + \en Construct a shell joining two faces by two curves on them. + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] curve1 - \ru Кривая на первой соединяемой поверхности. + \en A curve on the first surface to join. \~ + \param[in] curve2 - \ru Кривая на второй соединяемой поверхности. + \en A curve on the second surface to join. \~ + \param[in] parameters - \ru Параметры построения. + \en Parameters of a shell creation. \~ + \param[in] names - \ru Именователь граней. + \en An object for naming faces. \~ + \param[out] res - \ru Код результата построения. + \en Construction result code. \~ + \param[out] shell - \ru Построенная оболочка. + \en The resultant shell. \~ + \result \ru Возвращает строитель оболочки. + \en Returns the shell constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateJoinShell( MbSurfaceCurve & curve1, + MbSurfaceCurve & curve2, + JoinSurfaceValues & parameters, + const MbSNameMaker & names, + MbResultType & res, + MbFaceShell *& shell ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить оболочку соединения. + \en Construct a join shell. \~ + \details \ru Построить оболочку соединения по двум наборам ребер. + Рёбра двух наборов определяют набор граней соединения, каждая из которых побстроена по двум кривым. + Одновременно с построением оболочки функция создаёт её строитель.\n + \en Construct a shell of join given two sets of edges. + Edges of two sets define a set of join faces each of which is constructed by two curves. + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] edges1 - \ru Первый набор ребер. + \en The first set of edges. \~ + \param[in] orients1 - \ru Ориентация рёбер первого набора. + \en Orientation of edges from the first set. \~ + \param[in] edges2 - \ru Второй набор ребер. + \en The second set of edges. \~ + \param[in] orients2 - \ru Ориентация рёбер второго набора. + \en Orientation of edges of the second set. \~ + \param[in] matr1 - \ru Матрица преобразования рёбер первого набора. + \en Transformation matrix of edges from the first set. \~ + \param[in] matr2 - \ru Матрица преобразования рёбер второго набора. + \en Transformation matrix of edges from the second set. \~ + \param[in] parameters - \ru Параметры построения. + \en Parameters of a shell creation. \~ + \param[in] names - \ru Именователь граней. + \en An object for naming faces. \~ + \param[out] res - \ru Код результата операции. + \en Operation result code. \~ + \param[out] shell - \ru Построенная оболочка. + \en The resultant shell. \~ + \param[in] isPhantom - \ru Режим создания фантома. + \en Create in the phantom mode. \~ + \result \ru Возвращает строитель оболочки. + \en Returns the shell constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateJoinShell( const RPArray & edges1, + const SArray & orients1, + const RPArray & edges2, + const SArray & orients2, + const MbMatrix3D & matr1, + const MbMatrix3D & matr2, + JoinSurfaceValues & parameters, + const MbSNameMaker & names, + MbResultType & res, + MbFaceShell *& shell, + bool isPhantom ); + + +#endif // __CR_JOIN_SHELL_H diff --git a/C3d/Include/cr_median_shell.h b/C3d/Include/cr_median_shell.h index 57af1ed..edda9cf 100644 --- a/C3d/Include/cr_median_shell.h +++ b/C3d/Include/cr_median_shell.h @@ -1,115 +1,118 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Построение срединной оболочки между гранями тела. - \en Construction of a median shell between faces of solid. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_MEDIAN_SHELL_H -#define __CR_MEDIAN_SHELL_H - - -#include -#include -#include -#include - - -class MATH_CLASS MbSNameMaker; -class MATH_CLASS MedianShellFaces; - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель срединной оболочки тела. - \en Constructor of a median shell of solid. \~ - \details \ru Строитель осуществляет построение срединной оболочки между выбранными парами граней тела. - Поверхности выбранные граней должны быть эквидистантны по отношению друг к другу. \n - \en Constructor performs the building of a median shell between suitable selected face pairs of solid. - Suitable face pairs should be equidistant from each other. \n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbMedianShell : public MbCreator { -private : - MedianShellFaces faces; ///< \ru Выбранные грани. \en Selected faces . - MedianShellValues parameters; ///< \ru Параметры срединной оболочки. \en Parameters of median shell. - -public: - /// \ru Конструктор по выбранным граням и параметрам срединной оболочки. \en Constructor by selected faces and parameters of median shell. - MbMedianShell( const MedianShellFaces & faces, const MedianShellValues & params, const MbSNameMaker & snMaker ); - /// \ru Деструктор. \en Destructor. - virtual ~MbMedianShell(); - -private: - /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. - MbMedianShell( const MbMedianShell &, MbRegDuplicate * ); - -public: - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy - virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property - - // \ru Построение оболочки по исходным данным \en Construction of a shell from the given data - virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, - RPArray * items = NULL ); - - /// \ru Дать параметры. \en Get the parameters. - void GetParameters( MedianShellValues & params ) const { params = parameters; } - /// \ru Установить параметры. \en Set the parameters. - void SetParameters( const MedianShellValues & params ) { parameters = params; } - - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbMedianShell ) -OBVIOUS_PRIVATE_COPY( MbMedianShell ) -}; - -IMPL_PERSISTENT_OPS( MbMedianShell ) - -//------------------------------------------------------------------------------ -/** \brief \ru Построить срединную оболочку между выбранными парами граней тела. - \en Build a median shell between selected faces of solid. \~ - \details \ru Построить срединную оболочку между выбранными парами граней тела. - Выбранные грани должны быть эквидистантны по отношению друг к другу. - Грани должны принадлежать одному и тому же телу. - Одновременно с построением оболочки функция создаёт её строитель.\n - \en Build a median shell between suitable selected face pairs of solid. - Suitable face pairs should be offset from each other. - The faces must belong to the same body. - The function simultaneously creates the shell and its constructor.\n \~ - \param[in] solid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] faces - \ru Выбранные пары граней. - \en Selected face pairs. \~ - \param[in] parameters - \ru Параметры операции. - \en Parameters of operation. \~ - \param[in] operNames - \ru Именователь операции. - \en An object defining names generation in the operation \~ - \param[out] res - \ru Код результата операции. - \en Operation result code. \~ - \param[out] shell - \ru Построенная срединная оболочка. - \en Constructed median shell. \~ - \result \ru Возвращает строитель оболочки. - \en Returns the shell constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateMedianShell( const MbFaceShell & solid, - const std::vector & faceIndexes, - const MedianShellValues & parameters, - const MbSNameMaker & operNames, - MbResultType & res, - MbFaceShell *& shell ); - - +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Построение срединной оболочки между гранями тела. + \en Construction of a median shell between faces of solid. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_MEDIAN_SHELL_H +#define __CR_MEDIAN_SHELL_H + + +#include +#include +#include +#include + + +class MATH_CLASS MbSNameMaker; +class MATH_CLASS MedianShellFaces; + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель срединной оболочки тела. + \en Constructor of a median shell of solid. \~ + \details \ru Строитель осуществляет построение срединной оболочки между выбранными парами граней тела. + Поверхности выбранные граней должны быть эквидистантны по отношению друг к другу. \n + \en Constructor performs the building of a median shell between suitable selected face pairs of solid. + Suitable face pairs should be equidistant from each other. \n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbMedianShell : public MbCreator { +private : + MedianShellFaces faces; ///< \ru Выбранные грани. \en Selected faces . + MedianShellValues parameters; ///< \ru Параметры срединной оболочки. \en Parameters of median shell. + +public: + /// \ru Конструктор по выбранным граням и параметрам срединной оболочки. \en Constructor by selected faces and parameters of median shell. + MbMedianShell( const MedianShellFaces & faces, const MedianShellValues & params, const MbSNameMaker & snMaker ); + /// \ru Деструктор. \en Destructor. + virtual ~MbMedianShell(); + +private: + /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. + MbMedianShell( const MbMedianShell &, MbRegDuplicate * ); + +public: + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy + virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property + + // \ru Построение оболочки по исходным данным \en Construction of a shell from the given data + virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, + RPArray * items = NULL ); + + /// \ru Дать параметры. \en Get the parameters. + void GetParameters( MedianShellValues & params ) const { params = parameters; } + /// \ru Установить параметры. \en Set the parameters. + void SetParameters( const MedianShellValues & params ) { parameters = params; } + + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbMedianShell ) +OBVIOUS_PRIVATE_COPY( MbMedianShell ) +}; + +IMPL_PERSISTENT_OPS( MbMedianShell ) + +//------------------------------------------------------------------------------ +/** \brief \ru Построить срединную оболочку между выбранными парами граней тела. + \en Build a median shell between selected faces of solid. \~ + \details \ru Построить срединную оболочку между выбранными парами граней тела. + Выбранные грани должны быть эквидистантны по отношению друг к другу. + Грани должны принадлежать одному и тому же телу. + Одновременно с построением оболочки функция создаёт её строитель.\n + \en Build a median shell between suitable selected face pairs of solid. + Suitable face pairs should be offset from each other. + The faces must belong to the same body. + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] solid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] faces - \ru Выбранные пары граней. + \en Selected face pairs. \~ + \param[in] parameters - \ru Параметры операции. + \en Parameters of operation. \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation \~ + \param[out] res - \ru Код результата операции. + \en Operation result code. \~ + \param[out] medianFaces - \ru Множество граней для создания срединной оболочки. + \en Set of faces for build a median shell. \~ + \param[out] shell - \ru Построенная срединная оболочка. + \en Constructed median shell. \~ + \result \ru Возвращает строитель оболочки. + \en Returns the shell constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateMedianShell( const MbFaceShell & solid, + const c3d::IndicesPairsVector & faceIndexes, + const MedianShellValues & parameters, + const MbSNameMaker & operNames, + MbResultType & res, + MedianShellFaces & medianFaces, + MbFaceShell *& shell ); + + #endif // __CR_MEDIAN_SHELL_H \ No newline at end of file diff --git a/C3d/Include/cr_mesh_shell.h b/C3d/Include/cr_mesh_shell.h index 3a2cef5..5b59c37 100644 --- a/C3d/Include/cr_mesh_shell.h +++ b/C3d/Include/cr_mesh_shell.h @@ -1,103 +1,103 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Построение оболочки на сетке кривых. - \en Construction of a shell from a mesh of curves. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_MESH_SHELL_H -#define __CR_MESH_SHELL_H - - -#include -#include -#include -#include - - -class MATH_CLASS MbFaceShell; - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель оболочки на сетке кривых. - \en Constructor of a shell from a mesh of curves. \~ - \details \ru Строитель оболочки на сетке кривых, образованной двумя сечействами кривых. \n - \en Constructor of a shell from a mesh of curves formed by two sets of curves. \n \~ - \ingroup Model_Creators -*/ -//--- -class MATH_CLASS MbMeshShell : public MbCreator { -private : - MeshSurfaceValues parameters; ///< \ru Параметры построения. \en Construction parameters. - mutable bool changed; ///< \ru Флаг изменения параметров. \en Flag of parameters modification. -private: - /// \ru Конструктор копирования. \en Copy-constructor. - MbMeshShell( const MbMeshShell & obj, MbRegDuplicate * ireg ); -public: - /// \ru Конструктор по параметрам операции и именователю на оригиналах кривых и копиях поверхностей. \en Constructor by operation parameters and name-maker for original curves and copies of surfaces. - MbMeshShell( const MeshSurfaceValues & pars, const MbSNameMaker & n ); - virtual ~MbMeshShell(); - -public: // \ru Общие функции математического объекта \en Common functions of the mathematical object - virtual MbeCreatorType IsA() const; ///< \ru Тип элемента \en Element type - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; ///< \ru Сделать копию \en Make a copy - virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool SetEqual ( const MbCreator & ); ///< \ru Сделать равным \en Make equal - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); ///< \ru Преобразовать по матрице \en Transform according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); ///< \ru Сдвиг по вектору \en Translation by the vector - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); ///< \ru Поворот вокруг оси \en Rotation about an axis - - virtual MbePrompt GetPropertyName(); ///< \ru Выдать заголовок свойства объекта \en Get name of object property - virtual void GetProperties( MbProperties & ); ///< \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & ); ///< \ru Записать свойства объекта \en Write properties of the object - virtual void GetBasisItems( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - -public: - /// \ru Построение оболочки \en Creation of a shell - virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, - RPArray * items = NULL ); - // \ru Дать параметры. \en Get the parameters. - void GetParameters( MeshSurfaceValues & params ) const; - // \ru Установить параметры. \en Set the parameters. - void SetParameters( const MeshSurfaceValues & params ); - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbMeshShell ) -OBVIOUS_PRIVATE_COPY( MbMeshShell ) -}; // MbMeshShell - -IMPL_PERSISTENT_OPS( MbMeshShell ) - -//------------------------------------------------------------------------------ -/** \brief \ru Построить оболочку на сетке кривых. - \en Construct a shell from a mesh of curves. \~ - \details \ru Построить оболочку на сетке кривых, образованной двумя сечействами кривых. \n - Одновременно с построением оболочки функция создаёт её строитель.\n - \en Create a shell from a mesh of curves formed by two sets of curves. \n - The function simultaneously creates the shell and its constructor.\n \~ - \param[in] parameters - \ru Параметры построения. - \en Parameters of a shell creation. \~ - \param[in] operNames - \ru Именователь граней. - \en An object for naming faces. \~ - \param[in] isPhantom - \ru Режим создания фантома. - \en Create in the phantom mode. \~ - \param[out] res - \ru Код результата операции. - \en Operation result code. \~ - \param[out] shell - \ru Построенная оболочка. - \en The resultant shell. \~ - \result \ru Возвращает строитель оболочки. - \en Returns the shell constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateMeshShell( MeshSurfaceValues & parameters, - const MbSNameMaker & operNames, - bool isPhantom, - MbResultType & res, - MbFaceShell *& shell ); - - -#endif // __CR_MESH_SHELL_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Построение оболочки на сетке кривых. + \en Construction of a shell from a mesh of curves. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_MESH_SHELL_H +#define __CR_MESH_SHELL_H + + +#include +#include +#include +#include + + +class MATH_CLASS MbFaceShell; + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель оболочки на сетке кривых. + \en Constructor of a shell from a mesh of curves. \~ + \details \ru Строитель оболочки на сетке кривых, образованной двумя сечействами кривых. \n + \en Constructor of a shell from a mesh of curves formed by two sets of curves. \n \~ + \ingroup Model_Creators +*/ +//--- +class MATH_CLASS MbMeshShell : public MbCreator { +private : + MeshSurfaceValues parameters; ///< \ru Параметры построения. \en Construction parameters. + mutable bool changed; ///< \ru Флаг изменения параметров. \en Flag of parameters modification. +private: + /// \ru Конструктор копирования. \en Copy-constructor. + MbMeshShell( const MbMeshShell & obj, MbRegDuplicate * ireg ); +public: + /// \ru Конструктор по параметрам операции и именователю на оригиналах кривых и копиях поверхностей. \en Constructor by operation parameters and name-maker for original curves and copies of surfaces. + MbMeshShell( const MeshSurfaceValues & pars, const MbSNameMaker & n ); + virtual ~MbMeshShell(); + +public: // \ru Общие функции математического объекта \en Common functions of the mathematical object + virtual MbeCreatorType IsA() const; ///< \ru Тип элемента \en Element type + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; ///< \ru Сделать копию \en Make a copy + virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool SetEqual ( const MbCreator & ); ///< \ru Сделать равным \en Make equal + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); ///< \ru Преобразовать по матрице \en Transform according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); ///< \ru Сдвиг по вектору \en Translation by the vector + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); ///< \ru Поворот вокруг оси \en Rotation about an axis + + virtual MbePrompt GetPropertyName(); ///< \ru Выдать заголовок свойства объекта \en Get name of object property + virtual void GetProperties( MbProperties & ); ///< \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); ///< \ru Записать свойства объекта \en Write properties of the object + virtual void GetBasisItems( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + +public: + /// \ru Построение оболочки \en Creation of a shell + virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, + RPArray * items = NULL ); + // \ru Дать параметры. \en Get the parameters. + void GetParameters( MeshSurfaceValues & params ) const; + // \ru Установить параметры. \en Set the parameters. + void SetParameters( const MeshSurfaceValues & params ); + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbMeshShell ) +OBVIOUS_PRIVATE_COPY( MbMeshShell ) +}; // MbMeshShell + +IMPL_PERSISTENT_OPS( MbMeshShell ) + +//------------------------------------------------------------------------------ +/** \brief \ru Построить оболочку на сетке кривых. + \en Construct a shell from a mesh of curves. \~ + \details \ru Построить оболочку на сетке кривых, образованной двумя сечействами кривых. \n + Одновременно с построением оболочки функция создаёт её строитель.\n + \en Create a shell from a mesh of curves formed by two sets of curves. \n + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] parameters - \ru Параметры построения. + \en Parameters of a shell creation. \~ + \param[in] operNames - \ru Именователь граней. + \en An object for naming faces. \~ + \param[in] isPhantom - \ru Режим создания фантома. + \en Create in the phantom mode. \~ + \param[out] res - \ru Код результата операции. + \en Operation result code. \~ + \param[out] shell - \ru Построенная оболочка. + \en The resultant shell. \~ + \result \ru Возвращает строитель оболочки. + \en Returns the shell constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateMeshShell( MeshSurfaceValues & parameters, + const MbSNameMaker & operNames, + bool isPhantom, + MbResultType & res, + MbFaceShell *& shell ); + + +#endif // __CR_MESH_SHELL_H diff --git a/C3d/Include/cr_modified_nurbs_.h b/C3d/Include/cr_modified_nurbs_.h index c499843..7e5ca95 100644 --- a/C3d/Include/cr_modified_nurbs_.h +++ b/C3d/Include/cr_modified_nurbs_.h @@ -1,217 +1,217 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Строитель оболочки c деформируемыми гранями. - \en Constructor of a shell with deformable faces. -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_MODIFIED_NURBS_H -#define __CR_MODIFIED_NURBS_H - - -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель оболочки c деформируемыми гранями. - \en Constructor of a shell with deformable faces. \~ - \details \ru Строитель оболочки, выполняющий замену поверхностей указанных граней деформируемыми поверхностями. \n - \en Constructor of a shell performing replacement of the surfaces of the specified faces with deformable surfaces. \n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbModifiedNurbsItem : public MbCreator { -protected: - NurbsValues parameters; ///< \ru Параметры модифицированных поверхностей. \en Parameters of modified surfaces. - SArray itemIndices; ///< \ru Идентификаторы модифицируемых граней. \en Identifiers of faces being modified. - RPArray surfaces; ///< \ru Множество поверхностей модифицированных граней. \en A set of surfaces of the modified faces. - -public: // \ru конструктор по параметрам \en constructor by parameters - MbModifiedNurbsItem( const NurbsValues & p, const SArray & faces, - RPArray & surfs, const MbSNameMaker & names ); -private: // \ru конструктор дублирующий \en duplication constructor - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. - MbModifiedNurbsItem( const MbModifiedNurbsItem & init ); - MbModifiedNurbsItem( const MbModifiedNurbsItem & init, MbRegDuplicate * ireg ); - -public: // \ru деструктор \en destructor - virtual ~MbModifiedNurbsItem(); - -public: // \ru Общие функции математического объекта \en Common functions of the mathematical object - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru сделать копию \en create a copy - virtual bool IsSame( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool SetEqual ( const MbCreator & ); // \ru сделать равным \en make equal - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать по матрице \en Transform according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru сдвиг по вектору \en translation by a vector - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis - - virtual MbePrompt GetPropertyName(); // \ru выдать заголовок свойства объекта \en get a name of object property - virtual void GetProperties( MbProperties & properties ); // \ru выдать свойства объекта \en get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru записать свойства объекта \en set properties of the object - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - /// \ru Построение оболочки. \en creation of a shell - virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, - RPArray * items = NULL ); - virtual void Refresh( MbFaceShell & outer ); ///< \ru обновить форму оболочки \en update shape of the shell - // \ru Выдать базовые объекты. \en Get basis objects. - virtual void GetBasisItems( RPArray & s ); - - // \ru Дать параметры. \en Get the parameters. - void GetParameters( NurbsValues & params ) const { params = parameters; } - // \ru Установить параметры. \en Set the parameters. - void SetParameters( const NurbsValues & params ) { parameters = params; } - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - void operator = ( const MbModifiedNurbsItem & ); - void SurfacesFree(); // \ru Удалить поверхности \en Delete the surfaces - void SurfacesAddRef(); // \ru Учесть поверхности \en Consider the surfaces - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbModifiedNurbsItem ) -}; - -IMPL_PERSISTENT_OPS( MbModifiedNurbsItem ) - -//------------------------------------------------------------------------------ -/** \brief \ru Модификатор оболочки c деформируемой гранью. - \en Modifier of a shell with a deformable face. \~ - \details \ru Модификатор оболочки выполняет деформацию поверхности указанной грани. - Указанная грань должна быть дефолрмируемой. \n - \en Modifier of a shell performs deformation of a surface of the specified face. - The specified face should be deformable. \n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbNurbsModification : public MbCreator { -protected: - MbItemIndex faceIndex; ///< \ru Идентификатор деформируемой грани. \en Identifier of the deformable face. - MbSurface * faceSurface; ///< \ru Поверхность деформируемой грани. \en Surface of the deformable face. - Array2 fixedPoints; ///< \ru Матрица положений неизменяемых контрольных точек модифицируемой поверхности. \en Matrix of positions of the invariant control points of the modifiable surface. - -public: // \ru конструктор по параметрам \en constructor by parameters - MbNurbsModification( const MbItemIndex & index, MbSurface & fSurface, Array2 & fPoints, - const MbSNameMaker & names ); -private: // \ru конструктор дублирующий \en duplication constructor - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. - MbNurbsModification( const MbNurbsModification & init ); - MbNurbsModification( const MbNurbsModification & init, MbRegDuplicate * ireg ); - -public: // \ru деструктор \en destructor - virtual ~MbNurbsModification(); - -public: // \ru Общие функции математического объекта \en Common functions of the mathematical object - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru сделать копию \en create a copy - virtual bool IsSame( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool SetEqual ( const MbCreator & ); // \ru сделать равным \en make equal - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать по матрице \en Transform according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru сдвиг по вектору \en translation by a vector - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis - - virtual MbePrompt GetPropertyName(); // \ru выдать заголовок свойства объекта \en get a name of object property - virtual void GetProperties( MbProperties & properties ); // \ru выдать свойства объекта \en get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru записать свойства объекта \en set properties of the object - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - /// \ru построение оболочки \en creation of a shell - virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, - RPArray * items = NULL ); - virtual void Refresh( MbFaceShell & outer ); ///< \ru обновить форму оболочки \en update shape of the shell - // \ru Выдать базовые объекты. \en Get basis objects. - virtual void GetBasisItems( RPArray & s ); - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - void operator = ( const MbNurbsModification & ); // \ru не реализован!!! \en not implemented!!! - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbNurbsModification ) -}; - -IMPL_PERSISTENT_OPS( MbNurbsModification ) - -//------------------------------------------------------------------------------ -/** \brief \ru Построить оболочку c деформируемыми гранями. - \en Construct a shell with deformable faces. \~ - \details \ru Построить оболочку c заменjq указанных граней исходной оболочки деформируемыми гранями. - Поверхности выбранных граней аппроксимируются NURBS поверхностями или - деформируемыми поверхностями для последующего редактирования. - Одновременно с построением оболочки функция создаёт её строитель.\n - \en Construct a shell with replacement of the specified faces of the source shell with deformable faces. - Surfaces of the selected faces are approximated with NURBS surfaces or - deformable surfaces for the further editing. - The function simultaneously creates the shell and its constructor.\n \~ - \param[in] outer - \ru Исходная оболочка. - \en The source shell. \~ - \param[in] sameShell - \ru Режим копирования исходной оболочки. - \en Mode of copying the source shell. \~ - \param[in] parameters - \ru Параметры модификации. - \en Parameters of the modification. \~ - \param[in] faces - \ru Изменяемые грани тела. - \en Faces to be modified. \~ - \param[in] names - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[out] res - \ru Код результата операции выдавливания. - \en The extrusion operation result code. \~ - \param[out] shell - \ru Построенная оболочка. - \en The resultant shell. \~ - \result \ru Возвращает строитель. - \en Returns the constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateModifiedNurbsItem( MbFaceShell * outer, - MbeCopyMode sameShell, - const NurbsValues & parameters, - const RPArray & faces, - const MbSNameMaker & names, - MbResultType & res, - MbFaceShell *& shell ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить оболочку, в которой деформирована указанная грань. - \en Construct a shell in which the specified face is deformed. \~ - \details \ru Построить оболочку, в которой деформирована указанная грань путём - подстановки контрольных точек присланной NURBS-поверхности с фиксацией указанных точек.\n - Одновременно с построением оболочки функция создаёт её строитель.\n - \en Construct a shell in which the specified face is deformed by - replacement of the control points of the given NURBS surface with fixing the specified points.\n - The function simultaneously creates the shell and its constructor.\n \~ - \param[in] outer - \ru Исходная оболочка. - \en The source shell. \~ - \param[in] sameShell - \ru Режим копирования исходной оболочки. - \en Mode of copying the source shell. \~ - \param[in] face - \ru Деформируемая грань оболочки. - \en Deformable face of the shell. \~ - \param[in] faceSurface - \ru Новая деформируемая поверхность для грани. - \en The new deformable surface of the face. \~ - \param[in] fixedPoints - \ru Матрица положений неизменяемых контрольных точек деформируемой поверхности (false). - \en Matrix of positions of invariant control points of the deformable surface (false). \~ - \param[in] names - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[out] res - \ru Код результата операции выдавливания. - \en The extrusion operation result code. \~ - \param[out] shell - \ru Построенная оболочка. - \en The resultant shell. \~ - \result \ru Возвращает строитель оболочки. - \en Returns the shell constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateNurbsModification( MbFaceShell * outer, - MbeCopyMode sameShell, - MbFace * face, - MbSurface & faceSurface, - Array2 & fixedPoints, - const MbSNameMaker & names, - MbResultType & res, - MbFaceShell *& shell ); - - -#endif // __CR_MODIFIED_NURBS_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Строитель оболочки c деформируемыми гранями. + \en Constructor of a shell with deformable faces. +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_MODIFIED_NURBS_H +#define __CR_MODIFIED_NURBS_H + + +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель оболочки c деформируемыми гранями. + \en Constructor of a shell with deformable faces. \~ + \details \ru Строитель оболочки, выполняющий замену поверхностей указанных граней деформируемыми поверхностями. \n + \en Constructor of a shell performing replacement of the surfaces of the specified faces with deformable surfaces. \n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbModifiedNurbsItem : public MbCreator { +protected: + NurbsValues parameters; ///< \ru Параметры модифицированных поверхностей. \en Parameters of modified surfaces. + SArray itemIndices; ///< \ru Идентификаторы модифицируемых граней. \en Identifiers of faces being modified. + RPArray surfaces; ///< \ru Множество поверхностей модифицированных граней. \en A set of surfaces of the modified faces. + +public: // \ru конструктор по параметрам \en constructor by parameters + MbModifiedNurbsItem( const NurbsValues & p, const SArray & faces, + RPArray & surfs, const MbSNameMaker & names ); +private: // \ru конструктор дублирующий \en duplication constructor + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. + MbModifiedNurbsItem( const MbModifiedNurbsItem & init ); + MbModifiedNurbsItem( const MbModifiedNurbsItem & init, MbRegDuplicate * ireg ); + +public: // \ru деструктор \en destructor + virtual ~MbModifiedNurbsItem(); + +public: // \ru Общие функции математического объекта \en Common functions of the mathematical object + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru сделать копию \en create a copy + virtual bool IsSame( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool SetEqual ( const MbCreator & ); // \ru сделать равным \en make equal + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать по матрице \en Transform according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru сдвиг по вектору \en translation by a vector + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis + + virtual MbePrompt GetPropertyName(); // \ru выдать заголовок свойства объекта \en get a name of object property + virtual void GetProperties( MbProperties & properties ); // \ru выдать свойства объекта \en get properties of the object + virtual void SetProperties( const MbProperties & properties ); // \ru записать свойства объекта \en set properties of the object + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + /// \ru Построение оболочки. \en creation of a shell + virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, + RPArray * items = NULL ); + virtual void Refresh( MbFaceShell & outer ); ///< \ru обновить форму оболочки \en update shape of the shell + // \ru Выдать базовые объекты. \en Get basis objects. + virtual void GetBasisItems( RPArray & s ); + + // \ru Дать параметры. \en Get the parameters. + void GetParameters( NurbsValues & params ) const { params = parameters; } + // \ru Установить параметры. \en Set the parameters. + void SetParameters( const NurbsValues & params ) { parameters = params; } + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + void operator = ( const MbModifiedNurbsItem & ); + void SurfacesFree(); // \ru Удалить поверхности \en Delete the surfaces + void SurfacesAddRef(); // \ru Учесть поверхности \en Consider the surfaces + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbModifiedNurbsItem ) +}; + +IMPL_PERSISTENT_OPS( MbModifiedNurbsItem ) + +//------------------------------------------------------------------------------ +/** \brief \ru Модификатор оболочки c деформируемой гранью. + \en Modifier of a shell with a deformable face. \~ + \details \ru Модификатор оболочки выполняет деформацию поверхности указанной грани. + Указанная грань должна быть дефолрмируемой. \n + \en Modifier of a shell performs deformation of a surface of the specified face. + The specified face should be deformable. \n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbNurbsModification : public MbCreator { +protected: + MbItemIndex faceIndex; ///< \ru Идентификатор деформируемой грани. \en Identifier of the deformable face. + MbSurface * faceSurface; ///< \ru Поверхность деформируемой грани. \en Surface of the deformable face. + Array2 fixedPoints; ///< \ru Матрица положений неизменяемых контрольных точек модифицируемой поверхности. \en Matrix of positions of the invariant control points of the modifiable surface. + +public: // \ru конструктор по параметрам \en constructor by parameters + MbNurbsModification( const MbItemIndex & index, MbSurface & fSurface, Array2 & fPoints, + const MbSNameMaker & names ); +private: // \ru конструктор дублирующий \en duplication constructor + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. + MbNurbsModification( const MbNurbsModification & init ); + MbNurbsModification( const MbNurbsModification & init, MbRegDuplicate * ireg ); + +public: // \ru деструктор \en destructor + virtual ~MbNurbsModification(); + +public: // \ru Общие функции математического объекта \en Common functions of the mathematical object + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru сделать копию \en create a copy + virtual bool IsSame( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool SetEqual ( const MbCreator & ); // \ru сделать равным \en make equal + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать по матрице \en Transform according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru сдвиг по вектору \en translation by a vector + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis + + virtual MbePrompt GetPropertyName(); // \ru выдать заголовок свойства объекта \en get a name of object property + virtual void GetProperties( MbProperties & properties ); // \ru выдать свойства объекта \en get properties of the object + virtual void SetProperties( const MbProperties & properties ); // \ru записать свойства объекта \en set properties of the object + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + /// \ru построение оболочки \en creation of a shell + virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, + RPArray * items = NULL ); + virtual void Refresh( MbFaceShell & outer ); ///< \ru обновить форму оболочки \en update shape of the shell + // \ru Выдать базовые объекты. \en Get basis objects. + virtual void GetBasisItems( RPArray & s ); + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + void operator = ( const MbNurbsModification & ); // \ru не реализован!!! \en not implemented!!! + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbNurbsModification ) +}; + +IMPL_PERSISTENT_OPS( MbNurbsModification ) + +//------------------------------------------------------------------------------ +/** \brief \ru Построить оболочку c деформируемыми гранями. + \en Construct a shell with deformable faces. \~ + \details \ru Построить оболочку c заменой указанных граней исходной оболочки деформируемыми гранями. + Поверхности выбранных граней аппроксимируются NURBS поверхностями или + деформируемыми поверхностями для последующего редактирования. + Одновременно с построением оболочки функция создаёт её строитель.\n + \en Construct a shell with replacement of the specified faces of the source shell with deformable faces. + Surfaces of the selected faces are approximated with NURBS surfaces or + deformable surfaces for the further editing. + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] outer - \ru Исходная оболочка. + \en The source shell. \~ + \param[in] sameShell - \ru Режим копирования исходной оболочки. + \en Mode of copying the source shell. \~ + \param[in] parameters - \ru Параметры модификации. + \en Parameters of the modification. \~ + \param[in] faces - \ru Изменяемые грани тела. + \en Faces to be modified. \~ + \param[in] names - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[out] res - \ru Код результата операции выдавливания. + \en The extrusion operation result code. \~ + \param[out] shell - \ru Построенная оболочка. + \en The resultant shell. \~ + \result \ru Возвращает строитель. + \en Returns the constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateModifiedNurbsItem( MbFaceShell * outer, + MbeCopyMode sameShell, + const NurbsValues & parameters, + const RPArray & faces, + const MbSNameMaker & names, + MbResultType & res, + MbFaceShell *& shell ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить оболочку, в которой деформирована указанная грань. + \en Construct a shell in which the specified face is deformed. \~ + \details \ru Построить оболочку, в которой деформирована указанная грань путём + подстановки контрольных точек присланной NURBS-поверхности с фиксацией указанных точек.\n + Одновременно с построением оболочки функция создаёт её строитель.\n + \en Construct a shell in which the specified face is deformed by + replacement of the control points of the given NURBS surface with fixing the specified points.\n + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] outer - \ru Исходная оболочка. + \en The source shell. \~ + \param[in] sameShell - \ru Режим копирования исходной оболочки. + \en Mode of copying the source shell. \~ + \param[in] face - \ru Деформируемая грань оболочки. + \en Deformable face of the shell. \~ + \param[in] faceSurface - \ru Новая деформируемая поверхность для грани. + \en The new deformable surface of the face. \~ + \param[in] fixedPoints - \ru Матрица положений неизменяемых контрольных точек деформируемой поверхности (false). + \en Matrix of positions of invariant control points of the deformable surface (false). \~ + \param[in] names - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[out] res - \ru Код результата операции выдавливания. + \en The extrusion operation result code. \~ + \param[out] shell - \ru Построенная оболочка. + \en The resultant shell. \~ + \result \ru Возвращает строитель оболочки. + \en Returns the shell constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateNurbsModification( MbFaceShell * outer, + MbeCopyMode sameShell, + MbFace * face, + MbSurface & faceSurface, + Array2 & fixedPoints, + const MbSNameMaker & names, + MbResultType & res, + MbFaceShell *& shell ); + + +#endif // __CR_MODIFIED_NURBS_H diff --git a/C3d/Include/cr_nurbs3d.h b/C3d/Include/cr_nurbs3d.h index d5dbc14..48ba21e 100644 --- a/C3d/Include/cr_nurbs3d.h +++ b/C3d/Include/cr_nurbs3d.h @@ -1,129 +1,129 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Строитель пространственного сплайна с сопряжениями. - \en Constructor of the spatial spline with tangents. -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_NURBS3D_H -#define __CR_NURBS3D_H - - -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель пространственного сплайна. - \en Spatial spline constructor. \~ - \details \ru Строитель пространственного сплайна.\n - \en Spatial spline constructor.\n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbNurbs3DCreator : public MbCreator { -private: - SArray points; // \ru Точки, через которые проходит сплайн \en Points which the spline passes through - SArray weights; // \ru Веса \en Weights - SArray knots; // \ru Узлы \en Knots - RPArray< MbPntMatingData > matingData; // \ru Данные сопряжения в точках \en Data about mating in the points - MbeSplineParamType paramType; // \ru Тип параметризации \en Parametrization type - size_t degree; // \ru Степень сплайна \en Spline degree - bool closed; // \ru Замкнутость сплайна \en Spline closedness - bool throughPnts; // \ru через точки \en Through points - -protected: - MbNurbs3DCreator( const MbNurbs3DCreator &, MbRegDuplicate * iReg ); // \ru Конструктор копирования \en Copy-constructor - MbNurbs3DCreator( const MbNurbs3DCreator & ); // \ru Не реализовано \en Not implemented - MbNurbs3DCreator(); // \ru Не реализовано \en Not implemented -public: - MbNurbs3DCreator( const SArray & spacePnts, bool throughPnts, - MbeSplineParamType paramType, size_t degree, bool closed, - const SArray * weights, - const SArray * knots, - const RPArray< MbPntMatingData > & matingData, - const MbSNameMaker & snMaker ); -public: - virtual ~MbNurbs3DCreator(); - - // \ru Общие функции строителя. \en The common functions of the creator. - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element - virtual MbCreator & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Сделать копию \en Create a copy - - virtual bool IsSame ( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal - - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - - virtual MbePrompt GetPropertyName(); // \ru Дать имя свойства объекта \en Get the object property name - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - // \ru Построить кривую по журналу построения \en Create a curve from the history tree - virtual bool CreateWireFrame( MbWireFrame *&, MbeCopyMode, RPArray * items = NULL ); - - /** \} */ - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. - void operator = ( const MbNurbs3DCreator & ); // \ru Не реализовано!!! \en Not implemented!!! - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbNurbs3DCreator ) -}; - -IMPL_PERSISTENT_OPS( MbNurbs3DCreator ) - -//------------------------------------------------------------------------------ -/** \brief \ru Создать пространственный сплайн через точки и с сопряжениями. - \en Create a spatial spline through points and with the given tangents. \~ - \details \ru Создать пространственный сплайн через точки и с сопряжениями - Если есть сопряжения, то количество сопряжений д.б. равно количеству точек. - Отсутствующие сопряжения должны быть представлены нулевыми указателями в массиве. - \en Create a spatial spline through points with tangents - If the tangents are specified, then the number of tangents should be equal to the number of points. - The missing tangents should be represented as the null pointers in the array. \~ - \result \ru Возвращает строитель. - \en Returns the constructor. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbCreator *) CreateSplineThrough( const SArray & points, // \ru Точки \en Points - MbeSplineParamType paramType, // \ru Тип параметризации \en Parametrization type - size_t degree, // \ru Порядок сплайна \en Spline degree - bool closed, // \ru Замкнуть \en Make close - RPArray< MbPntMatingData > & transitions, // \ru Сопряжения \en Tangents - const MbSNameMaker & snMaker, // \ru Именователь \en An object for naming the new objects - MbResultType & resType, - MbCurve3D *& resCurve ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать пространственный сплайн по точкам и сопряжениями. - \en Create a spatial spline from points and tangents. \~ - \details \ru Создать пространственный сплайн по точкам и сопряжениями.\n - \en Create a spatial spline from points and tangents.\n \~ - \result \ru Возвращает строитель. - \en Returns the constructor. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbCreator *) CreateSplineBy( const SArray & points, // \ru Точки \en Points - size_t degree, // \ru Порядок сплайна \en Spline degree - bool closed, // \ru Замкнуть \en Make close - const SArray * weights, // \ru Веса \en Weights - const SArray * knots, // \ru Узлы \en Knots - MbPntMatingData * begData, // \ru Сопряжение в начале \en Tangent at the start point - MbPntMatingData * endData, // \ru Сопряжение в конце \en Tangent at the end point - const MbSNameMaker & snMaker, // \ru Именователь \en An object for naming the new objects - MbResultType & resType, - MbCurve3D *& resCurve ); - - -#endif // __CR_NURBS3D_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Строитель пространственного сплайна с сопряжениями. + \en Constructor of the spatial spline with tangents. +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_NURBS3D_H +#define __CR_NURBS3D_H + + +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель пространственного сплайна. + \en Spatial spline constructor. \~ + \details \ru Строитель пространственного сплайна.\n + \en Spatial spline constructor.\n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbNurbs3DCreator : public MbCreator { +private: + SArray points; // \ru Точки, через которые проходит сплайн \en Points which the spline passes through + SArray weights; // \ru Веса \en Weights + SArray knots; // \ru Узлы \en Knots + RPArray< MbPntMatingData > matingData; // \ru Данные сопряжения в точках \en Data about mating in the points + MbeSplineParamType paramType; // \ru Тип параметризации \en Parametrization type + size_t degree; // \ru Степень сплайна \en Spline degree + bool closed; // \ru Замкнутость сплайна \en Spline closedness + bool throughPnts; // \ru через точки \en Through points + +protected: + MbNurbs3DCreator( const MbNurbs3DCreator &, MbRegDuplicate * iReg ); // \ru Конструктор копирования \en Copy-constructor + MbNurbs3DCreator( const MbNurbs3DCreator & ); // \ru Не реализовано \en Not implemented + MbNurbs3DCreator(); // \ru Не реализовано \en Not implemented +public: + MbNurbs3DCreator( const SArray & spacePnts, bool throughPnts, + MbeSplineParamType paramType, size_t degree, bool closed, + const SArray * weights, + const SArray * knots, + const RPArray< MbPntMatingData > & matingData, + const MbSNameMaker & snMaker ); +public: + virtual ~MbNurbs3DCreator(); + + // \ru Общие функции строителя. \en The common functions of the creator. + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element + virtual MbCreator & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Сделать копию \en Create a copy + + virtual bool IsSame ( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + + virtual MbePrompt GetPropertyName(); // \ru Дать имя свойства объекта \en Get the object property name + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + // \ru Построить кривую по журналу построения \en Create a curve from the history tree + virtual bool CreateWireFrame( MbWireFrame *&, MbeCopyMode, RPArray * items = NULL ); + + /** \} */ + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. + void operator = ( const MbNurbs3DCreator & ); // \ru Не реализовано!!! \en Not implemented!!! + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbNurbs3DCreator ) +}; + +IMPL_PERSISTENT_OPS( MbNurbs3DCreator ) + +//------------------------------------------------------------------------------ +/** \brief \ru Создать пространственный сплайн через точки и с сопряжениями. + \en Create a spatial spline through points and with the given tangents. \~ + \details \ru Создать пространственный сплайн через точки и с сопряжениями + Если есть сопряжения, то количество сопряжений д.б. равно количеству точек. + Отсутствующие сопряжения должны быть представлены нулевыми указателями в массиве. + \en Create a spatial spline through points with tangents + If the tangents are specified, then the number of tangents should be equal to the number of points. + The missing tangents should be represented as the null pointers in the array. \~ + \result \ru Возвращает строитель. + \en Returns the constructor. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbCreator *) CreateSplineThrough( const SArray & points, // \ru Точки \en Points + MbeSplineParamType paramType, // \ru Тип параметризации \en Parametrization type + size_t degree, // \ru Порядок сплайна \en Spline degree + bool closed, // \ru Замкнуть \en Make close + RPArray< MbPntMatingData > & transitions, // \ru Сопряжения \en Tangents + const MbSNameMaker & snMaker, // \ru Именователь \en An object for naming the new objects + MbResultType & resType, + MbCurve3D *& resCurve ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать пространственный сплайн по точкам и сопряжениями. + \en Create a spatial spline from points and tangents. \~ + \details \ru Создать пространственный сплайн по точкам и сопряжениями.\n + \en Create a spatial spline from points and tangents.\n \~ + \result \ru Возвращает строитель. + \en Returns the constructor. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbCreator *) CreateSplineBy( const SArray & points, // \ru Точки \en Points + size_t degree, // \ru Порядок сплайна \en Spline degree + bool closed, // \ru Замкнуть \en Make close + const SArray * weights, // \ru Веса \en Weights + const SArray * knots, // \ru Узлы \en Knots + MbPntMatingData * begData, // \ru Сопряжение в начале \en Tangent at the start point + MbPntMatingData * endData, // \ru Сопряжение в конце \en Tangent at the end point + const MbSNameMaker & snMaker, // \ru Именователь \en An object for naming the new objects + MbResultType & resType, + MbCurve3D *& resCurve ); + + +#endif // __CR_NURBS3D_H diff --git a/C3d/Include/cr_nurbs_surfaces_shell.h b/C3d/Include/cr_nurbs_surfaces_shell.h index 623ab2c..02a0ede 100644 --- a/C3d/Include/cr_nurbs_surfaces_shell.h +++ b/C3d/Include/cr_nurbs_surfaces_shell.h @@ -1,73 +1,73 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Создание оболочки из нурбс-поверхностей -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __NURBS_SURFACES_SHELL_H -#define __NURBS_SURFACES_SHELL_H - -#include -#include -#include - - -class MATH_CLASS MbCreator; -class MATH_CLASS MbSNameMaker; -class MATH_CLASS MbFaceShell; -struct NurbsSurfaceValues; -class IProgressIndicator; - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить оболочку из NURBS-поверхностей. - \en Construct a shell from NURBS-surfaces. \~ - \details \ru Построить оболочку из NURBS-поверхностей MbSplineSurface по заданному множеству точек условно расположенных в узлах четырехугольной сетки. \n - \en Construct a shell from NURBS-surfaces MbSplineSurface by a given set of points conventionally located at the nodes of a quadrangle grid. \n \~ - \param[in] parameters - \ru Параметры построения. - \en Parameters of a shell creation. \~ - \param[in] operNames - \ru Именователь граней. - \en An object for naming faces. \~ - \param[in] isPhantom - \ru Режим создания фантома. - \en Create in the phantom mode. \~ - \param[out] res - \ru Код результата операции. - \en Operation result code. \~ - \param[out] shell - \ru Построенная оболочка. - \en The resultant shell. \~ - \param[out] indicator - \ru Индикатор хода построения позволяющий прервать построение. - \en Construction process indicator which allow to interrupt the construction. \~ - \result \ru Возвращает оболочку. - \en Returns the constructуed shell. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbFaceShell *) CreateNurbsSurfacesShell( NurbsSurfaceValues & params, - const MbSNameMaker & operNames, - bool isPhantom, - MbResultType & res, - IProgressIndicator * = NULL ); - - -//------------------------------------------------------------------------------ -// проверить оболочку из нурбс-поверхностей -/** \brief \ru Построить оболочку из NURBS-поверхностей. - \en Construct a shell from NURBS-surfaces. \~ - \details \ru Построить оболочку из NURBS-поверхностей MbSplineSurface по заданному множеству точек условно расположенных в узлах четырехугольной сетки. \n - \en Construct a shell from NURBS-surfaces MbSplineSurface by a given set of points conventionally located at the nodes of a quadrangle grid. \n \~ - \param[in] parameters - \ru Параметры построения. - \en Parameters of a shell creation. \~ - \param[in] shell - \ru Оболочка, построенная по заданным параметрам. - \en The shell constructed by given parameters. \~ - \param[out] indicator - \ru Индикатор хода построения позволяющий прервать построение. - \en Construction process indicator which allow to interrupt the construction. \~ - \result \ru Возвращает код результата операции. - \en Returns operation result code. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbResultType) CheckNurbsSurfacesShell( const NurbsSurfaceValues & params, - const MbFaceShell & shell, - IProgressIndicator * = NULL ); - - -#endif // __NURBS_SURFACES_SHELL_H +//////////////////////////////////////////////////////////////////////////////// +// +// Создание оболочки из нурбс-поверхностей +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __NURBS_SURFACES_SHELL_H +#define __NURBS_SURFACES_SHELL_H + +#include +#include +#include + + +class MATH_CLASS MbCreator; +class MATH_CLASS MbSNameMaker; +class MATH_CLASS MbFaceShell; +struct NurbsSurfaceValues; +class IProgressIndicator; + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить оболочку из NURBS-поверхностей. + \en Construct a shell from NURBS-surfaces. \~ + \details \ru Построить оболочку из NURBS-поверхностей MbSplineSurface по заданному множеству точек условно расположенных в узлах четырехугольной сетки. \n + \en Construct a shell from NURBS-surfaces MbSplineSurface by a given set of points conventionally located at the nodes of a quadrangle grid. \n \~ + \param[in] parameters - \ru Параметры построения. + \en Parameters of a shell creation. \~ + \param[in] operNames - \ru Именователь граней. + \en An object for naming faces. \~ + \param[in] isPhantom - \ru Режим создания фантома. + \en Create in the phantom mode. \~ + \param[out] res - \ru Код результата операции. + \en Operation result code. \~ + \param[out] shell - \ru Построенная оболочка. + \en The resultant shell. \~ + \param[out] indicator - \ru Индикатор хода построения позволяющий прервать построение. + \en Construction process indicator which allow to interrupt the construction. \~ + \result \ru Возвращает оболочку. + \en Returns the constructуed shell. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbFaceShell *) CreateNurbsSurfacesShell( NurbsSurfaceValues & params, + const MbSNameMaker & operNames, + bool isPhantom, + MbResultType & res, + IProgressIndicator * = NULL ); + + +//------------------------------------------------------------------------------ +// проверить оболочку из нурбс-поверхностей +/** \brief \ru Построить оболочку из NURBS-поверхностей. + \en Construct a shell from NURBS-surfaces. \~ + \details \ru Построить оболочку из NURBS-поверхностей MbSplineSurface по заданному множеству точек условно расположенных в узлах четырехугольной сетки. \n + \en Construct a shell from NURBS-surfaces MbSplineSurface by a given set of points conventionally located at the nodes of a quadrangle grid. \n \~ + \param[in] parameters - \ru Параметры построения. + \en Parameters of a shell creation. \~ + \param[in] shell - \ru Оболочка, построенная по заданным параметрам. + \en The shell constructed by given parameters. \~ + \param[out] indicator - \ru Индикатор хода построения позволяющий прервать построение. + \en Construction process indicator which allow to interrupt the construction. \~ + \result \ru Возвращает код результата операции. + \en Returns operation result code. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbResultType) CheckNurbsSurfacesShell( const NurbsSurfaceValues & params, + const MbFaceShell & shell, + IProgressIndicator * = NULL ); + + +#endif // __NURBS_SURFACES_SHELL_H diff --git a/C3d/Include/cr_nurbs_surfaces_solid.h b/C3d/Include/cr_nurbs_surfaces_solid.h index 32516a7..6cec11e 100644 --- a/C3d/Include/cr_nurbs_surfaces_solid.h +++ b/C3d/Include/cr_nurbs_surfaces_solid.h @@ -1,116 +1,116 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Построение оболочки из NURBS-поверхностей. - \en Construction of a sell from NURBS-surfaces. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_NURBS_SURFACES_SOLID_H -#define __CR_NURBS_SURFACES_SOLID_H - - -#include -#include -#include -#include - - -class MATH_CLASS MbCreator; -class MATH_CLASS MbSNameMaker; -class MATH_CLASS MbFaceShell; -struct MATH_CLASS NurbsSurfaceValues; -class IProgressIndicator; - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель оболочки из NURBS-поверхностей. - \en Constructor of a shell from NURBS-surfaces. \~ - \details \ru Строитель оболочки из NURBS-поверхностей MbSplineSurface. - Аббревиатура NURBS получена из первых букв словосочетания Non-Uniform Rational B-Spline. - \en Constructor of a shell from NURBS-surfaces MbSplineSurface. - Abbreviation of NURBS is obtained from the first letters of "Non-Uniform Rational B-Spline" phrase. \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbNurbsSurfacesSolid : public MbCreator { -protected: - NurbsSurfaceValues parameters; ///< \ru Параметры построения. \en Construction parameters. - mutable bool changed; ///< \ru Флаг изменения параметров. \en Flag of parameters modification. - -public: - // \ru конструктор, копирующий параметры \en constructor copying the parameters - MbNurbsSurfacesSolid( const NurbsSurfaceValues & params, const MbSNameMaker & names ); -private: - MbNurbsSurfacesSolid( const MbNurbsSurfacesSolid &, MbRegDuplicate * ireg ); -public: - // \ru деструктор \en destructor - ~MbNurbsSurfacesSolid(); - -public: // \ru Общие функции математического объекта \en Common functions of the mathematical object - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru сделать копию \en create a copy - virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool SetEqual ( const MbCreator & ); // \ru сделать равным \en make equal - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать по матрице \en Transform according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru сдвиг по вектору \en translation by a vector - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - - virtual MbePrompt GetPropertyName(); // \ru выдать заголовок свойства объекта \en get a name of object property - virtual void GetProperties( MbProperties & properties ); // \ru выдать свойства объекта \en get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru записать свойства объекта \en set properties of the object - virtual void GetBasisItems ( RPArray & s ); // \ru дать базовые объекты \en get the basis objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - -public: - /// \ru построение оболочки \en creation of a shell - virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, - RPArray * items = NULL ); - virtual void Refresh( MbFaceShell & outer ); ///< \ru обновить форму оболочки \en update shape of the shell - - // \ru Дать параметры. \en Get the parameters. - void GetParameters( NurbsSurfaceValues & params ) const { params = parameters; } - // \ru Установить параметры. \en Set the parameters. - void SetParameters( const NurbsSurfaceValues & params ) { parameters = params; } - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbNurbsSurfacesSolid ) -OBVIOUS_PRIVATE_COPY( MbNurbsSurfacesSolid ) -}; - -IMPL_PERSISTENT_OPS( MbNurbsSurfacesSolid ) - -//------------------------------------------------------------------------------ -/** \brief \ru Построить оболочку из NURBS-поверхностей. - \en Construct a shell from NURBS-surfaces. \~ - \details \ru Построить оболочку из NURBS-поверхностей MbSplineSurface. - Одновременно с построением оболочки функция создаёт её строитель.\n - \en Construct a shell from NURBS-surfaces MbSplineSurface. - The function simultaneously creates the shell and its constructor.\n \~ - \param[in] parameters - \ru Параметры построения. - \en Parameters of a shell creation. \~ - \param[in] operNames - \ru Именователь граней. - \en An object for naming faces. \~ - \param[in] isPhantom - \ru Режим создания фантома. - \en Create in the phantom mode. \~ - \param[out] res - \ru Код результата операции. - \en Operation result code. \~ - \param[out] shell - \ru Построенная оболочка. - \en The resultant shell. \~ - \param[out] indicator - \ru Индикатор хода построения. - \en Construction process indicator. \~ - \result \ru Возвращает строитель оболочки. - \en Returns the shell constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateNurbsShell( NurbsSurfaceValues & parameters, - const MbSNameMaker & operNames, - bool isPhantom, - MbResultType & res, - MbFaceShell *& shell, - IProgressIndicator * indicator = NULL ); - - -#endif // __CR_NURBS_SURFACES_SOLID_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Построение оболочки из NURBS-поверхностей. + \en Construction of a sell from NURBS-surfaces. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_NURBS_SURFACES_SOLID_H +#define __CR_NURBS_SURFACES_SOLID_H + + +#include +#include +#include +#include + + +class MATH_CLASS MbCreator; +class MATH_CLASS MbSNameMaker; +class MATH_CLASS MbFaceShell; +struct MATH_CLASS NurbsSurfaceValues; +class IProgressIndicator; + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель оболочки из NURBS-поверхностей. + \en Constructor of a shell from NURBS-surfaces. \~ + \details \ru Строитель оболочки из NURBS-поверхностей MbSplineSurface. + Аббревиатура NURBS получена из первых букв словосочетания Non-Uniform Rational B-Spline. + \en Constructor of a shell from NURBS-surfaces MbSplineSurface. + Abbreviation of NURBS is obtained from the first letters of "Non-Uniform Rational B-Spline" phrase. \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbNurbsSurfacesSolid : public MbCreator { +protected: + NurbsSurfaceValues parameters; ///< \ru Параметры построения. \en Construction parameters. + mutable bool changed; ///< \ru Флаг изменения параметров. \en Flag of parameters modification. + +public: + // \ru конструктор, копирующий параметры \en constructor copying the parameters + MbNurbsSurfacesSolid( const NurbsSurfaceValues & params, const MbSNameMaker & names ); +private: + MbNurbsSurfacesSolid( const MbNurbsSurfacesSolid &, MbRegDuplicate * ireg ); +public: + // \ru деструктор \en destructor + ~MbNurbsSurfacesSolid(); + +public: // \ru Общие функции математического объекта \en Common functions of the mathematical object + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru сделать копию \en create a copy + virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool SetEqual ( const MbCreator & ); // \ru сделать равным \en make equal + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать по матрице \en Transform according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru сдвиг по вектору \en translation by a vector + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + + virtual MbePrompt GetPropertyName(); // \ru выдать заголовок свойства объекта \en get a name of object property + virtual void GetProperties( MbProperties & properties ); // \ru выдать свойства объекта \en get properties of the object + virtual void SetProperties( const MbProperties & properties ); // \ru записать свойства объекта \en set properties of the object + virtual void GetBasisItems ( RPArray & s ); // \ru дать базовые объекты \en get the basis objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + +public: + /// \ru построение оболочки \en creation of a shell + virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, + RPArray * items = NULL ); + virtual void Refresh( MbFaceShell & outer ); ///< \ru обновить форму оболочки \en update shape of the shell + + // \ru Дать параметры. \en Get the parameters. + void GetParameters( NurbsSurfaceValues & params ) const { params = parameters; } + // \ru Установить параметры. \en Set the parameters. + void SetParameters( const NurbsSurfaceValues & params ) { parameters = params; } + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbNurbsSurfacesSolid ) +OBVIOUS_PRIVATE_COPY( MbNurbsSurfacesSolid ) +}; + +IMPL_PERSISTENT_OPS( MbNurbsSurfacesSolid ) + +//------------------------------------------------------------------------------ +/** \brief \ru Построить оболочку из NURBS-поверхностей. + \en Construct a shell from NURBS-surfaces. \~ + \details \ru Построить оболочку из NURBS-поверхностей MbSplineSurface. + Одновременно с построением оболочки функция создаёт её строитель.\n + \en Construct a shell from NURBS-surfaces MbSplineSurface. + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] parameters - \ru Параметры построения. + \en Parameters of a shell creation. \~ + \param[in] operNames - \ru Именователь граней. + \en An object for naming faces. \~ + \param[in] isPhantom - \ru Режим создания фантома. + \en Create in the phantom mode. \~ + \param[out] res - \ru Код результата операции. + \en Operation result code. \~ + \param[out] shell - \ru Построенная оболочка. + \en The resultant shell. \~ + \param[out] indicator - \ru Индикатор хода построения. + \en Construction process indicator. \~ + \result \ru Возвращает строитель оболочки. + \en Returns the shell constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateNurbsShell( NurbsSurfaceValues & parameters, + const MbSNameMaker & operNames, + bool isPhantom, + MbResultType & res, + MbFaceShell *& shell, + IProgressIndicator * indicator = NULL ); + + +#endif // __CR_NURBS_SURFACES_SOLID_H diff --git a/C3d/Include/cr_offset_curve.h b/C3d/Include/cr_offset_curve.h index 6e3952c..7cfd3fc 100644 --- a/C3d/Include/cr_offset_curve.h +++ b/C3d/Include/cr_offset_curve.h @@ -1,167 +1,167 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Строитель эквидистантной кривой. - \en Offset curve constructor. -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_OFFSET_CURVE_H -#define __CR_OFFSET_CURVE_H - - -#include -#include -#include - - -class MATH_CLASS MbCurve3D; - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель эквидистантной кривой. - \en Offset curve constructor. \~ - \details \ru Строитель эквидистантной кривой.\n - \en Offset curve constructor.\n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbOffsetCurveCreator : public MbCreator { -private: - // \ru Основные параметры \en The basic parameters - SPtr curve; // \ru Исходная кривая. \en The initial curve. - MbVector3D dir; // \ru Направление смещения. \en The offset direction. - double dist; // \ru Величина смещения. \en The offset distance. - bool fromBeg; // \ru Вектор смещения привязан к началу кривой (иначе к концу). \en The translation vector is associated with the beginning (with the end otherwise). - - // \ru Дополнительные параметры (эквидистанта в пространстве) \en Auxiliary parameters (spatial offset) - bool useFillet; // \ru Заполнять ли разрывы скруглениями (иначе продлять сегменты). \en Whether to fill the gaps with fillets (extend segments otherwise). - bool keepRadius; // \ru Сохранять ли радиусы в скруглениях. \en Whether to keep the radii at fillets. - bool bluntAngle; // \ru Притуплять острые углы стыков сегментов \en Whether to blunt the sharp edges of segments joints. - - // \ru Дополнительные параметры (эквидистанта на поверхности грани оболочки) \en Auxiliary parameters (offset on the shell face surface) - c3d::CreatorsSPtrVector shellCreators; // \ru Журнал построения оболочки. \en The shell history tree. - -protected: - MbOffsetCurveCreator( const MbOffsetCurveCreator &, MbRegDuplicate * iReg ); // \ru Конструктор копирования \en Copy-constructor - MbOffsetCurveCreator( const MbOffsetCurveCreator & ); // \ru Не реализовано \en Not implemented - MbOffsetCurveCreator(); // \ru Не реализовано \en Not implemented -public: - // \ru Конструктор эквидистанты в пространстве \en Constructor of offset in the space - MbOffsetCurveCreator( const MbCurve3D &, bool fromBeg, const MbVector3D & dir, double dist, - bool useFillet, bool keepRadius, bool bluntAngle, - const MbSNameMaker & snMaker ); - // \ru Конструктор эквидистанты на поверхности грани оболочки \en Constructor of offset on the shell face surface - MbOffsetCurveCreator( const MbCurve3D &, bool fromBeg, const MbVector3D & dir, double dist, - const RPArray & shellCreators, bool sameCreators, - const MbSNameMaker & snMaker ); -public : - virtual ~MbOffsetCurveCreator(); - - // \ru Общие функции строителя. \en The common functions of the creator. - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element - virtual MbCreator & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Сделать копию \en Create a copy - - virtual bool IsSame ( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal - - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - - virtual MbePrompt GetPropertyName(); // \ru Дать имя свойства объекта \en Get the object property name - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - virtual size_t GetCreatorsCount( MbeCreatorType ct ) const; // \ru Посчитать внутренние построители по типу. \en Count internal creators by type. - virtual bool GetInternalCreators( MbeCreatorType, c3d::ConstCreatorsSPtrVector & ) const; // \ru Получить внутренние построители по типу. \en Get internal creators by type. - virtual bool SetInternalCreators( MbeCreatorType, c3d::CreatorsSPtrVector & ); // \ru Получить внутренние построители по типу. \en Get internal creators by type. - - // \ru Построить кривую по журналу построения \en Create a curve from the history tree - virtual bool CreateWireFrame( MbWireFrame *&, MbeCopyMode, RPArray * items = NULL ); - - /** \} */ - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. - void operator = ( const MbOffsetCurveCreator & ); // \ru Не реализовано!!! \en Not implemented!!! - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbOffsetCurveCreator ) -}; - -IMPL_PERSISTENT_OPS( MbOffsetCurveCreator ) - -//------------------------------------------------------------------------------ -/** \brief \ru Создать офсетную кривую по трехмерной кривой и вектору направления. - \en Create an offset curve from three-dimensional curve and direction. \~ - \details \ru Создать офсетную кривую по трехмерной кривой и вектору направления. \n - \en Create an offset curve from three-dimensional curve and direction. \n \~ - \param[in] initCurve - \ru Постранственная кривая, к которой строится эквидистантная. - \en A space curve for which to construct the offset curve. \~ - \param[in] offsetVect - \ru Вектор, задающий смещение в точке кривой. - \en The displacement vector at a point of the curve. \~ - \param[in] useFillet - \ru Если true, то разрывы заполнять скруглением, иначе продолженными кривыми. - \en If 'true', the gaps are to be filled with fillet, otherwise with the extended curves. \~ - \param[in] keepRadius - \ru Если true, то в существующих скруглениях сохранять радиусы. - \en If 'true', the existent fillet radii are to be kept. \~ - \param[in] fromBeg - \ru Вектор смещения привязан к началу. - \en The translation vector is associated with the beginning. \~ - \param[in] snMaker - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] resType - \ru Код результата операции - \en Operation result code \~ - \param[out] resCurve - \ru Эквидистантная кривая. - \en The offset curve. \~ - \return \ru Возвращает строитель. - \en Returns the constructor. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbCreator *) CreateOffsetCurve( const MbCurve3D & initCurve, - const MbVector3D & offsetVect, - const bool useFillet, - const bool keepRadius, - const bool bluntAngle, - const bool fromBeg, - const MbSNameMaker & snMaker, - MbResultType & resType, - MbCurve3D *& resCurve ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать офсетную кривую по поверхностной кривой и значению смещения. - \en Create an offset curve from a spatial curve and offset value. \~ - \details \ru Создать офсетную кривую по поверхностной кривой и значению смещения. \n - \en Create an offset curve from a spatial curve and offset value. \n \~ - \param[in] curve - \ru Кривая на поверхности грани face. - \en A curve on face 'face' surface. \~ - \param[in] face - \ru Грань, на которой строится эквидистанта. - \en The edge on which to build the offset curve. \~ - \param[in] dirAxis - \ru Направление смещения с точкой приложения. - \en The offset direction with a point of application. \~ - \param[in] dist - \ru Величина смещения. - \en The offset distance. \~ - \param[in] snMaker - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] resType - \ru Код результата операции - \en Operation result code \~ - \param[out] resCurves - \ru Множество эквидистантных кривых. - \en Offset curve array. \~ - \return \ru Возвращает строитель. - \en Returns the constructor. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbCreator *) CreateOffsetCurve( const MbCurve3D & curve, - const MbFace & face, - const MbAxis3D & dirAxis, - double dist, - const MbSNameMaker & snMaker, - MbResultType & resType, - RPArray & resCurves ); - - -#endif // __CR_OFFSET_CURVE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Строитель эквидистантной кривой. + \en Offset curve constructor. +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_OFFSET_CURVE_H +#define __CR_OFFSET_CURVE_H + + +#include +#include +#include + + +class MATH_CLASS MbCurve3D; + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель эквидистантной кривой. + \en Offset curve constructor. \~ + \details \ru Строитель эквидистантной кривой.\n + \en Offset curve constructor.\n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbOffsetCurveCreator : public MbCreator { +private: + // \ru Основные параметры \en The basic parameters + SPtr curve; // \ru Исходная кривая. \en The initial curve. + MbVector3D dir; // \ru Направление смещения. \en The offset direction. + double dist; // \ru Величина смещения. \en The offset distance. + bool fromBeg; // \ru Вектор смещения привязан к началу кривой (иначе к концу). \en The translation vector is associated with the beginning (with the end otherwise). + + // \ru Дополнительные параметры (эквидистанта в пространстве) \en Auxiliary parameters (spatial offset) + bool useFillet; // \ru Заполнять ли разрывы скруглениями (иначе продлять сегменты). \en Whether to fill the gaps with fillets (extend segments otherwise). + bool keepRadius; // \ru Сохранять ли радиусы в скруглениях. \en Whether to keep the radii at fillets. + bool bluntAngle; // \ru Притуплять острые углы стыков сегментов \en Whether to blunt the sharp edges of segments joints. + + // \ru Дополнительные параметры (эквидистанта на поверхности грани оболочки) \en Auxiliary parameters (offset on the shell face surface) + c3d::CreatorsSPtrVector shellCreators; // \ru Журнал построения оболочки. \en The shell history tree. + +protected: + MbOffsetCurveCreator( const MbOffsetCurveCreator &, MbRegDuplicate * iReg ); // \ru Конструктор копирования \en Copy-constructor + MbOffsetCurveCreator( const MbOffsetCurveCreator & ); // \ru Не реализовано \en Not implemented + MbOffsetCurveCreator(); // \ru Не реализовано \en Not implemented +public: + // \ru Конструктор эквидистанты в пространстве \en Constructor of offset in the space + MbOffsetCurveCreator( const MbCurve3D &, bool fromBeg, const MbVector3D & dir, double dist, + bool useFillet, bool keepRadius, bool bluntAngle, + const MbSNameMaker & snMaker ); + // \ru Конструктор эквидистанты на поверхности грани оболочки \en Constructor of offset on the shell face surface + MbOffsetCurveCreator( const MbCurve3D &, bool fromBeg, const MbVector3D & dir, double dist, + const RPArray & shellCreators, bool sameCreators, + const MbSNameMaker & snMaker ); +public : + virtual ~MbOffsetCurveCreator(); + + // \ru Общие функции строителя. \en The common functions of the creator. + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element + virtual MbCreator & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Сделать копию \en Create a copy + + virtual bool IsSame ( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + + virtual MbePrompt GetPropertyName(); // \ru Дать имя свойства объекта \en Get the object property name + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + virtual size_t GetCreatorsCount( MbeCreatorType ct ) const; // \ru Посчитать внутренние построители по типу. \en Count internal creators by type. + virtual bool GetInternalCreators( MbeCreatorType, c3d::ConstCreatorsSPtrVector & ) const; // \ru Получить внутренние построители по типу. \en Get internal creators by type. + virtual bool SetInternalCreators( MbeCreatorType, c3d::CreatorsSPtrVector & ); // \ru Получить внутренние построители по типу. \en Get internal creators by type. + + // \ru Построить кривую по журналу построения \en Create a curve from the history tree + virtual bool CreateWireFrame( MbWireFrame *&, MbeCopyMode, RPArray * items = NULL ); + + /** \} */ + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. + void operator = ( const MbOffsetCurveCreator & ); // \ru Не реализовано!!! \en Not implemented!!! + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbOffsetCurveCreator ) +}; + +IMPL_PERSISTENT_OPS( MbOffsetCurveCreator ) + +//------------------------------------------------------------------------------ +/** \brief \ru Создать офсетную кривую по трехмерной кривой и вектору направления. + \en Create an offset curve from three-dimensional curve and direction. \~ + \details \ru Создать офсетную кривую по трехмерной кривой и вектору направления. \n + \en Create an offset curve from three-dimensional curve and direction. \n \~ + \param[in] initCurve - \ru Постранственная кривая, к которой строится эквидистантная. + \en A space curve for which to construct the offset curve. \~ + \param[in] offsetVect - \ru Вектор, задающий смещение в точке кривой. + \en The displacement vector at a point of the curve. \~ + \param[in] useFillet - \ru Если true, то разрывы заполнять скруглением, иначе продолженными кривыми. + \en If 'true', the gaps are to be filled with fillet, otherwise with the extended curves. \~ + \param[in] keepRadius - \ru Если true, то в существующих скруглениях сохранять радиусы. + \en If 'true', the existent fillet radii are to be kept. \~ + \param[in] fromBeg - \ru Вектор смещения привязан к началу. + \en The translation vector is associated with the beginning. \~ + \param[in] snMaker - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] resType - \ru Код результата операции + \en Operation result code \~ + \param[out] resCurve - \ru Эквидистантная кривая. + \en The offset curve. \~ + \return \ru Возвращает строитель. + \en Returns the constructor. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbCreator *) CreateOffsetCurve( const MbCurve3D & initCurve, + const MbVector3D & offsetVect, + const bool useFillet, + const bool keepRadius, + const bool bluntAngle, + const bool fromBeg, + const MbSNameMaker & snMaker, + MbResultType & resType, + MbCurve3D *& resCurve ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать офсетную кривую по поверхностной кривой и значению смещения. + \en Create an offset curve from a spatial curve and offset value. \~ + \details \ru Создать офсетную кривую по поверхностной кривой и значению смещения. \n + \en Create an offset curve from a spatial curve and offset value. \n \~ + \param[in] curve - \ru Кривая на поверхности грани face. + \en A curve on face 'face' surface. \~ + \param[in] face - \ru Грань, на которой строится эквидистанта. + \en The edge on which to build the offset curve. \~ + \param[in] dirAxis - \ru Направление смещения с точкой приложения. + \en The offset direction with a point of application. \~ + \param[in] dist - \ru Величина смещения. + \en The offset distance. \~ + \param[in] snMaker - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] resType - \ru Код результата операции + \en Operation result code \~ + \param[out] resCurves - \ru Множество эквидистантных кривых. + \en Offset curve array. \~ + \return \ru Возвращает строитель. + \en Returns the constructor. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbCreator *) CreateOffsetCurve( const MbCurve3D & curve, + const MbFace & face, + const MbAxis3D & dirAxis, + double dist, + const MbSNameMaker & snMaker, + MbResultType & resType, + RPArray & resCurves ); + + +#endif // __CR_OFFSET_CURVE_H diff --git a/C3d/Include/cr_patch_creator.h b/C3d/Include/cr_patch_creator.h index 185a412..249e62f 100644 --- a/C3d/Include/cr_patch_creator.h +++ b/C3d/Include/cr_patch_creator.h @@ -32,23 +32,23 @@ class MATH_CLASS MbFaceShell; // --- class MATH_CLASS MbPatchCreator : public MbCreator { protected: - RPArray initCurves; ///< \ru Кривые, определяющие края заплатки. \en Curves determining the boundaries of a patch. - PatchValues parameters; ///< \ru Параметры построения заплатки. \en Parameters of patch construction. + RPArray initCurves; ///< \ru Кривые, определяющие края заплатки. \en Curves determining the boundaries of a patch. + PatchValues parameters; ///< \ru Параметры построения заплатки. \en Parameters of patch construction. /// \ru Cледующие данные имеются только, если обрабатываются ребра. \en The following data are defined only when edges are being processed. - SArray orientations; ///< \ru Ориентация кривых для замыкания в цепь. \en Orientation of curves for enclosing into a chain. - SArray tolerances; ///< \ru Толерантности стыков кривых для замыкания в цепь. \en Tolerances of joints of curves for enclosing into a chain. - SArray surfInds; ///< \ru Номер поверхности кривой пересечения, отвечающей существующей грани. \en Number of surface of the intersection curve corresponding to the existent face. + SArray orientations; ///< \ru Ориентация кривых для замыкания в цепь. \en Orientation of curves for enclosing into a chain. + SArray tolerances; ///< \ru Толерантности стыков кривых для замыкания в цепь. \en Tolerances of joints of curves for enclosing into a chain. + SArray surfInds; ///< \ru Номер поверхности кривой пересечения, отвечающей существующей грани. \en Number of surface of the intersection curve corresponding to the existent face. private : MbPatchCreator( const MbPatchCreator &, MbRegDuplicate * ireg ); public : - MbPatchCreator( const RPArray & curves, - const PatchValues & params, - const MbSNameMaker & n, - const SArray * surfInds, - const SArray * orientations, - const SArray * tolerances ); + MbPatchCreator( const RPArray & curves, + const PatchValues & params, + const MbSNameMaker & n, + const SArray * surfInds, + const SArray * orientations, + const SArray * tolerances ); virtual ~MbPatchCreator(); // \ru Общие функции математического объекта \en Common functions of the mathematical object diff --git a/C3d/Include/cr_projection_curve.h b/C3d/Include/cr_projection_curve.h index 23ea950..0fcbee4 100644 --- a/C3d/Include/cr_projection_curve.h +++ b/C3d/Include/cr_projection_curve.h @@ -1,86 +1,86 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Строитель проволочного каркаса из проекционных кривых. - \en Projection wireframe constructor. -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_PROJECTION_CURVE_H -#define __CR_PROJECTION_CURVE_H - - -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель проволочного каркаса из проекционных кривых. - \en Projection wireframe constructor. \~ - \details \ru Строитель проволочного каркаса из проекционных кривых.\n - \en Projection wireframe constructor.\n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbProjCurveCreator : public MbCreator { -private: - MbWireFrame * wireFrame; // \ru Проецируемый проволочный каркас. \en Wireframe to project. - RPArray shellCreators; // \ru Протокол построения оболочки, на которую выполняется проецирование \en History tree of the shell the projection is performed onto - MbVector3D dir; // \ru Вектор направления (если нулевой, то проекция по нормали) \en Direction vector (if zero, the normal projection) - bool createExact; // \ru Создавать проекционную кривую при необходимости \en Create the projection curve if necessary - bool truncateByBounds; // \ru Усечь границами \en Truncate by bounds - -protected: - MbProjCurveCreator( const MbProjCurveCreator &, MbRegDuplicate * iReg ); // \ru Конструктор копирования \en Copy-constructor - MbProjCurveCreator( const MbProjCurveCreator & ); // \ru Не реализовано \en Not implemented - MbProjCurveCreator(); // \ru Не реализовано \en Not implemented -public: - MbProjCurveCreator( const MbCurve3D & curve, - const RPArray & shellCreators, bool sameCreators, - const MbVector3D * dir, bool exact, bool truncate, - const MbSNameMaker & snMaker ); - - MbProjCurveCreator( const MbWireFrame &wf, const bool sameWire, - const RPArray & shellCreators, bool sameCreators, - const MbVector3D * dir, bool exact, bool truncate, - const MbSNameMaker & snMaker ); -public: - virtual ~MbProjCurveCreator(); - - // \ru Общие функции строителя. \en The common functions of the creator. - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element - virtual MbCreator & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Сделать копию \en Create a copy - - virtual bool IsSame ( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal - - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - - virtual MbePrompt GetPropertyName(); // \ru Дать имя свойства объекта \en Get the object property name - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - virtual size_t GetCreatorsCount( MbeCreatorType ct ) const; // \ru Посчитать внутренние построители по типу. \en Count internal creators by type. - virtual bool GetInternalCreators( MbeCreatorType, c3d::ConstCreatorsSPtrVector & ) const; // \ru Получить внутренние построители по типу. \en Get internal creators by type. - virtual bool SetInternalCreators( MbeCreatorType, c3d::CreatorsSPtrVector & ); // \ru Получить внутренние построители по типу. \en Get internal creators by type. - - // \ru Построить кривую по журналу построения \en Create a curve from the history tree - virtual bool CreateWireFrame( MbWireFrame *&, MbeCopyMode, RPArray * items = NULL ); - - /** \} */ - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. - void operator = ( const MbProjCurveCreator & ); // \ru Не реализовано!!! \en Not implemented!!! - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbProjCurveCreator ) -}; - -IMPL_PERSISTENT_OPS( MbProjCurveCreator ) - -#endif // __CR_PROJECTION_CURVE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Строитель проволочного каркаса из проекционных кривых. + \en Projection wireframe constructor. +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_PROJECTION_CURVE_H +#define __CR_PROJECTION_CURVE_H + + +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель проволочного каркаса из проекционных кривых. + \en Projection wireframe constructor. \~ + \details \ru Строитель проволочного каркаса из проекционных кривых.\n + \en Projection wireframe constructor.\n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbProjCurveCreator : public MbCreator { +private: + MbWireFrame * wireFrame; // \ru Проецируемый проволочный каркас. \en Wireframe to project. + RPArray shellCreators; // \ru Протокол построения оболочки, на которую выполняется проецирование \en History tree of the shell the projection is performed onto + MbVector3D dir; // \ru Вектор направления (если нулевой, то проекция по нормали) \en Direction vector (if zero, the normal projection) + bool createExact; // \ru Создавать проекционную кривую при необходимости \en Create the projection curve if necessary + bool truncateByBounds; // \ru Усечь границами \en Truncate by bounds + +protected: + MbProjCurveCreator( const MbProjCurveCreator &, MbRegDuplicate * iReg ); // \ru Конструктор копирования \en Copy-constructor + MbProjCurveCreator( const MbProjCurveCreator & ); // \ru Не реализовано \en Not implemented + MbProjCurveCreator(); // \ru Не реализовано \en Not implemented +public: + MbProjCurveCreator( const MbCurve3D & curve, + const RPArray & shellCreators, bool sameCreators, + const MbVector3D * dir, bool exact, bool truncate, + const MbSNameMaker & snMaker ); + + MbProjCurveCreator( const MbWireFrame &wf, const bool sameWire, + const RPArray & shellCreators, bool sameCreators, + const MbVector3D * dir, bool exact, bool truncate, + const MbSNameMaker & snMaker ); +public: + virtual ~MbProjCurveCreator(); + + // \ru Общие функции строителя. \en The common functions of the creator. + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element + virtual MbCreator & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Сделать копию \en Create a copy + + virtual bool IsSame ( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + + virtual MbePrompt GetPropertyName(); // \ru Дать имя свойства объекта \en Get the object property name + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + virtual size_t GetCreatorsCount( MbeCreatorType ct ) const; // \ru Посчитать внутренние построители по типу. \en Count internal creators by type. + virtual bool GetInternalCreators( MbeCreatorType, c3d::ConstCreatorsSPtrVector & ) const; // \ru Получить внутренние построители по типу. \en Get internal creators by type. + virtual bool SetInternalCreators( MbeCreatorType, c3d::CreatorsSPtrVector & ); // \ru Получить внутренние построители по типу. \en Get internal creators by type. + + // \ru Построить кривую по журналу построения \en Create a curve from the history tree + virtual bool CreateWireFrame( MbWireFrame *&, MbeCopyMode, RPArray * items = NULL ); + + /** \} */ + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. + void operator = ( const MbProjCurveCreator & ); // \ru Не реализовано!!! \en Not implemented!!! + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbProjCurveCreator ) +}; + +IMPL_PERSISTENT_OPS( MbProjCurveCreator ) + +#endif // __CR_PROJECTION_CURVE_H diff --git a/C3d/Include/cr_revolution_solid.h b/C3d/Include/cr_revolution_solid.h index 665e662..0ead11a 100644 --- a/C3d/Include/cr_revolution_solid.h +++ b/C3d/Include/cr_revolution_solid.h @@ -66,12 +66,12 @@ public : \{ */ virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const ; // \ru Сделать копию \en Create a copy - virtual void Transform( const MbMatrix3D &matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move( const MbVector3D &to, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate( const MbAxis3D &axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property virtual void GetBasisItems ( RPArray & s ); // \ru Дать базовые объекты \en Get the base objects virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. @@ -87,13 +87,13 @@ public : \{ */ virtual MbFaceShell * InitShell( bool in ); virtual void InitBasis( RPArray & items ); - virtual bool GetPlacement( MbPlacement3D & p ) const; + virtual bool GetPlacement( MbPlacement3D & ) const; /** \} */ /** \ru \name Функции строителя оболочки тела вращения. \en \name Functions of the revolution solid's shell creator. \{ */ - const MbSurface * GetSurface() const { return sweptData.GetSurface(); } ///< \ru Поверхность двумерных контуров. \en Surface of two-dimensional contours. - const MbAxis3D & GetAxis() const { return axis; } ///< \ru Ось вращения. \en Rotation axis. + const MbSurface * GetSurface() const { return sweptData.GetSurface(); } ///< \ru Поверхность двумерных контуров. \en Surface of two-dimensional contours. + const MbAxis3D & GetAxis() const { return axis; } ///< \ru Ось вращения. \en Rotation axis. /// \ru Дать параметры. \en Get the parameters. void GetParameters( RevolutionValues & p ) const { p = parameters; } @@ -110,6 +110,7 @@ private : IMPL_PERSISTENT_OPS( MbCurveRevolutionSolid ) + //------------------------------------------------------------------------------ /** \brief \ru Создать оболочку тела вращения. \en Create a shell of the revolution solid. \~ diff --git a/C3d/Include/cr_ruled_shell.h b/C3d/Include/cr_ruled_shell.h index d170850..5d8b130 100644 --- a/C3d/Include/cr_ruled_shell.h +++ b/C3d/Include/cr_ruled_shell.h @@ -1,109 +1,109 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Построить линейчатую оболочку. - \en Construct a ruled shell. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_RULED_SHELL_H -#define __CR_RULED_SHELL_H - - -#include -#include -#include -#include -#include - - -class MATH_CLASS MbFaceShell; -class MATH_CLASS MbCurveEdge; -class MATH_CLASS MbOrientedEdge; -class MATH_CLASS MbLoop; -struct MATH_CLASS RuledSurfaceValues; - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель линейчатой оболочки. - \en Constructor of a ruled shell. \~ - \details \ru Строитель линейчатой оболочки по двум кривым. \n - \en Constructor of a ruled shell from two curves. \n \~ - \ingroup Model_Creators -*/ -//--- -class MATH_CLASS MbRuledShell : public MbCreator { - -private : - RuledSurfaceValues parameters; ///< \ru Параметры построения. \en Construction parameters. -private: - MbRuledShell( const MbRuledShell & obj, MbRegDuplicate * ireg ); -public: - /// \ru Конструктор по параметрам операции и именователю. \en Constructor by operation parameters and name-maker. - MbRuledShell( const RuledSurfaceValues & pars, const MbSNameMaker & n ); - virtual ~MbRuledShell(); - -public: // \ru Общие функции математического объекта \en Common functions of the mathematical object - virtual MbeCreatorType IsA() const; ///< \ru Тип элемента \en Element type - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; ///< \ru Сделать копию \en Make a copy - virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool SetEqual ( const MbCreator & ); ///< \ru Сделать равным \en Make equal - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); ///< \ru Преобразовать по матрице \en Transform according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); ///< \ru Сдвиг по вектору \en Translation by the vector - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); ///< \ru Поворот вокруг оси \en Rotation about an axis - - virtual void GetProperties( MbProperties & properties ); ///< \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ); ///< \ru Записать свойства объекта \en Write properties of the object - virtual MbePrompt GetPropertyName(); ///< \ru Выдать заголовок свойства объекта \en Get name of object property - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - -public: - /// \ru Построение оболочки \en Creation of a shell - virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, - RPArray * items = NULL ); - // \ru Дать параметры. \en Get the parameters. - void GetParameters( RuledSurfaceValues & params ) const; - // \ru Установить параметры. \en Set the parameters. - void SetParameters( const RuledSurfaceValues & params ); - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbRuledShell ) -OBVIOUS_PRIVATE_COPY( MbRuledShell ) -}; // MbRuledShell - -IMPL_PERSISTENT_OPS( MbRuledShell ) - -//------------------------------------------------------------------------------ -/** \brief \ru Построить линейчатую оболочку. - \en Construct a ruled shell. \~ - \details \ru Построить линейчатую оболочку по двум кривым. - Кривые могут быть составными. - Одновременно с построением оболочки функция создаёт её строитель.\n - \en Construct a ruled shell from two curves - Curves can be composite. - The function simultaneously creates the shell and its constructor.\n \~ - \param[in] parameters - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] operNames - \ru Именователь. - \en An object for naming the new objects. \~ - \param[in] isPhantom - \ru Режим создания фантома. - \en Create in the phantom mode. \~ - \param[out] res - \ru Код результата операции. - \en Operation result code. \~ - \param[out] shell - \ru Построенная оболочка. - \en The resultant shell. \~ - \result \ru Возвращает строитель оболочки. - \en Returns the shell constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateRuledShell( RuledSurfaceValues & parameters, - const MbSNameMaker & operNames, - bool isPhantom, - MbResultType & res, - MbFaceShell *& shell ); - - +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Построить линейчатую оболочку. + \en Construct a ruled shell. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_RULED_SHELL_H +#define __CR_RULED_SHELL_H + + +#include +#include +#include +#include +#include + + +class MATH_CLASS MbFaceShell; +class MATH_CLASS MbCurveEdge; +class MATH_CLASS MbOrientedEdge; +class MATH_CLASS MbLoop; +struct MATH_CLASS RuledSurfaceValues; + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель линейчатой оболочки. + \en Constructor of a ruled shell. \~ + \details \ru Строитель линейчатой оболочки по двум кривым. \n + \en Constructor of a ruled shell from two curves. \n \~ + \ingroup Model_Creators +*/ +//--- +class MATH_CLASS MbRuledShell : public MbCreator { + +private : + RuledSurfaceValues parameters; ///< \ru Параметры построения. \en Construction parameters. +private: + MbRuledShell( const MbRuledShell & obj, MbRegDuplicate * ireg ); +public: + /// \ru Конструктор по параметрам операции и именователю. \en Constructor by operation parameters and name-maker. + MbRuledShell( const RuledSurfaceValues & pars, const MbSNameMaker & n ); + virtual ~MbRuledShell(); + +public: // \ru Общие функции математического объекта \en Common functions of the mathematical object + virtual MbeCreatorType IsA() const; ///< \ru Тип элемента \en Element type + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; ///< \ru Сделать копию \en Make a copy + virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool SetEqual ( const MbCreator & ); ///< \ru Сделать равным \en Make equal + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); ///< \ru Преобразовать по матрице \en Transform according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); ///< \ru Сдвиг по вектору \en Translation by the vector + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); ///< \ru Поворот вокруг оси \en Rotation about an axis + + virtual void GetProperties( MbProperties & properties ); ///< \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & properties ); ///< \ru Записать свойства объекта \en Write properties of the object + virtual MbePrompt GetPropertyName(); ///< \ru Выдать заголовок свойства объекта \en Get name of object property + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + +public: + /// \ru Построение оболочки \en Creation of a shell + virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, + RPArray * items = NULL ); + // \ru Дать параметры. \en Get the parameters. + void GetParameters( RuledSurfaceValues & params ) const; + // \ru Установить параметры. \en Set the parameters. + void SetParameters( const RuledSurfaceValues & params ); + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbRuledShell ) +OBVIOUS_PRIVATE_COPY( MbRuledShell ) +}; // MbRuledShell + +IMPL_PERSISTENT_OPS( MbRuledShell ) + +//------------------------------------------------------------------------------ +/** \brief \ru Построить линейчатую оболочку. + \en Construct a ruled shell. \~ + \details \ru Построить линейчатую оболочку по двум кривым. + Кривые могут быть составными. + Одновременно с построением оболочки функция создаёт её строитель.\n + \en Construct a ruled shell from two curves + Curves can be composite. + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] parameters - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] operNames - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] isPhantom - \ru Режим создания фантома. + \en Create in the phantom mode. \~ + \param[out] res - \ru Код результата операции. + \en Operation result code. \~ + \param[out] shell - \ru Построенная оболочка. + \en The resultant shell. \~ + \result \ru Возвращает строитель оболочки. + \en Returns the shell constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateRuledShell( RuledSurfaceValues & parameters, + const MbSNameMaker & operNames, + bool isPhantom, + MbResultType & res, + MbFaceShell *& shell ); + + #endif // __CR_RULED_SHELL_H \ No newline at end of file diff --git a/C3d/Include/cr_section_shell.h b/C3d/Include/cr_section_shell.h new file mode 100644 index 0000000..a2fafd4 --- /dev/null +++ b/C3d/Include/cr_section_shell.h @@ -0,0 +1,137 @@ +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Строитель оболочки на поверхности переменного сечения. + \en Constructor of shell of evolution solid. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_SECTION_SHELL_H +#define __CR_SECTION_SHELL_H + + +#include +#include +#include +#include + + +class MATH_CLASS MbFaceShell; + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель оболочки на поверхности переменного сечения. + \en Constructor of the shell on swept mutable section surface. \~ + \details \ru Грань оболочки строится путём движения переменного сечения по опорной кривой. \n + \en Constructor of face by moving generating curve along a reference spine curve. \n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbSectionShell : public MbCreator { +protected : + MbSectionData sectionData; ///< \ru Данные о поверхности переменного сечения. \en Data about swept mutable section surface. + MbSectionCode sectionCode; ///< \ru Данные о поверхности переменного сечения. \en Data about swept mutable section surface. + + /** \brief \ru Конструктор. + \en Constructor. \~ + \param[in] data - \ru Данные о поверхности переменного сечения. + \en Data about swept mutable section surface. \~ + \param[in] names - \ru Именователь грани оболочки. + \en Generating face names. \~ + */ + MbSectionShell( const MbSectionData & data, + const MbSectionCode & code, + const MbSNameMaker & names ); +private : + MbSectionShell( const MbSectionShell & init, MbRegDuplicate * ireg ); + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. + MbSectionShell( const MbSectionShell & ); +public : + virtual ~MbSectionShell(); + + /** \ru \name Общие функции математического объекта. + \en \name Common functions of the mathematical object. + \{ */ + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const ; // \ru Сделать копию \en Create a copy + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property + virtual void GetBasisItems ( RPArray & s ); // \ru Дать базовые объекты \en Get the base objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + /** \} */ + + /** \ru \name Общие функции твердого тела (формообразующей операции). + \en \name Common functions of the rigid solid (forming operations). + \{ */ + virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, + RPArray * items = NULL ); // \ru Построение \en Construction + + virtual void SetYourVersion( VERSION version ); + /** \} */ + + /** \ru \name Функции строителя оболочки на поверхности переменного сечения. + \en \name Functions of creator of evolution solid shell. + \{ */ + /// \ru Дать параметры. \en Get the parameters. + const MbSectionData & GetSectionData() { return sectionData; } + /// \ru Установить параметры. \en Set the parameters. + void SetSectionData( const MbSectionData & data ) { sectionData = data; } + /// \ru Дать параметры. \en Get the parameters. + const MbSectionCode & GetSectionCode() { return sectionCode; } + /// \ru Установить параметры. \en Set the parameters. + void SetSectionCode( const MbSectionCode & code ) { sectionCode = code; } + /** \} */ + + /** \brief \ru Создать оболочку на поверхности переменного сечения. + \en Create a shell on swept mutable section surface. \~ + \details \ru Построить оболочку путём движения образующей кривой по направляющей кривой + и выполнить булеву операцию с оболочкой, если последняя задана. \n + Одновременно с построением оболочки функция создаёт её строитель.\n + \en Create a shell by moving the generating curve along the spine curve + and perform the Boolean operation with the shell if it is specified. \n + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] solid - \ru Набор граней, к которым дополняется построение. + \en Face set the construction is complemented with respect to. \~ + \param[in] sameShell - \ru Способ копирования граней. + \en The method of copying faces. \~ + \param[in] data - \ru Данные о поверхности переменного сечения. + \en Data about swept mutable section surface. \~ + \param[in] names - \ru Именователь грани оболочки. + \en Generating face names. \~ + \param[out] res - \ru Код результата операции. + \en Operation result code. \~ + \param[out] shell - \ru Код ошибки порстроения. + \en Result code of building. \~ + \result \ru Возвращает строитель. + \en Returns the constructor of operation. \~ + */ + static MbSectionShell * Create( MbFaceShell * solid, + MbeCopyMode sameShell, + const MbSectionData & data, + const MbSNameMaker & names, + MbResultType & res, + MbFaceShell *& shell ); + +private : + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + void operator = ( const MbSectionShell & ); + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSectionShell ) + +}; // MbSectionShell + +IMPL_PERSISTENT_OPS( MbSectionShell ) + + +#endif // __CR_SECTION_SHELL_H diff --git a/C3d/Include/cr_sheet_bend_any_solid.h b/C3d/Include/cr_sheet_bend_any_solid.h index 799360a..e1e4136 100644 --- a/C3d/Include/cr_sheet_bend_any_solid.h +++ b/C3d/Include/cr_sheet_bend_any_solid.h @@ -1,122 +1,122 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Построение оболочки тела с выполнеными сгибами. - \en Construction of a shell from any solid with bends. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_SHEET_BEND_ANY_SOLID_H -#define __CR_SHEET_BEND_ANY_SOLID_H - - -#include -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель оболочки из листового материала с выполненым сгибом/разгибом. - \en Constructor of a shell from sheet material with bend/unbend. \~ - \details \ru Строитель оболочки из листового материала с выполненым сгибом/разгибом. - Построение сгиба/разгиба на касательную плоскость к указанной грани в указанной - точке с индивидуальными для каждого сгиба параметрами. \n - \en Constructor of a shell from sheet material with bend/unbend. - Construction of a bend/unbend to the tangent plane to the specified face at - the given point with parameters individual for each bend. \n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbBendAnySolid : public MbCreator { - MbPlane cutPlane; - SArray bends; - -public : - MbBendAnySolid( const MbPlane & cutPlane, - const SArray & bends, - const MbSNameMaker & names ); -private: - MbBendAnySolid( const MbBendAnySolid &, MbRegDuplicate * iReg ); - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. - MbBendAnySolid( const MbBendAnySolid & ); - -public: - virtual ~MbBendAnySolid(); - - // \ru Общие функции математического объекта \en Common functions of the mathematical object - - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy - - virtual bool IsSame ( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & item ) const; // \ru Являются ли объекты подобными? \en Determine whether an object is similar? - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal - - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property - - // \ru Общие функции твердого тела \en Common functions of solid solid - - virtual bool CreateShell( MbFaceShell *& shell, - MbeCopyMode sameShell, - RPArray * items = NULL ); // \ru Построение \en Construction - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - MbBendAnySolid & operator = ( const MbBendAnySolid & ); // \ru Не реализовано \en Not implemented - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbBendAnySolid ) -}; - -IMPL_PERSISTENT_OPS( MbBendAnySolid ) - -//------------------------------------------------------------------------------ -/** \brief \ru Построить оболочку с выполнеными сгибами. - \en Construct a shell with bends. \~ - \details \ru Построить оболочку любого тела с выполнеными сгибами. - Построение сгиба/разгиба на касательную плоскость к указанной грани в указанной - точке с индивидуальными для каждого сгиба параметрами. \n - Одновременно с построением оболочки функция создаёт её строитель.\n - \en Construct a shell from sheet material with bend/unbend. - Construction of a bend/unbend to the tangent plane to the specified face at - the given point with parameters individual for each bend. \n - The function simultaneously creates the shell and its constructor.\n \~ - \param[in] initialShell - \ru Исходная оболочка. - \en The initial shell. \~ - \param[in] sameShell - \ru Режим копирования исходной оболочки. - \en Mode of copying the initial shell. \~ - \param[in] bends - \ru Сгибы оболочки. - \en Bends of a shell. \~ - \param[in] fixedFace - \ru Неподвижная грань. - \en Fixed face. \~ - \param[in] fixedPoint - \ru Неподвижная точка. - \en Fixed point. \~ - \param[in] names - \ru Именователь граней. - \en An object for naming faces. \~ - \param[out] res - \ru Код результата операции. - \en Operation result code. \~ - \param[out] shell - \ru Построенная оболочка. - \en The resultant shell. \~ - \result \ru Возвращает строитель оболочки. - \en Returns the shell constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateAnyBend( MbFaceShell & initialShell, - const MbeCopyMode sameShell, - const MbPlane & cutPlane, - const SArray & bends, - const MbSNameMaker & names, - MbResultType & res, - MbFaceShell *& shell ); - - - -#endif // __CR_SHEET_BEND_ANY_SOLID_H - +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Построение оболочки тела с выполнеными сгибами. + \en Construction of a shell from any solid with bends. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_SHEET_BEND_ANY_SOLID_H +#define __CR_SHEET_BEND_ANY_SOLID_H + + +#include +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель оболочки из листового материала с выполненым сгибом/разгибом. + \en Constructor of a shell from sheet material with bend/unbend. \~ + \details \ru Строитель оболочки из листового материала с выполненым сгибом/разгибом. + Построение сгиба/разгиба на касательную плоскость к указанной грани в указанной + точке с индивидуальными для каждого сгиба параметрами. \n + \en Constructor of a shell from sheet material with bend/unbend. + Construction of a bend/unbend to the tangent plane to the specified face at + the given point with parameters individual for each bend. \n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbBendAnySolid : public MbCreator { + MbPlane cutPlane; + SArray bends; + +public : + MbBendAnySolid( const MbPlane & cutPlane, + const SArray & bends, + const MbSNameMaker & names ); +private: + MbBendAnySolid( const MbBendAnySolid &, MbRegDuplicate * iReg ); + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. + MbBendAnySolid( const MbBendAnySolid & ); + +public: + virtual ~MbBendAnySolid(); + + // \ru Общие функции математического объекта \en Common functions of the mathematical object + + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy + + virtual bool IsSame ( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & item ) const; // \ru Являются ли объекты подобными? \en Determine whether an object is similar? + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property + + // \ru Общие функции твердого тела \en Common functions of solid solid + + virtual bool CreateShell( MbFaceShell *& shell, + MbeCopyMode sameShell, + RPArray * items = NULL ); // \ru Построение \en Construction + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + MbBendAnySolid & operator = ( const MbBendAnySolid & ); // \ru Не реализовано \en Not implemented + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbBendAnySolid ) +}; + +IMPL_PERSISTENT_OPS( MbBendAnySolid ) + +//------------------------------------------------------------------------------ +/** \brief \ru Построить оболочку с выполнеными сгибами. + \en Construct a shell with bends. \~ + \details \ru Построить оболочку любого тела с выполнеными сгибами. + Построение сгиба/разгиба на касательную плоскость к указанной грани в указанной + точке с индивидуальными для каждого сгиба параметрами. \n + Одновременно с построением оболочки функция создаёт её строитель.\n + \en Construct a shell from sheet material with bend/unbend. + Construction of a bend/unbend to the tangent plane to the specified face at + the given point with parameters individual for each bend. \n + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] initialShell - \ru Исходная оболочка. + \en The initial shell. \~ + \param[in] sameShell - \ru Режим копирования исходной оболочки. + \en Mode of copying the initial shell. \~ + \param[in] bends - \ru Сгибы оболочки. + \en Bends of a shell. \~ + \param[in] fixedFace - \ru Неподвижная грань. + \en Fixed face. \~ + \param[in] fixedPoint - \ru Неподвижная точка. + \en Fixed point. \~ + \param[in] names - \ru Именователь граней. + \en An object for naming faces. \~ + \param[out] res - \ru Код результата операции. + \en Operation result code. \~ + \param[out] shell - \ru Построенная оболочка. + \en The resultant shell. \~ + \result \ru Возвращает строитель оболочки. + \en Returns the shell constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateAnyBend( MbFaceShell & initialShell, + const MbeCopyMode sameShell, + const MbPlane & cutPlane, + const SArray & bends, + const MbSNameMaker & names, + MbResultType & res, + MbFaceShell *& shell ); + + + +#endif // __CR_SHEET_BEND_ANY_SOLID_H + diff --git a/C3d/Include/cr_sheet_bend_by_edge_solid.h b/C3d/Include/cr_sheet_bend_by_edge_solid.h index 8d4972b..37108f2 100644 --- a/C3d/Include/cr_sheet_bend_by_edge_solid.h +++ b/C3d/Include/cr_sheet_bend_by_edge_solid.h @@ -132,7 +132,7 @@ IMPL_PERSISTENT_OPS( MbBendsByEdgesSolid ) \ingroup Model_Creators */ // --- -MATH_FUNC (MbCreator *) CreateBendsByEdges( MbFaceShell & initialShell, +MATH_FUNC (MbCreator *) CreateBendsByEdges( SPtr & initialShell, const MbeCopyMode sameShell, const RPArray & edges, const bool unbended, diff --git a/C3d/Include/cr_sheet_bend_over_seg_solid.h b/C3d/Include/cr_sheet_bend_over_seg_solid.h index 2afdebd..2c14523 100644 --- a/C3d/Include/cr_sheet_bend_over_seg_solid.h +++ b/C3d/Include/cr_sheet_bend_over_seg_solid.h @@ -117,7 +117,7 @@ IMPL_PERSISTENT_OPS( MbBendOverSegSolid ) \ingroup Model_Creators */ // --- -MATH_FUNC (MbCreator *) CreateBendOverSegment( MbFaceShell & initialShell, +MATH_FUNC (MbCreator *) CreateBendOverSegment( SPtr & initialShell, MbeCopyMode sameShell, const RPArray & bendingFaces, MbCurve3D & curve, diff --git a/C3d/Include/cr_sheet_bend_unbend_solid.h b/C3d/Include/cr_sheet_bend_unbend_solid.h index d5f6164..d1d31e9 100644 --- a/C3d/Include/cr_sheet_bend_unbend_solid.h +++ b/C3d/Include/cr_sheet_bend_unbend_solid.h @@ -113,7 +113,7 @@ IMPL_PERSISTENT_OPS( MbBendUnbendSolid ) \ingroup Model_Creators */ // --- -MATH_FUNC (MbCreator *) CreateBendUnbend( MbFaceShell & initialShell, +MATH_FUNC (MbCreator *) CreateBendUnbend( SPtr & initialShell, MbeCopyMode sameShell, const RPArray & bends, const MbFace & fixedFace, diff --git a/C3d/Include/cr_sheet_builder_solid.h b/C3d/Include/cr_sheet_builder_solid.h new file mode 100644 index 0000000..1cbb454 --- /dev/null +++ b/C3d/Include/cr_sheet_builder_solid.h @@ -0,0 +1,114 @@ +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Строитель оболочки из листового материала на основе произвольного тела. + \en Constructor of the sheet metal shell based on an arbitrary solid. +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_SHEET_BUILDER_SOLID_H +#define __CR_SHEET_BUILDER_SOLID_H + + +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель оболочки из листового материала на основе произвольного тела. + \en Constructor of the sheet metal shell based on an arbitrary solid. \~ + \details \ru Строитель оболочки из листового материала на основе граней и ребер произвольного тела.\n + Оболочка строится на базе исходной плоской грани и указанных ребер сгиба и разреза. + \en Constructor of the sheet metal shell based on faces and edges of an arbitrary solid. \n + Shell builds based on initial planar face and given edges of bend and corner enclosure. \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbBuildSheetMetalSolid : public MbCreator { +private: + MbItemIndex faceIndex; ///< \ru Индекс исходной грани для построения листового тела. \en Index of initial face for sheet metal solid creation. + bool sense; ///< \ru Признак совпадения придания толщины с нормалью исходной грани. \en Attribute of coincidence of extrusion direction to the normal of the initial face. + MbSolidToSheetMetalValues parameters; ///< \ru Параметры построения листового тела по произвольному телу. \en The parameters of sheet metal solid building based on an arbitrary solid. + +public : + MbBuildSheetMetalSolid( const MbItemIndex & faceIndex, + const bool sense, + const MbSolidToSheetMetalValues & params, + const MbSNameMaker & names ); +private: + MbBuildSheetMetalSolid( const MbBuildSheetMetalSolid &, MbRegDuplicate * iReg ); + +public: + virtual ~MbBuildSheetMetalSolid(); + + // \ru Общие функции математического объекта. \en Common functions of the mathematical object. + + virtual MbeCreatorType IsA() const; // \ru Тип элемента. \en A type of element. + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию. \en Create a copy. + + virtual bool IsSame ( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & item ) const; // \ru Являются ли объекты подобными? \en Determine whether an object is similar? + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным. \en Make equal. + + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг. \en Translation. + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate around an axis. + + virtual void GetProperties ( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties ( const MbProperties & properties ); // \ru Записать свойства объекта. \en Set properties of the object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + + // \ru Общие функции твердого тела. \en Common functions of solid. + + // \ru Построение оболочки листового тела. \en Construction of a sheet metal shell. + virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, RPArray *items = NULL ); + // \ru Получить параметры. \en Get the parameters. + void GetParameters( MbSolidToSheetMetalValues & params ) const { params = parameters; } + // \ru Установить параметры. \en Set the parameters. + void SetParameters( const MbSolidToSheetMetalValues & params ) { parameters = params; } + +private: + OBVIOUS_PRIVATE_COPY( MbBuildSheetMetalSolid ) + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbBuildSheetMetalSolid ) +}; + +IMPL_PERSISTENT_OPS( MbBuildSheetMetalSolid ) + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель оболочки из листового материала на основе произвольного тела. + \en Constructor of the sheet metal shell based on an arbitrary solid. \~ + \details \ru На базе исходной произвольной оболочки построить оболочку из листового материала. \n + Одновременно с построением оболочки функция создаёт её строитель.\n + \en A shell is to be constructed on the basis of the source arbitary shell. \n + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] solid - \ru Исходная оболочка. + \en The source shell. \~ + \param[in] sameShell - \ru Режим копирования исходной оболочки. + \en Mode of copying the source shell. \~ + \param[in] initFace - \ru Исходная грань для построения листового тела. + \en The initial face for sheet metal solid construction. \~ + \param[in] sense - \ru Признак совпадения направления придания толщины с нормалью исходной грани. + \en Attribute of coincidence of extrusion direction to the normal of the initial face. \~ + \param[in] params - \ru Параметры построения листового тела по произвольному телу. + \en The parameters of sheet metal solid building based on an arbitrary solid. \~ + \param[in] nameMaker - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Результирующее тело. + \en The resultant solid. \~ + \result \ru Возвращает строитель оболочки. + \en Returns the shell constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) ConvertShellToSheetMetall( MbFaceShell * initialShell, // Исходная оболочка, + const MbeCopyMode sameShell, // флаг способа использования исходной оболочки, + const MbFace & initFace, // базовая грань, относительно которой будет строиться листовое тело, + bool sense, // признак совпадения придания толщины с нормалью базовой грани, + const MbSolidToSheetMetalValues & params, // параметры построения листового тела, + MbSNameMaker & nameMaker, // именователь, + MbResultType & res, // флаг успешности операции, + SPtr & resultShell ); // результирующая оболочка. + + +#endif // __CR_SHEET_BUILDER_SOLID_H \ No newline at end of file diff --git a/C3d/Include/cr_sheet_closed_corner_solid.h b/C3d/Include/cr_sheet_closed_corner_solid.h index f5f60cd..2618b11 100644 --- a/C3d/Include/cr_sheet_closed_corner_solid.h +++ b/C3d/Include/cr_sheet_closed_corner_solid.h @@ -118,7 +118,7 @@ IMPL_PERSISTENT_OPS( MbClosedCornerSolid ) \ingroup Model_Creators */ // --- -MATH_FUNC (MbCreator *) CreateClosedCorner( MbFaceShell & initialShell, +MATH_FUNC (MbCreator *) CreateClosedCorner( SPtr & initialShell, MbeCopyMode sameShell, MbCurveEdge * curveEdgePlus, MbCurveEdge * curveEdgeMinus, diff --git a/C3d/Include/cr_sheet_joint_bend_solid.h b/C3d/Include/cr_sheet_joint_bend_solid.h index 82ac1ce..925e7ed 100644 --- a/C3d/Include/cr_sheet_joint_bend_solid.h +++ b/C3d/Include/cr_sheet_joint_bend_solid.h @@ -130,7 +130,7 @@ IMPL_PERSISTENT_OPS( MbJointBendSolid ) \ingroup Model_Creators */ // --- -MATH_FUNC (MbCreator *) CreateJointBend( MbFaceShell & initialShell, +MATH_FUNC (MbCreator *) CreateJointBend( c3d::ShellSPtr & initialShell, const MbeCopyMode sameShell, const MbPlacement3D & placement, const MbContour & contour, diff --git a/C3d/Include/cr_sheet_metal_solid.h b/C3d/Include/cr_sheet_metal_solid.h index 369958d..8e9dadb 100644 --- a/C3d/Include/cr_sheet_metal_solid.h +++ b/C3d/Include/cr_sheet_metal_solid.h @@ -167,7 +167,7 @@ IMPL_PERSISTENT_OPS( MbSheetMetalSolid ) \ingroup Model_Creators */ // --- -MATH_FUNC (MbCreator *) CreateSheetMetal( MbFaceShell * solid, +MATH_FUNC (MbCreator *) CreateSheetMetal( SPtr & solid, MbeCopyMode sameShell, const MbPlacement3D & placement, RPArray & contours, diff --git a/C3d/Include/cr_sheet_simplified_flat_solid.h b/C3d/Include/cr_sheet_simplified_flat_solid.h index 4e4b5b6..d3d9c96 100644 --- a/C3d/Include/cr_sheet_simplified_flat_solid.h +++ b/C3d/Include/cr_sheet_simplified_flat_solid.h @@ -1,103 +1,103 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Построение упрощённой развёртки листового тела. - \en Construction of the simplified flat pattern. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_SHEET_SIMPLIFIED_FLAT_SOLID_H -#define __CR_SHEET_SIMPLIFIED_FLAT_SOLID_H - - -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель упрощения развёртки тела из листового материала. - \en Constructor of the simplified flat pattern. \~ - \details \ru Строитель упрощения развёртки тела из листового материала. - Возможно два вида упрощения: обработка углов и слияние подобных граней. \n - \en Constructor of the simplified flat pattern. - There are two types of simplification. The first one is the corners treatment. The second one is the similar faces unification. \n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbSimplifyFlatSolid : public MbCreator { - MbSimplifyFlatPatternValues parameters; - -public : - MbSimplifyFlatSolid( const MbSimplifyFlatPatternValues & params, - const MbSNameMaker & names ); -private: - MbSimplifyFlatSolid( const MbSimplifyFlatSolid &, MbRegDuplicate * iReg ); - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. - MbSimplifyFlatSolid( const MbSimplifyFlatSolid & ); - -public: - virtual ~MbSimplifyFlatSolid(); - - // \ru Общие функции математического объекта \en Common functions of the mathematical object - - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy - - virtual bool IsSame ( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & item ) const; // \ru Являются ли объекты подобными? \en Determine whether an object is similar? - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal - - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property - - // \ru Общие функции твердого тела \en Common functions of solid solid - - virtual bool CreateShell( MbFaceShell *& shell, - MbeCopyMode sameShell, - RPArray * items = NULL ); // \ru Построение \en Construction - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - MbSimplifyFlatSolid & operator = ( const MbSimplifyFlatSolid & ); // \ru Не реализовано \en Not implemented - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSimplifyFlatSolid ) -}; - -IMPL_PERSISTENT_OPS( MbSimplifyFlatSolid ) - -//------------------------------------------------------------------------------ -/** \brief \ru Упростить развёртку листового тела. - \en Simplify flattened sheet solid. \~ - \details \ru Упростить развёртку листового тела. \n - \en Simplify flattened sheet solid. \n \~ - \param[in] solid - \ru Исходное тело. - \en The source solid. \~ - \param[in] sameShell - \ru Флаг удаления оболочки исходного тела. - \en Whether to delete the shell of the source solid. \~ - \param[in] params - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] nameMaker - \ru Именователь. - \en An object for naming the new objects. \~ - \param[out] result - \ru Результирующее тело. - \en The resultant solid. \~ - \result \ru - Код результата операции. - \en - The operation result code. \~ - \ingroup Sheet_Metal_Modeling -*/ -// --- -MATH_FUNC (MbCreator *) CreateSimplifiedFlatPattern( MbFaceShell & initialShell, - const MbeCopyMode sameShell, - const MbSimplifyFlatPatternValues & params, - const MbSNameMaker & nameMaker, - MbResultType & res, - MbFaceShell *& shell ); - - -#endif // __CR_SHEET_SIMPLIFIED_FLAT_SOLID_H - +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Построение упрощённой развёртки листового тела. + \en Construction of the simplified flat pattern. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_SHEET_SIMPLIFIED_FLAT_SOLID_H +#define __CR_SHEET_SIMPLIFIED_FLAT_SOLID_H + + +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель упрощения развёртки тела из листового материала. + \en Constructor of the simplified flat pattern. \~ + \details \ru Строитель упрощения развёртки тела из листового материала. + Возможно два вида упрощения: обработка углов и слияние подобных граней. \n + \en Constructor of the simplified flat pattern. + There are two types of simplification. The first one is the corners treatment. The second one is the similar faces unification. \n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbSimplifyFlatSolid : public MbCreator { + MbSimplifyFlatPatternValues parameters; + +public : + MbSimplifyFlatSolid( const MbSimplifyFlatPatternValues & params, + const MbSNameMaker & names ); +private: + MbSimplifyFlatSolid( const MbSimplifyFlatSolid &, MbRegDuplicate * iReg ); + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. + MbSimplifyFlatSolid( const MbSimplifyFlatSolid & ); + +public: + virtual ~MbSimplifyFlatSolid(); + + // \ru Общие функции математического объекта \en Common functions of the mathematical object + + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy + + virtual bool IsSame ( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & item ) const; // \ru Являются ли объекты подобными? \en Determine whether an object is similar? + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property + + // \ru Общие функции твердого тела \en Common functions of solid solid + + virtual bool CreateShell( MbFaceShell *& shell, + MbeCopyMode sameShell, + RPArray * items = NULL ); // \ru Построение \en Construction + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + MbSimplifyFlatSolid & operator = ( const MbSimplifyFlatSolid & ); // \ru Не реализовано \en Not implemented + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSimplifyFlatSolid ) +}; + +IMPL_PERSISTENT_OPS( MbSimplifyFlatSolid ) + +//------------------------------------------------------------------------------ +/** \brief \ru Упростить развёртку листового тела. + \en Simplify flattened sheet solid. \~ + \details \ru Упростить развёртку листового тела. \n + \en Simplify flattened sheet solid. \n \~ + \param[in] solid - \ru Исходное тело. + \en The source solid. \~ + \param[in] sameShell - \ru Флаг удаления оболочки исходного тела. + \en Whether to delete the shell of the source solid. \~ + \param[in] params - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] nameMaker - \ru Именователь. + \en An object for naming the new objects. \~ + \param[out] result - \ru Результирующее тело. + \en The resultant solid. \~ + \result \ru - Код результата операции. + \en - The operation result code. \~ + \ingroup Sheet_Metal_Modeling +*/ +// --- +MATH_FUNC (MbCreator *) CreateSimplifiedFlatPattern( MbFaceShell & initialShell, + const MbeCopyMode sameShell, + const MbSimplifyFlatPatternValues & params, + const MbSNameMaker & nameMaker, + MbResultType & res, + MbFaceShell *& shell ); + + +#endif // __CR_SHEET_SIMPLIFIED_FLAT_SOLID_H + diff --git a/C3d/Include/cr_sheet_union_solid.h b/C3d/Include/cr_sheet_union_solid.h index 5029aaf..fb19969 100644 --- a/C3d/Include/cr_sheet_union_solid.h +++ b/C3d/Include/cr_sheet_union_solid.h @@ -1,112 +1,112 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Строитель операции объединения листовых тел по торцу. - \en Constructor of operation of union of sheet solids by butt. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_SHEET_UNION_SOLID_H -#define __CR_SHEET_UNION_SOLID_H - - -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель операции объединения листовых тел по торцу. - \en Constructor of operation of union of sheet solids by butt. \~ - \details \ru Строитель операции объединения листовых тел по торцу объединяет тела - только по указанному с помощью ориентированных рёбер торцу, - независимо от возможных пересечений тел в других местах.\n. - \en Constructor of operation of union of sheet solids by butt unites solids - only by the bound specified using oriented edges - independently of possible intersections of solids in other places.\n. \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbSheetUnionSolid : public MbCreator { -protected : - RPArray creators; ///< \ru Журнал построения: 0<=i & solid2, const bool same2, const MbSNameMaker & n ); -private : - MbSheetUnionSolid( const MbSheetUnionSolid & init, MbRegDuplicate *ireg ); - -public : - virtual ~MbSheetUnionSolid(); - - // \ru Общие функции математического объекта \en Common functions of the mathematical object - - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element - virtual void Transform( const MbMatrix3D &, MbRegTransform * ireg = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * ireg = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * ireg = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const ; // \ru Сделать копию \en Create a copy - - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - virtual bool IsSame ( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & item ) const; // \ru Являются ли объекты подобными? \en Determine whether an object is similar? - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal - - // \ru Общие функции твердого тела \en Common functions of solid - - virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, - RPArray * items = NULL ); // \ru Построение \en Construction - - virtual void SetYourVersion( VERSION version, bool forAll ); - - /// \ru Количество строителей первого тела. \en Count of creators of the first solid. - size_t GetCountOne() const { return countOne; } - /// \ru Общее количество строителей. \en Total count of creators. - size_t GetCreatorsCount() const { return creators.Count(); } - /// \ru Добавить в журнал. \en Add to the history tree. - void AddCreator ( MbCreator & creator ); - /// \ru Дать строитель. \en Get the constructor. - MbCreator * GetCreator ( const size_t ind ) const; - void DeleteCreator( const size_t ind ); - -private : - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. - MbSheetUnionSolid( const MbSheetUnionSolid & init ); - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - MbSheetUnionSolid & operator = ( const MbSheetUnionSolid & ); - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSheetUnionSolid ) -}; - -IMPL_PERSISTENT_OPS( MbSheetUnionSolid ) - -//------------------------------------------------------------------------------ -/** \brief \ru Создать оболочку объединённых по торцу листовых тел. - \en Create a shell of sheet solids united by a butt. \~ - \details \ru Для указанных оболочек построить оболочку как результат операции объединения над множествами граней двух тел. - Одновременно с построением оболочки функция создаёт её строитель.\n - \en For the specified shells create a shell using operation of union of face sets of two solids. - The function simultaneously creates the shell and its constructor.\n \~ - \result \ru Возвращает строитель. - \en Returns the constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateSheetUnion( MbFaceShell & faceShell1, - const MbeCopyMode sameShell1, - const RPArray & creators2, - MbFaceShell & faceShell2, - const MbeCopyMode sameShell2, - const MbSNameMaker & operNames, - MbResultType & res, - MbFaceShell *& shell ); - - -#endif // __CR_SHEET_UNION_SOLID_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Строитель операции объединения листовых тел по торцу. + \en Constructor of operation of union of sheet solids by butt. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_SHEET_UNION_SOLID_H +#define __CR_SHEET_UNION_SOLID_H + + +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель операции объединения листовых тел по торцу. + \en Constructor of operation of union of sheet solids by butt. \~ + \details \ru Строитель операции объединения листовых тел по торцу объединяет тела + только по указанному с помощью ориентированных рёбер торцу, + независимо от возможных пересечений тел в других местах.\n. + \en Constructor of operation of union of sheet solids by butt unites solids + only by the bound specified using oriented edges + independently of possible intersections of solids in other places.\n. \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbSheetUnionSolid : public MbCreator { +protected : + RPArray creators; ///< \ru Журнал построения: 0<=i & solid2, const bool same2, const MbSNameMaker & n ); +private : + MbSheetUnionSolid( const MbSheetUnionSolid & init, MbRegDuplicate *ireg ); + +public : + virtual ~MbSheetUnionSolid(); + + // \ru Общие функции математического объекта \en Common functions of the mathematical object + + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element + virtual void Transform( const MbMatrix3D &, MbRegTransform * ireg = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * ireg = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * ireg = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const ; // \ru Сделать копию \en Create a copy + + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + virtual bool IsSame ( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & item ) const; // \ru Являются ли объекты подобными? \en Determine whether an object is similar? + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + + // \ru Общие функции твердого тела \en Common functions of solid + + virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, + RPArray * items = NULL ); // \ru Построение \en Construction + + virtual void SetYourVersion( VERSION version, bool forAll ); + + /// \ru Количество строителей первого тела. \en Count of creators of the first solid. + size_t GetCountOne() const { return countOne; } + /// \ru Общее количество строителей. \en Total count of creators. + size_t GetCreatorsCount() const { return creators.Count(); } + /// \ru Добавить в журнал. \en Add to the history tree. + void AddCreator ( MbCreator & creator ); + /// \ru Дать строитель. \en Get the constructor. + MbCreator * GetCreator ( const size_t ind ) const; + void DeleteCreator( const size_t ind ); + +private : + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. + MbSheetUnionSolid( const MbSheetUnionSolid & init ); + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + MbSheetUnionSolid & operator = ( const MbSheetUnionSolid & ); + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSheetUnionSolid ) +}; + +IMPL_PERSISTENT_OPS( MbSheetUnionSolid ) + +//------------------------------------------------------------------------------ +/** \brief \ru Создать оболочку объединённых по торцу листовых тел. + \en Create a shell of sheet solids united by a butt. \~ + \details \ru Для указанных оболочек построить оболочку как результат операции объединения над множествами граней двух тел. + Одновременно с построением оболочки функция создаёт её строитель.\n + \en For the specified shells create a shell using operation of union of face sets of two solids. + The function simultaneously creates the shell and its constructor.\n \~ + \result \ru Возвращает строитель. + \en Returns the constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateSheetUnion( MbFaceShell & faceShell1, + const MbeCopyMode sameShell1, + const RPArray & creators2, + MbFaceShell & faceShell2, + const MbeCopyMode sameShell2, + const MbSNameMaker & operNames, + MbResultType & res, + MbFaceShell *& shell ); + + +#endif // __CR_SHEET_UNION_SOLID_H diff --git a/C3d/Include/cr_simple_creator.h b/C3d/Include/cr_simple_creator.h index 79d29d2..638eb1e 100644 --- a/C3d/Include/cr_simple_creator.h +++ b/C3d/Include/cr_simple_creator.h @@ -207,8 +207,8 @@ bool MbSimpleCreator::DeleteShellCopies( const CreatorsVector & creators ) //------------------------------------------------------------------------------ // \ru Есть ли в каком-то простом построителе (MbSimpleCreator) заданная оболочка. \en Is there a simple builder (MbSimpleCreator) that contains a given shell?. // --- -template -bool MbSimpleCreator::IsThisShell( const MbFaceShell & shell, const Creators & creators ) +template +bool MbSimpleCreator::IsThisShell( const MbFaceShell & shell, const CreatorsVector & creators ) { bool res = false; diff --git a/C3d/Include/cr_split_data.h b/C3d/Include/cr_split_data.h index 579ed47..ea8a9a5 100644 --- a/C3d/Include/cr_split_data.h +++ b/C3d/Include/cr_split_data.h @@ -1,523 +1,523 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Усекающие элементы оболочки. - \en Truncating elements of a shell. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_SPLIT_DATA_H -#define __CR_SPLIT_DATA_H - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class reader; -class writer; -class MATH_CLASS MbProperties; -class MATH_CLASS MbCartPoint3D; -class MATH_CLASS MbAxis3D; -class MATH_CLASS MbSpaceItem; -class MATH_CLASS MbCurve; -class MATH_CLASS MbSurfaceIntersectionCurve; -class MATH_CLASS MbSNameMaker; -class MATH_CLASS MbSolid; -struct MATH_CLASS MbControlData3D; -class MbRegDuplicate; -class MbRegTransform; -enum MbeSenseValue; -enum MbeCopyMode; - - -//------------------------------------------------------------------------------ -/** \brief \ru Усекающие элементы. - \en Truncating elements. \~ - \details \ru Усекающие элементы используются для разделения граней на части и усечения оболочек. - Усечение может выполняться двумерными кривыми, расположенными в плоскости XY локальной системы координат, - трёхмерными кривыми, поверхностями и оболочками. - Усекающие элементы используются в строителе усеченной оболочки MbTruncatedShell и - строителе оболочки с разбиением граней MbSplitShell. \n - \en Truncating elements are used for splitting faces into parts and truncation of shells. - Truncating can be performed by two-dimensional curves located in the XY plane of the local coordinate system, - by three-dimensional curves, surfaces and shells. - Truncating elements are used in the creator of truncated shell MbTruncatedShell and - in the creator of shell with face splitting MbSplitShell. \n \~ - \ingroup Build_Parameters -*/ -// --- -class MATH_CLASS MbSplitData : public MbRefItem { - /// \ru Типы усекающих объектов. \en Truncating objects types. - enum MbeSplitItemsType { - sit_NoItems = 0, ///< \ru Нет объектов. \en No objects. - sit_Curves2d = 1, ///< \ru Двумерные кривые в локальной системе координат. \en Two-dimensional curves in the local coordinate system. - sit_Curves3d = 2, ///< \ru Трехмерные кривые. \en Three-dimensional curves. - sit_Surfaces = 3, ///< \ru Поверхности. \en Surfaces. - sit_Creators = 4, ///< \ru Строители тела. \en Solid creators. - }; - -private: - // Sketch contours - c3d::PlaneContoursSPtrVector sketchContours; ///< \ru Двумерные кривые. \en Two-dimensional curves. - MbPlacement3D place; ///< \ru Локальная система координат двумерных кривых. \en Local coordinate system of two-dimensional curves. - MbVector3D direction; ///< \ru Вектор выдавливания двумерных кривых. \en Extrusion direction vector of two-dimensional curves. - MbeSenseValue sense; ///< \ru Направление выдавливания двумерных кривых относительно вектора. \en Extrusion direction of two-dimensional curves relative to direction vector. - // Space Curves - c3d::SpaceCurvesSPtrVector spaceCurves; ///< \ru Пространственные кривые. \en Spatial curves. - // Surfaces - c3d::SurfacesSPtrVector surfaces; ///< \ru Поверхности. \en Surfaces. - // Shell - c3d::CreatorsSPtrVector creators; ///< \ru Строители оболочки. \en Shell creators. - c3d::ShellSPtr solidShell; ///< \ru Оболочка. \en A shell. - -public: - /// \ru Конструктор. \en Constructor. - MbSplitData() - : place ( ) - , direction ( ) - , sense ( orient_BOTH ) - , sketchContours( ) - , spaceCurves ( ) - , surfaces ( ) - , creators ( ) - , solidShell ( NULL ) - { - } - /// \ru Конструктор по двумерному контуру в локальной системе координат. \en Constructor by two-dimensional contour in the local coordinate system. - MbSplitData( const MbPlacement3D & pl, MbeSenseValue dirSense, const MbContour & item, bool same ) - : place ( pl ) - , direction ( ) - , sense ( dirSense ) - , sketchContours( ) - , spaceCurves ( ) - , surfaces ( ) - , creators ( ) - , solidShell ( NULL ) - { - SPtr sketchContour; - sketchContour = same ? const_cast(&item) : static_cast(&item.Duplicate()); - sketchContours.push_back( sketchContour ); - } - /// \ru Конструктор по двумерному контуру в локальной системе координат. \en Constructor by two-dimensional contour in the local coordinate system. - MbSplitData( const MbPlacement3D & pl, const MbVector3D & dir, const MbContour & item, bool same ) - : place ( pl ) - , direction ( dir ) - , sense ( orient_BOTH ) - , sketchContours( ) - , spaceCurves ( ) - , surfaces ( ) - , creators ( ) - , solidShell ( NULL ) - { - C3D_ASSERT( (direction.MaxFactor() < LENGTH_EPSILON) || !direction.Orthogonal( place.GetAxisZ(), ANGLE_EPSILON ) ); - - SPtr sketchContour; - sketchContour = same ? const_cast(&item) : static_cast(&item.Duplicate()); - sketchContours.push_back( sketchContour ); - } - /// \ru Конструктор по двумерным контурам в локальной системе координат. \en Constructor by two-dimensional contours in the local coordinate system. - template - MbSplitData( const MbPlacement3D & pl, MbeSenseValue dirSense, const PlaneContoursVector & items, bool same ) - : place ( pl ) - , direction ( ) - , sense ( dirSense ) - , sketchContours( ) - , spaceCurves ( ) - , surfaces ( ) - , creators ( ) - , solidShell ( NULL ) - { - ::AddRefItems( items, same, sketchContours ); - } - /// \ru Конструктор по двумерным контурам в локальной системе координат. \en Constructor by two-dimensional contours in the local coordinate system. - template - MbSplitData( const MbPlacement3D & pl, const MbVector3D & dir, const PlaneContoursVector & items, bool same ) - : place ( pl ) - , direction ( dir ) - , sense ( orient_BOTH ) - , sketchContours( ) - , spaceCurves ( ) - , surfaces ( ) - , creators ( ) - , solidShell ( NULL ) - { - C3D_ASSERT( (direction.MaxFactor() < LENGTH_EPSILON) || !direction.Orthogonal( place.GetAxisZ(), ANGLE_EPSILON ) ); - - ::AddRefItems( items, same, sketchContours ); - } - /// \ru Конструктор по пространственным кривым. \en Constructor by spatial curves. - MbSplitData( const c3d::ConstSpaceCurvesSPtrVector & items, bool same ) - : place ( ) - , direction ( ) - , sense ( orient_BOTH ) - , sketchContours( ) - , spaceCurves ( ) - , surfaces ( ) - , creators ( ) - , solidShell ( NULL ) - { - ::AddRefItems( items, same, spaceCurves ); - } - /// \ru Конструктор по пространственным кривым. \en Constructor by spatial curves. - MbSplitData( const c3d::ConstSpaceCurvesVector & items, bool same ) - : place ( ) - , direction ( ) - , sense ( orient_BOTH ) - , sketchContours( ) - , spaceCurves ( ) - , surfaces ( ) - , creators ( ) - , solidShell ( NULL ) - { - ::AddRefItems( items, same, spaceCurves ); - } - /// \ru Конструктор по поверхности. \en Constructor by a surface. - MbSplitData( const MbSurface & item, bool same ) - : place ( ) - , direction ( ) - , sense ( orient_BOTH ) - , sketchContours( ) - , spaceCurves ( ) - , surfaces ( ) - , creators ( ) - , solidShell ( NULL ) - { - SPtr surface; - surface = same ? const_cast(&item) : static_cast(&item.Duplicate()); - surfaces.push_back( surface ); - } - /// \ru Конструктор по поверхностям. \en Constructor by surfaces. - MbSplitData( const c3d::ConstSurfacesSPtrVector & items, bool same ) - : place ( ) - , direction ( ) - , sense ( orient_BOTH ) - , sketchContours( ) - , spaceCurves ( ) - , surfaces ( ) - , creators ( ) - , solidShell ( NULL ) - { - ::AddRefItems( items, same, surfaces ); - } - /// \ru Конструктор по поверхностям. \en Constructor by surfaces. - MbSplitData( const c3d::ConstSurfacesVector & items, bool same ) - : place ( ) - , direction ( ) - , sense ( orient_BOTH ) - , sketchContours( ) - , spaceCurves ( ) - , surfaces ( ) - , creators ( ) - , solidShell ( NULL ) - { - ::AddRefItems( items, same, surfaces ); - } - /// \ru Конструктор по телу. \en Constructor by a solid. - MbSplitData( const MbSolid & solid, bool same, bool keepShell ); - /// \ru Конструктор копирования с регистратором копирования. \en Copy constructor with registrator of copying. - explicit MbSplitData( const MbSplitData &, bool same, MbRegDuplicate * iReg ); - /// \ru Деструктор. \en Destructor. - ~MbSplitData(); - -public: - /// \ru Инициализировать по двумерному контуру в локальной системе координат. \en Initialize by two-dimensional contour in the local coordinate system. - bool InitPlaneContour( const MbPlacement3D & pl, MbeSenseValue dirSense, const MbContour & item, bool same ) - { - DeleteItems(); - place.Init( pl ); - direction.SetZero(); - sense = dirSense; - - SPtr sketchContour; - sketchContour = same ? const_cast( &item ) : static_cast(&item.Duplicate()); - sketchContours.push_back( sketchContour ); - return true; - } - /// \ru Инициализировать по двумерному контуру в локальной системе координат. \en Initialize by two-dimensional contour in the local coordinate system. - bool InitPlaneContour( const MbPlacement3D & pl, const MbVector3D & dir, const MbContour & item, bool same ) - { - DeleteItems(); - place.Init( pl ); - direction.Init( dir ); - C3D_ASSERT( (direction.MaxFactor() < LENGTH_EPSILON) || !direction.Orthogonal( place.GetAxisZ(), ANGLE_EPSILON ) ); - sense = orient_BOTH; - - SPtr sketchContour; - sketchContour = same ? const_cast( &item ) : static_cast(&item.Duplicate()); - sketchContours.push_back( sketchContour ); - return true; - } - /// \ru Инициализировать по двумерным контурам в локальной системе координат. \en Initialize by two-dimensional contours in the local coordinate system. - template - bool InitPlaneContours( const MbPlacement3D & pl, MbeSenseValue dirSense, const PlaneContoursVector & items, bool same ) - { - if ( items.size() > 0 ) { - DeleteItems(); - place.Init( pl ); - direction.SetZero(); - sense = dirSense; - - ::AddRefItems( items, same, sketchContours ); - return true; - } - return false; - } - /// \ru Инициализировать по двумерным контурам в локальной системе координат. \en Initialize by two-dimensional contours in the local coordinate system. - template - bool InitPlaneContours( const MbPlacement3D & pl, const MbVector3D & dir, const PlaneContoursVector & items, bool same ) - { - if ( items.size() > 0 ) { - DeleteItems(); - place.Init( pl ); - direction.Init( dir ); - C3D_ASSERT( (direction.MaxFactor() < LENGTH_EPSILON) || !direction.Orthogonal( place.GetAxisZ(), ANGLE_EPSILON ) ); - sense = orient_BOTH; - - ::AddRefItems( items, same, sketchContours ); - return true; - } - return false; - } - /// \ru Инициализировать по пространственным кривым. \en Initialize by spatial curves. - template - bool InitSpaceCurves( const SpaceCurvesVector & items, bool same ) - { - if ( items.size() > 0 ) { - DeleteItems(); - - ::AddRefItems( items, same, spaceCurves ); - return true; - } - return false; - } - /// \ru Инициализировать по поверхностям. \en Initialize by surfaces. - template - bool InitSurfaces( const SurfacesVector & items, bool same ) - { - if ( items.size() > 0 ) { - DeleteItems(); - - ::AddRefItems( items, same, surfaces ); - return true; - } - return false; - } - /// \ru Инициализировать по телу. \en Initialize by a solid. - bool InitSolid( const MbSolid & solid, bool same, bool keepShell ); - /// \ru Инициализировать по построителям тела. \en Initialize by solid creators. - template - bool InitSolid( const CreatorsVector & solidCreators, bool sameCreators ) - { - DeleteItems(); - size_t creatorsCnt = solidCreators.size(); - if ( creatorsCnt > 0 ) { - MbRegDuplicate * iReg = NULL; - MbAutoRegDuplicate autoReg( iReg ); - SPtr creator; - creators.reserve( creatorsCnt ); - for ( size_t k = 0; k < creatorsCnt; ++k ) { - if ( solidCreators[k] != NULL ) { - creator = sameCreators ? &const_cast( *solidCreators[k] ) : static_cast( &solidCreators[k]->Duplicate( iReg ) ); - creators.push_back( creator ); - ::DetachItem( creator ); - } - } - if ( creators.size() > 0 ) - return true; - } - return false; - } - /// \ru Сделать равным. \en Make equal. - bool SetEqual ( const MbSplitData & ); - /// \ru Являются ли объекты подобными. \en Determine whether the objects are similar. - bool IsSimilar( const MbSplitData & ) const; - /// \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. - void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); - /// \ru Сдвинуть по вектору. \en Shift by a vector. - void Move ( const MbVector3D &, MbRegTransform * = NULL ); - /// \ru Повернуть вокруг оси. \en Rotate about an axis. - void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); - /// \ru Отсутствуют ли объекты? \en Are the objects absent? - bool IsEmpty() const { - return ( sketchContours.empty() && - spaceCurves.empty() && - surfaces.empty() && - (creators.empty() && (solidShell == NULL)) ); } - /// \ru Являются ли объекты равными? \en Determine whether an object is equal? - bool IsSame( const MbSplitData &, double accuracy ) const; - - /** \ru \name Доступ к эскизу. - \en \name Access to a sketch. - \{ */ - /// \ru Выдать количество двумерных кривых. \en Get number of two-dimensional curves. - size_t GetSketchCurvesCount() const { return sketchContours.size(); } - /// \ru Получить локальную систему координат двумерных кривых. \en Get the local coordinate system of two-dimensional curves. - const MbPlacement3D & GetSketchPlace() const { return place; } - /// \ru Получить локальную систему координат двумерных кривых. \en Get the local coordinate system of two-dimensional curves. - MbPlacement3D & SetSketchPlace() { return place; } - /// \ru Получить вектор направления выдавливания двумерных кривых. \en Get the extrusion direction vector of two-dimensional curves. - const MbVector3D & GetSketchDirection() const { return direction; } - /// \ru Получить вектор направления выдавливания двумерных кривых. \en Get the extrusion direction vector of two-dimensional curves. - MbVector3D & SetSketchDirection() { return direction; } - /// \ru Выдать направление выдавливания двумерных кривых. \en Get extrusion direction of two-dimensional curves. - const MbeSenseValue GetSketchSense() const { return sense; } - /// \ru Выдать направление выдавливания двумерных кривых. \en Get extrusion direction of two-dimensional curves. - MbeSenseValue & SetSketchSense() { return sense; } - /// \ru Установить направление выдавливания двумерных кривых. \en Set extrusion direction of two-dimensional curves. - void SetSketchSense( MbeSenseValue zdir ) { sense = zdir; } - /// \ru Получить двумерную кривую по индексу. \en Get two-dimensional curve by index. - const MbContour * GetSketchCurve( size_t k ) const { return ((k < sketchContours.size()) ? sketchContours[k].get() : NULL ); } - /// \ru Получить двумерную кривую по индексу. \en Get two-dimensional curve by index. - MbContour * SetSketchCurve( size_t k ) { return ((k < sketchContours.size()) ? sketchContours[k].get() : NULL ); } - /// \ru Получить все двумерные кривые. \en Get all two-dimensional curves. - template - void GetSketchCurves( PlaneContoursVector & curvs ) const - { - curvs.reserve( curvs.size() + sketchContours.size() ); - c3d::PlaneContourSPtr sketchContour; - for ( size_t k = 0, addCnt = sketchContours.size(); k < addCnt; ++k ) { - sketchContour = const_cast( sketchContours[k].get() ); - curvs.push_back( sketchContour ); - } - } - /// \ru Удалить двумерную кривую по индексу. \en Delete two-dimensional curve by index. - bool DeleteSketchCurve( size_t k ); - - /** \} */ - /** \ru \name Доступ к пространственным кривым. - \en \name Access to spatial curves. - \{ */ - /// \ru Выдать количество пространственных кривых. \en Get number of spatial curves. - size_t GetSpaceCurvesCount() const { return spaceCurves.size(); } - /// \ru Получить пространственную кривую по индексу. \en Get a spatial curve by index. - const MbCurve3D * GetSpaceCurve( size_t k ) const { return ((k < spaceCurves.size()) ? spaceCurves[k].get() : NULL ); } - /// \ru Получить пространственную кривую по индексу. \en Get a spatial curve by index. - MbCurve3D * SetSpaceCurve( size_t k ) { return ((k < spaceCurves.size()) ? spaceCurves[k].get() : NULL ); } - /// \ru Получить все пространственные кривые. \en Get all spatial curves. - template - void GetSpaceCurves( SpaceCurvesVector & curvs ) const - { - curvs.reserve( curvs.size() + spaceCurves.size() ); - c3d::SpaceCurveSPtr spaceCurve; - for ( size_t k = 0, addCnt = spaceCurves.size(); k < addCnt; ++k ) { - spaceCurve = const_cast( spaceCurves[k].get() ); - curvs.push_back( spaceCurve ); - } - } - /// \ru Установить пространственную кривую по индексу. \en Set spatial curve by index. - bool SetSpaceCurve( const MbCurve3D & curve, size_t k ); - - /** \} */ - /** \ru \name Доступ к поверхностям. - \en \name Access to surfaces. - \{ */ - /// \ru Выдать количество поверхностей. \en Get number of surfaces. - size_t GetSurfacesCount() const { return surfaces.size(); } - /// \ru Получить поверхность по индексу. \en Get a surface by index. - const MbSurface * GetSurface( size_t k ) const { return ((k < surfaces.size()) ? surfaces[k].get() : NULL); } - /// \ru Получить поверхность по индексу. \en Get a surface by index. - MbSurface * SetSurface( size_t k ) { return ((k < surfaces.size()) ? surfaces[k].get() : NULL); } - /// \ru Получить все поверхности. \en Get all surfaces. - template - void GetSurfaces( SurfacesVector & surfs ) const - { - surfs.reserve( surfs.size() + surfaces.size() ); - c3d::SurfaceSPtr surface; - for ( size_t k = 0, addCnt = surfaces.size(); k < addCnt; ++k ) { - surface = const_cast( surfaces[k].get() ); - surfs.push_back( surface ); - } - } - /// \ru Установить поверхность по индексу. \en Set a surface by index. - bool SetSurface( const MbSurface & surface, size_t k ); - - /** \} */ - /** \ru \name Доступ к строителям. - \en \name Access to creators. - \{ */ - /// \ru Выдать количество строителей тела. \en Get number of solid creators. - size_t GetCreatorsCount() const { return creators.size(); } - /// \ru Получить строитель по индексу. \en Get constructor by index. - const MbCreator * GetCreator( size_t k ) const { return ((k < creators.size()) ? creators[k].get() : NULL ); } - /// \ru Получить строитель по индексу. \en Get constructor by index. - MbCreator * SetCreator( size_t k ) { return ((k < creators.size()) ? creators[k].get() : NULL ); } - /// \ru Получить все строители. \en Get all creators. - template - void GetCreators( CreatorsVector & crs ) const - { - crs.reserve( crs.size() + creators.size() ); - c3d::ConstCreatorSPtr creator; - for ( size_t k = 0, addCnt = creators.size(); k < addCnt; ++k ) { - creator = creators[k]; - crs.push_back( creator ); - ::DetachItem( creator ); - } - } - /// \ru Получить все строители. \en Get all creators. - template - void GetCreatorsCopies( CreatorsVector & crs ) const - { - MbRegDuplicate * iReg = NULL; - MbAutoRegDuplicate autoReg( iReg ); - - crs.reserve( crs.size() + creators.size() ); - c3d::CreatorSPtr creator; - for ( size_t k = 0, addCnt = creators.size(); k < addCnt; ++k ) { - if ( creators[k] != NULL ) - creator = static_cast( &creators[k]->Duplicate( iReg ) ); - crs.push_back( creator ); - ::DetachItem( creator ); - creator = NULL; - } - } - /// \ru Получить все строители. \en Get all creators. - template - void SetCreators( CreatorsVector & crs ) - { - crs.reserve( crs.size() + creators.size() ); - c3d::CreatorSPtr creator; - for ( size_t k = 0, addCnt = creators.size(); k < addCnt; ++k ) { - creator = creators[k]; - crs.push_back( creator ); - ::DetachItem( creator ); - } - } - /// \ru Получить хранимую оболочку. \en Get stored shell. - const MbFaceShell * GetSolidShell() const { return solidShell; } - /// \ru Создать оболочку по строителям (solidShell остается нетронутой). \en Create a shell by creators (solidShell remains unchanged). - MbFaceShell * CreateShell( MbeCopyMode copyMode ); - /// \ru Создать оболочку по строителям. \en Create a shell by creators (solidShell remains unchanged). - bool UpdateShell( MbeCopyMode copyMode ); - /// \ru Удалить данные. \en Delete data. - void DeleteItems(); - /// \ru Прочитать данные. \en Read data. - void ReadItems ( reader & ); - /// \ru Записать данные. \en Write data. - void WriteItems( writer & ) const; - /// \ru Выдать свойства объекта. \en Get properties of the object. - void GetProperties( MbProperties & ); - /// \ru Записать свойства объекта. \en Set properties of the object. - void SetProperties( const MbProperties & ); - /// \ru Дать базовые объекты. \en Get the base objects. - void GetBasisItems ( RPArray & ); - /// \ru Выдать контрольные точки объекта. \en Get control points of object. - void GetBasisPoints( MbControlData3D & ) const; - /// \ru Изменить объект по контрольным точкам. \en Change the object by control points. - void SetBasisPoints( const MbControlData3D & ); - /** \} */ -OBVIOUS_PRIVATE_COPY( MbSplitData ) -}; - - -#endif // __CR_SPLIT_DATA_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Усекающие элементы оболочки. + \en Truncating elements of a shell. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_SPLIT_DATA_H +#define __CR_SPLIT_DATA_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class reader; +class writer; +class MATH_CLASS MbProperties; +class MATH_CLASS MbCartPoint3D; +class MATH_CLASS MbAxis3D; +class MATH_CLASS MbSpaceItem; +class MATH_CLASS MbCurve; +class MATH_CLASS MbSurfaceIntersectionCurve; +class MATH_CLASS MbSNameMaker; +class MATH_CLASS MbSolid; +struct MATH_CLASS MbControlData3D; +class MbRegDuplicate; +class MbRegTransform; +enum MbeSenseValue; +enum MbeCopyMode; + + +//------------------------------------------------------------------------------ +/** \brief \ru Усекающие элементы. + \en Truncating elements. \~ + \details \ru Усекающие элементы используются для разделения граней на части и усечения оболочек. + Усечение может выполняться двумерными кривыми, расположенными в плоскости XY локальной системы координат, + трёхмерными кривыми, поверхностями и оболочками. + Усекающие элементы используются в строителе усеченной оболочки MbTruncatedShell и + строителе оболочки с разбиением граней MbSplitShell. \n + \en Truncating elements are used for splitting faces into parts and truncation of shells. + Truncating can be performed by two-dimensional curves located in the XY plane of the local coordinate system, + by three-dimensional curves, surfaces and shells. + Truncating elements are used in the creator of truncated shell MbTruncatedShell and + in the creator of shell with face splitting MbSplitShell. \n \~ + \ingroup Build_Parameters +*/ +// --- +class MATH_CLASS MbSplitData : public MbRefItem { + /// \ru Типы усекающих объектов. \en Truncating objects types. + enum MbeSplitItemsType { + sit_NoItems = 0, ///< \ru Нет объектов. \en No objects. + sit_Curves2d = 1, ///< \ru Двумерные кривые в локальной системе координат. \en Two-dimensional curves in the local coordinate system. + sit_Curves3d = 2, ///< \ru Трехмерные кривые. \en Three-dimensional curves. + sit_Surfaces = 3, ///< \ru Поверхности. \en Surfaces. + sit_Creators = 4, ///< \ru Строители тела. \en Solid creators. + }; + +private: + // Sketch contours + c3d::PlaneContoursSPtrVector sketchContours; ///< \ru Двумерные кривые. \en Two-dimensional curves. + MbPlacement3D place; ///< \ru Локальная система координат двумерных кривых. \en Local coordinate system of two-dimensional curves. + MbVector3D direction; ///< \ru Вектор выдавливания двумерных кривых. \en Extrusion direction vector of two-dimensional curves. + MbeSenseValue sense; ///< \ru Направление выдавливания двумерных кривых относительно вектора. \en Extrusion direction of two-dimensional curves relative to direction vector. + // Space Curves + c3d::SpaceCurvesSPtrVector spaceCurves; ///< \ru Пространственные кривые. \en Spatial curves. + // Surfaces + c3d::SurfacesSPtrVector surfaces; ///< \ru Поверхности. \en Surfaces. + // Shell + c3d::CreatorsSPtrVector creators; ///< \ru Строители оболочки. \en Shell creators. + c3d::ShellSPtr solidShell; ///< \ru Оболочка. \en A shell. + +public: + /// \ru Конструктор. \en Constructor. + MbSplitData() + : place ( ) + , direction ( ) + , sense ( orient_BOTH ) + , sketchContours( ) + , spaceCurves ( ) + , surfaces ( ) + , creators ( ) + , solidShell ( NULL ) + { + } + /// \ru Конструктор по двумерному контуру в локальной системе координат. \en Constructor by two-dimensional contour in the local coordinate system. + MbSplitData( const MbPlacement3D & pl, MbeSenseValue dirSense, const MbContour & item, bool same ) + : place ( pl ) + , direction ( ) + , sense ( dirSense ) + , sketchContours( ) + , spaceCurves ( ) + , surfaces ( ) + , creators ( ) + , solidShell ( NULL ) + { + SPtr sketchContour; + sketchContour = same ? const_cast(&item) : static_cast(&item.Duplicate()); + sketchContours.push_back( sketchContour ); + } + /// \ru Конструктор по двумерному контуру в локальной системе координат. \en Constructor by two-dimensional contour in the local coordinate system. + MbSplitData( const MbPlacement3D & pl, const MbVector3D & dir, const MbContour & item, bool same ) + : place ( pl ) + , direction ( dir ) + , sense ( orient_BOTH ) + , sketchContours( ) + , spaceCurves ( ) + , surfaces ( ) + , creators ( ) + , solidShell ( NULL ) + { + C3D_ASSERT( (direction.MaxFactor() < LENGTH_EPSILON) || !direction.Orthogonal( place.GetAxisZ(), ANGLE_EPSILON ) ); + + SPtr sketchContour; + sketchContour = same ? const_cast(&item) : static_cast(&item.Duplicate()); + sketchContours.push_back( sketchContour ); + } + /// \ru Конструктор по двумерным контурам в локальной системе координат. \en Constructor by two-dimensional contours in the local coordinate system. + template + MbSplitData( const MbPlacement3D & pl, MbeSenseValue dirSense, const PlaneContoursVector & items, bool same ) + : place ( pl ) + , direction ( ) + , sense ( dirSense ) + , sketchContours( ) + , spaceCurves ( ) + , surfaces ( ) + , creators ( ) + , solidShell ( NULL ) + { + ::AddRefItems( items, same, sketchContours ); + } + /// \ru Конструктор по двумерным контурам в локальной системе координат. \en Constructor by two-dimensional contours in the local coordinate system. + template + MbSplitData( const MbPlacement3D & pl, const MbVector3D & dir, const PlaneContoursVector & items, bool same ) + : place ( pl ) + , direction ( dir ) + , sense ( orient_BOTH ) + , sketchContours( ) + , spaceCurves ( ) + , surfaces ( ) + , creators ( ) + , solidShell ( NULL ) + { + C3D_ASSERT( (direction.MaxFactor() < LENGTH_EPSILON) || !direction.Orthogonal( place.GetAxisZ(), ANGLE_EPSILON ) ); + + ::AddRefItems( items, same, sketchContours ); + } + /// \ru Конструктор по пространственным кривым. \en Constructor by spatial curves. + MbSplitData( const c3d::ConstSpaceCurvesSPtrVector & items, bool same ) + : place ( ) + , direction ( ) + , sense ( orient_BOTH ) + , sketchContours( ) + , spaceCurves ( ) + , surfaces ( ) + , creators ( ) + , solidShell ( NULL ) + { + ::AddRefItems( items, same, spaceCurves ); + } + /// \ru Конструктор по пространственным кривым. \en Constructor by spatial curves. + MbSplitData( const c3d::ConstSpaceCurvesVector & items, bool same ) + : place ( ) + , direction ( ) + , sense ( orient_BOTH ) + , sketchContours( ) + , spaceCurves ( ) + , surfaces ( ) + , creators ( ) + , solidShell ( NULL ) + { + ::AddRefItems( items, same, spaceCurves ); + } + /// \ru Конструктор по поверхности. \en Constructor by a surface. + MbSplitData( const MbSurface & item, bool same ) + : place ( ) + , direction ( ) + , sense ( orient_BOTH ) + , sketchContours( ) + , spaceCurves ( ) + , surfaces ( ) + , creators ( ) + , solidShell ( NULL ) + { + SPtr surface; + surface = same ? const_cast(&item) : static_cast(&item.Duplicate()); + surfaces.push_back( surface ); + } + /// \ru Конструктор по поверхностям. \en Constructor by surfaces. + MbSplitData( const c3d::ConstSurfacesSPtrVector & items, bool same ) + : place ( ) + , direction ( ) + , sense ( orient_BOTH ) + , sketchContours( ) + , spaceCurves ( ) + , surfaces ( ) + , creators ( ) + , solidShell ( NULL ) + { + ::AddRefItems( items, same, surfaces ); + } + /// \ru Конструктор по поверхностям. \en Constructor by surfaces. + MbSplitData( const c3d::ConstSurfacesVector & items, bool same ) + : place ( ) + , direction ( ) + , sense ( orient_BOTH ) + , sketchContours( ) + , spaceCurves ( ) + , surfaces ( ) + , creators ( ) + , solidShell ( NULL ) + { + ::AddRefItems( items, same, surfaces ); + } + /// \ru Конструктор по телу. \en Constructor by a solid. + MbSplitData( const MbSolid & solid, bool same, bool keepShell ); + /// \ru Конструктор копирования с регистратором копирования. \en Copy constructor with registrator of copying. + explicit MbSplitData( const MbSplitData &, bool same, MbRegDuplicate * iReg ); + /// \ru Деструктор. \en Destructor. + ~MbSplitData(); + +public: + /// \ru Инициализировать по двумерному контуру в локальной системе координат. \en Initialize by two-dimensional contour in the local coordinate system. + bool InitPlaneContour( const MbPlacement3D & pl, MbeSenseValue dirSense, const MbContour & item, bool same ) + { + DeleteItems(); + place.Init( pl ); + direction.SetZero(); + sense = dirSense; + + SPtr sketchContour; + sketchContour = same ? const_cast( &item ) : static_cast(&item.Duplicate()); + sketchContours.push_back( sketchContour ); + return true; + } + /// \ru Инициализировать по двумерному контуру в локальной системе координат. \en Initialize by two-dimensional contour in the local coordinate system. + bool InitPlaneContour( const MbPlacement3D & pl, const MbVector3D & dir, const MbContour & item, bool same ) + { + DeleteItems(); + place.Init( pl ); + direction.Init( dir ); + C3D_ASSERT( (direction.MaxFactor() < LENGTH_EPSILON) || !direction.Orthogonal( place.GetAxisZ(), ANGLE_EPSILON ) ); + sense = orient_BOTH; + + SPtr sketchContour; + sketchContour = same ? const_cast( &item ) : static_cast(&item.Duplicate()); + sketchContours.push_back( sketchContour ); + return true; + } + /// \ru Инициализировать по двумерным контурам в локальной системе координат. \en Initialize by two-dimensional contours in the local coordinate system. + template + bool InitPlaneContours( const MbPlacement3D & pl, MbeSenseValue dirSense, const PlaneContoursVector & items, bool same ) + { + if ( items.size() > 0 ) { + DeleteItems(); + place.Init( pl ); + direction.SetZero(); + sense = dirSense; + + ::AddRefItems( items, same, sketchContours ); + return true; + } + return false; + } + /// \ru Инициализировать по двумерным контурам в локальной системе координат. \en Initialize by two-dimensional contours in the local coordinate system. + template + bool InitPlaneContours( const MbPlacement3D & pl, const MbVector3D & dir, const PlaneContoursVector & items, bool same ) + { + if ( items.size() > 0 ) { + DeleteItems(); + place.Init( pl ); + direction.Init( dir ); + C3D_ASSERT( (direction.MaxFactor() < LENGTH_EPSILON) || !direction.Orthogonal( place.GetAxisZ(), ANGLE_EPSILON ) ); + sense = orient_BOTH; + + ::AddRefItems( items, same, sketchContours ); + return true; + } + return false; + } + /// \ru Инициализировать по пространственным кривым. \en Initialize by spatial curves. + template + bool InitSpaceCurves( const SpaceCurvesVector & items, bool same ) + { + if ( items.size() > 0 ) { + DeleteItems(); + + ::AddRefItems( items, same, spaceCurves ); + return true; + } + return false; + } + /// \ru Инициализировать по поверхностям. \en Initialize by surfaces. + template + bool InitSurfaces( const SurfacesVector & items, bool same ) + { + if ( items.size() > 0 ) { + DeleteItems(); + + ::AddRefItems( items, same, surfaces ); + return true; + } + return false; + } + /// \ru Инициализировать по телу. \en Initialize by a solid. + bool InitSolid( const MbSolid & solid, bool same, bool keepShell ); + /// \ru Инициализировать по построителям тела. \en Initialize by solid creators. + template + bool InitSolid( const CreatorsVector & solidCreators, bool sameCreators ) + { + DeleteItems(); + size_t creatorsCnt = solidCreators.size(); + if ( creatorsCnt > 0 ) { + MbRegDuplicate * iReg = NULL; + MbAutoRegDuplicate autoReg( iReg ); + SPtr creator; + creators.reserve( creatorsCnt ); + for ( size_t k = 0; k < creatorsCnt; ++k ) { + if ( solidCreators[k] != NULL ) { + creator = sameCreators ? &const_cast( *solidCreators[k] ) : static_cast( &solidCreators[k]->Duplicate( iReg ) ); + creators.push_back( creator ); + ::DetachItem( creator ); + } + } + if ( creators.size() > 0 ) + return true; + } + return false; + } + /// \ru Сделать равным. \en Make equal. + bool SetEqual ( const MbSplitData & ); + /// \ru Являются ли объекты подобными. \en Determine whether the objects are similar. + bool IsSimilar( const MbSplitData & ) const; + /// \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. + void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); + /// \ru Сдвинуть по вектору. \en Shift by a vector. + void Move ( const MbVector3D &, MbRegTransform * = NULL ); + /// \ru Повернуть вокруг оси. \en Rotate about an axis. + void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); + /// \ru Отсутствуют ли объекты? \en Are the objects absent? + bool IsEmpty() const { + return ( sketchContours.empty() && + spaceCurves.empty() && + surfaces.empty() && + (creators.empty() && (solidShell == NULL)) ); } + /// \ru Являются ли объекты равными? \en Determine whether an object is equal? + bool IsSame( const MbSplitData &, double accuracy ) const; + + /** \ru \name Доступ к эскизу. + \en \name Access to a sketch. + \{ */ + /// \ru Выдать количество двумерных кривых. \en Get number of two-dimensional curves. + size_t GetSketchCurvesCount() const { return sketchContours.size(); } + /// \ru Получить локальную систему координат двумерных кривых. \en Get the local coordinate system of two-dimensional curves. + const MbPlacement3D & GetSketchPlace() const { return place; } + /// \ru Получить локальную систему координат двумерных кривых. \en Get the local coordinate system of two-dimensional curves. + MbPlacement3D & SetSketchPlace() { return place; } + /// \ru Получить вектор направления выдавливания двумерных кривых. \en Get the extrusion direction vector of two-dimensional curves. + const MbVector3D & GetSketchDirection() const { return direction; } + /// \ru Получить вектор направления выдавливания двумерных кривых. \en Get the extrusion direction vector of two-dimensional curves. + MbVector3D & SetSketchDirection() { return direction; } + /// \ru Выдать направление выдавливания двумерных кривых. \en Get extrusion direction of two-dimensional curves. + const MbeSenseValue GetSketchSense() const { return sense; } + /// \ru Выдать направление выдавливания двумерных кривых. \en Get extrusion direction of two-dimensional curves. + MbeSenseValue & SetSketchSense() { return sense; } + /// \ru Установить направление выдавливания двумерных кривых. \en Set extrusion direction of two-dimensional curves. + void SetSketchSense( MbeSenseValue zdir ) { sense = zdir; } + /// \ru Получить двумерную кривую по индексу. \en Get two-dimensional curve by index. + const MbContour * GetSketchCurve( size_t k ) const { return ((k < sketchContours.size()) ? sketchContours[k].get() : NULL ); } + /// \ru Получить двумерную кривую по индексу. \en Get two-dimensional curve by index. + MbContour * SetSketchCurve( size_t k ) { return ((k < sketchContours.size()) ? sketchContours[k].get() : NULL ); } + /// \ru Получить все двумерные кривые. \en Get all two-dimensional curves. + template + void GetSketchCurves( PlaneContoursVector & curvs ) const + { + curvs.reserve( curvs.size() + sketchContours.size() ); + c3d::PlaneContourSPtr sketchContour; + for ( size_t k = 0, addCnt = sketchContours.size(); k < addCnt; ++k ) { + sketchContour = const_cast( sketchContours[k].get() ); + curvs.push_back( sketchContour ); + } + } + /// \ru Удалить двумерную кривую по индексу. \en Delete two-dimensional curve by index. + bool DeleteSketchCurve( size_t k ); + + /** \} */ + /** \ru \name Доступ к пространственным кривым. + \en \name Access to spatial curves. + \{ */ + /// \ru Выдать количество пространственных кривых. \en Get number of spatial curves. + size_t GetSpaceCurvesCount() const { return spaceCurves.size(); } + /// \ru Получить пространственную кривую по индексу. \en Get a spatial curve by index. + const MbCurve3D * GetSpaceCurve( size_t k ) const { return ((k < spaceCurves.size()) ? spaceCurves[k].get() : NULL ); } + /// \ru Получить пространственную кривую по индексу. \en Get a spatial curve by index. + MbCurve3D * SetSpaceCurve( size_t k ) { return ((k < spaceCurves.size()) ? spaceCurves[k].get() : NULL ); } + /// \ru Получить все пространственные кривые. \en Get all spatial curves. + template + void GetSpaceCurves( SpaceCurvesVector & curvs ) const + { + curvs.reserve( curvs.size() + spaceCurves.size() ); + c3d::SpaceCurveSPtr spaceCurve; + for ( size_t k = 0, addCnt = spaceCurves.size(); k < addCnt; ++k ) { + spaceCurve = const_cast( spaceCurves[k].get() ); + curvs.push_back( spaceCurve ); + } + } + /// \ru Установить пространственную кривую по индексу. \en Set spatial curve by index. + bool SetSpaceCurve( const MbCurve3D & curve, size_t k ); + + /** \} */ + /** \ru \name Доступ к поверхностям. + \en \name Access to surfaces. + \{ */ + /// \ru Выдать количество поверхностей. \en Get number of surfaces. + size_t GetSurfacesCount() const { return surfaces.size(); } + /// \ru Получить поверхность по индексу. \en Get a surface by index. + const MbSurface * GetSurface( size_t k ) const { return ((k < surfaces.size()) ? surfaces[k].get() : NULL); } + /// \ru Получить поверхность по индексу. \en Get a surface by index. + MbSurface * SetSurface( size_t k ) { return ((k < surfaces.size()) ? surfaces[k].get() : NULL); } + /// \ru Получить все поверхности. \en Get all surfaces. + template + void GetSurfaces( SurfacesVector & surfs ) const + { + surfs.reserve( surfs.size() + surfaces.size() ); + c3d::SurfaceSPtr surface; + for ( size_t k = 0, addCnt = surfaces.size(); k < addCnt; ++k ) { + surface = const_cast( surfaces[k].get() ); + surfs.push_back( surface ); + } + } + /// \ru Установить поверхность по индексу. \en Set a surface by index. + bool SetSurface( const MbSurface & surface, size_t k ); + + /** \} */ + /** \ru \name Доступ к строителям. + \en \name Access to creators. + \{ */ + /// \ru Выдать количество строителей тела. \en Get number of solid creators. + size_t GetCreatorsCount() const { return creators.size(); } + /// \ru Получить строитель по индексу. \en Get constructor by index. + const MbCreator * GetCreator( size_t k ) const { return ((k < creators.size()) ? creators[k].get() : NULL ); } + /// \ru Получить строитель по индексу. \en Get constructor by index. + MbCreator * SetCreator( size_t k ) { return ((k < creators.size()) ? creators[k].get() : NULL ); } + /// \ru Получить все строители. \en Get all creators. + template + void GetCreators( CreatorsVector & crs ) const + { + crs.reserve( crs.size() + creators.size() ); + c3d::ConstCreatorSPtr creator; + for ( size_t k = 0, addCnt = creators.size(); k < addCnt; ++k ) { + creator = creators[k]; + crs.push_back( creator ); + ::DetachItem( creator ); + } + } + /// \ru Получить все строители. \en Get all creators. + template + void GetCreatorsCopies( CreatorsVector & crs ) const + { + MbRegDuplicate * iReg = NULL; + MbAutoRegDuplicate autoReg( iReg ); + + crs.reserve( crs.size() + creators.size() ); + c3d::CreatorSPtr creator; + for ( size_t k = 0, addCnt = creators.size(); k < addCnt; ++k ) { + if ( creators[k] != NULL ) + creator = static_cast( &creators[k]->Duplicate( iReg ) ); + crs.push_back( creator ); + ::DetachItem( creator ); + creator = NULL; + } + } + /// \ru Получить все строители. \en Get all creators. + template + void SetCreators( CreatorsVector & crs ) + { + crs.reserve( crs.size() + creators.size() ); + c3d::CreatorSPtr creator; + for ( size_t k = 0, addCnt = creators.size(); k < addCnt; ++k ) { + creator = creators[k]; + crs.push_back( creator ); + ::DetachItem( creator ); + } + } + /// \ru Получить хранимую оболочку. \en Get stored shell. + const MbFaceShell * GetSolidShell() const { return solidShell; } + /// \ru Создать оболочку по строителям (solidShell остается нетронутой). \en Create a shell by creators (solidShell remains unchanged). + MbFaceShell * CreateShell( MbeCopyMode copyMode ); + /// \ru Создать оболочку по строителям. \en Create a shell by creators (solidShell remains unchanged). + bool UpdateShell( MbeCopyMode copyMode ); + /// \ru Удалить данные. \en Delete data. + void DeleteItems(); + /// \ru Прочитать данные. \en Read data. + void ReadItems ( reader & ); + /// \ru Записать данные. \en Write data. + void WriteItems( writer & ) const; + /// \ru Выдать свойства объекта. \en Get properties of the object. + void GetProperties( MbProperties & ); + /// \ru Записать свойства объекта. \en Set properties of the object. + void SetProperties( const MbProperties & ); + /// \ru Дать базовые объекты. \en Get the base objects. + void GetBasisItems ( RPArray & ); + /// \ru Выдать контрольные точки объекта. \en Get control points of object. + void GetBasisPoints( MbControlData3D & ) const; + /// \ru Изменить объект по контрольным точкам. \en Change the object by control points. + void SetBasisPoints( const MbControlData3D & ); + /** \} */ +OBVIOUS_PRIVATE_COPY( MbSplitData ) +}; + + +#endif // __CR_SPLIT_DATA_H diff --git a/C3d/Include/cr_stamp_bead_solid.h b/C3d/Include/cr_stamp_bead_solid.h index 6f02c0f..beddeaa 100644 --- a/C3d/Include/cr_stamp_bead_solid.h +++ b/C3d/Include/cr_stamp_bead_solid.h @@ -143,7 +143,7 @@ IMPL_PERSISTENT_OPS( MbBeadSolid ) \ingroup Model_Creators */ // --- -MATH_FUNC (MbCreator *) CreateBead( MbFaceShell * initialShell, +MATH_FUNC (MbCreator *) CreateBead( SPtr & initialShell, const MbeCopyMode sameShell, const MbFace * face, const MbPlacement3D & placement, diff --git a/C3d/Include/cr_stamp_jalousie_solid.h b/C3d/Include/cr_stamp_jalousie_solid.h index b52a413..7f7bd83 100644 --- a/C3d/Include/cr_stamp_jalousie_solid.h +++ b/C3d/Include/cr_stamp_jalousie_solid.h @@ -134,7 +134,7 @@ IMPL_PERSISTENT_OPS( MbJalousieSolid ) \ingroup Model_Creators */ // --- -MATH_FUNC (MbCreator *) CreateJalousie( MbFaceShell * initialShell, +MATH_FUNC (MbCreator *) CreateJalousie( SPtr & initialShell, const MbeCopyMode sameShell, const MbFace * face, const MbPlacement3D & placement, diff --git a/C3d/Include/cr_stamp_jog_solid.h b/C3d/Include/cr_stamp_jog_solid.h index bf1189e..e9e6ada 100644 --- a/C3d/Include/cr_stamp_jog_solid.h +++ b/C3d/Include/cr_stamp_jog_solid.h @@ -138,18 +138,18 @@ IMPL_PERSISTENT_OPS( MbJogSolid ) \ingroup Model_Creators */ // --- -MATH_FUNC (MbCreator *) CreateSheetSolidJog( MbFaceShell & solid, - MbeCopyMode sameShell, - const RPArray & bendingFaces, - MbCurve3D & curve, - const bool unbended, - const MbJogValues & parameters, - const MbBendValues & secondBendParams, - MbSNameMaker & names, - RPArray & firstBendFaces, - RPArray & secondBendFaces, - MbResultType & res, - MbFaceShell *& shell ); +MATH_FUNC (MbCreator *) CreateSheetSolidJog( SPtr & solid, + MbeCopyMode sameShell, + const RPArray & bendingFaces, + MbCurve3D & curve, + const bool unbended, + const MbJogValues & parameters, + const MbBendValues & secondBendParams, + MbSNameMaker & names, + RPArray & firstBendFaces, + RPArray & secondBendFaces, + MbResultType & res, + MbFaceShell *& shell ); #endif // __CR_STAMP_JOG_SOLID_H diff --git a/C3d/Include/cr_stamp_remove_solid.h b/C3d/Include/cr_stamp_remove_solid.h index c5a431b..ee56931 100644 --- a/C3d/Include/cr_stamp_remove_solid.h +++ b/C3d/Include/cr_stamp_remove_solid.h @@ -1,110 +1,110 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Построение оболочки тела с без указанной операции. - \en Construction of a shell without the specified operation. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_STAMP_REMOVE_SOLID_H -#define __CR_STAMP_REMOVE_SOLID_H - - -#include -//#include -//#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель оболочки из листового материала с удалёнными элементами указанной операции. - \en The constructor of a shell from sheet material without elements of the specified operation. \~ - \details \ru Строитель оболочки из листового материала с удалёнными элементами указанной операции. - Удаляет грани с указанным главным именем операции и затягивает образовавшуюся дыру расширением соседних граней. \n - \en The constructor of a shell from sheet material without elements of the specified operation. - It removes faces with specified main name and then mends the hole by stretching the neighbour faces. \n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbRemoveOperationSolid : public MbCreator { - SimpleName removeName; - -public : - MbRemoveOperationSolid( const SimpleName removeName, - const MbSNameMaker & names ); -private: - MbRemoveOperationSolid( const MbRemoveOperationSolid &, MbRegDuplicate * iReg ); - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. - MbRemoveOperationSolid( const MbRemoveOperationSolid & ); - -public: - virtual ~MbRemoveOperationSolid(); - - // \ru Общие функции математического объекта \en Common functions of the mathematical object - - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy - - virtual bool IsSame ( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & item ) const; // \ru Являются ли объекты подобными? \en Determine whether an object is similar? - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal - - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property - - // \ru Общие функции твердого тела \en Common functions of solid solid - - virtual bool CreateShell( MbFaceShell *& shell, - MbeCopyMode sameShell, - RPArray * items = NULL ); // \ru Построение \en Construction - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - MbRemoveOperationSolid & operator = ( const MbRemoveOperationSolid & ); // \ru Не реализовано \en Not implemented - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbRemoveOperationSolid ) -}; - -IMPL_PERSISTENT_OPS( MbRemoveOperationSolid ) - -//------------------------------------------------------------------------------ -/** \brief \ru Построить оболочку без указанной операции. - \en Constructs a shell without the specified operation. \~ - \details \ru Построить оболочку без указанной операции. - Удаляет грани с указанным главным именем операции и затягивает образовавшуюся дыру расширением соседних граней. \n - \en Constructs a shell without the specified operation. - It removes faces with specified main name and then mends the hole by stretching the neighbour faces. \n \~ - \param[in] initialShell - \ru Исходная оболочка. - \en The initial shell. \~ - \param[in] sameShell - \ru Режим копирования исходной оболочки. - \en Mode of copying the initial shell. \~ - \param[in] removeName - \ru Главное имя операции которую надо удалить. - \en The main name of the operation to be removed. \~ - \param[in] names - \ru Именователь граней. - \en An object for naming faces. \~ - \param[out] res - \ru Код результата операции. - \en Operation result code. \~ - \param[out] shell - \ru Построенная оболочка. - \en The resultant shell. \~ - \result \ru Возвращает строитель оболочки. - \en Returns the shell constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateRemovedOperationResult( MbFaceShell & initialShell, - const MbeCopyMode sameShell, - const SimpleName removeName, - const MbSNameMaker & names, - MbResultType & res, - MbFaceShell *& shell ); - - - -#endif // __CR_STAMP_REMOVE_SOLID_H - - +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Построение оболочки тела с без указанной операции. + \en Construction of a shell without the specified operation. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_STAMP_REMOVE_SOLID_H +#define __CR_STAMP_REMOVE_SOLID_H + + +#include +//#include +//#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель оболочки из листового материала с удалёнными элементами указанной операции. + \en The constructor of a shell from sheet material without elements of the specified operation. \~ + \details \ru Строитель оболочки из листового материала с удалёнными элементами указанной операции. + Удаляет грани с указанным главным именем операции и затягивает образовавшуюся дыру расширением соседних граней. \n + \en The constructor of a shell from sheet material without elements of the specified operation. + It removes faces with specified main name and then mends the hole by stretching the neighbour faces. \n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbRemoveOperationSolid : public MbCreator { + SimpleName removeName; + +public : + MbRemoveOperationSolid( const SimpleName removeName, + const MbSNameMaker & names ); +private: + MbRemoveOperationSolid( const MbRemoveOperationSolid &, MbRegDuplicate * iReg ); + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. + MbRemoveOperationSolid( const MbRemoveOperationSolid & ); + +public: + virtual ~MbRemoveOperationSolid(); + + // \ru Общие функции математического объекта \en Common functions of the mathematical object + + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy + + virtual bool IsSame ( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & item ) const; // \ru Являются ли объекты подобными? \en Determine whether an object is similar? + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property + + // \ru Общие функции твердого тела \en Common functions of solid solid + + virtual bool CreateShell( MbFaceShell *& shell, + MbeCopyMode sameShell, + RPArray * items = NULL ); // \ru Построение \en Construction + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + MbRemoveOperationSolid & operator = ( const MbRemoveOperationSolid & ); // \ru Не реализовано \en Not implemented + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbRemoveOperationSolid ) +}; + +IMPL_PERSISTENT_OPS( MbRemoveOperationSolid ) + +//------------------------------------------------------------------------------ +/** \brief \ru Построить оболочку без указанной операции. + \en Constructs a shell without the specified operation. \~ + \details \ru Построить оболочку без указанной операции. + Удаляет грани с указанным главным именем операции и затягивает образовавшуюся дыру расширением соседних граней. \n + \en Constructs a shell without the specified operation. + It removes faces with specified main name and then mends the hole by stretching the neighbour faces. \n \~ + \param[in] initialShell - \ru Исходная оболочка. + \en The initial shell. \~ + \param[in] sameShell - \ru Режим копирования исходной оболочки. + \en Mode of copying the initial shell. \~ + \param[in] removeName - \ru Главное имя операции которую надо удалить. + \en The main name of the operation to be removed. \~ + \param[in] names - \ru Именователь граней. + \en An object for naming faces. \~ + \param[out] res - \ru Код результата операции. + \en Operation result code. \~ + \param[out] shell - \ru Построенная оболочка. + \en The resultant shell. \~ + \result \ru Возвращает строитель оболочки. + \en Returns the shell constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateRemovedOperationResult( MbFaceShell & initialShell, + const MbeCopyMode sameShell, + const SimpleName removeName, + const MbSNameMaker & names, + MbResultType & res, + MbFaceShell *& shell ); + + + +#endif // __CR_STAMP_REMOVE_SOLID_H + + diff --git a/C3d/Include/cr_stamp_rib_solid.h b/C3d/Include/cr_stamp_rib_solid.h index b89ed07..1f945cb 100644 --- a/C3d/Include/cr_stamp_rib_solid.h +++ b/C3d/Include/cr_stamp_rib_solid.h @@ -1,135 +1,135 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Строитель листового тела с ребром жёсткости. - \en Constructor of a sheet solid with a rib. -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_STAMP_RIB_SOLID_H -#define __CR_STAMP_RIB_SOLID_H - - -#include -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель тела с ребром жёсткости. - \en Constructor of a sheet solid with a rib. \~ - \details \ru Строитель тела с ребром жёсткости, форма которого задана плоским контуром. - \en Constructor of a solid with a rib whose shape is specified by a planar contour. \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbStampRibSolid : public MbCreator { -protected : - MbPlacement3D place; ///< \ru Подложка для формообразующей кривой, точки и вектора уклона. \en Placement of the forming curve, point and inclination vector. - MbContour * spine; ///< \ru Формообразующая кривая (хребет ребра жёсткости). \en Forming curve (rib's spine). - size_t index; ///< \ru Индекс сегмента в контуре, от которого будет установлено направление уклона. \en The segment index in the contour from which the inclination direction will be set. - SheetRibValues parameters; ///< \ru Параметры формообразования. \en Forming parameters. - -public : - MbStampRibSolid( const MbPlacement3D & place, - const MbContour & contour, - size_t index, - const SheetRibValues & param, - const MbSNameMaker & names ); -private : - MbStampRibSolid( const MbStampRibSolid & bres, MbRegDuplicate * ireg ); - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. - MbStampRibSolid( const MbStampRibSolid & bres ); -public : - virtual ~MbStampRibSolid(); - - // \ru Общие функции математического объекта \en Common functions of the mathematical object - - virtual MbeCreatorType IsA () const; // \ru Тип элемента \en A type of element - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy - virtual void Transform ( const MbMatrix3D & matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D & to, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D & axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis - - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property - - virtual bool IsSame ( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & item ) const; // \ru Являются ли объекты подобными? \en Determine whether an object is similar? - virtual bool SetEqual( const MbCreator & ); // \ru Сделать равным \en Make equal - - // \ru Общие функции твердого тела \en Common functions of solid - - virtual bool CreateShell( MbFaceShell *&shell, MbeCopyMode sameShell, - RPArray *items = NULL ); // \ru Построение \en Construction - - // \ru Дать параметры. \en Get the parameters. - void GetParameters( SheetRibValues & params ) const { params = parameters; } - // \ru Установить параметры. \en Set the parameters. - void SetParameters( const SheetRibValues & params ) { parameters = params; } - -private : - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - void operator = ( const MbStampRibSolid & ); // \ru НЕЛЬЗЯ!!! \en NOT ALLOWED!!! - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbStampRibSolid ) -}; // MbRibSolid - -IMPL_PERSISTENT_OPS( MbStampRibSolid ) - -//------------------------------------------------------------------------------ -/** \brief \ru Создать оболочку с ребром жёсткости. - \en Create a shell with a rib. \~ - \details \ru Для указанной листовой оболочки построить оболочку с ребром жёсткости, форма которого задана плоским контуром.\n - Одновременно с построением оболочки функция создаёт её строитель. \n - \en For a specified sheet shell create a shell with a rib which shape is given by the planar contour.\n - The function simultaneously constructs the shell and creates its constructor. \n \~ - \param[in] solid - \ru Исходная оболочка. - \en The source shell. \~ - \param[in] sameShell - \ru Способ копирования граней исходной оболочки. - \en Method of copying the source shell faces. \~ - \param[in] place - \ru Локальная система координат, в плоскости XY которай расположен двумерный контур. - \en A local coordinate system the two-dimensional contour is located in XY plane of. \~ - \param[in] contour - \ru Двумерный контур ребра жесткости расположен в плоскости XY локальной системы координат. - \en Two-dimensional contour of a rib located in XY plane of the local coordinate system. \~ - \param[in] index - \ru Индекс сегмента в контуре, от которого будет установлено направление уклона. - \en Index of a segment in the contour at which the inclination direction will be set. \~ - \param[in] parameters - \ru Параметры операции. - \en The operation parameters. \~ - \param[in] operNames - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[out] res - \ru Код результата операции. - \en Operation result code. \~ - \param[out] shell - \ru Построенный набор граней. - \en Constructed set of faces. \~ - \result \ru Возвращает строитель, если операция была выполнена успешно. - \en Returns the constructor if the operation has been successfully performed. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateSheetRib( MbFaceShell * solid, - MbeCopyMode sameShell, - const MbPlacement3D & place, - const MbContour & contour, - size_t index, - SheetRibValues & parameters, - const MbSNameMaker & operNames, - MbResultType & res, - MbFaceShell *& shell ); - - -//------------------------------------------------------------------------------ -// Построение элементов ребра жёсткости. -// --- -MATH_FUNC (MbResultType) CreateSheetRibParts( MbFaceShell * solid, - MbeCopyMode sameShell, - const MbPlacement3D & place, - const MbContour & contour, - const size_t index, - const SheetRibValues & pars, - const MbSNameMaker & names, - MbFaceShell *& shellToAdd, - MbFaceShell *& shellToSubtract ); - -#endif // __CR_STAMP_RIB_SOLID_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Строитель листового тела с ребром жёсткости. + \en Constructor of a sheet solid with a rib. +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_STAMP_RIB_SOLID_H +#define __CR_STAMP_RIB_SOLID_H + + +#include +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель тела с ребром жёсткости. + \en Constructor of a sheet solid with a rib. \~ + \details \ru Строитель тела с ребром жёсткости, форма которого задана плоским контуром. + \en Constructor of a solid with a rib whose shape is specified by a planar contour. \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbStampRibSolid : public MbCreator { +protected : + MbPlacement3D place; ///< \ru Подложка для формообразующей кривой, точки и вектора уклона. \en Placement of the forming curve, point and inclination vector. + MbContour * spine; ///< \ru Формообразующая кривая (хребет ребра жёсткости). \en Forming curve (rib's spine). + size_t index; ///< \ru Индекс сегмента в контуре, от которого будет установлено направление уклона. \en The segment index in the contour from which the inclination direction will be set. + SheetRibValues parameters; ///< \ru Параметры формообразования. \en Forming parameters. + +public : + MbStampRibSolid( const MbPlacement3D & place, + const MbContour & contour, + size_t index, + const SheetRibValues & param, + const MbSNameMaker & names ); +private : + MbStampRibSolid( const MbStampRibSolid & bres, MbRegDuplicate * ireg ); + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. + MbStampRibSolid( const MbStampRibSolid & bres ); +public : + virtual ~MbStampRibSolid(); + + // \ru Общие функции математического объекта \en Common functions of the mathematical object + + virtual MbeCreatorType IsA () const; // \ru Тип элемента \en A type of element + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy + virtual void Transform ( const MbMatrix3D & matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D & to, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D & axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis + + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property + + virtual bool IsSame ( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & item ) const; // \ru Являются ли объекты подобными? \en Determine whether an object is similar? + virtual bool SetEqual( const MbCreator & ); // \ru Сделать равным \en Make equal + + // \ru Общие функции твердого тела \en Common functions of solid + + virtual bool CreateShell( MbFaceShell *&shell, MbeCopyMode sameShell, + RPArray *items = NULL ); // \ru Построение \en Construction + + // \ru Дать параметры. \en Get the parameters. + void GetParameters( SheetRibValues & params ) const { params = parameters; } + // \ru Установить параметры. \en Set the parameters. + void SetParameters( const SheetRibValues & params ) { parameters = params; } + +private : + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + void operator = ( const MbStampRibSolid & ); // \ru НЕЛЬЗЯ!!! \en NOT ALLOWED!!! + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbStampRibSolid ) +}; // MbRibSolid + +IMPL_PERSISTENT_OPS( MbStampRibSolid ) + +//------------------------------------------------------------------------------ +/** \brief \ru Создать оболочку с ребром жёсткости. + \en Create a shell with a rib. \~ + \details \ru Для указанной листовой оболочки построить оболочку с ребром жёсткости, форма которого задана плоским контуром.\n + Одновременно с построением оболочки функция создаёт её строитель. \n + \en For a specified sheet shell create a shell with a rib which shape is given by the planar contour.\n + The function simultaneously constructs the shell and creates its constructor. \n \~ + \param[in] solid - \ru Исходная оболочка. + \en The source shell. \~ + \param[in] sameShell - \ru Способ копирования граней исходной оболочки. + \en Method of copying the source shell faces. \~ + \param[in] place - \ru Локальная система координат, в плоскости XY которай расположен двумерный контур. + \en A local coordinate system the two-dimensional contour is located in XY plane of. \~ + \param[in] contour - \ru Двумерный контур ребра жесткости расположен в плоскости XY локальной системы координат. + \en Two-dimensional contour of a rib located in XY plane of the local coordinate system. \~ + \param[in] index - \ru Индекс сегмента в контуре, от которого будет установлено направление уклона. + \en Index of a segment in the contour at which the inclination direction will be set. \~ + \param[in] parameters - \ru Параметры операции. + \en The operation parameters. \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[out] res - \ru Код результата операции. + \en Operation result code. \~ + \param[out] shell - \ru Построенный набор граней. + \en Constructed set of faces. \~ + \result \ru Возвращает строитель, если операция была выполнена успешно. + \en Returns the constructor if the operation has been successfully performed. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateSheetRib( MbFaceShell * solid, + MbeCopyMode sameShell, + const MbPlacement3D & place, + const MbContour & contour, + size_t index, + SheetRibValues & parameters, + const MbSNameMaker & operNames, + MbResultType & res, + MbFaceShell *& shell ); + + +//------------------------------------------------------------------------------ +// Построение элементов ребра жёсткости. +// --- +MATH_FUNC (MbResultType) CreateSheetRibParts( MbFaceShell * solid, + MbeCopyMode sameShell, + const MbPlacement3D & place, + const MbContour & contour, + const size_t index, + const SheetRibValues & pars, + const MbSNameMaker & names, + MbFaceShell *& shellToAdd, + MbFaceShell *& shellToSubtract ); + +#endif // __CR_STAMP_RIB_SOLID_H diff --git a/C3d/Include/cr_stamp_solid.h b/C3d/Include/cr_stamp_solid.h index 5884f77..a025e29 100644 --- a/C3d/Include/cr_stamp_solid.h +++ b/C3d/Include/cr_stamp_solid.h @@ -131,7 +131,7 @@ IMPL_PERSISTENT_OPS( MbStampSolid ) \ingroup Model_Creators */ // --- -MATH_FUNC (MbCreator *) CreateStamp( MbFaceShell * initialShell, // исходная оболочка +MATH_FUNC (MbCreator *) CreateStamp( SPtr & initialShell, // исходная оболочка const MbeCopyMode sameShell, // флаг способа использования исходной оболочки const MbFace * face, // грань штамповки const MbPlacement3D & placement, // локальная система координат контура diff --git a/C3d/Include/cr_stamp_spherical_solid.h b/C3d/Include/cr_stamp_spherical_solid.h index 773d3ef..af6bb97 100644 --- a/C3d/Include/cr_stamp_spherical_solid.h +++ b/C3d/Include/cr_stamp_spherical_solid.h @@ -1,147 +1,147 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Строитель оболочки из листового материала сферической штамповкой. - \en Constructor of a shell from the sheet material with spherical stamping. -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_STAMP_SPHERICAL_SOLID_H -#define __CR_STAMP_SPHERICAL_SOLID_H - - -#include -#include -#include - - -class MATH_CLASS MbCurveBoundedSurface; - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель оболочки из листового материала сферической штамповкой. - \en Constructor of a shell from the sheet material with spherical stamping. \~ - \details \ru Строитель оболочки из листового материала сферической штамповкой. \n - Строится только закрытая штамповка. \n - \en Constructor of a shell from the sheet material by spherical stamping. \n - Stamping of closed type only are constructed. \n - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbSphericalStampSolid : public MbCreator { -private: - MbItemIndex faceIndex; ///< \ru Индекс грани, на которой строится штамповка. \en Index of the face the stamping is constructed on. - MbItemIndex pairFaceIndex; ///< \ru Индекс парной грани. \en Index of the pair face. - MbCurveBoundedSurface * boundSurface; ///< \ru Поверхность, границами которой надо подрезать штамповку. \en Surface, by which bounds the stamp is cutted. - MbPlacement3D placement; ///< \ru Локальная система координат контура штамповки. \en The local coordinate system of contour of stamping. - MbStampingValues parameters; ///< \ru Параметры штамповки. \en Stamping parameters. - double thickness; ///< \ru Толщина листа. \en Thickness of the plate. - bool add; ///< \ru Создавать добавляемую или вычитаемую часть штамповки. \en Create additional or subtructional part of a stamp. - MbCartPoint center; ///< \ru Центр донышка штамповки. \en Center of the bottom of the stamping. - -public : - MbSphericalStampSolid( const MbItemIndex & faceIndex, - const MbItemIndex & pairFaceIndex, - const MbCurveBoundedSurface * boundSurface, - const MbPlacement3D & placement, - const MbStampingValues & params, - const double thickness, - const bool add, - const MbCartPoint & center, - const MbSNameMaker & names ); -private: - MbSphericalStampSolid( const MbSphericalStampSolid &, MbRegDuplicate * iReg ); - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. - MbSphericalStampSolid( const MbSphericalStampSolid & ); - -public: - virtual ~MbSphericalStampSolid(); - - // \ru Общие функции математического объекта \en Common functions of the mathematical object - - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy - - virtual bool IsSame ( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & item ) const; // \ru Являются ли объекты подобными? \en Determine whether an object is similar? - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal - - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis - - virtual void GetProperties ( MbProperties &properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties ( const MbProperties &properties ); // \ru Записать свойства объекта \en Set properties of the object - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the base objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - // \ru Общие функции твердого тела \en Common functions of solid - - virtual bool CreateShell( MbFaceShell *&shell, MbeCopyMode sameShell, - RPArray *items = NULL ); // \ru Построение \en Construction - - // \ru Дать параметры. \en Get the parameters. - void GetParameters( MbStampingValues & params ) const { params = parameters; } - // \ru Установить параметры. \en Set the parameters. - void SetParameters( const MbStampingValues & params ) { parameters = params; } - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - MbSphericalStampSolid & operator = ( const MbSphericalStampSolid & ); // \ru Не реализовано \en Not implemented - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSphericalStampSolid ) -}; - -IMPL_PERSISTENT_OPS( MbSphericalStampSolid ) - -//------------------------------------------------------------------------------ -/** \brief \ru Построить оболочку из листового материала со сферической штамповкой. - \en Construct a shell form sheet material by spherical stamping. \~ - \details \ru На базе исходной оболочки из листового материала построить оболочку методом сферической штамповки или части сферической штамповки без исходного листового тела. \n - Одновременно с построением оболочки функция создаёт её строитель.\n - \en A shell is to be constructed on the basis of the source shell by the method of spherical stamping or parts of spherical stamp without the basis sheet solid. \n - The function simultaneously creates the shell and its constructor.\n \~ - \param[in] solid - \ru Исходная оболочка. - \en The source shell. \~ - \param[in] sameShell - \ru Режим копирования исходной оболочки. - \en Mode of copying the source shell. \~ - \param[in] face - \ru Грань штамповки. - \en The face for stamping. \~ - \param[in] placement - \ru Локальная система координат, в плоскости XY которй расположен контур штамповки. - \en The local coordinate system in the XY plane of which the stamping contour is located. \~ - \param[in] parameters - \ru Параметры штамповки. - \en The parameters of stamping. \~ - \param[in] thickness - \ru Толщина листа. - \en The thickness of the sheet solid. - \param[in] add - \ru Какую часть сферической штамповки создавать. - \en Which part of the spherical stamp to create. - \param[in] center - \ru Центр штамповки. - \en The center of the stamping. \~ - \param[in] operNames - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[out] res - \ru Код результата операции выдавливания. - \en The extrusion operation result code. \~ - \param[out] shell - \ru Построенная оболочка. - \en The resultant shell. \~ - \result \ru Возвращает строитель оболочки. - \en Returns the shell constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateSphericalStamp( MbFaceShell * solid, - const MbeCopyMode sameShell, - const MbFace * face, - const MbPlacement3D & placement, - const MbStampingValues & parameters, - const double thickness, - const bool add, - const MbCartPoint & center, - MbSNameMaker & operNames, - MbResultType & res, - SPtr & shell ); - - -#endif // __CR_STAMP_SPHERICAL_SOLID_H - +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Строитель оболочки из листового материала сферической штамповкой. + \en Constructor of a shell from the sheet material with spherical stamping. +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_STAMP_SPHERICAL_SOLID_H +#define __CR_STAMP_SPHERICAL_SOLID_H + + +#include +#include +#include + + +class MATH_CLASS MbCurveBoundedSurface; + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель оболочки из листового материала сферической штамповкой. + \en Constructor of a shell from the sheet material with spherical stamping. \~ + \details \ru Строитель оболочки из листового материала сферической штамповкой. \n + Строится только закрытая штамповка. \n + \en Constructor of a shell from the sheet material by spherical stamping. \n + Stamping of closed type only are constructed. \n + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbSphericalStampSolid : public MbCreator { +private: + MbItemIndex faceIndex; ///< \ru Индекс грани, на которой строится штамповка. \en Index of the face the stamping is constructed on. + MbItemIndex pairFaceIndex; ///< \ru Индекс парной грани. \en Index of the pair face. + MbCurveBoundedSurface * boundSurface; ///< \ru Поверхность, границами которой надо подрезать штамповку. \en Surface, by which bounds the stamp is cutted. + MbPlacement3D placement; ///< \ru Локальная система координат контура штамповки. \en The local coordinate system of contour of stamping. + MbStampingValues parameters; ///< \ru Параметры штамповки. \en Stamping parameters. + double thickness; ///< \ru Толщина листа. \en Thickness of the plate. + bool add; ///< \ru Создавать добавляемую или вычитаемую часть штамповки. \en Create additional or subtructional part of a stamp. + MbCartPoint center; ///< \ru Центр донышка штамповки. \en Center of the bottom of the stamping. + +public : + MbSphericalStampSolid( const MbItemIndex & faceIndex, + const MbItemIndex & pairFaceIndex, + const MbCurveBoundedSurface * boundSurface, + const MbPlacement3D & placement, + const MbStampingValues & params, + const double thickness, + const bool add, + const MbCartPoint & center, + const MbSNameMaker & names ); +private: + MbSphericalStampSolid( const MbSphericalStampSolid &, MbRegDuplicate * iReg ); + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. + MbSphericalStampSolid( const MbSphericalStampSolid & ); + +public: + virtual ~MbSphericalStampSolid(); + + // \ru Общие функции математического объекта \en Common functions of the mathematical object + + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy + + virtual bool IsSame ( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & item ) const; // \ru Являются ли объекты подобными? \en Determine whether an object is similar? + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis + + virtual void GetProperties ( MbProperties &properties ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties ( const MbProperties &properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the base objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + // \ru Общие функции твердого тела \en Common functions of solid + + virtual bool CreateShell( MbFaceShell *&shell, MbeCopyMode sameShell, + RPArray *items = NULL ); // \ru Построение \en Construction + + // \ru Дать параметры. \en Get the parameters. + void GetParameters( MbStampingValues & params ) const { params = parameters; } + // \ru Установить параметры. \en Set the parameters. + void SetParameters( const MbStampingValues & params ) { parameters = params; } + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + MbSphericalStampSolid & operator = ( const MbSphericalStampSolid & ); // \ru Не реализовано \en Not implemented + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSphericalStampSolid ) +}; + +IMPL_PERSISTENT_OPS( MbSphericalStampSolid ) + +//------------------------------------------------------------------------------ +/** \brief \ru Построить оболочку из листового материала со сферической штамповкой. + \en Construct a shell form sheet material by spherical stamping. \~ + \details \ru На базе исходной оболочки из листового материала построить оболочку методом сферической штамповки или части сферической штамповки без исходного листового тела. \n + Одновременно с построением оболочки функция создаёт её строитель.\n + \en A shell is to be constructed on the basis of the source shell by the method of spherical stamping or parts of spherical stamp without the basis sheet solid. \n + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] solid - \ru Исходная оболочка. + \en The source shell. \~ + \param[in] sameShell - \ru Режим копирования исходной оболочки. + \en Mode of copying the source shell. \~ + \param[in] face - \ru Грань штамповки. + \en The face for stamping. \~ + \param[in] placement - \ru Локальная система координат, в плоскости XY которй расположен контур штамповки. + \en The local coordinate system in the XY plane of which the stamping contour is located. \~ + \param[in] parameters - \ru Параметры штамповки. + \en The parameters of stamping. \~ + \param[in] thickness - \ru Толщина листа. + \en The thickness of the sheet solid. + \param[in] add - \ru Какую часть сферической штамповки создавать. + \en Which part of the spherical stamp to create. + \param[in] center - \ru Центр штамповки. + \en The center of the stamping. \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[out] res - \ru Код результата операции выдавливания. + \en The extrusion operation result code. \~ + \param[out] shell - \ru Построенная оболочка. + \en The resultant shell. \~ + \result \ru Возвращает строитель оболочки. + \en Returns the shell constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateSphericalStamp( SPtr & solid, + const MbeCopyMode sameShell, + const MbFace * face, + const MbPlacement3D & placement, + const MbStampingValues & parameters, + const double thickness, + const bool add, + const MbCartPoint & center, + MbSNameMaker & operNames, + MbResultType & res, + SPtr & shell ); + + +#endif // __CR_STAMP_SPHERICAL_SOLID_H + diff --git a/C3d/Include/cr_stamp_user_solid.h b/C3d/Include/cr_stamp_user_solid.h index a675b6d..4e1f13f 100644 --- a/C3d/Include/cr_stamp_user_solid.h +++ b/C3d/Include/cr_stamp_user_solid.h @@ -1,110 +1,110 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Строитель оболочки из листового материала штамповкой телом-инструментом. - \en Constructor of a shell from the sheet material with stamping by a tool solid. -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_USERSTAMP_SOLID_H -#define __CR_USERSTAMP_SOLID_H - - -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель оболочки из листового материала штамповкой телом-инструментом. - \en Constructor of a shell from the sheet material with stamping by tool solid. \~ - \details \ru Строитель оболочки из листового материала закрытой или открытой штамповкой телом-инструментом. - Тело-инструмент может являться пуансоном или матрицей.\n - Строятся штамповки двух типов: \n - закрытая - не указаны вскрываемые грани тела-инструмента, \n - открытая - когда лист пробит штамповкой насквозь, указаны вскрываемые грани. \n - \en Constructor of a shell from the sheet material by open or closed stamping by tool solid. - The tool solid may be a punch or a die. \n - Stamping of two types are constructed: \n - closed - pierce faces of tool solid are not specified, \n - open - when a sheet is punched through by stamping, pierce faces of tool solid are specified. \n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbUserStampSolid : public MbCreator { -private: - MbItemIndex faceIndex; ///< \ru Индекс грани, на которой строится штамповка. \en Index of the face the stamping is constructed on. - MbItemIndex pairFaceIndex; ///< \ru Индекс грани парной к грани штамповки. \en Index of the face which is pair to the stamp face. - SArray pierceIndices; ///< \ru Индексы граней для вырубки. \en Face indicies for opening. - RPArray creators; ///< \ru Журнал построения оболочки тела-инструмента. \en History tree of the shell of the tool solid. - size_t countOne; ///< \ru Разделитель строителей тел-операндов. \en Separator of operand solids creators. - MbUserStampingValues parameters; ///< \ru Параметры штамповки. \en Stamping parameters. - double thickness; ///< \ru Толщина листа. \en The thickness of the sheet metal. - bool punch; ///< \ru Является тело-инструмент пуансоном или матрицей? \en Is tool body a punch or a die. - -public : - MbUserStampSolid( const RPArray & creatorsTool, - const bool sameTool, - const MbItemIndex & faceIndex, - const MbItemIndex & pairFaceIndex, - SArray & pierceIndices, - const MbUserStampingValues & params, - const double thickness, - const bool isPunch, - const MbSNameMaker & names ); -private: - MbUserStampSolid( const MbUserStampSolid &, MbRegDuplicate * iReg ); - -public: - virtual ~MbUserStampSolid(); - - // \ru Общие функции математического объекта. \en Common functions of the mathematical object. - - virtual MbeCreatorType IsA() const; // \ru Тип элемента. \en A type of element. - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию. \en Create a copy. - - virtual bool IsSame ( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & item ) const; // \ru Являются ли объекты подобными? \en Determine whether an object is similar? - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным. \en Make equal. - - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг. \en Translation. - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate around an axis. - - virtual void GetProperties ( MbProperties &properties ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties ( const MbProperties &properties ); // \ru Записать свойства объекта. \en Set properties of the object. - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. - - // \ru Общие функции твердого тела. \en Common functions of solid. - - virtual bool CreateShell( MbFaceShell *&shell, MbeCopyMode sameShell, - RPArray *items = NULL ); // \ru Построение оболочки штамповки. \en Construction of a stamping shell. - - // \ru Получить параметры. \en Get the parameters. - void GetParameters( MbUserStampingValues & params ) const { params = parameters; } - // \ru Установить параметры. \en Set the parameters. - void SetParameters( const MbUserStampingValues & params ) { parameters = params; } - -private: - OBVIOUS_PRIVATE_COPY( MbUserStampSolid ) - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbUserStampSolid ) -}; - -IMPL_PERSISTENT_OPS( MbUserStampSolid ) - - -//------------------------------------------------------------------------------ -/** \brief \ru Построить оболочку из листового материала штамповкой телом-инструментом. - \en Construct a shell form sheet material by tool body stamping. \~ - \details \ru На базе исходной оболочки из листового материала построить оболочку методом закрытой или открытой штамповки. \n - Одновременно с построением оболочки функция создаёт её строитель.\n - \en A shell is to be constructed on the basis of the source shell by the method of closed or open stamping. \n - The function simultaneously creates the shell and its constructor.\n \~ - \param[in] solid - \ru Исходная оболочка. - \en The source shell. \~ - \param[in] sameShell - \ru Режим копирования исходной оболочки. - \en Mode of copying the source shell. \~ - \param[in] face - \ru Грань штамповки. - \en The face for stamping. \~ +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Строитель оболочки из листового материала штамповкой телом-инструментом. + \en Constructor of a shell from the sheet material with stamping by a tool solid. +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_USERSTAMP_SOLID_H +#define __CR_USERSTAMP_SOLID_H + + +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель оболочки из листового материала штамповкой телом-инструментом. + \en Constructor of a shell from the sheet material with stamping by tool solid. \~ + \details \ru Строитель оболочки из листового материала закрытой или открытой штамповкой телом-инструментом. + Тело-инструмент может являться пуансоном или матрицей.\n + Строятся штамповки двух типов: \n + закрытая - не указаны вскрываемые грани тела-инструмента, \n + открытая - когда лист пробит штамповкой насквозь, указаны вскрываемые грани. \n + \en Constructor of a shell from the sheet material by open or closed stamping by tool solid. + The tool solid may be a punch or a die. \n + Stamping of two types are constructed: \n + closed - pierce faces of tool solid are not specified, \n + open - when a sheet is punched through by stamping, pierce faces of tool solid are specified. \n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbUserStampSolid : public MbCreator { +private: + MbItemIndex faceIndex; ///< \ru Индекс грани, на которой строится штамповка. \en Index of the face the stamping is constructed on. + MbItemIndex pairFaceIndex; ///< \ru Индекс грани парной к грани штамповки. \en Index of the face which is pair to the stamp face. + SArray pierceIndices; ///< \ru Индексы граней для вырубки. \en Face indicies for opening. + RPArray creators; ///< \ru Журнал построения оболочки тела-инструмента. \en History tree of the shell of the tool solid. + size_t countOne; ///< \ru Разделитель строителей тел-операндов. \en Separator of operand solids creators. + MbToolStampingValues parameters; ///< \ru Параметры штамповки. \en Stamping parameters. + double thickness; ///< \ru Толщина листа. \en The thickness of the sheet metal. + bool punch; ///< \ru Является тело-инструмент пуансоном или матрицей? \en Is tool body a punch or a die. + +public : + MbUserStampSolid( const RPArray & creatorsTool, + const bool sameTool, + const MbItemIndex & faceIndex, + const MbItemIndex & pairFaceIndex, + SArray & pierceIndices, + const MbToolStampingValues & params, + const double thickness, + const bool isPunch, + const MbSNameMaker & names ); +private: + MbUserStampSolid( const MbUserStampSolid &, MbRegDuplicate * iReg ); + +public: + virtual ~MbUserStampSolid(); + + // \ru Общие функции математического объекта. \en Common functions of the mathematical object. + + virtual MbeCreatorType IsA() const; // \ru Тип элемента. \en A type of element. + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию. \en Create a copy. + + virtual bool IsSame ( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & item ) const; // \ru Являются ли объекты подобными? \en Determine whether an object is similar? + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным. \en Make equal. + + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг. \en Translation. + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate around an axis. + + virtual void GetProperties ( MbProperties &properties ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties ( const MbProperties &properties ); // \ru Записать свойства объекта. \en Set properties of the object. + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта. \en Get a name of object property. + + // \ru Общие функции твердого тела. \en Common functions of solid. + + virtual bool CreateShell( MbFaceShell *&shell, MbeCopyMode sameShell, + RPArray *items = NULL ); // \ru Построение оболочки штамповки. \en Construction of a stamping shell. + + // \ru Получить параметры. \en Get the parameters. + void GetParameters( MbToolStampingValues & params ) const { params = parameters; } + // \ru Установить параметры. \en Set the parameters. + void SetParameters( const MbToolStampingValues & params ) { parameters = params; } + +private: + OBVIOUS_PRIVATE_COPY( MbUserStampSolid ) + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbUserStampSolid ) +}; + +IMPL_PERSISTENT_OPS( MbUserStampSolid ) + + +//------------------------------------------------------------------------------ +/** \brief \ru Построить оболочку из листового материала штамповкой телом-инструментом. + \en Construct a shell form sheet material by tool body stamping. \~ + \details \ru На базе исходной оболочки из листового материала построить оболочку методом закрытой или открытой штамповки. \n + Одновременно с построением оболочки функция создаёт её строитель.\n + \en A shell is to be constructed on the basis of the source shell by the method of closed or open stamping. \n + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] solid - \ru Исходная оболочка. + \en The source shell. \~ + \param[in] sameShell - \ru Режим копирования исходной оболочки. + \en Mode of copying the source shell. \~ + \param[in] face - \ru Грань штамповки. + \en The face for stamping. \~ \param[in] toolSolid - \ru Оболочка тела-инструмента. \en A shell of tool solid. \~ \param[in] sameShellTool - \ru Режим копирования оболочки тела-инструмента. @@ -118,24 +118,24 @@ IMPL_PERSISTENT_OPS( MbUserStampSolid ) \param[in] nameMaker - \ru Именователь. \en An object for naming the new objects. \~ \param[out] result - \ru Результирующее тело. - \en The resultant solid. \~ - \result \ru Возвращает строитель оболочки. - \en Returns the shell constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateUserStamp( MbFaceShell & initialShell, // Исходная оболочка, - const MbeCopyMode sameShell, // флаг способа использования исходной оболочки, - const MbFace & targetFace, // грань штамповки, - const RPArray & creatorsTool, // журнал построения инструмента, - MbFaceShell & toolShell, // оболочка тела-инструмента, - const MbeCopyMode sameShellTool, // флаг способа использования оболочки инструмента, - bool isPunch, // является инструмент пуансоном или матрицей, - const RPArray & pierceFaces, // вскрываемые для вырубки грани инструмента, - const MbUserStampingValues & params, // параметры штамповки, - const MbSNameMaker & nameMaker, // именователь, - MbResultType & res, // флаг успешности операции, - SPtr & resultShell ); // результирующая оболочка. - - -#endif // __CR_USERSTAMP_SOLID_H + \en The resultant solid. \~ + \result \ru Возвращает строитель оболочки. + \en Returns the shell constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateUserStamp( MbFaceShell * initialShell, // Исходная оболочка, + const MbeCopyMode sameShell, // флаг способа использования исходной оболочки, + const MbFace & targetFace, // грань штамповки, + const RPArray & creatorsTool, // журнал построения инструмента, + MbFaceShell & toolShell, // оболочка тела-инструмента, + const MbeCopyMode sameShellTool, // флаг способа использования оболочки инструмента, + bool isPunch, // является инструмент пуансоном или матрицей, + const RPArray & pierceFaces, // вскрываемые для вырубки грани инструмента, + const MbToolStampingValues & params, // параметры штамповки, + const MbSNameMaker & nameMaker, // именователь, + MbResultType & res, // флаг успешности операции, + SPtr & resultShell ); // результирующая оболочка. + + +#endif // __CR_USERSTAMP_SOLID_H diff --git a/C3d/Include/cr_stitch_solid.h b/C3d/Include/cr_stitch_solid.h index cbd268a..94f6894 100644 --- a/C3d/Include/cr_stitch_solid.h +++ b/C3d/Include/cr_stitch_solid.h @@ -56,17 +56,20 @@ private: PArray< RPArray > creatorsArray; ///< \ru Множества строителей для каждой сшиваемой оболочки. \en Set of creators for each shell being stitched. bool formSolidBody; ///< \ru Флаг необходимости формирования замкнутой оболочки. \en Whether to construct a closed shell. double stitchPrecision; ///< \ru Максимально допустимое расстояние между сшиваемыми рёбрами. \en Maximal acceptable distance between edges being stitched. + bool mergeEdges; ///< \ru Сливать подобные ребра (true). \en Whether to merge similar edges (true). public : template MbStitchedSolid( const PArray & creatorsData, bool tryClosed, double precision, - const MbSNameMaker & names ) - : MbCreator( names ) - , creatorsArray( creatorsData.size(), 1, true ) - , formSolidBody( tryClosed ) - , stitchPrecision( precision ) + const MbSNameMaker & names, + bool edgesMerging ) + : MbCreator ( names ) + , creatorsArray ( creatorsData.size(), 1, true ) + , formSolidBody ( tryClosed ) + , stitchPrecision( precision ) + , mergeEdges ( edgesMerging ) { size_t i, setsCnt = creatorsData.size(); @@ -87,7 +90,7 @@ public : simpleCreators.reserve( estSimpleCnt ); } - SPtr creator; + c3d::CreatorSPtr creator; for ( i = 0; i < setsCnt; ++i ) { Creators * creatorsSet = creatorsData[i]; if ( creatorsSet != NULL ) { @@ -129,8 +132,8 @@ public: virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the base objects virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. @@ -158,6 +161,7 @@ private: IMPL_PERSISTENT_OPS( MbStitchedSolid ) + //------------------------------------------------------------------------------ /** \brief \ru Построение оболочки сшивки. \en Construction of a shell of stitching. \~ @@ -171,6 +175,8 @@ IMPL_PERSISTENT_OPS( MbStitchedSolid ) \en Maximal acceptable distance between edges being stitched. \~ \param[in] operNames - \ru Именователь операции. \en An object defining names generation in the operation. \~ + \param[in] mergeEdges - \ru Сливать подобные ребра (true). + \en Whether to merge similar edges (true). \~ \param[out] res - \ru Код результата операции выдавливания. \en The extrusion operation result code. \~ \result \ru Возвращает построенную оболочку, если операция была выполнена успешно. @@ -182,9 +188,40 @@ MATH_FUNC (MbFaceShell *) CreateStitchShell( const RPArray & initia bool formSolidBody, double stitchPrecision, const MbSNameMaker & operNames, + bool mergeEdges, MbeStitchResType & res ); +//------------------------------------------------------------------------------ +/** \brief \ru Построение оболочки сшивки. + \en Construction of a shell of stitching. \~ + \details \ru Построение оболочки сшивки. \n + \en Construction of a shell of stitching. \n \~ + \param[in] initialShells - \ru Множество оболочек для сшивки. + \en A set of shells for stitching. \~ + \param[in] formSolidBody - \ru Создавать тело? + \en Whether to create a solid. \~ + \param[in] stitchPrecision - \ru Максимально допустимое расстояние между сшиваемыми рёбрами. + \en Maximal acceptable distance between edges being stitched. \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] mergeEdges - \ru Сливать подобные ребра (true). + \en Whether to merge similar edges (true). \~ + \param[out] res - \ru Код результата операции выдавливания. + \en The extrusion operation result code. \~ + \result \ru Возвращает построенную оболочку, если операция была выполнена успешно. + \en Returns the constructed shell if the operation has been successfully performed. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbFaceShell *) CreateStitchShell( const c3d::ShellsSPtrVector & initialShells, + bool formSolidBody, + double stitchPrecision, + const MbSNameMaker & operNames, + bool mergeEdges, + MbeStitchResType & res ); + + //------------------------------------------------------------------------------ /** \brief \ru Построить оболочку путём сшивки граней. \en Create a shell by faces stitching. \~ @@ -204,6 +241,8 @@ MATH_FUNC (MbFaceShell *) CreateStitchShell( const RPArray & initia \en Maximal acceptable distance between edges being stitched. \~ \param[in] operNames - \ru Именователь операции. \en An object defining names generation in the operation. \~ + \param[in] mergeEdges - \ru Сливать подобные ребра (true). + \en Whether to merge similar edges (true). \~ \param[out] res - \ru Код результата операции выдавливания. \en The extrusion operation result code. \~ \param[out] resultShell - \ru Построенная оболочка. @@ -218,6 +257,7 @@ MATH_FUNC (MbCreator *) CreateStitchedSolid( const PArray< RPArray > bool formSolidBody, double stitchPrecision, const MbSNameMaker & operNames, + bool mergeEdges, MbeStitchResType & res, MbFaceShell *& resultShell ); diff --git a/C3d/Include/cr_surface_spline.h b/C3d/Include/cr_surface_spline.h index 8b95545..ccbe886 100644 --- a/C3d/Include/cr_surface_spline.h +++ b/C3d/Include/cr_surface_spline.h @@ -1,141 +1,141 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Строитель сплайна на поверхности по точками. - \en Constructor of spline on a surface by points. -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_SURFACE_SPLINE_H -#define __CR_SURFACE_SPLINE_H - - -#include -#include -#include -#include -#include - - -class MATH_CLASS MbCartPoint; -class MATH_CLASS MbSurface; - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель пространственного сплайна. - \en Spatial spline constructor. \~ - \details \ru Строитель пространственного сплайна.\n - \en Spatial spline constructor.\n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbSurfaceSplineCreator : public MbCreator { -private: - MbSurface * surface; // \ru Поверхность \en Surface - bool throughPnts; // \ru через точки \en Through points - SArray paramPnts; // \ru Параметрические точки \en Parametric points - SArray paramWts; // \ru Веса параметрических точек \en Parametric points weights - bool paramClosed; // \ru Замкнуть параметрический сплайн \en Make the parametric spline close - RPArray< MbPntMatingData > spaceTransitions; // \ru Сопряжения в точках \en Tangents at the points - -protected: - MbSurfaceSplineCreator( const MbSurfaceSplineCreator &, MbRegDuplicate * iReg ); // \ru Конструктор копирования \en Copy-constructor - MbSurfaceSplineCreator( const MbSurfaceSplineCreator & ); // \ru Не реализовано \en Not implemented - MbSurfaceSplineCreator(); // \ru Не реализовано \en Not implemented - -public: - MbSurfaceSplineCreator( const MbSurface &, bool sameSurf, bool thrPnts, - const SArray & pnts, - const SArray & wts, bool parCls, - RPArray< MbPntMatingData > & transitions, - const MbSNameMaker & snMaker ); -public : - virtual ~MbSurfaceSplineCreator(); - - // \ru Общие функции строителя. \en The common functions of the creator. - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element - virtual MbCreator & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Сделать копию \en Create a copy - - virtual bool IsSame ( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal - - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - - virtual MbePrompt GetPropertyName(); // \ru Дать имя свойства объекта \en Get the object property name - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - // \ru Построить кривую по журналу построения \en Create a curve from the history tree - virtual bool CreateWireFrame( MbWireFrame *&, MbeCopyMode, RPArray * items = NULL ); - - /** \} */ - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. - void operator = ( const MbSurfaceSplineCreator & ); // \ru Не реализовано!!! \en Not implemented!!! - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSurfaceSplineCreator ) -}; - -IMPL_PERSISTENT_OPS( MbSurfaceSplineCreator ) - -//------------------------------------------------------------------------------ -/** \brief \ru Создать кривую на поверхности. - \en Create a curve on a surface. \~ - \details \ru Создать кривую на поверхности. \n - Примечания: \n - 1. Если есть сопряжения, то количество сопряжений д.б. равно количеству точек. \n - Отсутствующие сопряжения должны быть представлены нулевыми указателями в массиве \n - 2. Если сплайн строится через точки, то сопряжения могуть быть заданы произвольно. \n - 2. Если сплайн строится по полюсам и он незамкнут, то сопряжения могут быть только на концах. \n - 3. Если сплайн строится по полюсам и он замкнут, то сопряжения должны отсутствовать. \n - 4. Множество весов д.б. пуст или синхронизирован с массивом точек по количеству (с опцией throughPoints веса игнорируются). \n - \en Create a curve on a surface. \n - Notes: \n - 1. If the tangents are specified, then the number of tangents should be equal to the number of points. \n - Missing tangents should be represented by null pointers in the array \n - 2. If the spline is created from points, arbitrary tangents can be defined. \n - 2. If the spline is created from poles and it is open, only the end tangents can be specified. \n - 3. If the spline is constructed from poles and it is closed, the tangents cannot be specified. \n - 4. The weight array should be empty or synchronized with the point array by size (with option throughPoints the weights are ignored). \n \~ - \param[in] surface - \ru Поверхность. - \en The surface. \~ - \param[in] throughPoints - \ru Провести сплайн через точки. - \en Create a spline through points. \~ - \param[in] paramPnts - \ru Множество параметрических точкек. - \en Parametric point array. \~ - \param[in] paramWts - \ru Множество весов параметрических точек. - \en An array of parametric point weights. \~ - \param[in] paramClosed - \ru Строить замкнутый параметрический сплайн. - \en Create a closed parametric spline. \~ - \param[in] spaceTransitions - \ru Сопряжения в точках. - \en Tangents at the points. \~ - \param[in] snMaker - \ru Именователь кривых каркаса. - \en An object defining the frame curves names. \~ - \param[out] resType - \ru Код результата операции - \en Operation result code \~ - \param[out] resCurves - \ru Множество эквидистантных кривых. - \en Offset curve array. \~ - \return \ru Возвращает строитель. - \en Returns the constructor. \~ - \ingroup Curve3D_Modeling -*/ -//--- -MATH_FUNC (MbCreator *) CreateSurfaceSpline( const MbSurface & surface, - bool throughPoints, - SArray & paramPnts, - SArray & paramWts, - bool paramClosed, - RPArray< MbPntMatingData > & spaceTransitions, - const MbSNameMaker & snMaker, - MbResultType & resType, - RPArray & resCurves ); - - -#endif // __CR_SURFACE_SPLINE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Строитель сплайна на поверхности по точками. + \en Constructor of spline on a surface by points. +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_SURFACE_SPLINE_H +#define __CR_SURFACE_SPLINE_H + + +#include +#include +#include +#include +#include + + +class MATH_CLASS MbCartPoint; +class MATH_CLASS MbSurface; + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель пространственного сплайна. + \en Spatial spline constructor. \~ + \details \ru Строитель пространственного сплайна.\n + \en Spatial spline constructor.\n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbSurfaceSplineCreator : public MbCreator { +private: + MbSurface * surface; // \ru Поверхность \en Surface + bool throughPnts; // \ru через точки \en Through points + SArray paramPnts; // \ru Параметрические точки \en Parametric points + SArray paramWts; // \ru Веса параметрических точек \en Parametric points weights + bool paramClosed; // \ru Замкнуть параметрический сплайн \en Make the parametric spline close + RPArray< MbPntMatingData > spaceTransitions; // \ru Сопряжения в точках \en Tangents at the points + +protected: + MbSurfaceSplineCreator( const MbSurfaceSplineCreator &, MbRegDuplicate * iReg ); // \ru Конструктор копирования \en Copy-constructor + MbSurfaceSplineCreator( const MbSurfaceSplineCreator & ); // \ru Не реализовано \en Not implemented + MbSurfaceSplineCreator(); // \ru Не реализовано \en Not implemented + +public: + MbSurfaceSplineCreator( const MbSurface &, bool sameSurf, bool thrPnts, + const SArray & pnts, + const SArray & wts, bool parCls, + RPArray< MbPntMatingData > & transitions, + const MbSNameMaker & snMaker ); +public : + virtual ~MbSurfaceSplineCreator(); + + // \ru Общие функции строителя. \en The common functions of the creator. + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element + virtual MbCreator & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Сделать копию \en Create a copy + + virtual bool IsSame ( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + + virtual MbePrompt GetPropertyName(); // \ru Дать имя свойства объекта \en Get the object property name + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + // \ru Построить кривую по журналу построения \en Create a curve from the history tree + virtual bool CreateWireFrame( MbWireFrame *&, MbeCopyMode, RPArray * items = NULL ); + + /** \} */ + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. + void operator = ( const MbSurfaceSplineCreator & ); // \ru Не реализовано!!! \en Not implemented!!! + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSurfaceSplineCreator ) +}; + +IMPL_PERSISTENT_OPS( MbSurfaceSplineCreator ) + +//------------------------------------------------------------------------------ +/** \brief \ru Создать кривую на поверхности. + \en Create a curve on a surface. \~ + \details \ru Создать кривую на поверхности. \n + Примечания: \n + 1. Если есть сопряжения, то количество сопряжений д.б. равно количеству точек. \n + Отсутствующие сопряжения должны быть представлены нулевыми указателями в массиве \n + 2. Если сплайн строится через точки, то сопряжения могуть быть заданы произвольно. \n + 2. Если сплайн строится по полюсам и он незамкнут, то сопряжения могут быть только на концах. \n + 3. Если сплайн строится по полюсам и он замкнут, то сопряжения должны отсутствовать. \n + 4. Множество весов д.б. пуст или синхронизирован с массивом точек по количеству (с опцией throughPoints веса игнорируются). \n + \en Create a curve on a surface. \n + Notes: \n + 1. If the tangents are specified, then the number of tangents should be equal to the number of points. \n + Missing tangents should be represented by null pointers in the array \n + 2. If the spline is created from points, arbitrary tangents can be defined. \n + 2. If the spline is created from poles and it is open, only the end tangents can be specified. \n + 3. If the spline is constructed from poles and it is closed, the tangents cannot be specified. \n + 4. The weight array should be empty or synchronized with the point array by size (with option throughPoints the weights are ignored). \n \~ + \param[in] surface - \ru Поверхность. + \en The surface. \~ + \param[in] throughPoints - \ru Провести сплайн через точки. + \en Create a spline through points. \~ + \param[in] paramPnts - \ru Множество параметрических точкек. + \en Parametric point array. \~ + \param[in] paramWts - \ru Множество весов параметрических точек. + \en An array of parametric point weights. \~ + \param[in] paramClosed - \ru Строить замкнутый параметрический сплайн. + \en Create a closed parametric spline. \~ + \param[in] spaceTransitions - \ru Сопряжения в точках. + \en Tangents at the points. \~ + \param[in] snMaker - \ru Именователь кривых каркаса. + \en An object defining the frame curves names. \~ + \param[out] resType - \ru Код результата операции + \en Operation result code \~ + \param[out] resCurves - \ru Множество эквидистантных кривых. + \en Offset curve array. \~ + \return \ru Возвращает строитель. + \en Returns the constructor. \~ + \ingroup Curve3D_Modeling +*/ +//--- +MATH_FUNC (MbCreator *) CreateSurfaceSpline( const MbSurface & surface, + bool throughPoints, + SArray & paramPnts, + SArray & paramWts, + bool paramClosed, + RPArray< MbPntMatingData > & spaceTransitions, + const MbSNameMaker & snMaker, + MbResultType & resType, + RPArray & resCurves ); + + +#endif // __CR_SURFACE_SPLINE_H diff --git a/C3d/Include/cr_transformed_solid.h b/C3d/Include/cr_transformed_solid.h index 7863eb7..d7b1c5f 100644 --- a/C3d/Include/cr_transformed_solid.h +++ b/C3d/Include/cr_transformed_solid.h @@ -1,114 +1,114 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Строитель трансформируемой оболочки. - \en Constructor of a transformed shell. -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CR_TRANSFORMED_SOLID_H -#define __CR_TRANSFORMED_SOLID_H - - -#include -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель трансформируемой оболочки. - \en Constructor of a transformed shell. \~ - \details \ru Строитель трансформируемой оболочки, матрица преобразования которой - получена по изменению положения контрольных точек габаритного куба. \n - \en Constructor of a transformed shell the transformation matrix of which - is obtained due to the change of bounding box control points positions. \n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbTransformedSolid : public MbCreator { -protected: - TransformValues parameters; ///< \ru Параметры преобразования оболочки. \en Parameters of a shell transformation. - MbCube cube; ///< \ru Габаритный куб. \en Bounding box. - SArray cubePoints; ///< \ru Контрольные точки куба. \en Control points of the bounding box. - -public: // \ru Конструктор по параметрам \en Constructor by parameters - MbTransformedSolid( const TransformValues &, const MbCube &, const MbSNameMaker & ); -private: // \ru Конструктор дублирующий \en Duplication constructor - MbTransformedSolid( const MbTransformedSolid &, MbRegDuplicate * ireg ); - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. - MbTransformedSolid( const MbTransformedSolid & ); - -public: // \ru Деструктор \en Destructor - ~MbTransformedSolid(); - -public: // \ru Общие функции математического объекта \en Common functions of the mathematical object - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy - virtual bool IsSame( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать по матрице \en Transform according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг по вектору \en Translation by a vector - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis - - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - /// \ru Построение оболочки \en Creation of a shell - virtual bool CreateShell( MbFaceShell *&, MbeCopyMode sameShell, - RPArray * items = NULL ); - virtual void Refresh( MbFaceShell & ); ///< \ru Обновить форму оболочки \en Update shape of the shell - // \ru Добавить модификацию по матрице \en Add a modification by a matrix - void AddMatrix( MbFaceShell &, const MbMatrix3D & ); - - // \ru Дать параметры. \en Get the parameters. - void GetParameters( TransformValues & params ) const { params = parameters; } - // \ru Установить параметры. \en Set the parameters. - void SetParameters( const TransformValues & params ) { parameters = params; } - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - void operator = ( const MbTransformedSolid & ); - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbTransformedSolid ) -}; - -IMPL_PERSISTENT_OPS( MbTransformedSolid ) - -//------------------------------------------------------------------------------ -/** \brief \ru Создание строителя масштабированной оболочки. - \en Creation of constructor of a scaled shell. \~ - \details \ru Построить оболочку путём трансформации исходной оболочки по матрице преобразования, - полученной по изменению положения контрольных точек габаритного куба.\n - Одновременно с построением оболочки функция создаёт её строитель.\n - \en Create a shell by transformation of the source shell according to the transformation matrix - obtained due to the change of the bounding box control points positions.\n - The function simultaneously creates the shell and its constructor.\n \~ - \param[in] outer - \ru Исходная оболочка. - \en The source shell. \~ - \param[in] sameShell - \ru Режим копирования исходной оболочки. - \en Mode of copying the source shell. \~ - \param[in] parameters - \ru Параметры модификации. - \en Parameters of the modification. \~ - \param[in] names - \ru Именователь операции. - \en An object defining names generation in the operation. \~ - \param[out] res - \ru Код результата операции выдавливания. - \en The extrusion operation result code. \~ - \param[out] shell - \ru Построенная оболочка. - \en The resultant shell. \~ - \result \ru Возвращает строитель оболочки. - \en Returns the shell constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) CreateTransformedSolid( MbFaceShell * outer, - MbeCopyMode sameShell, - const TransformValues & parameters, - const MbSNameMaker & names, - MbResultType & res, - MbFaceShell *& shell ); - - -#endif // __CR_TRANSFORMED_SOLID_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Строитель трансформируемой оболочки. + \en Constructor of a transformed shell. +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CR_TRANSFORMED_SOLID_H +#define __CR_TRANSFORMED_SOLID_H + + +#include +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель трансформируемой оболочки. + \en Constructor of a transformed shell. \~ + \details \ru Строитель трансформируемой оболочки, матрица преобразования которой + получена по изменению положения контрольных точек габаритного куба. \n + \en Constructor of a transformed shell the transformation matrix of which + is obtained due to the change of bounding box control points positions. \n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbTransformedSolid : public MbCreator { +protected: + TransformValues parameters; ///< \ru Параметры преобразования оболочки. \en Parameters of a shell transformation. + MbCube cube; ///< \ru Габаритный куб. \en Bounding box. + SArray cubePoints; ///< \ru Контрольные точки куба. \en Control points of the bounding box. + +public: // \ru Конструктор по параметрам \en Constructor by parameters + MbTransformedSolid( const TransformValues &, const MbCube &, const MbSNameMaker & ); +private: // \ru Конструктор дублирующий \en Duplication constructor + MbTransformedSolid( const MbTransformedSolid &, MbRegDuplicate * ireg ); + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. + MbTransformedSolid( const MbTransformedSolid & ); + +public: // \ru Деструктор \en Destructor + ~MbTransformedSolid(); + +public: // \ru Общие функции математического объекта \en Common functions of the mathematical object + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en A type of element + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy + virtual bool IsSame( const MbCreator & other, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool SetEqual ( const MbCreator & ); // \ru Сделать равным \en Make equal + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать по матрице \en Transform according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг по вектору \en Translation by a vector + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis + + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + /// \ru Построение оболочки \en Creation of a shell + virtual bool CreateShell( MbFaceShell *&, MbeCopyMode sameShell, + RPArray * items = NULL ); + virtual void Refresh( MbFaceShell & ); ///< \ru Обновить форму оболочки \en Update shape of the shell + // \ru Добавить модификацию по матрице \en Add a modification by a matrix + void AddMatrix( MbFaceShell &, const MbMatrix3D & ); + + // \ru Дать параметры. \en Get the parameters. + void GetParameters( TransformValues & params ) const { params = parameters; } + // \ru Установить параметры. \en Set the parameters. + void SetParameters( const TransformValues & params ) { parameters = params; } + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + void operator = ( const MbTransformedSolid & ); + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbTransformedSolid ) +}; + +IMPL_PERSISTENT_OPS( MbTransformedSolid ) + +//------------------------------------------------------------------------------ +/** \brief \ru Создание строителя масштабированной оболочки. + \en Creation of constructor of a scaled shell. \~ + \details \ru Построить оболочку путём трансформации исходной оболочки по матрице преобразования, + полученной по изменению положения контрольных точек габаритного куба.\n + Одновременно с построением оболочки функция создаёт её строитель.\n + \en Create a shell by transformation of the source shell according to the transformation matrix + obtained due to the change of the bounding box control points positions.\n + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] outer - \ru Исходная оболочка. + \en The source shell. \~ + \param[in] sameShell - \ru Режим копирования исходной оболочки. + \en Mode of copying the source shell. \~ + \param[in] parameters - \ru Параметры модификации. + \en Parameters of the modification. \~ + \param[in] names - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[out] res - \ru Код результата операции выдавливания. + \en The extrusion operation result code. \~ + \param[out] shell - \ru Построенная оболочка. + \en The resultant shell. \~ + \result \ru Возвращает строитель оболочки. + \en Returns the shell constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) CreateTransformedSolid( MbFaceShell * outer, + MbeCopyMode sameShell, + const TransformValues & parameters, + const MbSNameMaker & names, + MbResultType & res, + MbFaceShell *& shell ); + + +#endif // __CR_TRANSFORMED_SOLID_H diff --git a/C3d/Include/cr_truncated_shell.h b/C3d/Include/cr_truncated_shell.h index c40689e..8f4da09 100644 --- a/C3d/Include/cr_truncated_shell.h +++ b/C3d/Include/cr_truncated_shell.h @@ -1,156 +1,156 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Построение усеченной оболочки. - \en Construction of a truncated shell. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __TRUNCATED_SHELL_H -#define __TRUNCATED_SHELL_H - - -#include -#include -#include -#include -#include - - -class MATH_CLASS MbCurve; -class MATH_CLASS MbAxis3D; -class MATH_CLASS MbSpaceItem; -class MATH_CLASS MbPlacement3D; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbFace; -class MATH_CLASS MbSolid; -class MATH_CLASS MbSNameMaker; -struct MATH_CLASS MbMergingFlags; - - -//------------------------------------------------------------------------------ -/** \brief \ru Строитель усеченной оболочки. - \en Constructor of a truncated shell. \~ - \details \ru Строитель усеченной оболочки режет исходную оболочку на части указанными элементами, - которыми могут служить двумерные кривые в локальной системе координат, трёхмерные кривые, поверхности и оболочки. \n - \en Constructor of a truncated shell cuts the initial shell into parts by the specified elements - which can be two-dimensional curves in the local coordinate system, three-dimensional curves, surfaces and shells. \n \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbTruncatedShell : public MbCreator { -private : - std::vector selIndices; ///< \ru Идентификаторы выбранных граней усекаемой оболочки. \en Identifiers of selected faces of the shell being truncated. - MbSplitData splitItems; ///< \ru Усекающие элементы c ориентациями. \en Truncating elements with orientations. - SArray orients; ///< \ru Ориентация усекающих элементов. \en Orientation of truncating elements. - bool mergeFaces; ///< \ru Сливать подобные грани (true). \en Whether to merge similar faces (true). - bool mergeEdges; ///< \ru Сливать подобные ребра (true). \en Whether to merge similar edges (true). - -public: - /// \ru Конструктор по двумерным кривым. \en Constructor by two-dimensional curves. - MbTruncatedShell( const MbPlacement3D &, const RPArray &, bool same, - const SArray & orients, const MbMergingFlags &, const MbSNameMaker & ); - /// \ru Конструктор по трехмерным кривым. \en Constructor by three-dimensional curves. - MbTruncatedShell( const RPArray &, bool same, - const SArray & orients, const MbMergingFlags &, const MbSNameMaker & ); - /// \ru Конструктор по поверхностям. \en Constructor by surfaces. - MbTruncatedShell( const RPArray &, bool same, - const SArray & orients, const MbMergingFlags &, const MbSNameMaker & ); - /// \ru Конструктор по строителям тела. \en Constructor by solid creators. - MbTruncatedShell( const MbSolid &, bool same, bool keepShell, - bool orient, const MbMergingFlags &, const MbSNameMaker & ); - - virtual ~MbTruncatedShell(); - -private: - MbTruncatedShell( const MbTruncatedShell &, MbRegDuplicate * ); - -public: - virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element - virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy - - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object - virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the base objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - virtual size_t GetCreatorsCount( MbeCreatorType ct ) const; // \ru Посчитать внутренние построители по типу. \en Count internal creators by type. - virtual bool GetInternalCreators( MbeCreatorType, c3d::ConstCreatorsSPtrVector & ) const; // \ru Получить внутренние построители по типу. \en Get internal creators by type. - virtual bool SetInternalCreators( MbeCreatorType, c3d::CreatorsSPtrVector & ); // \ru Получить внутренние построители по типу. \en Get internal creators by type. - - virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSimilar ( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar - virtual bool SetEqual( const MbCreator & ); // \ru Сделать равным \en Make equal - - // \ru Построение оболочки по исходным данным \en Construction of a shell from the given data - virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, - RPArray * items = NULL ); - - // \ru Установить номера выбраных граней усекаемого тела \en Set indices of selected faces of the solid being truncated. - void SetSelIndices( const std::vector & selInds ); - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbTruncatedShell ) -OBVIOUS_PRIVATE_COPY( MbTruncatedShell ) -}; - -IMPL_PERSISTENT_OPS( MbTruncatedShell ) - -//------------------------------------------------------------------------------ -/** \brief \ru Построить усечённую оболочку. - \en Build a truncated shell. \~ - \details \ru Построить усечённую оболочку резкой исходного тела на части указанными элементами, - которыми могут служить двумерные кривые в локальной системе координат, трёхмерные кривые, поверхности и оболочки. - Одновременно с построением оболочки функция создаёт её строитель.\n - \en Construct a truncated shell by cutting the initial solid into parts by the specified elements - which can be two-dimensional curves in the local coordinate system, three-dimensional curves, surfaces and shells. - The function simultaneously creates the shell and its constructor.\n \~ - \param[in] initSolid - \ru Исходное тело. - \en The initial solid. \~ - \param[in] selIndices - \ru Идентификаторы выбранныых граней, приотсутствии - всё тело. - \en Identifiers of selected faces, if not specified - the whole solid. \~ - \param[in] sameShell - \ru Режим копирования исходной оболочки. - \en Mode of copying the initial shell. \~ - \param[in] operNames - \ru Именователь граней. - \en An object for naming faces. \~ - \param[in] items - \ru Усекающие объекты. - \en Truncating objects. \~ - \param[in] orients - \ru Ориентация усекающих объектов. - \en The truncating objects orientation. \~ - \param[in] curvesSplitMode - \ru Кривые используются как линии разъема. - \en The curves are used as parting lines. \~ - \param[in] solidsCopyMode - \ru Режим копирования усекающих объектов. - \en Mode of copying the truncating objects. \~ - \param[in] mergeFlags - \ru Флаги слияния элементов оболочки. - \en Control flags of shell items merging. \~ - \param[out] res - \ru Код результата операции. - \en Operation result code. \~ - \param[out] resShell - \ru Построенная усеченная оболочка. - \en Constructed truncated shell. \~ - \param[out] resDir - \ru Направление фантома усечения. - \en Direction of truncation phantom. \~ - \result \ru Возвращает строитель оболочки. - \en Returns the shell constructor. \~ - \ingroup Model_Creators -*/ -// --- -MATH_FUNC (MbCreator *) TruncateSurfacesSol( MbSolid & initSolid, - SArray & selIndices, - MbeCopyMode sameShell, - const MbSNameMaker & operNames, - RPArray & items, - SArray & orients, - bool curvesSplitMode, - MbeCopyMode solidsCopyMode, - const MbMergingFlags & mergeFlags, // флаги слияния граней и ребер - MbResultType & res, - MbFaceShell *& resShell, - MbPlacement3D *& resDir ); - - -#endif // __TRUNCATED_SHELL_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Построение усеченной оболочки. + \en Construction of a truncated shell. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __TRUNCATED_SHELL_H +#define __TRUNCATED_SHELL_H + + +#include +#include +#include +#include +#include + + +class MATH_CLASS MbCurve; +class MATH_CLASS MbAxis3D; +class MATH_CLASS MbSpaceItem; +class MATH_CLASS MbPlacement3D; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbFace; +class MATH_CLASS MbSolid; +class MATH_CLASS MbSNameMaker; +struct MATH_CLASS MbMergingFlags; + + +//------------------------------------------------------------------------------ +/** \brief \ru Строитель усеченной оболочки. + \en Constructor of a truncated shell. \~ + \details \ru Строитель усеченной оболочки режет исходную оболочку на части указанными элементами, + которыми могут служить двумерные кривые в локальной системе координат, трёхмерные кривые, поверхности и оболочки. \n + \en Constructor of a truncated shell cuts the initial shell into parts by the specified elements + which can be two-dimensional curves in the local coordinate system, three-dimensional curves, surfaces and shells. \n \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbTruncatedShell : public MbCreator { +private : + std::vector selIndices; ///< \ru Идентификаторы выбранных граней усекаемой оболочки. \en Identifiers of selected faces of the shell being truncated. + MbSplitData splitItems; ///< \ru Усекающие элементы c ориентациями. \en Truncating elements with orientations. + SArray orients; ///< \ru Ориентация усекающих элементов. \en Orientation of truncating elements. + bool mergeFaces; ///< \ru Сливать подобные грани (true). \en Whether to merge similar faces (true). + bool mergeEdges; ///< \ru Сливать подобные ребра (true). \en Whether to merge similar edges (true). + +public: + /// \ru Конструктор по двумерным кривым. \en Constructor by two-dimensional curves. + MbTruncatedShell( const MbPlacement3D &, const RPArray &, bool same, + const SArray & orients, const MbMergingFlags &, const MbSNameMaker & ); + /// \ru Конструктор по трехмерным кривым. \en Constructor by three-dimensional curves. + MbTruncatedShell( const RPArray &, bool same, + const SArray & orients, const MbMergingFlags &, const MbSNameMaker & ); + /// \ru Конструктор по поверхностям. \en Constructor by surfaces. + MbTruncatedShell( const RPArray &, bool same, + const SArray & orients, const MbMergingFlags &, const MbSNameMaker & ); + /// \ru Конструктор по строителям тела. \en Constructor by solid creators. + MbTruncatedShell( const MbSolid &, bool same, bool keepShell, + bool orient, const MbMergingFlags &, const MbSNameMaker & ); + + virtual ~MbTruncatedShell(); + +private: + MbTruncatedShell( const MbTruncatedShell &, MbRegDuplicate * ); + +public: + virtual MbeCreatorType IsA() const; // \ru Тип элемента \en Type of element + virtual MbCreator & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию \en Create a copy + + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual MbePrompt GetPropertyName(); // \ru Выдать заголовок свойства объекта \en Get a name of object property + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the base objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + virtual size_t GetCreatorsCount( MbeCreatorType ct ) const; // \ru Посчитать внутренние построители по типу. \en Count internal creators by type. + virtual bool GetInternalCreators( MbeCreatorType, c3d::ConstCreatorsSPtrVector & ) const; // \ru Получить внутренние построители по типу. \en Get internal creators by type. + virtual bool SetInternalCreators( MbeCreatorType, c3d::CreatorsSPtrVector & ); // \ru Получить внутренние построители по типу. \en Get internal creators by type. + + virtual bool IsSame( const MbCreator &, double accuracy ) const; // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSimilar ( const MbCreator & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar + virtual bool SetEqual( const MbCreator & ); // \ru Сделать равным \en Make equal + + // \ru Построение оболочки по исходным данным \en Construction of a shell from the given data + virtual bool CreateShell( MbFaceShell *& shell, MbeCopyMode sameShell, + RPArray * items = NULL ); + + // \ru Установить номера выбраных граней усекаемого тела \en Set indices of selected faces of the solid being truncated. + void SetSelIndices( const std::vector & selInds ); + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbTruncatedShell ) +OBVIOUS_PRIVATE_COPY( MbTruncatedShell ) +}; + +IMPL_PERSISTENT_OPS( MbTruncatedShell ) + +//------------------------------------------------------------------------------ +/** \brief \ru Построить усечённую оболочку. + \en Build a truncated shell. \~ + \details \ru Построить усечённую оболочку резкой исходного тела на части указанными элементами, + которыми могут служить двумерные кривые в локальной системе координат, трёхмерные кривые, поверхности и оболочки. + Одновременно с построением оболочки функция создаёт её строитель.\n + \en Construct a truncated shell by cutting the initial solid into parts by the specified elements + which can be two-dimensional curves in the local coordinate system, three-dimensional curves, surfaces and shells. + The function simultaneously creates the shell and its constructor.\n \~ + \param[in] initSolid - \ru Исходное тело. + \en The initial solid. \~ + \param[in] selIndices - \ru Идентификаторы выбранныых граней, приотсутствии - всё тело. + \en Identifiers of selected faces, if not specified - the whole solid. \~ + \param[in] sameShell - \ru Режим копирования исходной оболочки. + \en Mode of copying the initial shell. \~ + \param[in] operNames - \ru Именователь граней. + \en An object for naming faces. \~ + \param[in] items - \ru Усекающие объекты. + \en Truncating objects. \~ + \param[in] orients - \ru Ориентация усекающих объектов. + \en The truncating objects orientation. \~ + \param[in] curvesSplitMode - \ru Кривые используются как линии разъема. + \en The curves are used as parting lines. \~ + \param[in] solidsCopyMode - \ru Режим копирования усекающих объектов. + \en Mode of copying the truncating objects. \~ + \param[in] mergeFlags - \ru Флаги слияния элементов оболочки. + \en Control flags of shell items merging. \~ + \param[out] res - \ru Код результата операции. + \en Operation result code. \~ + \param[out] resShell - \ru Построенная усеченная оболочка. + \en Constructed truncated shell. \~ + \param[out] resDir - \ru Направление фантома усечения. + \en Direction of truncation phantom. \~ + \result \ru Возвращает строитель оболочки. + \en Returns the shell constructor. \~ + \ingroup Model_Creators +*/ +// --- +MATH_FUNC (MbCreator *) TruncateSurfacesSol( MbSolid & initSolid, + SArray & selIndices, + MbeCopyMode sameShell, + const MbSNameMaker & operNames, + RPArray & items, + SArray & orients, + bool curvesSplitMode, + MbeCopyMode solidsCopyMode, + const MbMergingFlags & mergeFlags, // флаги слияния граней и ребер + MbResultType & res, + MbFaceShell *& resShell, + MbPlacement3D *& resDir ); + + +#endif // __TRUNCATED_SHELL_H diff --git a/C3d/Include/cr_union_solid.h b/C3d/Include/cr_union_solid.h index e25208b..b0851e5 100644 --- a/C3d/Include/cr_union_solid.h +++ b/C3d/Include/cr_union_solid.h @@ -11,6 +11,7 @@ #include #include +#include #include @@ -27,15 +28,38 @@ class MATH_CLASS MbUnionSolid : public MbCreator { protected : c3d::CreatorsSPtrVector creators; ///< \ru Набор данных для построения исходных оболочек. \en Data set for construction of the source shells. - c3d::IndicesVector countNumbers; ///< \ru Индекс начального строителя для следующей оболочки. \en Index of the source creator for the next shell. + c3d::IndicesVector countNumbers; ///< \ru Индексы начального строителя для следующей оболочки. \en Indices of the source creator for the next shell. c3d::IndicesVector sharedLinks; ///< \ru Номера общих наборов строителей для оболочек. \en Numbers of common creators sets for shells. - size_t sharedCount; ///< \ru Количество общих набор строителей. \en The number of common creators sets. + size_t sharedCount; ///< \ru Количество общих наборов строителей. \en The number of common creators sets. OperationType operation; ///< \ru Тип булевой операции над оболочками. \en Type of Boolean operation on shells. bool checkIntersection; ///< \ru Проверять ли на пересечение тела. \en Whether to check the solids for intersection. - bool mergeFaces; ///< \ru Объединять подобные грани (true). \en Whether to union similar faces (true). + MbMergingFlags mergeFlags; ///< \ru Управляющие флаги слияния элементов оболочки. \en Control flags of shell items merging double buildSag; ///< \ru Угловое отклонение при движении по кривым и поверхностям. \en Angular deviation while moving along curves and surfaces. public : + /** \brief \ru Конструктор по параметрам. + \en Constructor by operation parameters. \~ + \details \ru Конструктор по параметрам. \n + \en Constructor by operation parameters. \n \~ + \param[in] creators - \ru Построители оболочек. + \en Shells creators. \~ + \param[in] sameCreators - \ru Использовать оригиналы построителей. + \en Use creators originals (true). \~ + \param[in] countNumbers - \ru Индексы начального строителя для следующей оболочки. + \en Indices of the source creator for the next shell. \~ + \param[in] sharedCnt - \ru Количество общих наборов строителей. + \en The number of common creators sets. \~ + \param[in] sharedLinks - \ru Номера общих наборов строителей для оболочек. + \en Numbers of common creators sets for shells. \~ + \param[in] operType - \ru Тип булевой операции над оболочками. + \en Type of Boolean operation on shells. \~ + \param[in] checkIntersection - \ru Проверять ли на пересечение тела. + \en Whether to check the solids for intersection. \~ + \param[in] mergeFlags - \ru Управляющие флаги слияния элементов оболочки. + \en Control flags of shell items merging. \~ + \param[in] nameMaker - \ru Именователь. + \en Names maker. \~ + */ MbUnionSolid( const c3d::CreatorsSPtrVector & creators, bool sameCreators, const c3d::IndicesVector & countNumbers, @@ -43,7 +67,7 @@ public : const c3d::IndicesVector & sharedLinks, OperationType operType, bool checkIntersection, - bool mergeFaces, + const MbMergingFlags & mergeFlags, const MbSNameMaker & nameMaker ); private : MbUnionSolid( const MbUnionSolid &, MbRegDuplicate * ); @@ -132,8 +156,8 @@ IMPL_PERSISTENT_OPS( MbUnionSolid ) \en A Boolean operation type. \~ \param[in] checkIntersect - \ru Проверять ли пересечение оболочек. \en Whether to check shells intersection. \~ - \param[in] mergeFaces - \ru Сливать подобные грани. - \en Whether to merge similar faces. \~ + \param[in] mergeFlags - \ru Управляющие флаги слияния элементов оболочки. + \en Control flags of shell items merging. \~ \param[in] operNames - \ru Именователь операции. \en An object defining names generation in the operation. \~ \param[in] isArray - \ru Являются ли оболочки размноженными по прямоугольной сетке копиями? @@ -157,7 +181,7 @@ MATH_FUNC (MbCreator *) CreateUnion( MbFaceShell * solid, MbeCopyMode sameShells, OperationType oType, bool checkIntersect, - bool mergeFaces, + const MbMergingFlags & mergeFlags, const MbSNameMaker & operNames, bool isArray, // \ru Флаг массива \en Flag of array MbResultType & res, diff --git a/C3d/Include/creator.h b/C3d/Include/creator.h index 78c2f0d..5eaeba2 100644 --- a/C3d/Include/creator.h +++ b/C3d/Include/creator.h @@ -136,6 +136,7 @@ enum MbeCreatorType { ct_SimplifyFlatSolid = 618, ///< \ru Строитель упрощения развёртки листового тела. \en Constructor of the sheet solid flat pattern simplification. ct_UserStampSolid = 619, ///< \ru Строитель оболочки с штамповкой телом. \en Constructor of a shell with stamping by solid. ct_RemoveOperationSolid = 620, ///< \ru Строитель удаления операции листового тела. \en Constructor of removing of the sheet solid. + ct_BuildSheetMetalSolid = 621, ///< \ru Строитель листового тела по произвольному телу. \en Constructor of building sheet metal solid based on an arbitary solid. // \ru Строители оболочек. \en Creators of shells. ct_JoinShell = 701, ///< \ru Строитель оболочки соединения. \en Constructor of a joint shell. @@ -147,6 +148,7 @@ enum MbeCreatorType { ct_PatchSetCreator = 707, ///< \ru Строитель заплатки по кривым на оболочке. \en Constructor of a patch by curves on the shell. ct_FilletShell = 708, ///< \ru Строитель оболочки грани соединения. \en Constructor of a shell of a fillet face. ct_MedianShell = 709, ///< \ru Строитель срединной оболочки тела. \en Constructor of a median shell of solid. \n + ct_SectionShell = 710, ///< \ru Строитель оболочки на поверхности переменного сечения. \en Constructor of the shell on swept mutable section surface. \n // \ru Строители других объектов (вставлять новые типы перед этим типом). \en Creators of the other objects (insert new types before this type). ct_AttributeProvider = 801, ///< \ru Поставщик атрибутов для примитивов оболочки. \en Attribute provider for the shell primitives. @@ -463,12 +465,14 @@ public : virtual void SetYourVersion( VERSION version, bool forAll ); /// \ru Выдать версию объекта. \en Get the object version. VERSION GetYourVersion() const { return names.GetMathVersion(); } + /// \ru Выдать именователь объекта. \en Get the name-maker. - const MbSNameMaker & GetYourName() const { return names; } + const MbSNameMaker & GetYourNameMaker() const { return names; } /// \ru Выдать именователь объекта для редактирования. \en Get the object's name-maker for editing. - MbSNameMaker & SetYourName() { return names; } + MbSNameMaker & SetYourNameMaker() { return names; } /// \ru Установить именователь объекта. \en Set the object's name-maker. - void SetName( const MbSNameMaker & n ) { names.SetName( n ); } + void SetNameMaker( const MbSNameMaker & n ) { names.SetNameMaker( n ); } + /// \ru Выдать главное имя объекта. \en Get the main name of the object. SimpleName GetMainName() const { return names.GetMainName(); } /// \ru Установить главное имя объекта. \en Set the main name of the object. @@ -489,7 +493,7 @@ public : The function sets a flag that allow to write the object once and to use the references to the recorded instance in the other records. Reading is performed once too, in other cases of reading the address of the already read object is used. \~ */ - void PrepareWrite() { SetRegistrable( (GetUseCount() > 1) ? registrable : noRegistrable ); } + void PrepareWrite() const { SetRegistrable( (GetUseCount() > 1) ? registrable : noRegistrable ); } /** \} */ private: diff --git a/C3d/Include/creator_transaction.h b/C3d/Include/creator_transaction.h index 6c5368f..430c7ac 100644 --- a/C3d/Include/creator_transaction.h +++ b/C3d/Include/creator_transaction.h @@ -1,159 +1,159 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Журнал построения объекта. - \en The history tree of object. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CREATOR_TRANSACTION_H -#define __CREATOR_TRANSACTION_H - - -#include -#include -#include -#include - - -class MATH_CLASS reader; -class MATH_CLASS writer; -class MATH_CLASS IProgressIndicator; -class MATH_CLASS MbCartPoint3D; -class MATH_CLASS MbVector3D; -class MATH_CLASS MbAxis3D; -class MATH_CLASS MbMatrix3D; -class MATH_CLASS MbSpaceItem; -class MATH_CLASS MbFaceShell; -class MATH_CLASS MbWireFrame; -class MATH_CLASS MbPointFrame; -class MATH_CLASS MbProperties; -struct MATH_CLASS MbControlData3D; -class MbRegDuplicate; -class MbRegTransform; -enum MbeCopyMode; - - -//------------------------------------------------------------------------------ -/** \brief \ru Журнал построения объекта. - \en The history tree of object. \~ - \details \ru Журнал построения содержит упорядоченное множество строителей, - последовательная работа которых строит объект. \n - Неактивные строители (с состоянием mps_Skip) не принимают участия в построении объекта. - \en The history tree contains an ordered set of creators - whose successive work creates the objects. \n - Inactive creators (with state mps_Skip) are not used in the object construction. \~ - \ingroup Model_Creators -*/ -// --- -class MATH_CLASS MbTransactions -{ -private: - c3d::CreatorsVector transactions; ///< \ru Упорядоченное множество строителей. \en An ordered set of creators. - -protected: - /// \ru Конструктор копирования с регистратором. \en Copy-constructor with registrator. - MbTransactions( const MbTransactions &, MbRegDuplicate * iReg ); -public: - /// \ru Конструктор без параметров. \en Constructor without parameters. - MbTransactions(); - /// \ru Конструктор по строителям. \en Constructor by creators. - template - MbTransactions( const Creators & creators ) - : transactions() - { - size_t iCount = creators.size(); - if ( iCount > 0 ) { - transactions.reserve( iCount ); - for ( size_t i = 0; i < iCount; i++ ) { - MbCreator * creator = const_cast( creators[i] ); - if ( creator != NULL ) { - creator->AddRef(); - transactions.push_back( creator ); - } - } - } - } - - /// \ru Деструктор. \en Destructor. - virtual ~MbTransactions(); -public: - - /// \ru Перестроить объект по протоколу построения. \en Reconstruct object according to the history tree. - virtual bool RebuildItem( MbeCopyMode sameShell, RPArray * items, IProgressIndicator * progInd ); - - /// \ru Очистить присланный журнал и скопировать в него строители. \en Clear the given history tree and copy the creators to it. - void CreatorsCopy ( MbTransactions & other, MbRegDuplicate * iReg = NULL ) const; - /// \ru Очистить журнал и скопировать в него строители из присланного журнала. \en Clear the history tree and copy the creators from the given history tree to it. - void CreatorsAssign ( const MbTransactions & other ); - /// \ru Сделать строители равными соответствующим строителям присланного журнала, если строители подобны. \en Make the creators equal to the creators from the given history tree if the creators are similar. - bool SetCreatorsEqual ( const MbTransactions & other ); - /// \ru Проверить, являются ли соответствующие строители присланного журнала подобными. \en Check whether the corresponding creators of the given history tree are similar. - bool IsCreatorsSimilar( const MbTransactions & other ) const; - /// \ru Преобразовать согласно матрице строители. \en Transform the creators according to the matrix. - void CreatorsTransform( const MbMatrix3D &, MbRegTransform * = NULL ); - /// \ru Сдвинуть вдоль вектора строители. \en Move creators along the vector. - void CreatorsMove ( const MbVector3D &, MbRegTransform * = NULL ); - /// \ru Повернуть вокруг оси строители на заданный угол. \en Rotate the creators about the axis by the given angle. - void CreatorsRotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); - /// \ru Выдать количество строителей. \en Get the creators count. - size_t GetCreatorsCount() const { return transactions.size(); } - /// \ru Зарезервировать место для строителей. \en Reserve space for creators. - void Reserve( size_t count ) { transactions.reserve( transactions.size() + count ); } - /// \ru Выдать строитель по его индексу. \en Get constructor by its index. - const MbCreator * GetCreator( size_t ind ) const; - /// \ru Выдать строитель по его индексу с возможностью редактирования. \en Get constructor by its index with possibility of editing. - MbCreator * SetCreator( size_t ind ); - /// \ru Добавить свои строители в присланный массив. \en Add your own creators to the given array. - virtual bool GetCreators( RPArray & ) const; - /// \ru Добавить свои строители в присланный массив. \en Add your own creators to the given array. - virtual bool GetCreators( c3d::CreatorsSPtrVector & ) const; - /// \ru Добавить копии своих строителей в присланный массив. \en Add copies of your own creators to the given array. - bool GetCreatorsCopies( RPArray & ) const; - /// \ru Добавить копии своих строителей в присланный массив. \en Add copies of your own creators to the given array. - bool GetCreatorsCopies( c3d::CreatorsSPtrVector & ) const; - /// \ru Найти номер строителя в журнале или вернуть SYS_MAX_T в случае отсутствия. \en Find the number of creators in the history tree or return SYS_MAX_T if it is absent. - size_t FindCreator( const MbCreator * creator ); - /// \ru Добавить строитель (addSame = false) или его копию (addSame = true) в журнал. \en Add the constructor (addSame = false) or its copy (addSame = true) to the history tree. - bool AddCreator ( const MbCreator &, bool addSame = false ); - /// \ru Добавить строитель (addSame = false) или его копию (addSame = true) в журнал. \en Add the constructor (addSame = false) or its copy (addSame = true) to the history tree. - bool AddCreator ( const MbCreator *, bool addSame = false ); - /// \ru Добавить строители в журнал. \en Add creators to the history tree. - void AddCreators( const RPArray & ); - /// \ru Вытереть строитель с указанным номером из журнала и отдать его. \en Remove the constructor with the specified index from the history tree and return it. - MbCreator * DetachCreator ( size_t ind ); - /// \ru Удалить строитель с указанным номером и вытереть его из журнала. \en Delete the constructor with the specified index and remove it from the history tree. - bool DeleteCreator ( size_t ind ); - /// \ru Удалить все строители и очистить журнал. \en Delete all the creators and clear the history tree. - void DeleteCreators(); - /// \ru Дать статус строителя с указанным номером. \en Get the status of creator with the specified index. - int GetCreatorStatus( size_t ind ) const; - /// \ru Установить строителю с указанным номером статус. \en Set status to creator with the specified index. - bool SetCreatorStatus( size_t ind, MbeProcessState ); - /// \ru Дать количество активных строителей. \en Get the active creators count. - size_t GetActiveCreatorsCount() const; - /// \ru Установить количество активных строителей от начала до заданного номера. \en Set the count of active creators from the beginning to the given index. - bool SetActiveCreatorsCount( size_t activeCount ); - /// \ru Выдать создаваемый заданным числом строителей объект и базовые объекты остальных строителей. \en Get the object created by the specified number of creators and the basis items of the other creators. - void BreakCreatorsToBasisItem( size_t c, RPArray & ); - /// \ru Выдать базовые объекты строителей. \en Get the basis items of the creators. - void GetCreatorsBasisItems ( RPArray & ); - /// \ru Выдать базовые точки строителей. \en Get the basis points of the creators. - void GetCreatorsBasisPoints( MbControlData3D & ) const; - /// \ru Изменить объект по контрольным точкам. \en Change the object by control points. - void SetCreatorsBasisPoints( const MbControlData3D & ); - /// \ru Выдать свойства строителей (на копиях или на оригиналах строителей). \en Get properties of the creators (using original creators or their copies). - void GetProperties( MbProperties &, bool sameCreators = false ); - /// \ru Установить свойства строителей. \en Set properties of the creators. - void SetProperties( const MbProperties & ); - /// \ru Прочитать строители из потока. \en Read creators from the stream. - void CreatorsRead ( reader & in ); - /// \ru Записать строители в поток. \en Write creators to the stream. - void CreatorsWrite( writer & out ) const; - -OBVIOUS_PRIVATE_COPY( MbTransactions ) -}; - - -#endif // __CREATOR_TRANSACTION_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Журнал построения объекта. + \en The history tree of object. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CREATOR_TRANSACTION_H +#define __CREATOR_TRANSACTION_H + + +#include +#include +#include +#include + + +class MATH_CLASS reader; +class MATH_CLASS writer; +class MATH_CLASS IProgressIndicator; +class MATH_CLASS MbCartPoint3D; +class MATH_CLASS MbVector3D; +class MATH_CLASS MbAxis3D; +class MATH_CLASS MbMatrix3D; +class MATH_CLASS MbSpaceItem; +class MATH_CLASS MbFaceShell; +class MATH_CLASS MbWireFrame; +class MATH_CLASS MbPointFrame; +class MATH_CLASS MbProperties; +struct MATH_CLASS MbControlData3D; +class MbRegDuplicate; +class MbRegTransform; +enum MbeCopyMode; + + +//------------------------------------------------------------------------------ +/** \brief \ru Журнал построения объекта. + \en The history tree of object. \~ + \details \ru Журнал построения содержит упорядоченное множество строителей, + последовательная работа которых строит объект. \n + Неактивные строители (с состоянием mps_Skip) не принимают участия в построении объекта. + \en The history tree contains an ordered set of creators + whose successive work creates the objects. \n + Inactive creators (with state mps_Skip) are not used in the object construction. \~ + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbTransactions +{ +private: + c3d::CreatorsVector transactions; ///< \ru Упорядоченное множество строителей. \en An ordered set of creators. + +protected: + /// \ru Конструктор копирования с регистратором. \en Copy-constructor with registrator. + MbTransactions( const MbTransactions &, MbRegDuplicate * iReg ); +public: + /// \ru Конструктор без параметров. \en Constructor without parameters. + MbTransactions(); + /// \ru Конструктор по строителям. \en Constructor by creators. + template + MbTransactions( const Creators & creators ) + : transactions() + { + size_t iCount = creators.size(); + if ( iCount > 0 ) { + transactions.reserve( iCount ); + for ( size_t i = 0; i < iCount; i++ ) { + MbCreator * creator = const_cast( creators[i] ); + if ( creator != NULL ) { + creator->AddRef(); + transactions.push_back( creator ); + } + } + } + } + + /// \ru Деструктор. \en Destructor. + virtual ~MbTransactions(); +public: + + /// \ru Перестроить объект по протоколу построения. \en Reconstruct object according to the history tree. + virtual bool RebuildItem( MbeCopyMode sameShell, RPArray * items, IProgressIndicator * progInd ); + + /// \ru Очистить присланный журнал и скопировать в него строители. \en Clear the given history tree and copy the creators to it. + void CreatorsCopy ( MbTransactions & other, MbRegDuplicate * iReg = NULL ) const; + /// \ru Очистить журнал и скопировать в него строители из присланного журнала. \en Clear the history tree and copy the creators from the given history tree to it. + void CreatorsAssign ( const MbTransactions & other ); + /// \ru Сделать строители равными соответствующим строителям присланного журнала, если строители подобны. \en Make the creators equal to the creators from the given history tree if the creators are similar. + bool SetCreatorsEqual ( const MbTransactions & other ); + /// \ru Проверить, являются ли соответствующие строители присланного журнала подобными. \en Check whether the corresponding creators of the given history tree are similar. + bool IsCreatorsSimilar( const MbTransactions & other ) const; + /// \ru Преобразовать согласно матрице строители. \en Transform the creators according to the matrix. + void CreatorsTransform( const MbMatrix3D &, MbRegTransform * = NULL ); + /// \ru Сдвинуть вдоль вектора строители. \en Move creators along the vector. + void CreatorsMove ( const MbVector3D &, MbRegTransform * = NULL ); + /// \ru Повернуть вокруг оси строители на заданный угол. \en Rotate the creators about the axis by the given angle. + void CreatorsRotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); + /// \ru Выдать количество строителей. \en Get the creators count. + size_t GetCreatorsCount() const { return transactions.size(); } + /// \ru Зарезервировать место для строителей. \en Reserve space for creators. + void Reserve( size_t count ) { transactions.reserve( transactions.size() + count ); } + /// \ru Выдать строитель по его индексу. \en Get constructor by its index. + const MbCreator * GetCreator( size_t ind ) const; + /// \ru Выдать строитель по его индексу с возможностью редактирования. \en Get constructor by its index with possibility of editing. + MbCreator * SetCreator( size_t ind ); + /// \ru Добавить свои строители в присланный массив. \en Add your own creators to the given array. + virtual bool GetCreators( RPArray & ) const; + /// \ru Добавить свои строители в присланный массив. \en Add your own creators to the given array. + virtual bool GetCreators( c3d::CreatorsSPtrVector & ) const; + /// \ru Добавить копии своих строителей в присланный массив. \en Add copies of your own creators to the given array. + bool GetCreatorsCopies( RPArray & ) const; + /// \ru Добавить копии своих строителей в присланный массив. \en Add copies of your own creators to the given array. + bool GetCreatorsCopies( c3d::CreatorsSPtrVector & ) const; + /// \ru Найти номер строителя в журнале или вернуть SYS_MAX_T в случае отсутствия. \en Find the number of creators in the history tree or return SYS_MAX_T if it is absent. + size_t FindCreator( const MbCreator * creator ); + /// \ru Добавить строитель (addSame = false) или его копию (addSame = true) в журнал. \en Add the constructor (addSame = false) or its copy (addSame = true) to the history tree. + bool AddCreator ( const MbCreator &, bool addSame = false ); + /// \ru Добавить строитель (addSame = false) или его копию (addSame = true) в журнал. \en Add the constructor (addSame = false) or its copy (addSame = true) to the history tree. + bool AddCreator ( const MbCreator *, bool addSame = false ); + /// \ru Добавить строители в журнал. \en Add creators to the history tree. + void AddCreators( const RPArray & ); + /// \ru Вытереть строитель с указанным номером из журнала и отдать его. \en Remove the constructor with the specified index from the history tree and return it. + MbCreator * DetachCreator ( size_t ind ); + /// \ru Удалить строитель с указанным номером и вытереть его из журнала. \en Delete the constructor with the specified index and remove it from the history tree. + bool DeleteCreator ( size_t ind ); + /// \ru Удалить все строители и очистить журнал. \en Delete all the creators and clear the history tree. + void DeleteCreators(); + /// \ru Дать статус строителя с указанным номером. \en Get the status of creator with the specified index. + int GetCreatorStatus( size_t ind ) const; + /// \ru Установить строителю с указанным номером статус. \en Set status to creator with the specified index. + bool SetCreatorStatus( size_t ind, MbeProcessState ); + /// \ru Дать количество активных строителей. \en Get the active creators count. + size_t GetActiveCreatorsCount() const; + /// \ru Установить количество активных строителей от начала до заданного номера. \en Set the count of active creators from the beginning to the given index. + bool SetActiveCreatorsCount( size_t activeCount ); + /// \ru Выдать создаваемый заданным числом строителей объект и базовые объекты остальных строителей. \en Get the object created by the specified number of creators and the basis items of the other creators. + void BreakCreatorsToBasisItem( size_t c, RPArray & ); + /// \ru Выдать базовые объекты строителей. \en Get the basis items of the creators. + void GetCreatorsBasisItems ( RPArray & ); + /// \ru Выдать базовые точки строителей. \en Get the basis points of the creators. + void GetCreatorsBasisPoints( MbControlData3D & ) const; + /// \ru Изменить объект по контрольным точкам. \en Change the object by control points. + void SetCreatorsBasisPoints( const MbControlData3D & ); + /// \ru Выдать свойства строителей (на копиях или на оригиналах строителей). \en Get properties of the creators (using original creators or their copies). + void GetProperties( MbProperties &, bool sameCreators = false ); + /// \ru Установить свойства строителей. \en Set properties of the creators. + void SetProperties( const MbProperties & ); + /// \ru Прочитать строители из потока. \en Read creators from the stream. + void CreatorsRead ( reader & in ); + /// \ru Записать строители в поток. \en Write creators to the stream. + void CreatorsWrite( writer & out ) const; + +OBVIOUS_PRIVATE_COPY( MbTransactions ) +}; + + +#endif // __CREATOR_TRANSACTION_H diff --git a/C3d/Include/cur_arc.h b/C3d/Include/cur_arc.h index db401d9..b622cb6 100644 --- a/C3d/Include/cur_arc.h +++ b/C3d/Include/cur_arc.h @@ -57,7 +57,7 @@ class DiskreteLengthData; \ingroup Curves_2D */ // --- -class MATH_CLASS MbArc : public MbCurve, public MbSyncItem { +class MATH_CLASS MbArc : public MbCurve { protected : MbPlacement position; ///< \ru Локальная система координат. \en Local coordinate system. double a; ///< \ru Радиус полуоси вдоль X. \en Radius of semiaxis along X. @@ -948,9 +948,6 @@ public : */ void Init( const MbCartPoint & pc, const MbCartPoint & p1, const MbCartPoint & p2, int initSense ); - // \ru NES 9.12.2011 не нашла реализации этой функции. \en NES 9.12.2011 didn't find the implementation of this function. - // void Init( double t1, double t2, int initSense ); - // \ru Инициализация по начальной и конечной точкам и 1/2 угла раствора дуги \en Initialization by the starting and end points and 1/2 of the arc opening angle // \ru Если diskrData != NULL, то округлить радиус и скорректировать первую \en If diskrData != NULL, then round the radius and correct the first // \ru Или вторую точку (зависит от correctFirstPnt) \en Or the second point (depends on correctFirstPnt) diff --git a/C3d/Include/cur_arc3d.h b/C3d/Include/cur_arc3d.h index 1ceceeb..e40e931 100644 --- a/C3d/Include/cur_arc3d.h +++ b/C3d/Include/cur_arc3d.h @@ -15,8 +15,8 @@ #include -#define CONIC_COUNT 32 -#define LINES_COUNT 10 +const_expr size_t CONIC_COUNT = 32; +const_expr size_t LINES_COUNT = 10; class MATH_CLASS MbArc; diff --git a/C3d/Include/cur_b_spline.h b/C3d/Include/cur_b_spline.h index b4f1622..199e50e 100644 --- a/C3d/Include/cur_b_spline.h +++ b/C3d/Include/cur_b_spline.h @@ -1,108 +1,108 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Набор В-сплайнов NURBS кривой. - \en B-spline set of NURBS-curve. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CUR_B_SPLINE_H -#define __CUR_B_SPLINE_H - - -#include -#include - - -class MATH_CLASS MbNurbs3D; -class MATH_CLASS MbNurbs; - - -//------------------------------------------------------------------------------ -/** \brief \ru Набор В-сплайнов NURBS кривой. - \en B-spline set of NURBS-curve. \~ - \details \ru Объект со свойствами кривой служит для визуализации набора В-сплайнов некоторой NURBS кривой. \n - В-сплайны NURBS кривой располагаются в плоскости XY локальной системы координат position. - Длина рисунка всех В-сплайнов равна параметру a, высота рисунка всех В-сплайнов равна параметру h. - \en Object with curve properties is used to visualize B-spline set of some NURBS-curve. \n - B-splines of NURBS-curve are located in the XY plane of the local coordinate system. - Picture length of all B-splines is equal to the parameter a, the height of all B-splines is the parameter h. \~ - \ingroup Curves_3D -*/ -// --- -class MATH_CLASS MbBSpline : public MbCurve3D { -private: - ptrdiff_t degree; ///< \ru Степень В-сплайнов. \en Degree of B-splines. - SArray knots; ///< \ru Узловой вектор. \en Knot vector. - ptrdiff_t pCount; ///< \ru Число точек. \en Number of points. - bool closed; ///< \ru Признак замкнутости кривой. \en An attribute of curve closedness. - MbPlacement3D position; ///< \ru Плоскость для отрисовки. \en A plane for drawing. - double a; ///< \ru Горизонтальный размер (длина рисунка). \en Horizontal size (picture length). - double h; ///< \ru Вертикальный размер (высота рисунка). \en Vertical size (picture height). - -public: - MbBSpline( const MbCartPoint3D & p0, const MbCartPoint3D & p1, const MbCartPoint3D & p2, const MbNurbs & nurbs ); - MbBSpline( const MbCartPoint3D & p0, const MbCartPoint3D & p1, const MbCartPoint3D & p2, const MbNurbs3D & nurbs ); - MbBSpline( const MbCartPoint3D & p0, const MbCartPoint3D & p1, const MbCartPoint3D & p2, const SArray & t ); -private: - MbBSpline( const MbBSpline & init ); -public: - virtual ~MbBSpline(); - -public: - VISITING_CLASS( MbBSpline ); - - // \ru Общие функции математического объекта \en The common functions of the mathematical object - - virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element - virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; - virtual bool SetEqual( const MbSpaceItem & init ); // \ru Сделать равным \en Make equal - virtual void Transform( const MbMatrix3D & matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move( const MbVector3D & to, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate( const MbAxis3D & axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis - virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. - - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - // \ru Общие функции кривой \en Common functions of curve - - virtual double GetTMin() const; // \ru Вернуть минимальное значение параметра \en Get the minimum value of parameter - virtual double GetTMax() const; // \ru Вернуть максимальное значение параметра \en Get the maximum value of parameter - virtual bool IsClosed() const; // \ru Проверка замкнутости кривой \en Check for curve closedness - // \ru Функции для работы внутри области определения кривой. \en Functions for working inside of the curve domain. \~ - virtual void PointOn ( double &t, MbCartPoint3D &pnt ) const; // \ru Точка на кривой \en The point on the curve - virtual void FirstDer ( double &t, MbVector3D &fd ) const; // \ru Первая производная \en First derivative - virtual void SecondDer( double &t, MbVector3D &sd ) const; // \ru Вторая производная \en Second derivative - virtual void ThirdDer ( double &t, MbVector3D &td ) const; // \ru Третья производная по t \en Third derivative with respect to t - // \ru Вычислить значения точки и производных для заданного параметра. \en Calculate point and derivatives of object for given parameter. \~ - virtual void Explore( double & t, bool ext, - MbCartPoint3D & pnt, MbVector3D & fir, MbVector3D * sec, MbVector3D * thir ) const; - - virtual double Step( double t, double sag ) const; // \ru Вычисление шага аппроксимации \en Calculate step of approximation - - virtual MbNurbs3D * NurbsCurve( const MbCurveIntoNurbsInfo & ) const; - - virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменить направление \en Change direction - - void CalculateOnePolygon( size_t i, const MbStepData & stepData, MbPolygon3D * polygon ) const; // \ru Pассчитать полигон по параметру T \en Calculate polygon of the parameter T - // \ru Расчет весовых функций и их первых, вторых и третьих производных \en Calculation of the weight functions and their first, second and third derivatives - ptrdiff_t CalculateFunctions( double x, double * m, - double * mm0, double * mm1, double * mm2, double * mm3 ) const; - ptrdiff_t CalculateParam( double & t, double & x ) const; // \ru Расчет параметра и номера сплайна \en Calculation of the parameter and spline number - void GetWeightFunctions( double t, double * m, - double * mm0, double * mm1, double * mm2, double * mm3 ) const; // \ru Определение В-сплайнов \en Definition of B-splines - -private: - void operator = ( const MbBSpline & ); // \ru Не реализовано. \en Not implemented. - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbBSpline ) -}; // MbBSpline - -IMPL_PERSISTENT_OPS( MbBSpline ) - -#endif // __CUR_B_SPLINE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Набор В-сплайнов NURBS кривой. + \en B-spline set of NURBS-curve. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CUR_B_SPLINE_H +#define __CUR_B_SPLINE_H + + +#include +#include + + +class MATH_CLASS MbNurbs3D; +class MATH_CLASS MbNurbs; + + +//------------------------------------------------------------------------------ +/** \brief \ru Набор В-сплайнов NURBS кривой. + \en B-spline set of NURBS-curve. \~ + \details \ru Объект со свойствами кривой служит для визуализации набора В-сплайнов некоторой NURBS кривой. \n + В-сплайны NURBS кривой располагаются в плоскости XY локальной системы координат position. + Длина рисунка всех В-сплайнов равна параметру a, высота рисунка всех В-сплайнов равна параметру h. + \en Object with curve properties is used to visualize B-spline set of some NURBS-curve. \n + B-splines of NURBS-curve are located in the XY plane of the local coordinate system. + Picture length of all B-splines is equal to the parameter a, the height of all B-splines is the parameter h. \~ + \ingroup Curves_3D +*/ +// --- +class MATH_CLASS MbBSpline : public MbCurve3D { +private: + ptrdiff_t degree; ///< \ru Степень В-сплайнов. \en Degree of B-splines. + SArray knots; ///< \ru Узловой вектор. \en Knot vector. + ptrdiff_t pCount; ///< \ru Число точек. \en Number of points. + bool closed; ///< \ru Признак замкнутости кривой. \en An attribute of curve closedness. + MbPlacement3D position; ///< \ru Плоскость для отрисовки. \en A plane for drawing. + double a; ///< \ru Горизонтальный размер (длина рисунка). \en Horizontal size (picture length). + double h; ///< \ru Вертикальный размер (высота рисунка). \en Vertical size (picture height). + +public: + MbBSpline( const MbCartPoint3D & p0, const MbCartPoint3D & p1, const MbCartPoint3D & p2, const MbNurbs & nurbs ); + MbBSpline( const MbCartPoint3D & p0, const MbCartPoint3D & p1, const MbCartPoint3D & p2, const MbNurbs3D & nurbs ); + MbBSpline( const MbCartPoint3D & p0, const MbCartPoint3D & p1, const MbCartPoint3D & p2, const SArray & t ); +private: + MbBSpline( const MbBSpline & init ); +public: + virtual ~MbBSpline(); + +public: + VISITING_CLASS( MbBSpline ); + + // \ru Общие функции математического объекта \en The common functions of the mathematical object + + virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element + virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; + virtual bool SetEqual( const MbSpaceItem & init ); // \ru Сделать равным \en Make equal + virtual void Transform( const MbMatrix3D & matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move( const MbVector3D & to, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate( const MbAxis3D & axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. + + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + // \ru Общие функции кривой \en Common functions of curve + + virtual double GetTMin() const; // \ru Вернуть минимальное значение параметра \en Get the minimum value of parameter + virtual double GetTMax() const; // \ru Вернуть максимальное значение параметра \en Get the maximum value of parameter + virtual bool IsClosed() const; // \ru Проверка замкнутости кривой \en Check for curve closedness + // \ru Функции для работы внутри области определения кривой. \en Functions for working inside of the curve domain. \~ + virtual void PointOn ( double &t, MbCartPoint3D &pnt ) const; // \ru Точка на кривой \en The point on the curve + virtual void FirstDer ( double &t, MbVector3D &fd ) const; // \ru Первая производная \en First derivative + virtual void SecondDer( double &t, MbVector3D &sd ) const; // \ru Вторая производная \en Second derivative + virtual void ThirdDer ( double &t, MbVector3D &td ) const; // \ru Третья производная по t \en Third derivative with respect to t + // \ru Вычислить значения точки и производных для заданного параметра. \en Calculate point and derivatives of object for given parameter. \~ + virtual void Explore( double & t, bool ext, + MbCartPoint3D & pnt, MbVector3D & fir, MbVector3D * sec, MbVector3D * thir ) const; + + virtual double Step( double t, double sag ) const; // \ru Вычисление шага аппроксимации \en Calculate step of approximation + + virtual MbNurbs3D * NurbsCurve( const MbCurveIntoNurbsInfo & ) const; + + virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменить направление \en Change direction + + void CalculateOnePolygon( size_t i, const MbStepData & stepData, MbPolygon3D * polygon ) const; // \ru Pассчитать полигон по параметру T \en Calculate polygon of the parameter T + // \ru Расчет весовых функций и их первых, вторых и третьих производных \en Calculation of the weight functions and their first, second and third derivatives + ptrdiff_t CalculateFunctions( double x, double * m, + double * mm0, double * mm1, double * mm2, double * mm3 ) const; + ptrdiff_t CalculateParam( double & t, double & x ) const; // \ru Расчет параметра и номера сплайна \en Calculation of the parameter and spline number + void GetWeightFunctions( double t, double * m, + double * mm0, double * mm1, double * mm2, double * mm3 ) const; // \ru Определение В-сплайнов \en Definition of B-splines + +private: + void operator = ( const MbBSpline & ); // \ru Не реализовано. \en Not implemented. + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbBSpline ) +}; // MbBSpline + +IMPL_PERSISTENT_OPS( MbBSpline ) + +#endif // __CUR_B_SPLINE_H diff --git a/C3d/Include/cur_bezier.h b/C3d/Include/cur_bezier.h index 9dda0c1..f75bccf 100644 --- a/C3d/Include/cur_bezier.h +++ b/C3d/Include/cur_bezier.h @@ -128,6 +128,8 @@ public : \en Create copy of spline. \~ \details \ru Создать копию сплайна.\n \en Create copy of spline.\n \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbBezier * Create( const MbBezier & other ); /** \brief \ru Создать сплайн по четырем точкам. @@ -142,6 +144,8 @@ public : When number of points is more then four classical cubic bezier is created for each sequential four points. The smoothness of result curve can be broken. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbBezier * Create( const SArray & initList ); /** \brief \ru Создать сплайн по массиву всех точек. @@ -160,6 +164,8 @@ public : It must be obtained from point array of three-dimensional curve MbBezier3D which is planar, by projecting them onto the plane of the curve. The number of points must be multiple of three. The minimal number of points is six. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbBezier * Create( bool closed, const SArray & points ); /** \brief \ru Создать сплайн по сегменту Bezier-кривой. @@ -170,6 +176,8 @@ public : \en A given curve. \~ \param[in] iseg - \ru Номер сегмента кривой. \en A number of curve segment. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbBezier * Create( const MbBezier & pCurve, ptrdiff_t iseg ); /** \brief \ru Создать сплайн по полюсам. @@ -183,11 +191,13 @@ public : \param[in] cls - \ru Замкнутость кривой. \en A curve closedness. \~ \param[in] initForm - \ru Форма сплайна. Возможные значения:\n - 0 - Стандартная форма. - 1 - Более выпуклая форма кривой. + 0 - Стандартная форма. \n + 1 - Более выпуклая форма кривой. \en The shape of spline. Possible values:\n - 0 - The standard form. - 1 - More convex form of curve. \~ + 0 - The standard form. \n + 1 - More convex form of curve. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbBezier * Create( const SArray & initList, bool cls, int initForm = 0 ); /** \brief \ru Создать сплайн по дуге окружности. @@ -196,6 +206,8 @@ public : \en There was constructed a Bezier curve which approximates a given arc of a circle. \~ \param[in] arc - \ru Дуга окружности. \en Circle arc. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbBezier * Create( const MbArc & arc ); // \ru Инициализация по дуге окружности \en Initialization by a circle arc @@ -285,7 +297,7 @@ public : /** \ru \name Функции движения по кривой \en \name Functions of the motion along the curve \{ */ - virtual double Step( double t, double sag ) const; // \ru Вычисление шага аппроксимации с учетом радиуса кривизны \en Calculation of the approximation step with consideration of the curvature radius + virtual double Step( double t, double sag ) const; // \ru Вычисление шага аппроксимации с учетом радиуса кривизны \en Calculation of the approximation step with consideration of the curvature radius virtual double DeviationStep( double t, double angle ) const; // \ru Вычисление шага аппроксимации по угловой толерантности \en Calculation of the approximation step by angular tolerance /** \} */ @@ -296,7 +308,7 @@ public : virtual MbNurbs * NurbsCurve( const MbCurveIntoNurbsInfo & ) const; virtual MbCurve * NurbsCurve( const MbNurbsParameters & ) const; // \ru Построить NURBS-копию кривой \en Create a NURBS-copy of the curve virtual MbCurve * Trimmed( double t1, double t2, int sense ) const; - virtual MbContour * NurbsContour() const; + virtual MbContour * NurbsContour() const; virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменение направления кривой на противоположное \en Change to the opposite direction of a curve @@ -314,7 +326,7 @@ public : virtual size_t GetPointsCount() const; ///< \ru Вернуть количество несовпадающих контрольных точек. \en Return the number of non-coincedent control points. virtual void GetPoint( ptrdiff_t index, MbCartPoint & pnt ) const; // \ru Выдать точку \en Get point - virtual ptrdiff_t GetNearPointIndex( const MbCartPoint & pnt ) const; ///< \ru Выдать индекс точки, ближайшей к заданной. \en Get the point index which is nearest to the given. + virtual ptrdiff_t GetNearPointIndex( const MbCartPoint & pnt ) const; ///< \ru Выдать индекс точки, ближайшей к заданной. \en Get the point index which is nearest to the given. virtual void GetRuleInterval( ptrdiff_t index, double & t1, double & t2 ) const; // \ru Выдать интервал влияния точки \en Get the interval of point influence @@ -461,7 +473,8 @@ public : VERSION version = Math::DefaultMathVersion() ) const; virtual bool IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = NULL, double epsilon = EPSILON ) const; // \ru Непрерывна ли первая производная? \en Have the first derivative the continuous? - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); /** \} */ @@ -469,11 +482,11 @@ protected: virtual bool CanChangeClosed() const; // \ru Можно ли поменять признак замкнутости \en Whether it is possible to change the attribute of closedness private : void CheckData( double & t ) const; - void EvaluateSlope ( ptrdiff_t index ); // \ru Рассчитать производные в полюсе index \en Calculate derivatives at the pole "index" - void EvaluateSlope0 ( ptrdiff_t index ); // \ru Рассчитать производные в полюсе index для 0-ой формы \en Calculate derivatives at the pole "index" for 0-th form - void EvaluateSlope1 ( ptrdiff_t index ); // \ru Рассчитать производные в полюсе index для 1-ой формы \en Calculate derivatives at the pole "index" for 1-th form + void EvaluateSlope ( ptrdiff_t index ); // \ru Рассчитать производные в полюсе index \en Calculate derivatives at the pole "index" + void EvaluateSlope0 ( ptrdiff_t index ); // \ru Рассчитать производные в полюсе index для 0-ой формы \en Calculate derivatives at the pole "index" for 0-th form + void EvaluateSlope1 ( ptrdiff_t index ); // \ru Рассчитать производные в полюсе index для 1-ой формы \en Calculate derivatives at the pole "index" for 1-th form void SetDerives(); // \ru Рассчитать все производные \en Calculate all derivatives - void SetDerives ( ptrdiff_t index ); // \ru Рассчитать производные в полюсах при изменении полюса index. \en Calculate derivatives at poles when changing the pole "index". + void SetDerives ( ptrdiff_t index ); // \ru Рассчитать производные в полюсах при изменении полюса index. \en Calculate derivatives at poles when changing the pole "index". void operator = ( const MbBezier & ); // \ru Не реализовано. \en Not implemented. diff --git a/C3d/Include/cur_bezier3d.h b/C3d/Include/cur_bezier3d.h index 4fd36d0..45215cf 100644 --- a/C3d/Include/cur_bezier3d.h +++ b/C3d/Include/cur_bezier3d.h @@ -58,6 +58,8 @@ public : \en Create copy of spline. \~ \details \ru Создать копию сплайна.\n \en Create copy of spline.\n \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbBezier3D * Create( const MbBezier3D & other ); /** \brief \ru Создать сплайн. @@ -70,6 +72,8 @@ public : \en A closedness attribute. \~ \param[in] initForm - \ru Форма кривой. \en Form of curve. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbBezier3D * Create( const SArray & initList, bool cls, int initForm = 0 ); /** \brief \ru Создать сплайн. @@ -80,6 +84,8 @@ public : \en The two-dimensional spline. \~ \param[in] place - \ru Локальная система координат сплайна. \en Local coordinate system of spline. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbBezier3D * Create( const MbBezier & bezier, const MbPlacement3D & place ); /** \brief \ru Создать сплайн. @@ -90,6 +96,8 @@ public : \en Start point of curve. \~ \param[in] p2 - \ru Конечная точка кривой. \en End point of curve. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbBezier3D * Create( const MbCartPoint3D & p1, const MbCartPoint3D & p2 ); @@ -184,7 +192,8 @@ public : VERSION version = Math::DefaultMathVersion() ) const; virtual bool IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = NULL, double epsilon = EPSILON ) const; // \ru Непрерывна ли первая производная? \en Have the first derivative the continuous? - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); private : void CheckBezierClosed(); // \ru Проверка признака замкнутости. \en Check closed. diff --git a/C3d/Include/cur_bridge3d.h b/C3d/Include/cur_bridge3d.h index 358d30b..fecd521 100644 --- a/C3d/Include/cur_bridge3d.h +++ b/C3d/Include/cur_bridge3d.h @@ -27,7 +27,8 @@ \ingroup Curves_3D */ // --- -class MATH_CLASS MbBridgeCurve3D : public MbCurve3D { +class MATH_CLASS MbBridgeCurve3D : public MbCurve3D +{ protected: MbCurve3D * curve1; ///< \ru Первая соединяемая кривая. \en The first curve to connect. MbCurve3D * curve2; ///< \ru Вторая соединяемая кривая. \en The second curve to connect. @@ -103,7 +104,7 @@ public: virtual MbNurbs3D * NurbsCurve( const MbCurveIntoNurbsInfo & ) const; virtual MbCurve3D * NurbsCurve( const MbNurbsParameters & ) const; // \ru Построить NURBS-копию кривой \en Create a NURBS-copy of the curve - const MbCube &GetGabarit() const { if ( cube.IsEmpty() ) CalculateGabarit( cube ); return cube; } // \ru Выдать габарит кривой \en Get the bounding box of a curve + const MbCube & GetGabarit() const; // \ru Выдать габарит кривой \en Get the bounding box of a curve private: inline void CheckParam ( double & t ) const; // \ru Проверка параметра \en Check parameter diff --git a/C3d/Include/cur_character_curve.h b/C3d/Include/cur_character_curve.h index eb22602..ce9dccb 100644 --- a/C3d/Include/cur_character_curve.h +++ b/C3d/Include/cur_character_curve.h @@ -1,151 +1,152 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Двумерная кривая, координатные функции которой заданы в символьном виде. - \en Functionally defined two-dimensional curve. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CUR_CHARACTER_CURVE_H -#define __CUR_CHARACTER_CURVE_H - -#include -#include -#include -#include -#include - - -class MATH_CLASS MbFunction; - - -//------------------------------------------------------------------------------ -/** \brief \ru Двумерная кривая, координатные функции которой заданы в символьном виде. - \en Functionally defined two-dimensional curve. \~ - \details \ru Координатные функции кривой заданы в виде пользовательских функций общего параметра t. - Каждая координата кривой описана своей функцией в виде строкового выражения. - Параметр кривой, он же параметр координатных функций, изменяется на отрезке [tmin tmax]. \n - Все пользовательские функции заданы в локальной системе координат position. - Система координат может быть декартовой или полярной. - Радиус-вектор кривой в методе PointOn(double&t,MbCartPoint3D&r) описывается векторной функцией: \n - r(t) = position.origin + (position.axisX xFunction(t)) + (position.axisY yFunction(t)). - \en Functions of coordinates of curve are given as custom functions of common parameter t. - Each coordinate of the curve is described by its own function in form of string expression. - Parameter of the curve, which is also the parameter of functions of coordinates, varies in range [tmin tmax]. \n - All the custom functions are given in the local coordinate system position. - The coordinate system can be Cartesian or polar. - The radius-vector of the curve in method PointOn(double&t,MbCartPoint3D&r) is described by a vector function: \n - r(t) = position.origin + (position.axisX xFunction(t)) + (position.axisY yFunction(t)). \~ - \ingroup Curves_2D -*/ -// --- -class MATH_CLASS MbCharacterCurve : public MbCurve { -private: - MbFunction * xFunction; ///< \ru Функция координаты x. \en Function of x-coordinate. - MbFunction * yFunction; ///< \ru Функция координаты y. \en Function of y-coordinate. - MbPlacement position; ///< \ru Локальная система координат, в которой заданы координатные функции. \en The local coordinate system the functions of coordinates are specified in. - MbMatrix transform; ///< \ru Матрица трансформации кривой. \en Transformation matrix of the curve. - double tmin; ///< \ru Минимальное значение параметра кривой. \en The minimal value of a curve parameter. - double tmax; ///< \ru Максимальное значение параметра кривой. \en The maximal value of the curve parameter. - bool closed; ///< \ru Признак замкнутости кривой. \en An attribute of curve closedness. - MbeLocalSystemType coordinateType; ///< \ru Тип системы координат, в которой заданы координатные функции \en Type of coordinate system the functions of coordinates are specified in - c3d::DoubleVector specialParams; ///< \ru Перечень параметров особых точек кривой \en List of parameters of curve's singular points - // \ru Буферные данные для ускорения вычислений. \en Buffer data to speed up computations. - mutable double metricLength; - mutable MbRect rect; - -public: - MbCharacterCurve( MbFunction & x, MbFunction & y, - MbeLocalSystemType cs, - const MbPlacement & place, - double tmin_, double tmax_ ); -protected: - MbCharacterCurve( const MbCharacterCurve & ); -public: - virtual ~MbCharacterCurve(); - -public: - VISITING_CLASS( MbCharacterCurve ); - - // \ru Общие функции геометрического объекта. \en Common functions of a geometric object. - - virtual MbePlaneType IsA() const; // \ru Тип элемента \en A type of element - virtual MbPlaneItem & Duplicate ( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element - virtual bool IsSame ( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Кривая есть копия этой кривой ? \en Is a curve a copy of this curve? - virtual bool SetEqual ( const MbPlaneItem & ); // \ru Сделать равным \en Make equal - virtual void Transform( const MbMatrix &, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector &, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis - virtual void AddYourGabaritTo( MbRect & ) const; // \ru Добавь в прям-к свой габарит \en Add own bounding rectangle to the bounding rectangle - virtual void PrepareIntegralData( const bool forced ) const; // \ru Рассчитать временные (mutable) данные объекта. \en Calculate temporary (mutable) data of an object. - - // \ru Общие функции кривой. \en Common functions of the curve. - - virtual double GetTMin () const; // \ru Вернуть минимальное значение параметра \en Get the minimum value of parameter - virtual double GetTMax () const; // \ru Вернуть максимальное значение параметра \en Get the maximum value of parameter - virtual bool IsClosed() const; // \ru Проверка замкнутости кривой \en Check for curve closedness - // \ru Функции для работы внутри области определения кривой. \en Functions for working inside of the curve domain. \~ - virtual void PointOn ( double & t, MbCartPoint & ) const; // \ru Точка на кривой \en Point on the curve - virtual void FirstDer( double & t, MbVector & ) const; // \ru Первая производная \en The first derivative - virtual void SecondDer( double & t, MbVector & ) const; // \ru Вторая производная \en The second derivative - virtual void ThirdDer( double & t, MbVector & ) const; // \ru Третья производная по t \en The third derivative with respect to t - // \ru Вычислить значения точки и производных для заданного параметра. \en Calculate point and derivatives of object for given parameter. \~ - virtual void Explore( double & t, bool ext, - MbCartPoint & pnt, MbVector & fir, MbVector * sec, MbVector * thir ) const; - - virtual double Step ( double t, double sag ) const; // \ru Вычисление шага параметра по величине прогиба кривой \en Calculation of parameter step by value of sag of the curve - virtual double DeviationStep ( double t, double ang ) const; // \ru Вычисление шага параметра по углу отклонения касательной \en Calculation of parameter by the angle of tangent deviation - - virtual bool HasLength ( double & length ) const; // \ru Метрическая длина кривой \en Metric length of a curve - - virtual double GetMetricLength () const; // \ru Метрическая длина кривой \en Metric length of a curve - virtual double GetLengthEvaluation() const; // \ru Оценка метрической длины кривой \en Metric length evaluation of a curve - - virtual void Inverse ( MbRegTransform * iReg = NULL ); // \ru Изменить направление \en Change direction - - virtual size_t GetCount () const; // \ru Определить количество разбиений для прохода в операциях. \en Define the number of splittings for one passage in operations. - virtual MbNurbs * NurbsCurve ( const MbCurveIntoNurbsInfo & ) const; - - virtual MbCurve * Trimmed ( double t1, double t2, int sense ) const; - virtual MbeState DeletePart ( double t1, double t2, MbCurve *& part2 ); // \ru Удалить часть кривой между параметрами t1 и t2 \en Remove a piece of curve between t1 and t2 parameters - virtual MbeState TrimmPart ( double t1, double t2, MbCurve *& part2 ); // \ru Оставить часть кривой между параметрами t1 и t2 \en Keep a piece of curve between t1 and t2 parameters - - virtual void GetBasisPoints( MbControlData & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - virtual void GetProperties ( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties ( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object - - void CheckParam ( double & t ) const; - void CalculateParam( double t, MbCartPoint & point, - MbVector & firstDer, MbVector & secondDer, MbVector & thirdDer ) const; - - const MbFunction * GetX() const { return xFunction; } - const MbFunction * GetY() const { return yFunction; } - const MbMatrix & GetMatrix() const { return transform; } - const MbPlacement & GetPlacement() const { return position; } - MbeLocalSystemType GetCoordinateType() const { return coordinateType; } - void GetSpecialParams( std::vector & params ) const; - -protected: - double ApproximationStep( double t, bool isAngle, double sag ) const; - void ConvertParamsInd( size_t componentIndex, - const std::vector & tComponent, - std::vector & tCrv ) const; - void ConvertParams( const double tCrv, - double (&tComponents) [2], - double (&proportionFactors)[2]) const; - -private: - // \ru Проверить и установить признак замкнутости. \en Check and set the flag of closedness. - void CheckClosed(); - -private: - void operator = ( const MbCharacterCurve & ); // \ru Не реализовано. \en Not implemented. - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCharacterCurve ) -}; - -IMPL_PERSISTENT_OPS( MbCharacterCurve ) - -#endif // __CUR_CHARACTER_CURVE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Двумерная кривая, координатные функции которой заданы в символьном виде. + \en Functionally defined two-dimensional curve. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CUR_CHARACTER_CURVE_H +#define __CUR_CHARACTER_CURVE_H + +#include +#include +#include +#include +#include + + +class MATH_CLASS MbFunction; + + +//------------------------------------------------------------------------------ +/** \brief \ru Двумерная кривая, координатные функции которой заданы в символьном виде. + \en Functionally defined two-dimensional curve. \~ + \details \ru Координатные функции кривой заданы в виде пользовательских функций общего параметра t. + Каждая координата кривой описана своей функцией в виде строкового выражения. + Параметр кривой, он же параметр координатных функций, изменяется на отрезке [tmin tmax]. \n + Все пользовательские функции заданы в локальной системе координат position. + Система координат может быть декартовой или полярной. + Радиус-вектор кривой в методе PointOn(double&t,MbCartPoint3D&r) описывается векторной функцией: \n + r(t) = position.origin + (position.axisX xFunction(t)) + (position.axisY yFunction(t)). + \en Functions of coordinates of curve are given as custom functions of common parameter t. + Each coordinate of the curve is described by its own function in form of string expression. + Parameter of the curve, which is also the parameter of functions of coordinates, varies in range [tmin tmax]. \n + All the custom functions are given in the local coordinate system position. + The coordinate system can be Cartesian or polar. + The radius-vector of the curve in method PointOn(double&t,MbCartPoint3D&r) is described by a vector function: \n + r(t) = position.origin + (position.axisX xFunction(t)) + (position.axisY yFunction(t)). \~ + \ingroup Curves_2D +*/ +// --- +class MATH_CLASS MbCharacterCurve : public MbCurve { +private: + MbFunction * xFunction; ///< \ru Функция координаты x. \en Function of x-coordinate. + MbFunction * yFunction; ///< \ru Функция координаты y. \en Function of y-coordinate. + MbPlacement position; ///< \ru Локальная система координат, в которой заданы координатные функции. \en The local coordinate system the functions of coordinates are specified in. + MbMatrix transform; ///< \ru Матрица трансформации кривой. \en Transformation matrix of the curve. + double tmin; ///< \ru Минимальное значение параметра кривой. \en The minimal value of a curve parameter. + double tmax; ///< \ru Максимальное значение параметра кривой. \en The maximal value of the curve parameter. + bool closed; ///< \ru Признак замкнутости кривой. \en An attribute of curve closedness. + MbeLocalSystemType coordinateType; ///< \ru Тип системы координат, в которой заданы координатные функции \en Type of coordinate system the functions of coordinates are specified in + c3d::DoubleVector specialParams; ///< \ru Перечень параметров особых точек кривой \en List of parameters of curve's singular points + // \ru Буферные данные для ускорения вычислений. \en Buffer data to speed up computations. + mutable double metricLength; + mutable MbRect rect; + +public: + MbCharacterCurve( MbFunction & x, MbFunction & y, + MbeLocalSystemType cs, + const MbPlacement & place, + double tmin_, double tmax_ ); +protected: + MbCharacterCurve( const MbCharacterCurve & ); +public: + virtual ~MbCharacterCurve(); + +public: + VISITING_CLASS( MbCharacterCurve ); + + // \ru Общие функции геометрического объекта. \en Common functions of a geometric object. + + virtual MbePlaneType IsA() const; // \ru Тип элемента \en A type of element + virtual MbPlaneItem & Duplicate ( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element + virtual bool IsSame ( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Кривая есть копия этой кривой ? \en Is a curve a copy of this curve? + virtual bool SetEqual ( const MbPlaneItem & ); // \ru Сделать равным \en Make equal + virtual void Transform( const MbMatrix &, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector &, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis + virtual void AddYourGabaritTo( MbRect & ) const; // \ru Добавь в прям-к свой габарит \en Add own bounding rectangle to the bounding rectangle + virtual void PrepareIntegralData( const bool forced ) const; // \ru Рассчитать временные (mutable) данные объекта. \en Calculate temporary (mutable) data of an object. + + // \ru Общие функции кривой. \en Common functions of the curve. + + virtual double GetTMin () const; // \ru Вернуть минимальное значение параметра \en Get the minimum value of parameter + virtual double GetTMax () const; // \ru Вернуть максимальное значение параметра \en Get the maximum value of parameter + virtual bool IsClosed() const; // \ru Проверка замкнутости кривой \en Check for curve closedness + // \ru Функции для работы внутри области определения кривой. \en Functions for working inside of the curve domain. \~ + virtual void PointOn ( double & t, MbCartPoint & ) const; // \ru Точка на кривой \en Point on the curve + virtual void FirstDer( double & t, MbVector & ) const; // \ru Первая производная \en The first derivative + virtual void SecondDer( double & t, MbVector & ) const; // \ru Вторая производная \en The second derivative + virtual void ThirdDer( double & t, MbVector & ) const; // \ru Третья производная по t \en The third derivative with respect to t + // \ru Вычислить значения точки и производных для заданного параметра. \en Calculate point and derivatives of object for given parameter. \~ + virtual void Explore( double & t, bool ext, + MbCartPoint & pnt, MbVector & fir, MbVector * sec, MbVector * thir ) const; + + virtual double Step ( double t, double sag ) const; // \ru Вычисление шага параметра по величине прогиба кривой \en Calculation of parameter step by value of sag of the curve + virtual double DeviationStep ( double t, double ang ) const; // \ru Вычисление шага параметра по углу отклонения касательной \en Calculation of parameter by the angle of tangent deviation + + virtual bool HasLength ( double & length ) const; // \ru Метрическая длина кривой \en Metric length of a curve + + virtual double GetMetricLength () const; // \ru Метрическая длина кривой \en Metric length of a curve + virtual double GetLengthEvaluation() const; // \ru Оценка метрической длины кривой \en Metric length evaluation of a curve + virtual void CalculateGabarit( MbRect & ) const; // \ru Определить габаритный прямоугольник кривой. + + virtual void Inverse ( MbRegTransform * iReg = NULL ); // \ru Изменить направление \en Change direction + + virtual size_t GetCount () const; // \ru Определить количество разбиений для прохода в операциях. \en Define the number of splittings for one passage in operations. + virtual MbNurbs * NurbsCurve ( const MbCurveIntoNurbsInfo & ) const; + + virtual MbCurve * Trimmed ( double t1, double t2, int sense ) const; + virtual MbeState DeletePart ( double t1, double t2, MbCurve *& part2 ); // \ru Удалить часть кривой между параметрами t1 и t2 \en Remove a piece of curve between t1 and t2 parameters + virtual MbeState TrimmPart ( double t1, double t2, MbCurve *& part2 ); // \ru Оставить часть кривой между параметрами t1 и t2 \en Keep a piece of curve between t1 and t2 parameters + + virtual void GetBasisPoints( MbControlData & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + virtual void GetProperties ( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties ( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + + void CheckParam ( double & t ) const; + void CalculateParam( double t, MbCartPoint & point, + MbVector & firstDer, MbVector & secondDer, MbVector & thirdDer ) const; + + const MbFunction * GetX() const { return xFunction; } + const MbFunction * GetY() const { return yFunction; } + const MbMatrix & GetMatrix() const { return transform; } + const MbPlacement & GetPlacement() const { return position; } + MbeLocalSystemType GetCoordinateType() const { return coordinateType; } + void GetSpecialParams( std::vector & params ) const; + +protected: + double ApproximationStep( double t, bool isAngle, double sag ) const; + void ConvertParamsInd( size_t componentIndex, + const std::vector & tComponent, + std::vector & tCrv ) const; + void ConvertParams( const double tCrv, + double (&tComponents) [2], + double (&proportionFactors)[2]) const; + +private: + // \ru Проверить и установить признак замкнутости. \en Check and set the flag of closedness. + void CheckClosed(); + +private: + void operator = ( const MbCharacterCurve & ); // \ru Не реализовано. \en Not implemented. + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCharacterCurve ) +}; + +IMPL_PERSISTENT_OPS( MbCharacterCurve ) + +#endif // __CUR_CHARACTER_CURVE_H diff --git a/C3d/Include/cur_character_curve3d.h b/C3d/Include/cur_character_curve3d.h index 213d8b6..84382ba 100644 --- a/C3d/Include/cur_character_curve3d.h +++ b/C3d/Include/cur_character_curve3d.h @@ -1,153 +1,160 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Кривая, координатные функции которой заданы в символьном виде. - \en Functionally defined curve. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CUR_CHARCTER_CURVE3D_H -#define __CUR_CHARCTER_CURVE3D_H - - -#include -#include -#include -#include -#include - - -class MATH_CLASS MbFunction; -class MATH_CLASS MbCharacterCurve; - - -//------------------------------------------------------------------------------ -/** \brief \ru Кривая, координатные функции которой заданы в символьном виде. - \en Functionally defined curve. \~ - \details \ru Координатные функции кривой заданы в виде пользовательских функций общего параметра t. - Каждая координата кривой описана своей функцией в виде строкового выражения. - Параметр кривой, он же параметр координатных функций, изменяется на отрезке [tmin tmax]. \n - Все пользовательские функции заданы в локальной системе координат position. - Система координат может быть декартовой, цилиндрической или сферической. - Радиус-вектор кривой в методе PointOn(double&t,MbCartPoint3D&r) описывается векторной функцией: \n - r(t) = position.origin + (position.axisX xFunction(t)) + (position.axisY yFunction(t)) + (position.axisZ zFunction(t)). - \en Functions of coordinates of the curve are given as custom functions of common parameter t. - Each coordinate of the curve is described by its own function in form of string expression. - Parameter of the curve, which is also the parameter of functions of coordinates, varies in range [tmin tmax]. \n - All the custom functions are given in the local coordinate system position. - The coordinate system can be Cartesian, cylindrical or spherical. - The radius-vector of the curve in method PointOn(double&t,MbCartPoint3D&r) is described by a vector function: \n - r(t) = position.origin + (position.axisX xFunction(t)) + (position.axisY yFunction(t)) + (position.axisZ zFunction(t)). \~ - \ingroup Curves_3D -*/ -// --- -class MATH_CLASS MbCharacterCurve3D : public MbCurve3D { -private: - MbFunction * xFunction; ///< \ru Функция координаты x. \en Function of x-coordinate. - MbFunction * yFunction; ///< \ru Функция координаты y. \en Function of y-coordinate. - MbFunction * zFunction; ///< \ru Функция координаты z. \en Function of z-coordinate - MbPlacement3D position; ///< \ru Локальная система координат, в которой заданы координатные функции. \en The local coordinate system the functions of coordinates are specified in. - MbMatrix3D transform; ///< \ru Матрица трансформации кривой. \en Transformation matrix of the curve. - double tmin; ///< \ru Минимальное значение параметра кривой. \en The minimal value of a curve parameter. - double tmax; ///< \ru Максимальное значение параметра кривой. \en The maximal value of the curve parameter. - bool closed; ///< \ru Признак замкнутости кривой. \en An attribute of curve closedness. - MbeLocalSystemType3D coordinateType; ///< \ru Тип системы координат, в которой заданы координатные функции. \en Type of coordinate system the functions of coordinates are specified in. - c3d::DoubleVector specialParams; ///< \ru Множество параметров особых точек кривой. \en Set of parameters of curve's singular points. - // \ru Буферные данные для ускорения вычислений. \en Buffer data to speed up computations. - mutable double metricLength; - mutable MbCube cube; - -public: - MbCharacterCurve3D( MbFunction & x, MbFunction & y, MbFunction & z, - MbeLocalSystemType3D cs, const MbPlacement3D & place, - double tmin_, double tmax_ ); - MbCharacterCurve3D( const MbCharacterCurve & init, const MbPlacement3D & place ); // \ru Конструктор по двумерной кривой \en Constructor by two-dimensional curve -protected: - MbCharacterCurve3D( const MbCharacterCurve3D & ); -public: - virtual ~MbCharacterCurve3D(); - -public: - VISITING_CLASS( MbCharacterCurve3D ) - - virtual MbeSpaceType IsA () const; // \ru Тип элемента \en A type of element - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Кривая есть копия этой кривой ? \en Is a curve a copy of this curve? - virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным \en Make equal - virtual void Transform ( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis - virtual void AddYourGabaritTo( MbCube & ) const; - virtual void PrepareIntegralData( const bool forced ) const; // \ru Рассчитать временные (mutable) данные объекта. \en Calculate temporary (mutable) data of an object. - - virtual void GetProperties ( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties ( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - // \ru Общие функции кривой. \en Common functions of the curve. - - virtual double GetTMin () const; // \ru Вернуть минимальное значение параметра \en Get the minimum value of parameter - virtual double GetTMax () const; // \ru Вернуть максимальное значение параметра \en Get the maximum value of parameter - virtual bool IsClosed() const; // \ru Проверка замкнутости кривой \en Check for curve closedness - // \ru Функции для работы внутри области определения кривой. \en Functions for working inside of the curve domain. \~ - virtual void PointOn ( double & t, MbCartPoint3D & ) const; // \ru Точка на кривой \en Point on the curve - virtual void FirstDer( double & t, MbVector3D & ) const; // \ru Первая производная \en The first derivative - virtual void SecondDer( double & t, MbVector3D & ) const; // \ru Вторая производная \en The second derivative - virtual void ThirdDer( double & t, MbVector3D & ) const; // \ru Третья производная по t \en The third derivative with respect to t - // \ru Вычислить значения точки и производных для заданного параметра. \en Calculate point and derivatives of object for given parameter. \~ - virtual void Explore( double & t, bool ext, - MbCartPoint3D & pnt, MbVector3D & fir, MbVector3D * sec, MbVector3D * thir ) const; - - virtual double Step ( double t, double sag ) const; ///< \ru Вычисление шага параметра по величине прогиба кривой \en Calculation of parameter step by value of sag of the curve - virtual double DeviationStep( double t, double ang ) const; ///< \ru Вычисление шага параметра по углу отклонения касательной \en Calculation of parameter by the angle of tangent deviation - - virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменить направление \en Change direction - virtual double GetMetricLength() const; // \ru Метрическая длина кривой \en Metric length of a curve - virtual double GetLengthEvaluation() const; - - virtual bool IsPlanar() const; // \ru Является ли кривая плоской \en Whether the curve is planar - virtual bool GetPlacement( MbPlacement3D & place, VERSION version = Math::DefaultMathVersion() ) const; // \ru Заполнить плейсемент, если кривая плоская \en Fill the placement if curve is planar - // \ru Дать плоскую кривую и плейсмент, если пространственная кривая плоская (после использования вызывать DeleteItem на двумерную кривую) \en Get a planar curve and placement, if the spatial curve is planar (after using the DeleteItem must be called on a three-dimensional curve) - virtual bool GetPlaneCurve ( MbCurve *& curve2d, MbPlacement3D & place, bool saveParams, VERSION version = Math::DefaultMathVersion() ) const; - virtual MbCurve * GetMap( const MbMatrix3D & into, MbRect1D * pRegion = NULL, - VERSION version = Math::DefaultMathVersion(), bool * coincParams = NULL ) const; // \ru Дать плоскую проекцию кривой \en Get a planar projection of curve - // \ru Определить количество разбиений для прохода в операциях. \en Define the number of splittings for one passage in operations. - virtual size_t GetCount() const; - virtual MbNurbs3D * NurbsCurve( const MbCurveIntoNurbsInfo & ) const; - - void CheckParam ( double & t ) const; - void CalculateParam( double t, MbCartPoint3D & point, - MbVector3D & firstDer, MbVector3D & secondDer, MbVector3D & thirdDer ) const; - - const MbFunction * GetX() const { return xFunction; } - const MbFunction * GetY() const { return yFunction; } - const MbFunction * GetZ() const { return zFunction; } - const MbMatrix3D & GetMatrix() const { return transform; } - const MbPlacement3D & GetPlacement() const { return position; } - MbeLocalSystemType3D GetCoordinateType() const { return coordinateType; } - -protected: -// void GetSpecialParams( std::vector & params ) const; - double ApproximationStep( double t, bool isAngle, double constraint ) const; - void ConvertParamsInd( size_t componentIndex, - const std::vector & tComponent, - std::vector & tCrv ) const; - void ConvertParams( const double tCrv, - double (&tComponents)[3], - double (&proportionFactors)[3]) const; - -private: - // \ru Проверить и установить признак замкнутости. \en Check and set the flag of closedness. - void CheckClosed(); - -private: - void operator = ( const MbCharacterCurve3D & ); // \ru Не реализовано. \en Not implemented. - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCharacterCurve3D ) -}; - -IMPL_PERSISTENT_OPS( MbCharacterCurve3D ) - -#endif // __CUR_CHARCTER_CURVE3D_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Кривая, координатные функции которой заданы в символьном виде. + \en Functionally defined curve. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CUR_CHARCTER_CURVE3D_H +#define __CUR_CHARCTER_CURVE3D_H + + +#include +#include +#include +#include +#include + + +class MATH_CLASS MbFunction; +class MATH_CLASS MbCharacterCurve; + + +//------------------------------------------------------------------------------ +/** \brief \ru Кривая, координатные функции которой заданы в символьном виде. + \en Functionally defined curve. \~ + \details \ru Координатные функции кривой заданы в виде пользовательских функций общего параметра t. + Каждая координата кривой описана своей функцией в виде строкового выражения. + Параметр кривой, он же параметр координатных функций, изменяется на отрезке [tmin tmax]. \n + Все пользовательские функции заданы в локальной системе координат position. + Система координат может быть декартовой, цилиндрической или сферической. + Радиус-вектор кривой в методе PointOn(double&t,MbCartPoint3D&r) описывается векторной функцией: \n + r(t) = position.origin + (position.axisX xFunction(t)) + (position.axisY yFunction(t)) + (position.axisZ zFunction(t)). + \en Functions of coordinates of the curve are given as custom functions of common parameter t. + Each coordinate of the curve is described by its own function in form of string expression. + Parameter of the curve, which is also the parameter of functions of coordinates, varies in range [tmin tmax]. \n + All the custom functions are given in the local coordinate system position. + The coordinate system can be Cartesian, cylindrical or spherical. + The radius-vector of the curve in method PointOn(double&t,MbCartPoint3D&r) is described by a vector function: \n + r(t) = position.origin + (position.axisX xFunction(t)) + (position.axisY yFunction(t)) + (position.axisZ zFunction(t)). \~ + \ingroup Curves_3D +*/ +// --- +class MATH_CLASS MbCharacterCurve3D : public MbCurve3D { +private: + MbFunction * xFunction; ///< \ru Функция координаты x. \en Function of x-coordinate. + MbFunction * yFunction; ///< \ru Функция координаты y. \en Function of y-coordinate. + MbFunction * zFunction; ///< \ru Функция координаты z. \en Function of z-coordinate + MbPlacement3D position; ///< \ru Локальная система координат, в которой заданы координатные функции. \en The local coordinate system the functions of coordinates are specified in. + MbMatrix3D transform; ///< \ru Матрица трансформации кривой. \en Transformation matrix of the curve. + double tmin; ///< \ru Минимальное значение параметра кривой. \en The minimal value of a curve parameter. + double tmax; ///< \ru Максимальное значение параметра кривой. \en The maximal value of the curve parameter. + bool closed; ///< \ru Признак замкнутости кривой. \en An attribute of curve closedness. + MbeLocalSystemType3D coordinateType; ///< \ru Тип системы координат, в которой заданы координатные функции. \en Type of coordinate system the functions of coordinates are specified in. + c3d::DoubleVector specialParams; ///< \ru Множество параметров особых точек кривой. \en Set of parameters of curve's singular points. + + // \ru Буферные данные для ускорения вычислений. \en Buffer data to speed up computations. + mutable double metricLength; + mutable MbCube cube; + +public: + /// \ru Конструктор (использует оригиналы функций). \en Constructor (uses functions originals). + MbCharacterCurve3D( MbFunction & x, + MbFunction & y, + MbFunction & z, + MbeLocalSystemType3D cs, + const MbPlacement3D & place, + double tmin, + double tmax ); + /// \ru Конструктор по двумерной кривой \en Constructor by two-dimensional curve + MbCharacterCurve3D( const MbCharacterCurve &, const MbPlacement3D & ); +protected: + MbCharacterCurve3D( const MbCharacterCurve3D & ); +public: + virtual ~MbCharacterCurve3D(); + +public: + VISITING_CLASS( MbCharacterCurve3D ) + + virtual MbeSpaceType IsA () const; // \ru Тип элемента \en A type of element + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Кривая есть копия этой кривой ? \en Is a curve a copy of this curve? + virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным \en Make equal + virtual void Transform ( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis + virtual void AddYourGabaritTo( MbCube & ) const; + virtual void PrepareIntegralData( const bool forced ) const; // \ru Рассчитать временные (mutable) данные объекта. \en Calculate temporary (mutable) data of an object. + + virtual void GetProperties ( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties ( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + // \ru Общие функции кривой. \en Common functions of the curve. + + virtual double GetTMin () const; // \ru Вернуть минимальное значение параметра \en Get the minimum value of parameter + virtual double GetTMax () const; // \ru Вернуть максимальное значение параметра \en Get the maximum value of parameter + virtual bool IsClosed() const; // \ru Проверка замкнутости кривой \en Check for curve closedness + // \ru Функции для работы внутри области определения кривой. \en Functions for working inside of the curve domain. \~ + virtual void PointOn ( double & t, MbCartPoint3D & ) const; // \ru Точка на кривой \en Point on the curve + virtual void FirstDer ( double & t, MbVector3D & ) const; // \ru Первая производная \en The first derivative + virtual void SecondDer( double & t, MbVector3D & ) const; // \ru Вторая производная \en The second derivative + virtual void ThirdDer ( double & t, MbVector3D & ) const; // \ru Третья производная по t \en The third derivative with respect to t + // \ru Вычислить значения точки и производных для заданного параметра. \en Calculate point and derivatives of object for given parameter. \~ + virtual void Explore ( double & t, bool ext, + MbCartPoint3D & pnt, MbVector3D & fir, MbVector3D * sec, MbVector3D * thir ) const; + + virtual double Step ( double t, double sag ) const; ///< \ru Вычисление шага параметра по величине прогиба кривой \en Calculation of parameter step by value of sag of the curve + virtual double DeviationStep( double t, double ang ) const; ///< \ru Вычисление шага параметра по углу отклонения касательной \en Calculation of parameter by the angle of tangent deviation + + virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменить направление \en Change direction + virtual double GetMetricLength() const; // \ru Метрическая длина кривой \en Metric length of a curve + virtual double GetLengthEvaluation() const; + + virtual bool IsPlanar() const; // \ru Является ли кривая плоской \en Whether the curve is planar + virtual bool GetPlacement( MbPlacement3D & place, VERSION version = Math::DefaultMathVersion() ) const; // \ru Заполнить плейсемент, если кривая плоская \en Fill the placement if curve is planar + // \ru Дать плоскую кривую и плейсмент, если пространственная кривая плоская (после использования вызывать DeleteItem на двумерную кривую) \en Get a planar curve and placement, if the spatial curve is planar (after using the DeleteItem must be called on a three-dimensional curve) + virtual bool GetPlaneCurve ( MbCurve *& curve2d, MbPlacement3D & place, bool saveParams, VERSION version = Math::DefaultMathVersion() ) const; + virtual MbCurve * GetMap( const MbMatrix3D & into, MbRect1D * pRegion = NULL, + VERSION version = Math::DefaultMathVersion(), bool * coincParams = NULL ) const; // \ru Дать плоскую проекцию кривой \en Get a planar projection of curve + // \ru Определить количество разбиений для прохода в операциях. \en Define the number of splittings for one passage in operations. + virtual size_t GetCount() const; + virtual MbNurbs3D * NurbsCurve( const MbCurveIntoNurbsInfo & ) const; + + void CheckParam ( double & t ) const; + void CalculateParam( double t, MbCartPoint3D & point, + MbVector3D & firstDer, MbVector3D & secondDer, MbVector3D & thirdDer ) const; + + const MbFunction * GetX() const { return xFunction; } + const MbFunction * GetY() const { return yFunction; } + const MbFunction * GetZ() const { return zFunction; } + const MbMatrix3D & GetMatrix() const { return transform; } + const MbPlacement3D & GetPlacement() const { return position; } + MbeLocalSystemType3D GetCoordinateType() const { return coordinateType; } + +protected: +// void GetSpecialParams( std::vector & params ) const; + double ApproximationStep( double t, bool isAngle, double constraint ) const; + void ConvertParamsInd( size_t componentIndex, + const std::vector & tComponent, + std::vector & tCrv ) const; + void ConvertParams( const double tCrv, + double (&tComponents)[3], + double (&proportionFactors)[3]) const; + +private: + // \ru Проверить и установить признак замкнутости. \en Check and set the flag of closedness. + void CheckClosed(); + +private: + void operator = ( const MbCharacterCurve3D & ); // \ru Не реализовано. \en Not implemented. + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCharacterCurve3D ) +}; + +IMPL_PERSISTENT_OPS( MbCharacterCurve3D ) + +#endif // __CUR_CHARCTER_CURVE3D_H diff --git a/C3d/Include/cur_cone_spiral.h b/C3d/Include/cur_cone_spiral.h index cc48305..dc5b0c2 100644 --- a/C3d/Include/cur_cone_spiral.h +++ b/C3d/Include/cur_cone_spiral.h @@ -20,7 +20,7 @@ \details \ru Плоская, коническая или цилиндрическая спираль. Ось спирали направлена вдоль оси Z локальной системы координат. \n Параметр кривой отсчитывается от оси position.axisX локальной системы координат. - Радиус-вектор кривой в методе PointOn(double&t,MbCartPoint3D&r) описывается векторной функцией: \n + Радиус-вектор кривой в методе PointOn(double&t,MbCartPoint3D&r) описывается векторной функцией: \n для плоской спирали: \n r(t) = position.origin + (position.axisX rad cos(t)) + (position.axisY rad sin(t)), где rad = radius + (stepd2pi t), если tgAlpha = pi/2 или rad = radius - (stepd2pi t), если tgAlpha = -pi/2; @@ -32,7 +32,7 @@ \en Planar, Conical or cylindrical spiral. A spiral axis is directed along the Z-axis of the local coordinate system. \n The curve parameter is measured from the axis "position.axisX" of the local coordinate system. - The radius-vector of curve in the method PointOn(double&t,MbCartPoint3D&r) is described by a vector function: \n + The radius-vector of curve in the method PointOn(double&t,MbCartPoint3D&r) is described by a vector function: \n for planar spiral: \n r(t) = position.origin + (position.axisX rad cos(t)) + (position.axisY rad sin(t)), where rad = radius + (stepd2pi t), if tgAlpha = pi/2 or rad = radius - (stepd2pi t), if tgAlpha = -pi/2; @@ -44,7 +44,7 @@ \ingroup Curves_3D */ // --- -class MATH_CLASS MbConeSpiral: public MbSpiral +class MATH_CLASS MbConeSpiral : public MbSpiral { public: enum ConeSpiralType { @@ -59,43 +59,146 @@ protected: double tgAlpha; ///< \ru Тангенс угла полуконуса. \en Tangent of the semicone angle. double stepd2pi; ///< \ru Шаг спирали, деленный на 2*pi. \en Spiral step divided by 2*pi. -public: - /// \ru Цилиндрическая спираль по трем точкам и шагу. \en Cylindrical spiral by three points and step. +protected: + /// \ru Цилиндрическая спираль по трем разным точкам и ненулевому шагу. \en Cylindrical spiral by three different points and non-zero step. MbConeSpiral( const MbCartPoint3D & p0, const MbCartPoint3D & p1, const MbCartPoint3D & p2, double st, bool left = false ); - /// \ru Коническая спираль по основанию, радиусам двух оснований, и высоте с шагом. \en Conical spiral by the base, radii of the two bases and height with step. + /// \ru Коническая спираль по основанию, радиусам двух оснований и высоте с шагом. \en Conical spiral by the base, radii of the two bases and height with step. MbConeSpiral( const MbPlacement3D & pl, double radius1, double radius2, double height, double st ); - /// \ru Коническая спираль , радиусу, углу уклона, высоте с шагом, и основанию. \en Conical spiral by radius, inclination angle, height with step and base. + /// \ru Коническая спираль по радиусу, углу уклона, высоте с шагом, и основанию. \en Conical spiral by radius, inclination angle, height with step and base. MbConeSpiral( double radius, double angle, double height, double st, const MbPlacement3D & pl ); /// \ru Цилиндрическая спираль по радиусу основания, шагу, основанию, и двум параметрам. \en Cylindrical spiral by bottom radius, step, base and two parameters. MbConeSpiral( double radius, double st, const MbPlacement3D & pl, double t1, double t2 ); - /// \ru Коническая спираль по радиусу основания, высоте, тангенсу угла наклона, основанию, и параметрам. \en Conical spiral by bottom radius, height, tangent of inclination angle, base and parameters. + /// \ru Коническая спираль по радиусу основания, ненулевой высоте, тангенсу угла наклона, основанию, и параметрам. \en Conical spiral by bottom radius, non-zero height, tangent of inclination angle, base and parameters. MbConeSpiral( double r0, double h, double tgAH, const MbPlacement3D & pl, double u1, double v1, double u2, double v2 ); protected: /// \ru Конструктор копирования. \en Copy-constructor. - MbConeSpiral( const MbConeSpiral & init ); - + MbConeSpiral( const MbConeSpiral & ); public: /// \ru Деструктор. \en Destructor. virtual ~MbConeSpiral(); +public: + /** \brief \ru Создать цилиндрическую спираль по трем разным точкам и ненулевому шагу. + \en Create cylindrical spiral by three different points and non-zero step. \~ + \details \ru Создать цилиндрическую спираль по трем разным точкам и ненулевому шагу. \n + \en Create cylindrical spiral by three different points and non-zero step. \n \~ + \param[in] p0 - \ru Начало локальной системы координат. + \en Origin point of the local coordinate system. \~ + \param[in] p1 - \ru Точка в направлении оси Z, определяющая высоту спирали. + \en Point in the direction of the Z axis determining the height of the spiral. \~ + \param[in] p2 - \ru Точка в направлении оси X. + \en Point in the direction of the X axis. \~ + \param[in] st - \ru Шаг между витками спирали. + \en Step between coils of spiral. \~ + \param[in] left - \ru Признак левой системы координат. + \en Flag of the left coordinate system. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ + */ + static MbConeSpiral * Create( const MbCartPoint3D & p0, const MbCartPoint3D & p1, const MbCartPoint3D & p2, double st, bool left = false ); + /** \brief \ru Создать коническую спираль по основанию, радиусам двух оснований и высоте с шагом. + \en Create conical spiral by the base, radii of the two bases and height with step. \~ + \details \ru Создать коническую спираль по основанию, радиусам двух оснований и высоте с ненулевым шагом. \n + \en Create conical spiral by the base, radii of the two bases and height with non-zero step. \n \~ + \param[in] pl - \ru Локальная система координат. + \en A local coordinate system. \~ + \param[in] radius1 - \ru Начальный радиус основания спирали. + \en Starting radius of spiral. \~ + \param[in] radius2 - \ru Конечный радиус основания спирали. + \en Ending radius of spiral. \~ + \param[in] height - \ru Высота спирали. + \en Height of spiral. \~ + \param[in] st - \ru Шаг между витками спирали. + \en Step between coils of spiral. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ + */ + static MbConeSpiral * Create( const MbPlacement3D & pl, double radius1, double radius2, double height, double st ); + + /** \brief \ru Создать коническую спираль по радиусу, углу уклона, высоте с шагом, и основанию. + \en Create conical spiral by radius, inclination angle, height, step and base. \~ + \details \ru Создать коническую спираль по радиусу, углу уклона, высоте, шагу между витками и основанию (локальной системе координат). \n + \en Create conical spiral by radius, inclination angle, height, step between coils of spiral and base (locale coordinate system). \n \~ + \param[in] radius - \ru Радиус основания спирали. + \en Radius of spiral. \~ + \param[in] angle - \ru Угол уклона. + \en Inclination angle. \~ + \param[in] height - \ru Высота спирали. + \en Height of spiral. \~ + \param[in] st - \ru Шаг между витками спирали. + \en Step between coils of spiral. \~ + \param[in] pl - \ru Локальная система координат. + \en A local coordinate system. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ + */ + static MbConeSpiral * Create( double radius, double angle, double height, double st, const MbPlacement3D & pl ); + /** \brief \ru Создать цилиндрическую спираль по радиусу основания, шагу, основанию, и двум параметрам. + \en Create cylindrical spiral by bottom radius, step, base and two parameters. \~ + \details \ru Создать цилиндрическую спираль по радиусу основания, шагу, основанию, и двум параметрам. \n + \en Create cylindrical spiral by bottom radius, step, base and starting and ending parameters. \n \~ + \param[in] radius - \ru Радиус основания спирали. + \en Radius of spiral. \~ + \param[in] st - \ru Шаг между витками спирали. + \en Step between coils of spiral. \~ + \param[in] pl - \ru Локальная система координат. + \en A local coordinate system. \~ + \param[in] t1 - \ru Начальный параметр. + \en Starting parameter. \~ + \param[in] t2 - \ru Конечный параметр. + \en Ending parameter. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ + */ + static MbConeSpiral * Create( double radius, double st, const MbPlacement3D & pl, double t1, double t2 ); + + /** \brief \ru Создать коническую спираль по радиусу основания, ненулевой высоте, тангенсу угла наклона, основанию, и параметрам. + \en Create conical spiral by bottom radius, non-zero height, tangent of inclination angle, base and parameters. \~ + \details \ru Создать коническую спираль по отрезку на конусе : радиусу основания, ненулевой высоте, тангенсу угла наклона, основанию, и параметрам. + Конструктор предназначен для создания конической спирали по отрезку на конической поверхности. \n + \en Create conical spiral by bottom radius, non-zero height, tangent of inclination angle, base and parameters. + The constructor is used internally to create a conical spiral representing a segment on a conical surface. \n \~ + \param[in] r0 - \ru Радиус основания конуса. + \en Radius of spiral. \~ + \param[in] h - \ru Высота спирали. + \en Height of spiral. \~ + \param[in] tgAH - \ru Вспомогательная величина h*tan(angle) где angle - угол между осью Z и боковой образующей. + \en Auxiliary value h*tan(angle) where angle is angle between Z-axis and lateral generatrix. \~ + \param[in] pl - \ru Локальная система координат. + \en A local coordinate system. \~ + \param[in] u1 - \ru Начальный параметр по U. + \en Starting parameter by U. \~ + \param[in] v1 - \ru Начальный параметр по V. + \en Starting parameter by V. \~ + \param[in] u2 - \ru Конечный параметр по U. + \en Ending parameter by U. \~ + \param[in] v2 - \ru Конечный параметр по V. + \en Ending parameter by V. \~ + \warning \ru Для внутреннего использования. + \en For internal use only. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ + */ + static MbConeSpiral * Create( double r0, double h, double tgAH, const MbPlacement3D & pl, double u1, double v1, double u2, double v2 ); + public: VISITING_CLASS( MbConeSpiral ); /// \ru Инициализация конической спирали по конической спирали. \en Initialization of conical spiral by conical spiral. - void Init( const MbConeSpiral & init ); + void Init( const MbConeSpiral & ); /// \ru Инициализация цилиндрической спирали по основанию. \en Initialization of cylindrical spiral by base. - void Init( const MbPlacement3D & place ); + bool Init( const MbPlacement3D &, bool resetToCylindrical ); /// \ru Инициализация конической спирали по радиусам оснований, высоте и шагу. \en Initialization of conical spiral by bottom radii, height and step. - void Init( double radius1, double radius2, double height, double st ); + bool Init( double radius1, double radius2, double height, double st ); /// \ru Инициализация конической спирали по основанию, радиусам оснований, и высоте с шагом. \en Initialization of conical spiral by the base, radii of bases and height with step. - void Init( const MbPlacement3D & place, double radius1, double radius2, double height, double st ); + bool Init( const MbPlacement3D & place, double radius1, double radius2, double height, double st ); public: // \ru Общие функции математического объекта. \en The common functions of the mathematical object. virtual MbeSpaceType IsA() const; // \ru Получить тип. \en Get a type. virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Создать копию. \en Create a copy. - virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; + virtual bool IsSame( const MbSpaceItem &, double accuracy = LENGTH_EPSILON ) const; virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта. \en Set properties of the object. @@ -125,7 +228,7 @@ public: virtual MbCurve3D * Trimmed( double t1, double t2, int sense ) const; // \ru Создание усеченной кривой. \en Creation of a trimmed curve. - virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменить направление. \en Change the direction. + virtual void Inverse( MbRegTransform * = NULL ); // \ru Изменить направление. \en Change the direction. virtual MbNurbs3D * NurbsCurve( const MbCurveIntoNurbsInfo & ) const; virtual double CalculateLength( double t1, double t2 ) const; @@ -134,7 +237,7 @@ public: virtual bool NearPointProjection( const MbCartPoint3D & p, double & t, bool ext, MbRect1D * tRange = NULL ) const; // \ru Частные функции спирали. \en Special functions for spiral. - virtual void SetStep( double s ); // \ru Изменить шаг. \en Change the step. + virtual bool SetStep( double s ); // \ru Изменить шаг. \en Change the step. virtual double GetSpiralRadius( double t ) const; // \ru Выдать радиус физический спирали. \en Get a radius of the physical spiral. virtual void GetPointsByEvenLengthDelta( size_t n, std::vector & pnts ) const; // \ru Выдать n точек кривой с равными интервалами по длине дуги. \en Get n points of curve with equal intervals along the length of the arc. @@ -153,28 +256,34 @@ public: /// \ru Узнать тип конической спирали \en Learn the type of conical spiral. ConeSpiralType GetType() const { return type; } private: + /// \ru Явяется ли спираль конической (в том числе плоской). \en Whether the spiral is conic (including flat). bool IsConeType() const { return ::fabs( tgAlpha ) > LENGTH_EPSILON; } - void CalConeTMax(); // \ru Усеченное значение tmax для случая закручивающихся в точку плоской или конической спиралей. \en Trimmed value tmax for the case of planar or conical spirals trailing to the point. - double GetR( double t ) const; // \ru Текущий радиус. \en The current radius. - double GetRDerive( double t ) const; // \ru Производная текущего радиуса. \en The derivative of the current radius. - // \ru Ближайшая проекция точки на плоскую спираль. \en The nearest point projection on the planar spiral. + /// \ru Усеченное значение tmax для случая закручивающихся в точку плоской или конической спиралей. \en Trimmed value tmax for the case of planar or conical spirals trailing to the point. + void CalConeTMax(); + /// \ru Текущий радиус. \en The current radius. + double GetR( double t ) const; + /// \ru Производная текущего радиуса. \en The derivative of the current radius. + double GetRDerive( double t ) const; + /// \ru Ближайшая проекция точки на плоскую спираль. \en The nearest point projection on the planar spiral. bool PlaneProjection( const MbCartPoint3D & pSpace, double & tProj, bool ext, MbRect1D * tRange ) const; - // \ru Ближайшая проекция точки на цилиндрическую спираль. \en The nearest point projection on the cylindrical spiral. + /// \ru Ближайшая проекция точки на цилиндрическую спираль. \en The nearest point projection on the cylindrical spiral. bool CylindricalProjection( const MbCartPoint3D & pSpace, double & tProj, bool ext, MbRect1D * tRange ) const; private: // \ru Объявление (перегрузка) оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en Declaration (overload) of the assignment operator without its implementation, to prevent the default assignment. MbConeSpiral & operator = ( const MbConeSpiral & ); - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbConeSpiral ) +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbConeSpiral ) }; IMPL_PERSISTENT_OPS( MbConeSpiral ) + //------------------------------------------------------------------------------ // \ru Дать направление движения по спирали \en Give the direction of motion on a spiral // --- -inline void MbConeSpiral::GetSpiralDir( MbVector3D & dir ) const +inline +void MbConeSpiral::GetSpiralDir( MbVector3D & dir ) const { dir.Init( position.GetAxisZ() ); @@ -193,7 +302,8 @@ inline void MbConeSpiral::GetSpiralDir( MbVector3D & dir ) const //------------------------------------------------------------------------------ // \ru Внутренний радиус \en Inner radius // --- -inline double MbConeSpiral::GetR( double t ) const +inline +double MbConeSpiral::GetR( double t ) const { //C3D_ASSERT( position.IsIsotropic() || ::fabs(tgAlpha) < LENGTH_EPSILON ); // \ru Проверить корректность работы \en Check the correctness of working switch ( type ) { // t = [0; 2*pi] @@ -207,7 +317,8 @@ inline double MbConeSpiral::GetR( double t ) const //------------------------------------------------------------------------------ // \ru Производная внутреннего радиуса \en Derivative of inner radius // --- -inline double MbConeSpiral::GetRDerive( double /*t */) const +inline +double MbConeSpiral::GetRDerive( double /*t */) const { switch ( type ) { // t = [0; 2*pi] case cst_Plane : return tgAlpha > 0.0 ? stepd2pi : -stepd2pi; diff --git a/C3d/Include/cur_contour.h b/C3d/Include/cur_contour.h index 41b5a99..dfd947a 100644 --- a/C3d/Include/cur_contour.h +++ b/C3d/Include/cur_contour.h @@ -69,7 +69,7 @@ typedef std::vector ConstPlaneContoursSPtrVector; \ingroup Curves_2D */ // --- -class MATH_CLASS MbContour : public MbCurve, public MbNestSyncItem { +class MATH_CLASS MbContour : public MbCurve { protected : RPArray segments; ///< \ru Множество сегментов контура. \en An array of contour segments. bool closed; ///< \ru Признак замкнутости кривой. \en An Attribute of curve closedness. @@ -84,7 +84,7 @@ public : MbContour(); /// \ru Конструктор по набору кривых. \en Constructor by curves vector. template - MbContour( const Curves &, bool same ); + MbContour( const Curves &, bool sameCurves ); protected : explicit MbContour( const MbContour *, MbRegDuplicate * ); ///< \ru Конструктор копирования. \en Copy constructor. public : @@ -102,7 +102,7 @@ public: virtual MbPlaneItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element. virtual bool IsSimilar ( const MbPlaneItem & ) const; // \ru Являются ли элементы подобными \en Whether the elements are similar. virtual bool SetEqual( const MbPlaneItem & ); // \ru Сделать элементы равными \en Make equal elements. - virtual bool IsSame( const MbPlaneItem &, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли кривая curve копией данной кривой ? \en Whether the curve "curve" is a copy of a given curve? + virtual bool IsSame( const MbPlaneItem &, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли кривая curve копией данной кривой ? \en Whether the curve "curve" is a copy of a given curve? virtual void Transform( const MbMatrix &, MbRegTransform * ireg = NULL, const MbSurface * newSurface = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix. virtual void Move( const MbVector &, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Сдвиг \en Translation. virtual void Rotate( const MbCartPoint &, const MbDirection & angle, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Поворот \en Rotation. @@ -136,8 +136,12 @@ public: const MbRect & GetGabarit() const { if ( rect.IsEmpty() ) CalculateGabarit( rect ); return rect; } const MbRect & GetCube() const { if ( rect.IsEmpty() ) CalculateGabarit( rect ); return rect; } + + /// \ru Сбросить рассчитанный габарит контура. \en Reset the calculated contour bounding box. void SetDirtyGabarit() const { rect.SetEmpty(); } + /// \ru Копировать габарит контура в контур (использовать только для передачи габарита в копию контура). \en Copy contour bounding box to contour copy (use only to transfer into contour copy). void CopyGabarit( const MbContour & c ) { rect = c.rect; } + /// \ru Пуст ли габарит контура? \en Is the contour bounding box empty? bool IsGabaritEmpty() const { return rect.IsEmpty(); } virtual double DistanceToPoint( const MbCartPoint & ) const; // \ru Расстояние до точки \en Distance to a point. @@ -219,6 +223,7 @@ public: double GetParamLength() const { return paramLength; } double CalculateParamLength(); // \ru Посчитать параметрическую длину \en Calculate the parametric length + /// \ru Вычисление площади контура, если контур замкнут. \en Calculation of contour area if contour is closed. double GetArea( double sag = Math::deviateSag ) const { sag = ::fabs(sag); @@ -228,10 +233,10 @@ public: } virtual MbNurbs * NurbsCurve( const MbCurveIntoNurbsInfo & ) const; - virtual MbContour * NurbsContour() const; + virtual MbContour * NurbsContour() const; - void SetClosed(); ///< \ru Установить признак замкнутости контура. \en Set the closedness attribute of contour. - void CheckClosed( double eps ); ///< \ru Установить признак замкнутости контура. \en Set the closedness attribute of contour. + void SetClosed(); ///< \ru Установить признак замкнутости контура. \en Set the closedness attribute of contour. + void CheckClosed( double closedEps ); ///< \ru Установить признак замкнутости контура. \en Set the closedness attribute of contour. void InitClosed( bool c ) { closed = c; } ///< \ru Установить признак замкнутости контура. \en Set the closedness attribute of contour. /** \brief \ru Проверить замкнутость и непрерывность точек контура. @@ -249,7 +254,7 @@ public: */ bool IsClosedContinuousC0( double eps = 5.0 * PARAM_NEAR ) const; - void CloseByLineSeg( bool calcData ); ///< \ru Замкнуть контур отрезком. \en Close the contour by segment. + void CloseByLineSeg( bool calcInternalData ); ///< \ru Замкнуть контур отрезком. \en Close the contour by segment. // \ru Посчитать метрическую длину разомкнутой кривой с заданной точностью \en Calculate the metric length of unclosed curve within the given tolerance virtual double CalculateLength( double t1, double t2 ) const; @@ -265,8 +270,20 @@ public: rect.SetEmpty(); areaSign.first = -1.0; } - ptrdiff_t FindSegment( double & t, double & tSeg ) const; ///< \ru Нахождение сегмента контура. \en Finding of a contour segment. - size_t GetSegmentsCount() const { return segments.size(); } ///< \ru Выдать количество сегментов контура. \en Get the number of contour segments. + /** \brief \ru Найти сегмент контура. + \en Find a contour segment. \~ + \details \ru Найти сегмент контура по параметру контура. \n + \en Find a contour segment by parameter on contour. \n \~ + \param[in,out] t - \ru Параметр контура. + \en Contour parameter. \~ + \param[out] tSeg - \ru Параметр сегмента контура. + \en Contour segment parameter. \~ + \return \ru Возвращает номер сегмента в случае успешного выполнения или -1. + \en Returns the segment number in case of successful execution or -1. \~ + */ + ptrdiff_t FindSegment( double & t, double & tSeg ) const; + + size_t GetSegmentsCount() const { return segments.size(); } ///< \ru Выдать количество сегментов контура. \en Get the number of contour segments. const MbCurve * GetSegment( size_t ind ) const { return segments[ind]; } ///< \ru Выдать сегмент контура по индексу. \en Get contour segment by the index. MbCurve * SetSegment( size_t ind ) { return segments[ind]; } ///< \ru Выдать сегмент контура по индексу. \en Get contour segment by the index. @@ -281,7 +298,19 @@ public: virtual double PointProjection( const MbCartPoint & ) const; // \ru Проекция точки на кривую \en Point projection on the curve virtual bool NearPointProjection( const MbCartPoint &, double xEpsilon, double yEpsilon, double & t, bool ext, MbRect1D * tRange = NULL ) const; // \ru Проекция точки на кривую или её продолжение в области поиска проекции \en Point projection on the curve or its extension in the projection search area - double DistanceToBorder( const MbCartPoint & ) const; ///< \ru Параметрическое расстояние до ближайшей границы \en Parametric distance to the nearest boundary + + /** \brief \ru Параметрическое расстояние до ближайшей границы. + \en Parametric distance to the nearest boundary. + \details \ru Параметрическое расстояние до ближайшей границы. \n + \en Parametric distance to the nearest boundary. \n \~ + \param[in] pnt - \ru Тестируемая точка. + \en A testing point. \~ + \param[in] eps - \ru Точность. + \en Accuracy. \~ + \return \ru Расстояние до ближайшей границы. + \en Distance to the nearest boundary. \~ + */ + double DistanceToBorder( const MbCartPoint & pnt, double eps = Math::paramRegion ) const; void Trimm( double t1, double t2 ); ///< \ru Выделить часть контура. \en Trim a part of the contour. @@ -383,12 +412,15 @@ public: /// \ru Установить направление обхода контура. \en Set the traverse direction of the contour. void SetSense( int sense ); - // \ru Изменить направление обхода контура \en Change the traverse direction of the contour + // \ru Изменить направление обхода контура \en Change the traverse direction of the contour virtual void Inverse( MbRegTransform * = NULL ); + // \ru Согласовать параметризацию сегментов, если до инвертации она была согласованной. \en Agree on segment parameterization, if it was consistent before inversion. + bool NormalizeReparametrization(); virtual size_t GetCount() const; // \ru Количество разбиений для прохода в операциях \en The number of partitions for passage in the operations // \ru Выдать характерную точку ограниченной кривой если она ближе чем dmax \en Get characteristic point of bounded curve if it is closer than dmax virtual bool DistanceToPointIfLess( const MbCartPoint & toP, double & d ) const; // \ru Расстояние до точки, если оно меньше d \en Distance to the point if it is less than d virtual bool GetSpecificPoint( const MbCartPoint & from, double & dmax, MbCartPoint & pnt ) const; + /// \ru Выдать среднюю точку сегмента контура. \en Get a mid-point of the contour segment. bool GetSegmentMiddlePoint( const MbCartPoint & from, MbCartPoint & midPoint ) const; /// \ru Выдать линейный сегмент контура. \en Get the linear segment of contour. @@ -401,9 +433,9 @@ public: virtual bool GetWeightCentre( MbCartPoint & ) const; // \ru Выдать центр тяжести контура \en Get gravity center of contour virtual bool GetCentre ( MbCartPoint & ) const; // \ru Выдать центр кривой \en Get the center of curve - /// \ru Найти ближайший к точке узел контура \en Find the nearest node of contour to point + /// \ru Найти ближайший к точке узел контура. \en Find the nearest node of contour to point. ptrdiff_t FindNearestNode( const MbCartPoint & to ) const; - /// \ru Найти ближайший к точке сегмент контура \en Find the nearest segment of contour to point + /// \ru Найти ближайший к точке сегмент контура. \en Find the nearest segment of contour to point. ptrdiff_t FindNearestSegment( const MbCartPoint & to ) const; // \ru Определение особых точек офсетной кривой \en Determination of singular points of the offset curve @@ -419,9 +451,9 @@ public: virtual void SetBasisPoints( const MbControlData & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. /** \brief \ru Непрерывна ли первая производная кривой по длине и направлению? - \en Have the first derivative of the curve the continuous length and direction? + \en Have the first derivative of the curve the continuous length and direction? \details \ru Отсутствуют ли разрывы производной по длине и направлению в стыках сегментов контура? \n - \en Are absent any discontinuities of the derivative at length or at direction in the junction of path segments? \n \~ + \en Are absent any discontinuities of the derivative at length or at direction in the junction of path segments? \n \~ \param[out] contLength - \ru Непрерывность длины (да/нет). \en The length is continuous (true/false). \~ \param[out] contDirect - \ru Непрерывность направления (да/нет). @@ -432,13 +464,15 @@ public: virtual bool IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = NULL, double epsilon = EPSILON ) const; /** \brief \ru Устранить разрывы производных по длине в стыках сегментов. - \en Eliminate the discontinuities of the derivatives of the length of the joints of the segments. + \en Eliminate the discontinuities of the derivatives of the length of the joints of the segments. \details \ru Устранить разрывы производных по длине в стыках сегментов. \n - \en Eliminate the discontinuities of the derivatives of the length of the joints of the segments. \n \~ + \en Eliminate the discontinuities of the derivatives of the length of the joints of the segments. \n \~ \param[in] epsilon - \ru Погрешность вычисления. - \en The accuracy of the calculation. \~ + \en The accuracy of the calculation. \~ + \param[in] version - \ru Версия математики. + \en Math version. \~ */ - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); /// \ru Получить границы участков кривой, на которых сохраняется непрерывность кривизны. /// \en Get the boundaries of the sections of the curve on which the continuity of curvature is preserved. \~ @@ -449,21 +483,73 @@ public: \en \name Function for working with segments of contour \{ */ - bool Init( List & curves ); ///< \ru Инициализация по списку кривых. \en Initialization by list of curves. - void Init( const MbContour & other ); ///< \ru Инициализация по контуру. \en Initialization by a contour. + bool Init( List & ); ///< \ru Инициализация по списку кривых. \en Initialization by list of curves. + void Init( const MbContour & ); ///< \ru Инициализация по контуру. \en Initialization by a contour. + + /** \brief \ru Инициализация по массиву кривых. + \en Initialization by array of curves. \~ + \details \ru Инициализация по массиву кривых. \n + \en Initialization by array of curves. \n \~ + \param[in] curves - \ru Кривые. + \en Curves. \~ + \param[in] sameCurves - \ru Использовать оригиналы кривых (true) или их копии (false). + \en Use original curves (true) or copies thereof (false). \~ + \return \ru Возвращает true, если кривые были добавлена. + \en Returns true if curves were added. \~ + */ template - bool Init( Curves & curves, bool same ); ///< \ru Инициализация по массиву кривых. \en Initialization by array of curves. + bool Init( Curves & curves, bool sameCurves ); + /// \ru Инициализация по массиву точек (замкнутый контур). \en Initialization by array of points (closed contour). template - bool InitByPoints( const Points & ); ///< \ru Инициализация по массиву точек (замкнутый контур). \en Initialization by array of points (closed contour). + bool InitByPoints( const Points & ); + bool InitAsRectangle( const MbCartPoint * ); ///< \ru Инициализация как прямоугольника ( приходит 4 точки ) \en Initialization as rectangle (4 points are given). - bool InitByRectangle( const MbRect & ); ///< \ru Инициализация по прямоугольнику габарита. \en Initialization by rectangle of bounding box. + bool InitByRectangle( const MbRect & ); ///< \ru Инициализация по прямоугольнику габарита. \en Initialization by rectangle of bounding box. bool AddSegment ( MbCurve * ); ///< \ru Добавить сегмент в контур. \en Add a segment to the contour. bool AddSegmentOrDeleteCurve( MbCurve * ); ///< \ru Добавить кривую как сегмент или удалить ее. \en Add a curve as segment or remove its. + + /** \brief \ru Добавить (усеченную) копию сегмента в конец контура. + \en Add a (truncated) segment copy to the end of the contour. \~ + \details \ru Добавить (усеченную) копию сегмента в конец контура. \n + \en Add a (truncated) segment copy to the end of the contour. \n \~ + \param[in] pBasis- \ru Исходная кривая. + \en Initial curve. \~ + \param[in] t1 - \ru Начальный параметр усечения. + \en Truncation starting parameter. \~ + \param[in] t2 - \ru Конечный параметр усечения. + \en Truncation ending parameter. \~ + \param[in] sense - \ru Направление усеченной кривой относительно исходной. \n + sense = 1 - направление кривой сохраняется. + sense = -1 - направление кривой меняется на обратное. + \en Direction of a trimmed curve in relation to an initial curve. + sense = 1 - direction does not change. + sense = -1 - direction changes to the opposite value. \~ + \return \ru Возвращает в случае успешного выполнения ненулевой указатель на добавленную кривую. + \en Returns, if successful, a non-zero pointer to the added curve. \~ + */ MbCurve * AddSegment( const MbCurve * pBasis, double t1, double t2, int sense = 1 ); + bool AddAtSegment ( MbCurve * newSegment, size_t index ); ///< \ru Вставить сегмент перед сегментом контура с индексом index. \en Insert a segment before the contour segment with the index "index". bool AddAfterSegment( MbCurve * newSegment, size_t index ); ///< \ru Вставить сегмент после сегмента контура с индексом index. \en Insert a segment after the contour segment with the index "index". - /// \ru Функция добавления новой кривой с управляемой проверкой. \en Function for addition of a new curve with checking. + + /** \brief \ru Добавить новый элемент в начало или конец контура. + \en Add the new element to the beginning or end of contour. \~ + \details \ru Добавить новый элемент в начало или конец контура. \n + \en Add the new element to the beginning or end of contour. \n \~ + \param[in] curve - \ru Добавляемая кривая. + \en Added curve. \~ + \param[in] absEps - \ru Точность проверки совпадения концов кривых (1e-8 - 1e-4). + \en Accuracy of verification of curve end coincidence (1e-8 - 1e-4). \~ + \param[in] toEndOnly - \ru Добавлять кривую только в конец контура. + \en Add the curve only at the end of the contour. \~ + \param[in] checkSame - \ru Проверять наличие такой же (добавляемой) кривой в контуре. + \en Check a presence of the same curve in the contour. \~ + \param[in] version - \ru Версия. + \en Version. \~ + \return \ru Возвращает true, если кривая была добавлена. + \en Returns true if the curve was added. \~ + */ bool AddCurveWithRuledCheck( MbCurve & newCur, double absEps, bool toEndOnly = false, bool checkSame = true, VERSION version = Math::DefaultMathVersion() ); @@ -495,7 +581,7 @@ public: void GetPolygon( double sag, SArray & poly, double eps ) const; ///< \ru Дать точки полигона. \en Get points of polygon. bool IsAnyCurvilinear() const; ///< \ru Есть ли в контуре криволинейный сегмент. \en Whether the contour has a curved segment. - bool IsSameSegments( const MbContour & cntr ) const; ///< \ru Содержат ли контура идентичные сегменты. \en Whether contours contains identical segments. + bool IsSameSegments( const MbContour &, double accuracy = PARAM_PRECISION ) const; ///< \ru Содержат ли контура идентичные сегменты. \en Whether contours contains identical segments. bool GetBegSegmentPoint( size_t i, MbCartPoint & ) const; ///< \ru Дать начальную точку i-го сегмента. \en Get the start point of i-th segment. bool GetEndSegmentPoint( size_t i, MbCartPoint & ) const; ///< \ru Дать конечную точку i-го сегмента. \en Get the end point of i-th segment. diff --git a/C3d/Include/cur_contour3d.h b/C3d/Include/cur_contour3d.h index a271260..49d9bfa 100644 --- a/C3d/Include/cur_contour3d.h +++ b/C3d/Include/cur_contour3d.h @@ -171,7 +171,10 @@ public: // \ru Преобразование в NURBS кривую \en Transform to NURBS-curve virtual MbNurbs3D * NurbsCurve( const MbCurveIntoNurbsInfo & ) const; virtual MbCurve3D * Trimmed( double t1, double t2, int sense ) const; // \ru Создание усеченной кривой \en Creation of a trimmed curve - virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменить направление \en Change direction + // \ru Изменить направление \en Change direction + virtual void Inverse( MbRegTransform * iReg = NULL ); + // \ru Согласовать параметризацию сегментов, если до инвертации она была согласованной. \en Agree on segment parameterization, if it was consistent before inversion. + bool NormalizeReparametrization(); /// \ru Подобные ли кривые для объединения (слива). \en Whether the curves to union (joining) are similar. virtual bool IsSimilarToCurve( const MbCurve3D & other, double precision = METRIC_PRECISION ) const; @@ -231,11 +234,13 @@ public: \en Eliminate the discontinuities of the derivatives of the length of the joints of the segments. \n \~ \param[in] epsilon - \ru Погрешность вычисления. \en The accuracy of the calculation. \~ + \param[in] version - \ru Версия математики. + \en Math version. \~ */ - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); /// \ru Найти все особые точки функции кривизны кривой. \en Find all the special points of the curvature function of the curve. - virtual void GetCurvatureSpecialPoints( std::vector & points ) const; + virtual void GetCurvatureSpecialPoints( std::vector & points ) const; /** \} */ /** \ru \name Функции работы с сегментами контура @@ -245,25 +250,62 @@ public: /// \ru Инициализация по набору кривых (sameCurves - кривые или их копии). \en Initialize by curves (sameCurves - curves or their copies). template bool Init( const CurvesVector & initSegments, bool sameCurves, bool cls ); - /// \ru Инициализация по набору кривых (замкнутый контур). \en Initialize by curves (closed contour). + /// \ru Инициализация по набору точек (замкнутый контур). \en Initialize by points (closed contour). template bool Init( const PointsVector & points ); - ptrdiff_t FindSegment( double & t, double & tSeg ) const; ///< \ru Нахождение сегмента контура. \en Finding of a contour segment. + /** \brief \ru Найти сегмент контура. + \en Find a contour segment. \~ + \details \ru Найти сегмент контура по параметру контура. \n + \en Find a contour segment by parameter on contour. \n \~ + \param[in,out] t - \ru Параметр контура. + \en Contour parameter. \~ + \param[out] tSeg - \ru Параметр сегмента контура. + \en Contour segment parameter. \~ + \return \ru Возвращает номер сегмента в случае успешного выполнения или -1. + \en Returns the segment number in case of successful execution or -1. \~ + */ + ptrdiff_t FindSegment( double & t, double & tSeg ) const; + size_t GetSegmentsCount() const { return segments.size(); } ///< \ru Выдать количество сегментов контура. \en Get the number of contour segments. template void GetSegments( CurvesVector & curves ) const; ///< \ru Получить кривые контура. \en Get contour segments. + void DetachSegments(); ///< \ru Отцепить все сегменты контура. \en Detach all segments of contour. void DeleteSegments(); ///< \ru Отсоединить используемые сегменты и удалить остальные. \en Delete used segments and remove other segments. + void DeleteSegment( size_t ind ); ///< \ru Удалить сегмент контура. \en Delete the segment of contour. MbCurve3D * DetachSegment( size_t ind ); ///< \ru Отцепить сегмент контура. \en Detach the segment of contour. + const MbCurve3D * GetSegment( size_t ind ) const { return segments[ind]; } ///< \ru Выдать сегмент контура по индексу. \en Get contour segment by the index. MbCurve3D * SetSegment( size_t ind ) { return segments[ind]; } ///< \ru Выдать сегмент контура по индексу. \en Get contour segment by the index. + void SetSegment ( MbCurve3D & newSegment, size_t ind, bool same ); ///< \ru Заменить сегмент в контуре. \en Replace a segment in the contour. void AddSegment ( MbCurve3D & newSegment, bool same ); ///< \ru Добавить сегмент в контур. \en Add a segment to the contour. void AddAtSegment ( MbCurve3D & newSegment, size_t ind, bool same ); ///< \ru Добавить сегмент в контур перед сегментом с индексом ind. \en Add a segment to the contour before the segment with index ind. void AddAfterSegment( MbCurve3D & newSegment, size_t ind, bool same ); ///< \ru Добавить сегмент в контур после сегмента с индексом ind. \en Add a segment to the contour after the segment with index ind. + + /** \brief \ru Добавить (усеченную) копию сегмента в конец контура. + \en Add a (truncated) segment copy to the end of the contour. \~ + \details \ru Добавить (усеченную) копию сегмента в конец контура. \n + \en Add a (truncated) segment copy to the end of the contour. \n \~ + \param[in] pBasis- \ru Исходная кривая. + \en Initial curve. \~ + \param[in] t1 - \ru Начальный параметр усечения. + \en Truncation starting parameter. \~ + \param[in] t2 - \ru Конечный параметр усечения. + \en Truncation ending parameter. \~ + \param[in] sense - \ru Направление усеченной кривой относительно исходной. \n + sense = 1 - направление кривой сохраняется. + sense = -1 - направление кривой меняется на обратное. + \en Direction of a trimmed curve in relation to an initial curve. + sense = 1 - direction does not change. + sense = -1 - direction changes to the opposite value. \~ + \return \ru Возвращает в случае успешного выполнения ненулевой указатель на добавленную кривую. + \en Returns, if successful, a non-zero pointer to the added curve. \~ + */ MbCurve3D * AddSegment( MbCurve3D & pBasis, double t1, double t2, int sense ); + void SegmentsAdd( MbCurve3D & newSegment, bool calculateParamLength = true ); ///< \ru Добавить сегмент в контур без проверки. \en Add a segment to the contour without checking. bool GetCornerAngle( size_t index, MbCartPoint3D & origin, MbVector3D & axis, MbVector3D & tau, double & angle, double angleEps ) const; @@ -275,15 +317,34 @@ public: /// \ru Управление распределением памяти в массиве segments. \en Control of memory allocation in the array "segments". void SegmentsReserve( size_t additionalSpace ) { segments.Reserve( additionalSpace ); } ///< \ru Зарезервировать место. \en Reserve space. void SegmentsAdjust () { segments.Adjust(); } ///< \ru Удалить лишнюю память. \en Free the unnecessary memory. - /// \ru Добавить новый элемент в начало или конец контура. \en Add the new element to the beginning or end of contour. - bool AddCurveWithRuledCheck( MbCurve3D &, double absEps, bool toEndOnly = false, bool checkSame = true, + + /** \brief \ru Добавить новый элемент в начало или конец контура. + \en Add the new element to the beginning or end of contour. \~ + \details \ru Добавить новый элемент в начало или конец контура. \n + \en Add the new element to the beginning or end of contour. \n \~ + \param[in] curve - \ru Добавляемая кривая. + \en Added curve. \~ + \param[in] absEps - \ru Точность проверки совпадения концов кривых (1e-8 - 1e-4). + \en Accuracy of verification of curve end coincidence (1e-8 - 1e-4). \~ + \param[in] toEndOnly - \ru Добавлять кривую только в конец контура. + \en Add the curve only at the end of the contour. \~ + \param[in] checkSame - \ru Проверять наличие такой же (добавляемой) кривой в контуре. + \en Check a presence of the same curve in the contour. \~ + \param[in] checkSame - \ru Версия. + \en Version. \~ + \return \ru Возвращает true, если кривая была добавлена. + \en Returns true if the curve was added. \~ + */ + bool AddCurveWithRuledCheck( MbCurve3D & curve, double absEps, bool toEndOnly = false, bool checkSame = true, VERSION version = Math::DefaultMathVersion() ); + /// \ru Проверка непрерывности контура. \en Check for contour continuity. bool CheckConnection( double eps = METRIC_PRECISION ) const; void CalculateParamLength(); ///< \ru Рассчитать параметрическую длину. \en Calculate parametric length. - void CheckClosed( double eps ); ///< \ru Установить признак замкнутости контура. \en Set the closedness attribute of contour. - /// \ru Содержат ли контура идентичные сегменты \en Whether contours contains identical segments. - bool IsSameSegments( const MbContour3D & ) const; + void CheckClosed( double /*closedEps*/ ); ///< \ru Установить признак замкнутости контура. \en Set the closedness attribute of contour. + /// \ru Содержат ли контура идентичные сегменты. \en Whether contours contains identical segments. + bool IsSameSegments( const MbContour3D &, double accuracy = METRIC_PRECISION ) const; + /// \ru Нахождение точки сегмента контура по индексу сегмента. \en Finding the point of a contour segment by segment index. void FindCorner( size_t index, MbCartPoint3D & ) const; /** \} */ diff --git a/C3d/Include/cur_contour_on_plane.h b/C3d/Include/cur_contour_on_plane.h index 2a6ef58..fbda561 100644 --- a/C3d/Include/cur_contour_on_plane.h +++ b/C3d/Include/cur_contour_on_plane.h @@ -12,9 +12,9 @@ #include +#include -class MATH_CLASS MbPlane; class MATH_CLASS MbAxis3D; @@ -65,7 +65,7 @@ public : virtual MbeSpaceType IsA() const; // \ru Дать тип элемента. \en Get a type of the element. virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const ; // \ru Сделать копию элемента. \en Create a copy of the element. virtual MbContourOnSurface & CurvesDuplicate() const; // \ru Сделать копию со старой подложкой. \en Make a copy with old substrate. - virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Определить, является ли копией данного объекта? \en Determine whether the object is copy of a given object. + virtual bool IsSame( const MbSpaceItem &, double accuracy = LENGTH_EPSILON ) const; // \ru Определить, является ли копией данного объекта? \en Determine whether the object is copy of a given object. virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта. \en Set properties of the object. @@ -96,7 +96,7 @@ public : virtual double GetLengthEvaluation() const; // \ru Оценить метрическую длину кривой. \en Evaluate the metric length of curve. virtual double CalculateMetricLength() const; // \ru Вычислить метрическую длину кривой. \en Calculate the metric length of curve. - virtual bool ChangeSurface( MbSurface & newsurf ); // \ru Заменить поверхность контура. \en Replace the surface of contour. + virtual bool ChangeSurface( const MbSurface & newsurf ); // \ru Заменить поверхность контура. \en Replace the surface of contour. virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменить носителя. \en Change the carrier. virtual bool IsPlanar() const; // \ru Является ли кривая плоской? \en Whether the curve is planar? // \ru Дать плоскую кривую и плейсмент, если пространственная кривая плоская (после использования вызывать DeleteItem на двумерную кривую). \en Get planar curve and placement if the space curve is planar (after the using call DeleteItem for two-dimensional curves) @@ -106,11 +106,11 @@ public : virtual double GetRadius() const; // \ru Дать физический радиус объекта или ноль, если это невозможно. \en Get the physical radius of the object or null if it impossible. virtual bool GetCircleAxis( MbAxis3D & ) const; // \ru Дать ось кривой. \en Get the curve axis. - virtual void GetCentre( MbCartPoint3D & wc ) const; // \ru Дать центр тяжести. \en Get the center of gravity. - virtual void GetWeightCentre( MbCartPoint3D & wc ) const; // \ru Дать центр тяжести. \en Get the center of gravity. + virtual void GetCentre( MbCartPoint3D & ) const; // \ru Дать центр тяжести. \en Get the center of gravity. + virtual void GetWeightCentre( MbCartPoint3D & ) const; // \ru Дать центр тяжести. \en Get the center of gravity. virtual void CalculateGabarit( MbCube & ) const; // \ru Вычислить габарит кривой. \en Calculate the bounding box of curve. - virtual bool IsStraight() const; // \ru Определить, является ли линия прямолинейной? \en Wetermine whether the line is straight. + virtual bool IsStraight() const; // \ru Определить, является ли линия прямолинейной? \en Determine whether the line is straight. virtual MbCurve * GetMap( const MbMatrix3D &, MbRect1D *pRgn = NULL, VERSION version = Math::DefaultMathVersion(), bool * coincParams = NULL ) const; // \ru Дать плоскую проекцию кривой. \en Get a planar projection of curve. @@ -120,7 +120,7 @@ public : /// \ru Получить локальную систему координат плоскости. \en Get the local coordinate system of a plane. const MbPlacement3D & GetPlacement() const; /// \ru Получить плоскость. \en Get the plane. - const MbPlane & GetPlane() const { return (const MbPlane &)GetSurface(); } + const MbPlane & GetPlane() const { return static_cast(GetSurface()); } /// \ru Сделать правой локальную систему координат плоскости. \en Make the local coordinate system of plane right. void SetRightPlacement(); @@ -129,12 +129,12 @@ public : /// \ru Заменить локальную систему координат плоскости. \en Replace the local coordinate system of a plane. void SetPlacement( const MbPlacement3D & ); /// \ru Инвертировать нормаль плоскости. \en Invert the normal of plane. - void InvertNormal( MbRegTransform * ireg = NULL ); + void InvertNormal( MbRegTransform * = NULL ); private: void operator = ( const MbContourOnPlane & ); // \ru Не реализовано !!! \en Not implemented !!! - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbContourOnPlane ) +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbContourOnPlane ) }; IMPL_PERSISTENT_OPS( MbContourOnPlane ) diff --git a/C3d/Include/cur_contour_on_surface.h b/C3d/Include/cur_contour_on_surface.h index 639e7e1..b3adb1e 100644 --- a/C3d/Include/cur_contour_on_surface.h +++ b/C3d/Include/cur_contour_on_surface.h @@ -41,7 +41,7 @@ class MbSegmentsSearchTree; \ingroup Curves_3D */ // --- -class MATH_CLASS MbContourOnSurface : public MbCurve3D, public MbSyncItem { +class MATH_CLASS MbContourOnSurface : public MbCurve3D { protected : MbSurface * surface; ///< \ru Указатель на базовую поверхность (всегда не NULL). \en The pointer to the base surface (this value is never NULL). @@ -155,16 +155,16 @@ public : virtual void CalculateGabarit( MbCube & ) const; // \ru Вычислить габарит кривой. \en Calculate the bounding box of curve. /// \ru Сбросить рассчитанный габарит. \en Reset the calculated bounding box. - void SetDirtyGabarit() const { cube.SetEmpty(); } + void SetDirtyGabarit() const; /// \ru Выдать габарит кривой. \en Get the bounding box of curve. - const MbCube & GetGabarit() const { if ( cube.IsEmpty() ) CalculateGabarit( cube ); return cube; } + const MbCube & GetGabarit() const; /// \ru Вычислить параметрический габарит контура. \en Calculate the parametric bounding box of the contour. virtual void CalculateUVLimits( MbRect & uvRect ); virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменить носителя. \en Change the carrier. virtual bool ChangeCarrierBorne( const MbSpaceItem & item, MbSpaceItem & init, const MbMatrix & matr ); // \ru Изменить носимые элементы. \en Change carrier elements. /// \ru Заменить поверхность контура. \en Replace the surface of contour. - virtual bool ChangeSurface( MbSurface & ); + virtual bool ChangeSurface( const MbSurface & ); /// \ru Заменить двумерный контур. \en Replace the two-dimensional contour. void ChangeContour( MbContour & ); virtual bool IsPlanar() const; // \ru Определить, является ли кривая плоской. \en Determine whether the curve is planar. @@ -186,7 +186,8 @@ public : /// \en Find all the special points of the curvature function of the curve. virtual void GetCurvatureSpecialPoints( std::vector & points ) const; virtual bool IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = NULL, double epsilon = EPSILON ) const; // \ru Непрерывна ли первая производная? \en Have the first derivative the continuous? - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); // \ru Доступ к полям класса. \en Access to the fields of a class. @@ -210,7 +211,17 @@ public : /// \ru Вычислить нормали к поверхности по параметру кривой. \en Calculate normals to the surface in the curve parameter. void SurfaceNormal( double t, MbVector3D & n ) const; - /// \ru Найти сегмент контура по параметру на контуре. \en Find a segment of contour in the contour parameter. + /** \brief \ru Найти сегмент контура. + \en Find a contour segment. \~ + \details \ru Найти сегмент контура по параметру контура. \n + \en Find a contour segment by parameter on contour. \n \~ + \param[in,out] t - \ru Параметр контура. + \en Contour parameter. \~ + \param[out] tSeg - \ru Параметр сегмента контура. + \en Contour segment parameter. \~ + \return \ru Возвращает номер сегмента в случае успешного выполнения или -1. + \en Returns the segment number in case of successful execution or -1. \~ + */ ptrdiff_t FindSegment ( double & t, double & tSeg ) const; /// \ru Найти точку сегмента контура по индексу. \en Find a point of the contour segment by the index. void FindCorner ( ptrdiff_t index, MbCartPoint & ) const; diff --git a/C3d/Include/cur_contour_with_breaks.h b/C3d/Include/cur_contour_with_breaks.h index 094d086..456155e 100644 --- a/C3d/Include/cur_contour_with_breaks.h +++ b/C3d/Include/cur_contour_with_breaks.h @@ -1,982 +1,982 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Контур с разрывами. - \en Contour with breaks. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CUR_CONTOUR_WITH_BREAKS_H -#define __CUR_CONTOUR_WITH_BREAKS_H - - -#include -#include -#include - - -class MATH_CLASS MbBreaksPart; -class MATH_CLASS MbBreak; - - -//------------------------------------------------------------------------------ -/** \brief \ru Контур c разрывами. - \en Contour with breaks. \~ - \details \ru Контур c разрывами.\n - Для использования в мультилинии MbMultiline.\n - Содержит разрывы MbBreak, видимые части - контуры MbContour.\n - При использовании в мультилинии каждуй раз при перестроении контура в нем обновляется - список номеров по количеству сегментов контура. - Каждый номер показывает номер сегмента базовой кривой мультилинии, - которому соответствует этот сегмент контура.\n - Если сегмент контура является эквидистантой сегмента базовой кривой, - то ему соответствует номер этого сегмента.\n - Если контур является сегментом обхода вершины мультилинии, - то ему соответствует номер, соответствующий предыдущему сегменту контура.\n - Разрывы в контуре не могут накладываться друг на друга. - Если в результате перестроения один разрыв наложился на другой, то они объединяются. - \en Contour with breaks.\n - For using in the multiline MbMultiline.\n - Contains breaks MbBreak, visible parts - contours MbContour.\n - When using in a multiline every time when contour is rebuilding in it are updated - list of numbers by the number of contour segments - Each number indicates the segment number of base curve of multiline. - which corresponds to this segment of contour.\n - If segment of contour is equidistant of base curve segment, - then it corresponds to the number of this segment.\n - If contour is segment of multiline vertices traverse. - then it corresponds to the number corresponding the previous segment of the contour.\n - Breaks in the contour can not be overlapped each other. - If in the result of rebuilding a break overlapped on the other break then they are combined. \~ - \ingroup Curves_2D -*/ // --- -class MATH_CLASS MbContourWithBreaks : public MbContour -{ -private: - RPArray breaks; // \ru Разрывы. \en Breaks. - RPArray visibleContours; // \ru Видимые контуры \en Visible contours. - SArray baseSegNumbers; // \ru Номера сегментов базового контура \en Numbers of segments of base contour - // \ru для задания неподвижных точек разрыва \en to define the fixed points of break - // \ru заполняется при использовании контура в мультилинии. \en filled when using the contour in the multiline. -public: - - /** \brief \ru Создание пустого контура. - \en Creation of empty contour. \~ - \details \ru Создание пустого контура без сегментов.\n - \en Creation of empty contour without segments. \n \~ - */ - MbContourWithBreaks(); - - MbContourWithBreaks( const MbContour & cnt ); ///< \ru Копирующий конструктор. \en Copy-constructor. - -private: - MbContourWithBreaks( const MbContourWithBreaks & ); // \ru не реализовано \en not implemented -protected : - MbContourWithBreaks( const MbContourWithBreaks &, MbRegDuplicate * ); -public: - virtual ~MbContourWithBreaks(); - -public : - VISITING_CLASS( MbContourWithBreaks ); - - /**\ru \name Общие функции геометрического объекта. - \en \name Common functions of a geometric object. - \{ */ - virtual MbePlaneType IsA() const; // \ru Тип элемента. \en A type of element. - virtual void Transform( const MbMatrix & matr, MbRegTransform * ireg = NULL, const MbSurface * newSurface = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. - virtual void Move( const MbVector & to, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Сдвиг. \en Move. - virtual void Rotate( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Поворот. \en Rotation. - virtual MbPlaneItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - /** \} */ - /**\ru \name Функции доступа к данным: разрывы. - \en \name Functions for access to data: breaks. - \{ */ - size_t GetBreaksCount () const { return breaks.Count(); } ///< \ru Число разрывов. \en The number of breaks. - - /** \brief \ru Разрыв по номеру. - \en A break by the number. \~ - \details \ru Разрыв по номеру.\n - Номер не проверяется на корректность. - \en A break by the number. \n - A number isn't checked for correctness. \~ - \param[in] i - \ru Номер разрыва, должен быть меньше количества разрывов. - \en The number of break must be less than the number of breaks. \~ - \return \ru Указатель на разрыв. - \en Pointer to break. \~ - */ - MbBreak * GetBreak ( size_t i ) const { return breaks[i]; } - - /** \brief \ru Разрыв по номеру в параметрах контура. - \en A break by the number in contour parameters. \~ - \details \ru Разрыв по номеру в параметрах контура.\n - Номер проверяется на корректность. - В случае, если номер больше числа сегментов результат - вывернутая область. - \en A break by the number in contour parameters. \n - A number Is checked for correctness. - If number is more than numbers of segments then result is everted region. \~ - \param[in] i - \ru Номер разрыва, должен быть меньше количества разрывов. - \en The break number must be less than the number of all breaks. \~ - \return \ru Интервал разрыва в параметрах контура. - \en Break interval in contour parameters. \~ - */ - MbRect1D GetBreaksRange ( size_t i ) const; - - /** \} */ - /**\ru \name Функции доступа к данным: видимые участки. - \en \name Functions for access to data: visible regions. - \{ */ - - /** \brief \ru Количество видимых частей. - \en The number of visible parts. \~ - \details \ru Количество видимых частей.\n - Если в контуре нет разрывов, то возвращает 0. - \en The number of visible parts. \n - If contour does not contain breaks, then returns 0. \~ - \return \ru Число видимых частей. - \en The number of visible parts. \~ - */ - size_t GetVisibleCount () const { return visibleContours.Count(); } - - /** \brief \ru Видимая часть по номеру. - \en A visible part by the number. \~ - \details \ru Видимая часть по номеру.\n - Номер не проверяется на корректность. - \en A visible part by the number. \n - A number isn't checked for correctness. \~ - \param[in] i - \ru Номер видимой части, должен быть меньше количества видимых частей. - \en The number of visible part must be less than the number of visible parts. \~ - \return \ru Указатель не контур - видимую часть. - \en A pointer to visible part of the contour. \~ - */ - const MbContour * GetVisibleContour ( size_t i ) const { return visibleContours[i]; } - - /** \} */ - /**\ru \name Функции доступа к данным: невидимые участки. - \en \name Functions for access to data: Invisible regions. - \{ */ - - /** \brief \ru Невидимая часть по номеру разрыва. - \en Invisible part by the number of break. \~ - \details \ru Невидимая часть по номеру разрыва.\n - Номер проверяется на корректность. - В случае, если номер не меньше числа разрывов, функция вернет NULL.\n - После использования полученный контур нужно удалить. - \en Invisible part by the number of break. \n - A number Is checked for correctness. - If the number is not less than the number of breaks, the function returns NULL. \n - The resulting contour is to be deleted after use. \~ - \param[in] i - \ru Номер разрыва, должен быть меньше количества видимых частей. - \en The number of break must be less than the number of visible parts. \~ - \return \ru Указатель не контур - видимую часть. - \en A pointer to visible part of the contour. \~ - */ - MbContour * GetInvisibleContour ( size_t i ) const; - - /** \} */ - /**\ru \name Работа с разрывами: добавление. - \en \name Working with breaks: addition. - \{ */ - - /** \brief \ru Добавить разрыв между точками. - \en Add a break between points. \~ - \details \ru Добавить разрыв между точками.\n - \en Add a break between points. \n \~ - \param[in] point1 - \ru Первая граница разрыва. - \en The first boundary of break. \~ - \param[in] point2 - \ru Вторая граница разрыва. - \en The second boundary of break. \~ - \param[in] point3 - \ru Точка, которая показывает удаляемую часть замкнутого контура,\n - в случае разомкнутого контура она игнорируется. - \en A point which indicates a removable part of the closed contour, \n - in the case of open contour it is ignored. \~ - \param[in] invertBreak - \ru Признак добавления разрыва на противоположную часть контура. - \en addition attribute of break on the opposite part of contour. \~ - \return \ru true, если разрыв был добавлен. - \en true if break has been added. \~ - */ - bool AddBreak ( const MbCartPoint & point1, const MbCartPoint & point2, - const MbCartPoint & point3, bool invertBreak = false ); - - /** \brief \ru Добавить разрыв между параметрами контура. - \en Add a break between contour parameters. \~ - \details \ru Добавить разрыв между параметрами контура.\n - \en Add a break between contour parameters. \n \~ - \param[in] t1 - \ru Первая граница разрыва. - \en The first boundary of break. \~ - \param[in] t2 - \ru Вторая граница разрыва. - \en The second boundary of break. \~ - \param[in] t3 - \ru Параметр, который показывает удаляемую часть замкнутого контура,\n - в случае разомкнутого контура он игнорируется. - \en A parameter which indicates a removable part of the closed contour, \n - in the case if contour is open it is ignored. \~ - \param[in] invertBreak - \ru Признак добавления разрыва на противоположную часть контура. - \en addition attribute of break on the opposite part of contour. \~ - \return \ru true, если разрыв был добавлен. - \en true if break has been added. \~ - */ - bool AddBreak ( double t1, double t2, double t3, bool invertBreak = false ); - - /** \brief \ru Добавить разрыв по интервалу параметров контура. - \en Add a break by interval of contour parameters. \~ - \details \ru Добавить разрыв по интервалу параметров контура.\n - \en Add a break by interval of contour parameters. \n \~ - \param[in] range - \ru Интервал параметров контура,\n - если range.zmin больше range.zmax, то результат работы функции - будет корректным только в случае замкнутого контура - - добавится разрыв, проходящий через начало контура. - \en An interval of contour parameters. \n - if range.zmin is greater than range.zmax then the function result - is correct only in the case of closed contour - - added a break passing through the origin of the contour. \~ - \return \ru true, если разрыв был добавлен. - \en true if break has been added. \~ - */ - bool AddBreak ( const MbRect1D & range ); - - /** \} */ - /**\ru \name Работа с разрывами: удаление. - \en \name Working with breaks: removing. - \{ */ - - /** \brief \ru Удалить разрывы. - \en Remove breaks. \~ - \details \ru Удалить все разрывы.\n - \en Remove all breaks. \n \~ - \return \ru true, если хотя бы один разрыв был удален. - \en true, if at least one break has been removed. \~ - */ - bool DeleteBreaks (); ///< \ru Удалить разрывы. \en Remove breaks. - - /** \brief \ru Удалить разрыв по номеру разрыва. - \en Remove a break by the number. \~ - \details \ru Удалить разрыв по номеру разрыва.\n - \en Remove a break by the number. \n \~ - \param[in] breakIndex - \ru Номер разрыва,\n - проверяется на корректность.\n - \en The number of break. \n - checked for correctness. \n \~ - \param[in] rebuild - \ru Нужно ли перестроить контур после удаления разрыва.\n - Если контур не перестраивать, видимые части контура не будут соответствовать разрывам.\n - Перестроить контур можно отдельно вызовом RebuildBreaks. - \en Is the contour to be rebuilt after removing break. \n - If don't rebuild contour then the visible parts of contour do not match breaks. \n - Rebuild contour you can be separately by call RebuildBreaks. \~ - \return \ru true, если разрыв был удален. - \en true if break has been removed. \~ - */ - bool DeleteBreakAtNumber ( size_t breakIndex, bool rebuild = false); - - /** \brief \ru Удалить разрыв по параметру на контуре. - \en Remove a break by parameter on the contour. \~ - \details \ru Удалить разрыв по параметру на контуре.\n - \en Remove a break by parameter on the contour. \n \~ - \return \ru true, если разрыв был удален - \en true if break has been removed \~ - */ - bool DeleteBreakAtParam ( double t ); - - /** \brief \ru Удалить разрывы на сегментах с соответствующим базовым номером. - \en Remove breaks on the segments with the corresponding base number. \~ - \details \ru Удалить разрывы на сегментах с соответствующим базовым номером.\n - Разрывы удаляться, если в контуре заполнены базовые номера сегментов. - Номера заполняются при перестроении мультилинии, содержащей контур.\n - Если сегменту контура соответствует одна из частей разрыва, то весь разрыв будет удален. - \en Remove breaks on the segments with the corresponding base number. \n - Break are removed if base numbers of segments are filled in contour. - The number are filled when multiline is rebuilt which contrains contour. \n - If segment of contour corresponds to one of break parts then entire break is removed. \~ - \param[in] baseNumber - \ru Номер сегмента базовой кривой. - \en A segment number of base curve. \~ - \param[in] delTracingBreaks - \ru Нужно ли удалять разрывы с сегментоа, соответствующих обходу вершины мультилинии. - \en Is it necessary for breaks to be removed from segments which correspond to multiline vertices traverse. \~ - \param[in] delEquidBreaks - \ru Нужно ли удалять разрывы с сегментов, соответствующих эквидистантам. - \en breaks to be removed from segments which correspond to equidistants. \~ - \param[in] delInLineSeg - \ru Удалять ли разрывы с прямолинейных сегментов. - \en Is it necessary to remode breaks from straight segments. \~ - */ - void DeleteBreaksAtBaseNumber ( size_t baseNumber, bool delTracingBreaks, - bool delEquidBreaks, bool delInLineSeg = true ); - /** \} */ - /**\ru \name Удаление разрывов и видимых частей малой метрической длины. - \en \name Removing of breaks and visible parts of the small metric length. - \{ */ - - /** \brief \ru Удалить разрывы малой метрической длины. - \en Remove breaks of the small metric length. \~ - \details \ru Удалить разрывы малой метрической длины.\n - В случае успеха видимые контуры перестраиваются соответственно разрывам. - \en Remove breaks of the small metric length. \n - In case of success the visible contours are rebuilt by breaks. \~ - \param[in] length - \ru Минимальная длина невидимой части. - \en Minimal length of invisible part. \~ - \return \ru true, если хотя бы один разрыв был удален. - \en true, if at least one break has been removed. \~ - */ - bool DeleteSmallBreaks ( double length ); - - /** \brief \ru Удалить видимые части малой метрической длины. - \en Remove visible parts of the small metric length. \~ - \details \ru Удалить видимые части малой метрической длины.\n - Соответствует объединению близких разрывов в один. - В случае успеха видимые контуры перестраиваются соответственно разрывам. - \en Remove visible parts of the small metric length. \n - Corresponds to union of close discontinuities into one. - In case of success the visible contours are rebuilt by breaks. \~ - \param[in] length - \ru Минимальная длина видимой части. - \en Minimal length of visible part. \~ - \return \ru true, если разрывы были изменены. - \en true if breaks have been changed. \~ - */ - bool DeleteSmallVisContours ( double length ); - - /** \} */ - /**\ru \name Работа с разрывами - \en \name Working with breaks - \{ */ - - /** \brief \ru Номер разрыва, край которого попал в окрестность точки. - \en The number of break the edge of which is into point neighbourhood. \~ - \details \ru Номер разрыва, край которого попал в окрестность точки.\n - \en The number of break the edge of which is into point neighbourhood. \n \~ - \param[in] p - \ru Точка. - \en Point. \~ - \param[in] rad - \ru Радиус окрестности точки для поиска разрыва. - \en Radius of point neighborhood to search the break. \~ - \param[out] index - \ru Номер разрыва. - \en The number of break. \~ - \return \ru true, если разрыв найден. - \en true if the break is found. \~ - */ - bool GetBreakAtPoint ( const MbCartPoint & p, double rad, - size_t & index ) const; - - /** \brief \ru Номера разрывов, которые хотя бы одним краем попадают в область. - \en Numbers of breaks which at least one edge fall into the region. \~ - \details \ru Номера разрывов, которые хотя бы одним краем попадают в область.\n - \en Numbers of breaks which at least one edge fall into the region. \n \~ - \param[in] rect - \ru Область поиска. - \en Region of search. \~ - \param[out] breaksNumbers - \ru Номера разрывов. - \en Numbers of breaks. \~ - \return \ru true, если хотя бы один разрыв найден. - \en true, if at least one break has been found. \~ - */ - bool GetBreaksInRect ( const MbRect & rect, - SArray & breaksNumbers ) const; - - /** \brief \ru Номера разрывов, которые хотя бы одним краем попадают в область. - \en Numbers of breaks which at least one edge fall into the region. \~ - \details \ru Номера разрывов, которые хотя бы одним краем попадают в область, заданную контуром.\n - \en Numbers of breaks which at least one edge fall into the region given contour. \n \~ - \param[in] contour - \ru Контур должен быть замкнутым и иметь правильное направление. - \en Contour must be closed and have the right direction. \~ - \param[out] breaksNumbers - \ru Номера разрывов. - \en Numbers of breaks. \~ - \return \ru true, если хотя бы один разрыв найден. - \en true, if at least one break has been found. \~ - */ - bool GetBreaksInRect ( const MbContour & contour, - SArray & breaksNumbers ) const; - - /** \brief \ru Определить попадает ли точка в любой из разрывов. - \en Determine whether a point it inside a break. \~ - \details \ru Определить попадает ли точка в любой из разрывов.\n - \en Determine whether a point it inside a break. \n \~ - \param[in] p - \ru Точка для проверки. - \en A point for the check. \~ - \return \ru true, если точка попадает в любой из разрывов. - \en true, if point falls into any break. \~ - */ - bool HitToBreaks ( const MbCartPoint & p ) const; - - /** \brief \ru Находится ли интервал параметров на разрыве. - \en Whether the interval of parameters is on break. \~ - \details \ru Находится ли интервал параметров на разрыве.\n - \en Whether the interval of parameters is on break. \n \~ - \param[in] rect - \ru Интервал для проверки. - \en Interval to check. \~ - \return \ru true, если интервал полностью находится на разрыве или совпадает с ним. - \en true if interval entirely is on the break or coincides with it. \~ - */ - bool IsRectInBreak ( const MbRect1D & rect ); - - /** \brief \ru Обновить невидимые и видимые контуры. - \en Update visible and invisible contours. \~ - \details \ru Обновить невидимые и видимые контуры соответственно базовой кривой мультилинии.\n - После перестроения разрывы должны соответствовать сегментам базовой кривой мультилинии. - \en Update visible and invisible contours by base curve of multiline respectively. \n - After rebuilding the breaks must correspond to segments of base multiline curve. \~ - \param[in] oldBaseNumbers - \ru Старые номера базовых сегментов,\n - должны быть запомнены до изменения мультилинии. - \en Old numbers of base segments, \n - must be saved before multiline is changed. \~ - */ - void RebuildBreaks ( SArray & oldBaseNumbers ); - - /** \brief \ru Обновить невидимые и видимые контуры. - \en Update visible and invisible contours. \~ - \details \ru Обновить невидимые и видимые контуры соответственно разрывам. - \en Update visible and invisible contours according to breaks. \~ - */ - void RebuildBreaks ( ); - - /** \brief \ru Преобразование в соответствии с матрицей. - \en Transform according to matrix. \~ - \details \ru Преобразование в соответствии с матрицей.\n - Используется для преобразования мультилинии. - Преобразует длину и расстояние от фиксированной точки прямолинейного разрыва, - а так же фиксированную точку. - \en Transform according to matrix.\n - Used to transform multiline. - Transforms length and distance from fixed point of straight break, - and fixed point. \~ - \param[in] matr - \ru Матрица преобразования. - \en A transformation matrix. \~ - */ - void TransformMultlinesBreaks ( const MbMatrix & matr ); - - /** \brief \ru Параметр привязки части разрыва. - \en A binding parameter of a break part. \~ - \details \ru Посчитать параметр привязки части разрыва в зависимости от типа сегмента контура.\n - \en Calculate binding parameter of a break part according to the type of contour segment. \n \~ - \param[in] brPart - \ru Часть разрыва этого контура. - \en A break part of this contour. \~ - \param[out] segNumber - \ru Номер сегмента контура. - \en A number of the contour segment. \~ - \return \ru Параметр центра разрыва на сегменте контура. - \en A parameter of the break center on the contour segment. \~ - */ - double GetLocalBreaksParam ( const MbBreaksPart & brPart, - size_t & segNumber ) const; - - /** \} */ - /**\ru \name Работа с разрывами: изменение разрыва. - \en \name Working with breaks: changing a break. - \{ */ - - /** \brief \ru Фиксировать точку. - \en Fix the point. \~ - \details \ru Поставить фиксированную точку части разрыва.\n - \en Set the fixed point of a break part. \n \~ - \param[in] newPoint - \ru Новая фиксированная точка. - \en A new fixed point. \~ - \param[out] part - \ru Часть разрыва контура для изменения. - \en A part of contour break to change. \~ - */ - void SetBreakFixedPoint ( const MbCartPoint & newPoint, - MbBreaksPart & part ); - - /** \brief \ru Фиксировать переменную. - \en Fix the variable. \~ - \details \ru Поставить фиксированную переменную части разрыва.\n - \en Set the fixed variable of a break part. \n \~ - \param[in] newFixedVar - \ru Новая фиксированная переменная. - \en New fixed variable. \~ - \param[out] part - \ru Часть разрыва контура для изменения. - \en A part of contour break to change. \~ - */ - void SetBreakFixedVar ( double newFixedVar, MbBreaksPart & part); - - /** \} */ - /**\ru \name Работа с разрывами: отслеживание разрыва - \en \name Working with breaks: tracking a break - \{ */ - /// \ru Количество номеров сегментов базового контура \en Count of segments numbers of the base contour - // \ru (должно соответствовать количеству сегментов контура) \en (must be equal to count of contour segments) - size_t GetBaseNumbersCount () const { return baseSegNumbers.Count(); } - - /// \ru Номера сегментов базового контура. \en Numbers of segments of base contour. - void GetBaseNumbers ( SArray & baseNumbers ) const; - - /** \brief \ru Номер сегмента базового контура. - \en Segment number of the base contour. \~ - \details \ru Номер сегмента базового контура.\n - Номер сегмента будет найден, если массив номеров не пуст и - корректно насчитан, то есть число номеров совпадает с числом сегментов контура - \en Segment number of the base contour. \n - Segment number will be found if the array of numbers is not empty and - it was numbered correctly, ie count of numbers is equal to count of contour segments \~ - \param[in] i - \ru Индекс номера. - \en Index of number. \~ - \return \ru Номер сегмента из массива номеров. - \en Number of segment from the array of numbers. \~ - */ - size_t GetBaseNumber ( size_t i ) const; - - /** \brief \ru Добавить номер базового сегмента. - \en Add number of the base segment. \~ - \details \ru Добавить номер сегмента базового контура в конец массива.\n - \en Add segment number of base contour to the end of array. \n \~ - \param[in] number - \ru Номер для добавления. - \en Number to add. \~ - */ - void AddBaseSegNumber ( size_t number ); - - /** \brief \ru Добавить номер базового сегмента. - \en Add number of the base segment. \~ - \details \ru Добавить номер сегмента базового контура в начало массива.\n - \en Add segment number of base contour to the beginning of array. \n \~ - \param[in] number - \ru Номер для добавления. - \en Number to add. \~ - */ - void AddBaseSegNumberAtBegin ( size_t number ); - - /** \brief \ru Вставить номер после lastInd. - \en Insert number after the lastInd. \~ - \details \ru Вставить после элемента номер lastInd номер, соответствующий lastInd.\n - \en Insert number corresponding lastInd after element lastInd. \n \~ - \param[in] lastInd - \ru Индекс номера. - \en Index of number. \~ - */ - void InsertLastSegNumber ( size_t lastInd ); - - /** \brief \ru Удалить элемент по индексу. - \en Delete element by an index. \~ - \details \ru Удалить номер базового сегмента по индексу.\n - \en Remove number of the base segment by an index. \n \~ - \param[in] ind - \ru Индекс номера. - \en Index of number. \~ - */ - void DeleteBaseSegNumber ( size_t ind ); - - /// \ru Очистить массив с номерами базовых сегментов. \en Clear the array with numbers of base segments. - void ClearBaseSegNumbers () { baseSegNumbers.HardFlush(); } - - /** \brief \ru Изменить номера сегментов в разрывах. - \en Change numbers of segments in the breaks. \~ - \details \ru Изменить номера сегментов в разрывах.\n - Заданному номеру базового сегмента соответствует сегмент - эквидистанта на контуре. - Для всех разрывов: - если хотя бы часть разрыва находится на этом сегменте, - у всех его частей номер сегмента будет изменен. - \en Change numbers of segments in the breaks. \n - A given number of the base segments corresponds to the segment - equidistant on the contour. - For all breaks: - if at least one part of break is in this segment, - the segment number of all of its parts will be changed. \~ - \param[in] begBaseNumber - \ru Номер базового сегмента. - \en An index of the base segment. \~ - \param[in] deltaN - \ru Величина изменение номера сегмента частей разрывов. - \en The change value of segment number of breaks parts. \~ - */ - void ChangeBreaksSegNumbers ( size_t begBaseNumber, ptrdiff_t deltaN ); - - /** \brief \ru Изменить разрывы соотвестсвенно замкнутости. - \en Change breaks of closedness respectively. \~ - \details \ru Изменить разрывы соотвестсвенно замкнутости.\n - При изменении признака замкнутости контура нужно изменить разрывы - соответственно новому значению замкнутости.\n - Если контур стал разомкнутым - разрыв, находящийся на первом и последнем - сегменте одновременно, делится на 2 части.\n - Если контур стал замкнутым - разрывы, первый из который примыкает к левому краю - первого сегмента контура, а второй из которых примыкает к правому краю последнего - сегмента контура, объединяется в один. - \en Change breaks of closedness respectively. \n - When changing attribute of contour closedness breaks need to change - respectively to the new value of closedness. \n - If contour has become open - the break located on the first and last - segment is divided into 2 parts at the same time. \n - If contour has become closed - breaks, the first of which is adjacent to the left boundary - of the first contour segment and the second of which is adjacent to the right boundary of the last - contour segment are united into one. \~ - \param[in] newClosed - \ru Новый признак замкнутости контура. - \en The new closedness attribute of contour. \~ - */ - void ChangeBreaksAtClosed ( bool newClosed ); - - /** \brief \ru Изменить номера сегментов частей разрывов. - \en Change segments numbers of breaks parts. \~ - \details \ru Изменить номера сегментов частей разрывов.\n - \en Change segments numbers of breaks parts. \n \~ - \param[in] deltaN - \ru Величина изменения. - \en A change value. \~ - */ - void MoveBreaksSegNumbers ( ptrdiff_t deltaN ); - - /** \brief \ru Находится ли часть разрыва этого контура на сегменте обхода вершин. - \en Is the break part of this contour located on the segment of vertices traverse. \~ - \details \ru Находится ли часть разрыва этого контура на сегменте обхода вершин.\n - \en Is the break part of this contour located on the segment of vertices traverse. \n \~ - \param[in] part - \ru Часть разрыва этого контура. - \en A break part of this contour. \~ - \param[out] vertNumber - \ru В случае успеха вернет номер вершины. - \en In the case of success returns the vertex number. \~ - \return \ru true, если часть разрыва находится на сегменте обхода вершины. - \en true, if the break part is located on the segment of vertices traverse. \~ - */ - bool IsTrasingBreaksPart ( const MbBreaksPart & part, - size_t & vertNumber ) const; - - /** \brief \ru Поменяться разрывами. - \en Swap breaks. \~ - \details \ru Поменяться разрывами.\n - \en Swap breaks. \n \~ - \param[in] other - \ru Контур с разрывами для обмена. - \en A contour with breaks to swap. \~ - */ - void SwapBreaksAndBaseNumbers ( MbContourWithBreaks & other ); - - /** \brief \ru Добавить разрывы контура. - \en Add contour breaks. \~ - \details \ru Добавить разрывы контура.\n - \en Add contour breaks. \n \~ - \param[in] other - \ru Контур с разрывами для добавления разрывов. - \en A contour with breaks for adding breaks. \~ - */ - void AddContoursBreaks ( const MbContourWithBreaks & other ); - - /** \brief \ru Заменить номера базовых сегментов. - \en Replace the numbers of base segments. \~ - \details \ru Заменить номера базовых сегментов.\n - \en Replace the numbers of base segments. \n \~ - \param[in] other - \ru Контур с новыми номерами. - \en A contour with new numbers. \~ - */ - void ChangeBaseNumbers ( const MbContourWithBreaks & other ); - /** \} */ - -private: - void DeleteBreaks ( size_t segmentIndex, - bool delInLineSeg = true ); // \ru удалить разрывы на сегменте с номером index \en remove breaks from segment with number "index" - void DeleteBreaksPartAtSegNum ( size_t segNumber, size_t oldSegCount ); // \ru часть разрыва по номеру сегмента \en part of break by the segment number - bool DeleteBreaksPartOrBreak ( size_t breakIndex, size_t partIndex, - size_t oldSegCount ); // \ru часть разрыва или разрыв \en part of break or break -private: - void CalculateVisibleContours ( ); // \ru посчитать видимые части \en calculate visible parts - void CalculateInvisibleContours( RPArray & invisibleContours ); // \ru посчитать невидимые части \en calculate invisible parts - void CalculateContours ( const CSSArray & ranges, // \ru посчитать контуры по интервалам \en calculate contours by intervals - RPArray & contours, - bool visible ) const; // \ru для видимых частей вызывать AddRef() \en call AddRef() for visible parts - void CalculateInVisibleRanges ( CSSArray & breaksRanges ); // \ru посчитать невидимые интервалы \en calculate invisible intervals - void CalculateVisibleRanges ( CSSArray & breaksRanges, - CSSArray & visibleRanges ) const; // \ru посчитать видимые интервалы \en Calculate visible intervals - void CalculateRanges ( const CSSArray & startingInt, // \ru посчитать противоположные интервалы \en Calculate opposite intervals - CSSArray & resultInt ) const; // \ru второй массив по первому \en the second array by the first - void CalculateBreaksPart ( const MbRect1D & segParams, - size_t segNumber, MbBreak & brRange ) const; // \ru посчитать часть разрыва \en Calculate a part of the break - void CalculateBreak ( const MbRect1D & range, - MbBreak & brRange ) const; // \ru посчитать разрыв по интервалу \en Calculate the break by interval - MbRect1D GetLocalBreaksRange ( const MbBreaksPart & part, - double brParam, size_t segNumber ) const; // \ru интервал по параметру привязки \en interval by the binding parameter - void AddCalcBreak ( const MbRect1D & range ); - void ChangeBreaksSegNumbers ( const SArray & oldBaseNumbers, - SArray & oldEqCounts, - SArray & newEqCounts ); // \ru изменить номера сегментов у разрывов \en change segments numbers of breaks - void RedefineBreaksParts (); // \ru доопределить неопределенные части разрыва \en complete the definition of indefinite parts of the break - - // \ru для преобразования контуров \en for transformation of contours - void TransformBreaks ( const MbMatrix & matr ); // \ru преобразование в соответствии с матрицей \en transform according to the matrix - - size_t GetLocalBreaksRange ( const MbBreaksPart & part, - MbRect1D & localRect ) const; // \ru разрыв по номеру в параметрах сегмента \en a break by the number in segment parameters - - - void operator = ( const MbContourWithBreaks & ); // \ru не реализован \en not implemented - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbContourWithBreaks ) - -}; // MbContourWithBreaks - -IMPL_PERSISTENT_OPS( MbContourWithBreaks ) - -//------------------------------------------------------------------------------ -/** \brief \ru Часть разрыва. - \en Part of break. \~ - \details \ru Часть разрыва контура мультилинии. Относится к одному сегменту контура.\n - Для использования в разрыве MbBreak. - \en Part of multiline contour break. Applicable to one segment of the contour.\n - For using in the break MbBreak. \~ - \ingroup Algorithms_2D -*/ // --- -class MATH_CLASS MbBreaksPart { - -private : - size_t segNumber; // \ru Номер сегмента \en Number of the segment. - double fixedVar; // \ru Фиксированная переменная \en Fixed variable - // \ru ( tMax - tCentre ) / ( tCentre - tMin ), tCentre - параметр центра разрыва \en ( tMax - tCentre ) / ( tCentre - tMin ), tCentre - center of the break - // \ru для отрезка - расстояние до проекции неподвижной точки \en for segment - distance to projection of the fixed point - double length; // \ru Длина части разрыва (для отрезка) \en Length of the break part (for segment) - // \ru для дуг и по умолчанию сохраняем параметрическую длину \en For arcs and save parametric length by default. - // \ru (для дуги - угол) \en (for arc - angle) - MbCartPoint fixedPoint; // \ru неподвижная точка ( корректное значение для отрезка ) \en fixed point (correct value for segment) - -public: - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор по номеру сегмента, фиксированной переменной, длине, неподвижной точке. - \en Constructor by a number of segment, fixed variable, length, fixed point. \~ - \param[in] sNumber - \ru Номер сегмента контура, на котором находится часть разрыва. - \en Number of contour segment where a part of break is located. \~ - \param[in] fixVar - \ru Фиксированная переменная:\n - для отрезка - расстояние до проекции неподвижной точки,\n - в общем случае - величина, равная ( tMax - tCentre ) / ( tCentre - tMin ), где\n - tMin - минимальный параметр сегмента,\n - tMax - максимальный параметр сегмента,\n - tCentre - параметр центра части разрыва. - \en Fixed variable:\n - for segment - distance to projection of the fixed point,\n - In general case - a value which is equal to ( tMax - tCentre ) / ( tCentre - tMin ), where \n - tMin - minimal parameter of the segment,\n - tMax - maximal parameter of the segment,\n - tCentre - parameter of the break part center. \~ - \param[in] len - \ru Длина части разрыва:\n - для отрезка - метрическая длина,\n - в общем случае - параметрическая длина. - \en Length of the break part:\n - for segment - metric length, \n - In the general case - parametric length. \~ - \param[in] p - \ru Неподвижная точка:\n - для отрезка - используется для привязки части разрыва,\n - в общем случае - не имеет смысла. - \en Fixed point:\n - for segment - used to bind part of the break,\n - in general case - it is useless. \~ - */ - MbBreaksPart( size_t sNumber, double fixVar, double len, const MbCartPoint & p ) - : segNumber ( sNumber ), - fixedVar ( fixVar ), - length ( len ), - fixedPoint( p ) - { - } - - /// \ru Копирующий конструктор. \en Copy-constructor. - MbBreaksPart( const MbBreaksPart & other ) - : segNumber ( other.GetSegmentNumber() ), - fixedVar ( other.GetFixedVar() ), - length ( other.GetLength() ), - fixedPoint( other.GetFixedPoint() ) - { - } - - ~MbBreaksPart(){}; - - /**\ru \name Функции доступа к данным. - \en \name Functions for access to data. - \{ */ - size_t GetSegmentNumber() const { return segNumber; } ///< \ru Номер сегмента контура. \en A number of the contour segment. - double GetFixedVar() const { return fixedVar; } ///< \ru Фиксированная переменная. \en fixed variable - double GetLength() const { return length; } ///< \ru Длина части разрыва. \en Length of the break part. - const MbCartPoint & GetFixedPoint() const { return fixedPoint; } ///< \ru Фиксированная точка. \en Fixed point. - - /** \} */ - /**\ru \name Функции изменения данных. - \en \name Functions for changing data. - \{ */ - - /// \ru Изменить номер сегмента контура. \en Change a number of the contour segment. - void SetSegmentNumber( size_t newNumber ) { segNumber = newNumber; } - /// \ru Изменить фиксированную переменную. \en Change a fixed variable. - void SetFixedFar ( double newFixedVar ) { fixedVar = newFixedVar; } - /// \ru Изменить длину части разрыва. \en Change the length of the break part. - void SetLength ( double newLength ) { length = newLength; } - /// \ru Изменить фиксированную точку. \en Change a fixed point. - void SetFixedPoint ( const MbCartPoint & point ) { fixedPoint.Init( point ); } - - /** \brief \ru Переместить. - \en Move. \~ - \details \ru Переместить на вектор.\n - \en Move by vector.\n \~ - \param[in] to - \ru Вектор перемещения. - \en Movement vector. \~ - */ - void Move ( const MbVector & to ) { fixedPoint.Move( to ); } - - /** \brief \ru Повернуть. - \en Rotate. \~ - \details \ru Повернуть на угол вокруг точки.\n - \en Rotate at angle around a point.\n \~ - \param[in] pnt - \ru Точка - центр поворота. - \en A point is a rotation center. \~ - \param[in] angle - \ru Двумерный нормализованный вектор, задающий угол вращения. - \en A two-dimensional normalized vector which defines a rotation angle. \~ - */ - void Rotate ( const MbCartPoint & pnt, const MbDirection & angle ) { fixedPoint.Rotate( pnt, angle ); } - - /** \brief \ru Преобразование. - \en Transformation. \~ - \details \ru Преобразование в соответствии с матрицей.\n - \en Transform according to matrix.\n \~ - \param[in] matr - \ru Матрица трансформации. - \en Transformation matrix. \~ - */ - void Transform ( const MbMatrix & matr ) { fixedPoint.Transform( matr ); } - - /** \brief \ru Изменить номер сегмента. - \en Change a number of the segment. \~ - \details \ru Изменить номер сегмента на заданную величину.\n - Номер сегмента не изменится, если величина изменения будет отрицательной и большей по модулю, чем номер. - \en Change a number of the segment by a given value. \n - The number of the segment does not change if the amount of change is negative and greater in absolute value than the number. \~ - \param[in] deltaN - \ru Величина увеличения номера сегмента. - \en Increase value of the segment number. \~ - */ - void ChangeSegNumber ( ptrdiff_t deltaN ) { if( deltaN >= 0 || (ptrdiff_t)segNumber >= -deltaN ) segNumber += deltaN; } - /** \} */ -private: - void operator = ( const MbBreaksPart & ); // \ru не реализован \en not implemented -}; // MbBreaksPart - - -//------------------------------------------------------------------------------ -/** \brief \ru Разрыв. - \en Break. \~ - \details \ru Разрыв контура.\n - Для использования в контуре с разрывом MbContourWithBreaks.\n - Разрыв состоит из частей MbBreaksPart, каждая из которых находится на одном сегменте контура.\n - В разрыве может быть 1 или 2 части. - Если разрыв должен располагаться более чем на трех сегментах, то он имеет 2 части, - соответствующие первому и последнему сегментам. - \en Contour break.\n - For using in the contour with break MbContourWithBreaks.\n - The break consists of parts MbBreaksPart all of which are on the same segment of the contour. \n - The break can have 1 or 2 parts. - If the break must be located more than three segments it has two parts, - corresponding to the first and the last segments. \~ - \ingroup Algorithms_2D -*/ // --- -class MATH_CLASS MbBreak { - -private: - SArray parts; // \ru части разрыва: \en part of break: - // \ru одна, если разрыв на одном сегменте, \en one if the break is on the one segment, - // \ru две, если на нескольких сегментах - первая и последняя \en two if break is on the several segments - the first and the last - -public: - - /** \brief \ru Конструктор пустого разрыва. - \en Constructor of an empty break. \~ - \details \ru Конструктор пустого разрыва.\n - Такой разрыв не может находиться в контуре с разрывом MbContourWithBreaks. - Он будет удален при перестроении. - \en Constructor of an empty break. \n - Such break can not be in the contour with break MbContourWithBreaks. - It will be removed when rebuilding. \~ - */ - MbBreak(): parts () { } - - /// \ru Копирующий конструктор. \en Copy-constructor. - MbBreak( const MbBreak & other ): parts ( other.parts ) {} - - ~MbBreak() {} - -public: - - /**\ru \name Функции доступа к данным. - \en \name Functions for access to data. - \{ */ - - ///< \ru Количество частей. \en The number of parts. - size_t PartsCount () const { return parts.Count(); } - - /** \brief \ru Часть по номеру. - \en A part by the number. \~ - \details \ru Часть по номеру части разрыва.\n - Номер не проверяется на корректность. - \en A part by the number of the break part. \n - A number isn't checked for correctness. \~ - \param[in] number - \ru Номер части разрыва, должен быть меньше количества частей. - \en The number of break part must be less than the number of parts. \~ - \return \ru Ссылку на часть разрыва. - \en Reference to part of break. \~ - */ - MbBreaksPart & GetPart ( size_t number ) const { return parts[number]; } - - /** \} */ - /**\ru \name Функции изменения данных. - \en \name Functions for changing data. - \{ */ - - /// \ru Добавить часть разрыва. \en Add a part of break. - void AddPart ( MbBreaksPart part ) { parts.Add( part ); } - /// \ru Удалить все части разрыва. \en Remove all parts of break. - void DeleteParts () { parts.HardFlush(); } - - /** \brief \ru Удалить часть. - \en Remove a part. \~ - \details \ru Удалить часть разрыва по номеру.\n - Номер проверяется на корректность. - Если номер не меньше количества частей, то разрыв не изменится. - \en Remove a part of break by the number. \n - A number Is checked for correctness. - If the number isn't less than the number of parts the break doesn't change. \~ - \param[in] number - \ru Номер части разрыва, должен быть меньше количества частей. - \en The number of break part must be less than the number of parts. \~ - */ - void DeletePart ( size_t number ) { if( number < PartsCount() ) parts.RemoveInd( number ); } - - /** \brief \ru Переместить. - \en Move. \~ - \details \ru Переместить на вектор.\n - \en Move by vector.\n \~ - \param[in] to - \ru Вектор перемещения. - \en Movement vector. \~ - */ - void Move( const MbVector & to ) - { - for( size_t i = 0, count = parts.Count(); i < count; ++i ) - parts[i].Move( to ); - } - - /** \brief \ru Повернуть. - \en Rotate. \~ - \details \ru Повернуть на угол вокруг точки.\n - \en Rotate at angle around a point.\n \~ - \param[in] pnt - \ru Точка - центр поворота. - \en A point is a rotation center. \~ - \param[in] angle - \ru Двумерный нормализованный вектор, задающий угол вращения. - \en A two-dimensional normalized vector which defines a rotation angle. \~ - */ - void Rotate( const MbCartPoint & pnt, const MbDirection & angle ) - { - for( size_t i = 0, count = parts.Count(); i < count; ++i ) - parts[i].Rotate( pnt, angle ); - } - - /** \brief \ru Преобразовать. - \en Transform. \~ - \details \ru Преобразовать в соответствии с матрицей.\n - \en Transform according to matrix.\n \~ - \param[in] matr - \ru Матрица трансформации. - \en Transformation matrix. \~ - */ - void Transform( const MbMatrix & matr ) - { - for( size_t i = 0, count = parts.Count(); i < count; ++i ) - parts[i].Transform( matr ); - } - /** \} */ -private: - void operator =( const MbBreak & ); // \ru не реализован \en not implemented - -}; // MbBreak - - -//------------------------------------------------------------------------------ -/** \brief \ru Ближайшие проекции на контур. - \en Nearest projections on the contour. \~ - \details \ru Ближайшие проекции точки на контур.\n - \en Nearest projections of point on the contour. \n \~ - \param[in] contour - \ru Контур. - \en A contour. \~ - \param[in] pnt - \ru Проецируемая точка. - \en Projecting point. \~ - \param[out] tProjs - \ru Параметры ближайших проекций. - \en Parameters of nearest projections. \~ - \param[in] isNear - \ru Выбрать только проекции, - находящиеся от проецируемой точки не дальше заданной точности. - \en Select only the projections - which are located from projecting point within a given tolerance. \~ - \param[in] mEps - \ru Точность выбора ближайших точек. - \en A tolerance of nearest points selection. \~ - \ingroup Algorithms_2D -*/ -// --- -MATH_FUNC (void) NearPointProjections( const MbContour & contour, const MbCartPoint & pnt, - SArray & tProjs, bool isNear, double mEps = METRIC_REGION ); - - - -#endif // __CUR_CONTOUR_WITH_BREAKS_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Контур с разрывами. + \en Contour with breaks. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CUR_CONTOUR_WITH_BREAKS_H +#define __CUR_CONTOUR_WITH_BREAKS_H + + +#include +#include +#include + + +class MATH_CLASS MbBreaksPart; +class MATH_CLASS MbBreak; + + +//------------------------------------------------------------------------------ +/** \brief \ru Контур c разрывами. + \en Contour with breaks. \~ + \details \ru Контур c разрывами.\n + Для использования в мультилинии MbMultiline.\n + Содержит разрывы MbBreak, видимые части - контуры MbContour.\n + При использовании в мультилинии каждуй раз при перестроении контура в нем обновляется + список номеров по количеству сегментов контура. + Каждый номер показывает номер сегмента базовой кривой мультилинии, + которому соответствует этот сегмент контура.\n + Если сегмент контура является эквидистантой сегмента базовой кривой, + то ему соответствует номер этого сегмента.\n + Если контур является сегментом обхода вершины мультилинии, + то ему соответствует номер, соответствующий предыдущему сегменту контура.\n + Разрывы в контуре не могут накладываться друг на друга. + Если в результате перестроения один разрыв наложился на другой, то они объединяются. + \en Contour with breaks.\n + For using in the multiline MbMultiline.\n + Contains breaks MbBreak, visible parts - contours MbContour.\n + When using in a multiline every time when contour is rebuilding in it are updated + list of numbers by the number of contour segments + Each number indicates the segment number of base curve of multiline. + which corresponds to this segment of contour.\n + If segment of contour is equidistant of base curve segment, + then it corresponds to the number of this segment.\n + If contour is segment of multiline vertices traverse. + then it corresponds to the number corresponding the previous segment of the contour.\n + Breaks in the contour can not be overlapped each other. + If in the result of rebuilding a break overlapped on the other break then they are combined. \~ + \ingroup Curves_2D +*/ // --- +class MATH_CLASS MbContourWithBreaks : public MbContour +{ +private: + RPArray breaks; // \ru Разрывы. \en Breaks. + RPArray visibleContours; // \ru Видимые контуры \en Visible contours. + SArray baseSegNumbers; // \ru Номера сегментов базового контура \en Numbers of segments of base contour + // \ru для задания неподвижных точек разрыва \en to define the fixed points of break + // \ru заполняется при использовании контура в мультилинии. \en filled when using the contour in the multiline. +public: + + /** \brief \ru Создание пустого контура. + \en Creation of empty contour. \~ + \details \ru Создание пустого контура без сегментов.\n + \en Creation of empty contour without segments. \n \~ + */ + MbContourWithBreaks(); + + MbContourWithBreaks( const MbContour & cnt ); ///< \ru Копирующий конструктор. \en Copy-constructor. + +private: + MbContourWithBreaks( const MbContourWithBreaks & ); // \ru не реализовано \en not implemented +protected : + MbContourWithBreaks( const MbContourWithBreaks &, MbRegDuplicate * ); +public: + virtual ~MbContourWithBreaks(); + +public : + VISITING_CLASS( MbContourWithBreaks ); + + /**\ru \name Общие функции геометрического объекта. + \en \name Common functions of a geometric object. + \{ */ + virtual MbePlaneType IsA() const; // \ru Тип элемента. \en A type of element. + virtual void Transform( const MbMatrix & matr, MbRegTransform * ireg = NULL, const MbSurface * newSurface = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. + virtual void Move( const MbVector & to, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Сдвиг. \en Move. + virtual void Rotate( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Поворот. \en Rotation. + virtual MbPlaneItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + /** \} */ + /**\ru \name Функции доступа к данным: разрывы. + \en \name Functions for access to data: breaks. + \{ */ + size_t GetBreaksCount () const { return breaks.Count(); } ///< \ru Число разрывов. \en The number of breaks. + + /** \brief \ru Разрыв по номеру. + \en A break by the number. \~ + \details \ru Разрыв по номеру.\n + Номер не проверяется на корректность. + \en A break by the number. \n + A number isn't checked for correctness. \~ + \param[in] i - \ru Номер разрыва, должен быть меньше количества разрывов. + \en The number of break must be less than the number of breaks. \~ + \return \ru Указатель на разрыв. + \en Pointer to break. \~ + */ + MbBreak * GetBreak ( size_t i ) const { return breaks[i]; } + + /** \brief \ru Разрыв по номеру в параметрах контура. + \en A break by the number in contour parameters. \~ + \details \ru Разрыв по номеру в параметрах контура.\n + Номер проверяется на корректность. + В случае, если номер больше числа сегментов результат - вывернутая область. + \en A break by the number in contour parameters. \n + A number Is checked for correctness. + If number is more than numbers of segments then result is everted region. \~ + \param[in] i - \ru Номер разрыва, должен быть меньше количества разрывов. + \en The break number must be less than the number of all breaks. \~ + \return \ru Интервал разрыва в параметрах контура. + \en Break interval in contour parameters. \~ + */ + MbRect1D GetBreaksRange ( size_t i ) const; + + /** \} */ + /**\ru \name Функции доступа к данным: видимые участки. + \en \name Functions for access to data: visible regions. + \{ */ + + /** \brief \ru Количество видимых частей. + \en The number of visible parts. \~ + \details \ru Количество видимых частей.\n + Если в контуре нет разрывов, то возвращает 0. + \en The number of visible parts. \n + If contour does not contain breaks, then returns 0. \~ + \return \ru Число видимых частей. + \en The number of visible parts. \~ + */ + size_t GetVisibleCount () const { return visibleContours.Count(); } + + /** \brief \ru Видимая часть по номеру. + \en A visible part by the number. \~ + \details \ru Видимая часть по номеру.\n + Номер не проверяется на корректность. + \en A visible part by the number. \n + A number isn't checked for correctness. \~ + \param[in] i - \ru Номер видимой части, должен быть меньше количества видимых частей. + \en The number of visible part must be less than the number of visible parts. \~ + \return \ru Указатель не контур - видимую часть. + \en A pointer to visible part of the contour. \~ + */ + const MbContour * GetVisibleContour ( size_t i ) const { return visibleContours[i]; } + + /** \} */ + /**\ru \name Функции доступа к данным: невидимые участки. + \en \name Functions for access to data: Invisible regions. + \{ */ + + /** \brief \ru Невидимая часть по номеру разрыва. + \en Invisible part by the number of break. \~ + \details \ru Невидимая часть по номеру разрыва.\n + Номер проверяется на корректность. + В случае, если номер не меньше числа разрывов, функция вернет NULL.\n + После использования полученный контур нужно удалить. + \en Invisible part by the number of break. \n + A number Is checked for correctness. + If the number is not less than the number of breaks, the function returns NULL. \n + The resulting contour is to be deleted after use. \~ + \param[in] i - \ru Номер разрыва, должен быть меньше количества видимых частей. + \en The number of break must be less than the number of visible parts. \~ + \return \ru Указатель не контур - видимую часть. + \en A pointer to visible part of the contour. \~ + */ + MbContour * GetInvisibleContour ( size_t i ) const; + + /** \} */ + /**\ru \name Работа с разрывами: добавление. + \en \name Working with breaks: addition. + \{ */ + + /** \brief \ru Добавить разрыв между точками. + \en Add a break between points. \~ + \details \ru Добавить разрыв между точками.\n + \en Add a break between points. \n \~ + \param[in] point1 - \ru Первая граница разрыва. + \en The first boundary of break. \~ + \param[in] point2 - \ru Вторая граница разрыва. + \en The second boundary of break. \~ + \param[in] point3 - \ru Точка, которая показывает удаляемую часть замкнутого контура,\n + в случае разомкнутого контура она игнорируется. + \en A point which indicates a removable part of the closed contour, \n + in the case of open contour it is ignored. \~ + \param[in] invertBreak - \ru Признак добавления разрыва на противоположную часть контура. + \en addition attribute of break on the opposite part of contour. \~ + \return \ru true, если разрыв был добавлен. + \en true if break has been added. \~ + */ + bool AddBreak ( const MbCartPoint & point1, const MbCartPoint & point2, + const MbCartPoint & point3, bool invertBreak = false ); + + /** \brief \ru Добавить разрыв между параметрами контура. + \en Add a break between contour parameters. \~ + \details \ru Добавить разрыв между параметрами контура.\n + \en Add a break between contour parameters. \n \~ + \param[in] t1 - \ru Первая граница разрыва. + \en The first boundary of break. \~ + \param[in] t2 - \ru Вторая граница разрыва. + \en The second boundary of break. \~ + \param[in] t3 - \ru Параметр, который показывает удаляемую часть замкнутого контура,\n + в случае разомкнутого контура он игнорируется. + \en A parameter which indicates a removable part of the closed contour, \n + in the case if contour is open it is ignored. \~ + \param[in] invertBreak - \ru Признак добавления разрыва на противоположную часть контура. + \en addition attribute of break on the opposite part of contour. \~ + \return \ru true, если разрыв был добавлен. + \en true if break has been added. \~ + */ + bool AddBreak ( double t1, double t2, double t3, bool invertBreak = false ); + + /** \brief \ru Добавить разрыв по интервалу параметров контура. + \en Add a break by interval of contour parameters. \~ + \details \ru Добавить разрыв по интервалу параметров контура.\n + \en Add a break by interval of contour parameters. \n \~ + \param[in] range - \ru Интервал параметров контура,\n + если range.zmin больше range.zmax, то результат работы функции + будет корректным только в случае замкнутого контура - + добавится разрыв, проходящий через начало контура. + \en An interval of contour parameters. \n + if range.zmin is greater than range.zmax then the function result + is correct only in the case of closed contour - + added a break passing through the origin of the contour. \~ + \return \ru true, если разрыв был добавлен. + \en true if break has been added. \~ + */ + bool AddBreak ( const MbRect1D & range ); + + /** \} */ + /**\ru \name Работа с разрывами: удаление. + \en \name Working with breaks: removing. + \{ */ + + /** \brief \ru Удалить разрывы. + \en Remove breaks. \~ + \details \ru Удалить все разрывы.\n + \en Remove all breaks. \n \~ + \return \ru true, если хотя бы один разрыв был удален. + \en true, if at least one break has been removed. \~ + */ + bool DeleteBreaks (); ///< \ru Удалить разрывы. \en Remove breaks. + + /** \brief \ru Удалить разрыв по номеру разрыва. + \en Remove a break by the number. \~ + \details \ru Удалить разрыв по номеру разрыва.\n + \en Remove a break by the number. \n \~ + \param[in] breakIndex - \ru Номер разрыва,\n + проверяется на корректность.\n + \en The number of break. \n + checked for correctness. \n \~ + \param[in] rebuild - \ru Нужно ли перестроить контур после удаления разрыва.\n + Если контур не перестраивать, видимые части контура не будут соответствовать разрывам.\n + Перестроить контур можно отдельно вызовом RebuildBreaks. + \en Is the contour to be rebuilt after removing break. \n + If don't rebuild contour then the visible parts of contour do not match breaks. \n + Rebuild contour you can be separately by call RebuildBreaks. \~ + \return \ru true, если разрыв был удален. + \en true if break has been removed. \~ + */ + bool DeleteBreakAtNumber ( size_t breakIndex, bool rebuild = false); + + /** \brief \ru Удалить разрыв по параметру на контуре. + \en Remove a break by parameter on the contour. \~ + \details \ru Удалить разрыв по параметру на контуре.\n + \en Remove a break by parameter on the contour. \n \~ + \return \ru true, если разрыв был удален + \en true if break has been removed \~ + */ + bool DeleteBreakAtParam ( double t ); + + /** \brief \ru Удалить разрывы на сегментах с соответствующим базовым номером. + \en Remove breaks on the segments with the corresponding base number. \~ + \details \ru Удалить разрывы на сегментах с соответствующим базовым номером.\n + Разрывы удаляться, если в контуре заполнены базовые номера сегментов. + Номера заполняются при перестроении мультилинии, содержащей контур.\n + Если сегменту контура соответствует одна из частей разрыва, то весь разрыв будет удален. + \en Remove breaks on the segments with the corresponding base number. \n + Break are removed if base numbers of segments are filled in contour. + The number are filled when multiline is rebuilt which contrains contour. \n + If segment of contour corresponds to one of break parts then entire break is removed. \~ + \param[in] baseNumber - \ru Номер сегмента базовой кривой. + \en A segment number of base curve. \~ + \param[in] delTracingBreaks - \ru Нужно ли удалять разрывы с сегментоа, соответствующих обходу вершины мультилинии. + \en Is it necessary for breaks to be removed from segments which correspond to multiline vertices traverse. \~ + \param[in] delEquidBreaks - \ru Нужно ли удалять разрывы с сегментов, соответствующих эквидистантам. + \en breaks to be removed from segments which correspond to equidistants. \~ + \param[in] delInLineSeg - \ru Удалять ли разрывы с прямолинейных сегментов. + \en Is it necessary to remode breaks from straight segments. \~ + */ + void DeleteBreaksAtBaseNumber ( size_t baseNumber, bool delTracingBreaks, + bool delEquidBreaks, bool delInLineSeg = true ); + /** \} */ + /**\ru \name Удаление разрывов и видимых частей малой метрической длины. + \en \name Removing of breaks and visible parts of the small metric length. + \{ */ + + /** \brief \ru Удалить разрывы малой метрической длины. + \en Remove breaks of the small metric length. \~ + \details \ru Удалить разрывы малой метрической длины.\n + В случае успеха видимые контуры перестраиваются соответственно разрывам. + \en Remove breaks of the small metric length. \n + In case of success the visible contours are rebuilt by breaks. \~ + \param[in] length - \ru Минимальная длина невидимой части. + \en Minimal length of invisible part. \~ + \return \ru true, если хотя бы один разрыв был удален. + \en true, if at least one break has been removed. \~ + */ + bool DeleteSmallBreaks ( double length ); + + /** \brief \ru Удалить видимые части малой метрической длины. + \en Remove visible parts of the small metric length. \~ + \details \ru Удалить видимые части малой метрической длины.\n + Соответствует объединению близких разрывов в один. + В случае успеха видимые контуры перестраиваются соответственно разрывам. + \en Remove visible parts of the small metric length. \n + Corresponds to union of close discontinuities into one. + In case of success the visible contours are rebuilt by breaks. \~ + \param[in] length - \ru Минимальная длина видимой части. + \en Minimal length of visible part. \~ + \return \ru true, если разрывы были изменены. + \en true if breaks have been changed. \~ + */ + bool DeleteSmallVisContours ( double length ); + + /** \} */ + /**\ru \name Работа с разрывами + \en \name Working with breaks + \{ */ + + /** \brief \ru Номер разрыва, край которого попал в окрестность точки. + \en The number of break the edge of which is into point neighbourhood. \~ + \details \ru Номер разрыва, край которого попал в окрестность точки.\n + \en The number of break the edge of which is into point neighbourhood. \n \~ + \param[in] p - \ru Точка. + \en Point. \~ + \param[in] rad - \ru Радиус окрестности точки для поиска разрыва. + \en Radius of point neighborhood to search the break. \~ + \param[out] index - \ru Номер разрыва. + \en The number of break. \~ + \return \ru true, если разрыв найден. + \en true if the break is found. \~ + */ + bool GetBreakAtPoint ( const MbCartPoint & p, double rad, + size_t & index ) const; + + /** \brief \ru Номера разрывов, которые хотя бы одним краем попадают в область. + \en Numbers of breaks which at least one edge fall into the region. \~ + \details \ru Номера разрывов, которые хотя бы одним краем попадают в область.\n + \en Numbers of breaks which at least one edge fall into the region. \n \~ + \param[in] rect - \ru Область поиска. + \en Region of search. \~ + \param[out] breaksNumbers - \ru Номера разрывов. + \en Numbers of breaks. \~ + \return \ru true, если хотя бы один разрыв найден. + \en true, if at least one break has been found. \~ + */ + bool GetBreaksInRect ( const MbRect & rect, + SArray & breaksNumbers ) const; + + /** \brief \ru Номера разрывов, которые хотя бы одним краем попадают в область. + \en Numbers of breaks which at least one edge fall into the region. \~ + \details \ru Номера разрывов, которые хотя бы одним краем попадают в область, заданную контуром.\n + \en Numbers of breaks which at least one edge fall into the region given contour. \n \~ + \param[in] contour - \ru Контур должен быть замкнутым и иметь правильное направление. + \en Contour must be closed and have the right direction. \~ + \param[out] breaksNumbers - \ru Номера разрывов. + \en Numbers of breaks. \~ + \return \ru true, если хотя бы один разрыв найден. + \en true, if at least one break has been found. \~ + */ + bool GetBreaksInRect ( const MbContour & contour, + SArray & breaksNumbers ) const; + + /** \brief \ru Определить попадает ли точка в любой из разрывов. + \en Determine whether a point it inside a break. \~ + \details \ru Определить попадает ли точка в любой из разрывов.\n + \en Determine whether a point it inside a break. \n \~ + \param[in] p - \ru Точка для проверки. + \en A point for the check. \~ + \return \ru true, если точка попадает в любой из разрывов. + \en true, if point falls into any break. \~ + */ + bool HitToBreaks ( const MbCartPoint & p ) const; + + /** \brief \ru Находится ли интервал параметров на разрыве. + \en Whether the interval of parameters is on break. \~ + \details \ru Находится ли интервал параметров на разрыве.\n + \en Whether the interval of parameters is on break. \n \~ + \param[in] rect - \ru Интервал для проверки. + \en Interval to check. \~ + \return \ru true, если интервал полностью находится на разрыве или совпадает с ним. + \en true if interval entirely is on the break or coincides with it. \~ + */ + bool IsRectInBreak ( const MbRect1D & rect ); + + /** \brief \ru Обновить невидимые и видимые контуры. + \en Update visible and invisible contours. \~ + \details \ru Обновить невидимые и видимые контуры соответственно базовой кривой мультилинии.\n + После перестроения разрывы должны соответствовать сегментам базовой кривой мультилинии. + \en Update visible and invisible contours by base curve of multiline respectively. \n + After rebuilding the breaks must correspond to segments of base multiline curve. \~ + \param[in] oldBaseNumbers - \ru Старые номера базовых сегментов,\n + должны быть запомнены до изменения мультилинии. + \en Old numbers of base segments, \n + must be saved before multiline is changed. \~ + */ + void RebuildBreaks ( SArray & oldBaseNumbers ); + + /** \brief \ru Обновить невидимые и видимые контуры. + \en Update visible and invisible contours. \~ + \details \ru Обновить невидимые и видимые контуры соответственно разрывам. + \en Update visible and invisible contours according to breaks. \~ + */ + void RebuildBreaks ( ); + + /** \brief \ru Преобразование в соответствии с матрицей. + \en Transform according to matrix. \~ + \details \ru Преобразование в соответствии с матрицей.\n + Используется для преобразования мультилинии. + Преобразует длину и расстояние от фиксированной точки прямолинейного разрыва, + а так же фиксированную точку. + \en Transform according to matrix.\n + Used to transform multiline. + Transforms length and distance from fixed point of straight break, + and fixed point. \~ + \param[in] matr - \ru Матрица преобразования. + \en A transformation matrix. \~ + */ + void TransformMultlinesBreaks ( const MbMatrix & matr ); + + /** \brief \ru Параметр привязки части разрыва. + \en A binding parameter of a break part. \~ + \details \ru Посчитать параметр привязки части разрыва в зависимости от типа сегмента контура.\n + \en Calculate binding parameter of a break part according to the type of contour segment. \n \~ + \param[in] brPart - \ru Часть разрыва этого контура. + \en A break part of this contour. \~ + \param[out] segNumber - \ru Номер сегмента контура. + \en A number of the contour segment. \~ + \return \ru Параметр центра разрыва на сегменте контура. + \en A parameter of the break center on the contour segment. \~ + */ + double GetLocalBreaksParam ( const MbBreaksPart & brPart, + size_t & segNumber ) const; + + /** \} */ + /**\ru \name Работа с разрывами: изменение разрыва. + \en \name Working with breaks: changing a break. + \{ */ + + /** \brief \ru Фиксировать точку. + \en Fix the point. \~ + \details \ru Поставить фиксированную точку части разрыва.\n + \en Set the fixed point of a break part. \n \~ + \param[in] newPoint - \ru Новая фиксированная точка. + \en A new fixed point. \~ + \param[out] part - \ru Часть разрыва контура для изменения. + \en A part of contour break to change. \~ + */ + void SetBreakFixedPoint ( const MbCartPoint & newPoint, + MbBreaksPart & part ); + + /** \brief \ru Фиксировать переменную. + \en Fix the variable. \~ + \details \ru Поставить фиксированную переменную части разрыва.\n + \en Set the fixed variable of a break part. \n \~ + \param[in] newFixedVar - \ru Новая фиксированная переменная. + \en New fixed variable. \~ + \param[out] part - \ru Часть разрыва контура для изменения. + \en A part of contour break to change. \~ + */ + void SetBreakFixedVar ( double newFixedVar, MbBreaksPart & part); + + /** \} */ + /**\ru \name Работа с разрывами: отслеживание разрыва + \en \name Working with breaks: tracking a break + \{ */ + /// \ru Количество номеров сегментов базового контура \en Count of segments numbers of the base contour + // \ru (должно соответствовать количеству сегментов контура) \en (must be equal to count of contour segments) + size_t GetBaseNumbersCount () const { return baseSegNumbers.Count(); } + + /// \ru Номера сегментов базового контура. \en Numbers of segments of base contour. + void GetBaseNumbers ( SArray & baseNumbers ) const; + + /** \brief \ru Номер сегмента базового контура. + \en Segment number of the base contour. \~ + \details \ru Номер сегмента базового контура.\n + Номер сегмента будет найден, если массив номеров не пуст и + корректно насчитан, то есть число номеров совпадает с числом сегментов контура + \en Segment number of the base contour. \n + Segment number will be found if the array of numbers is not empty and + it was numbered correctly, ie count of numbers is equal to count of contour segments \~ + \param[in] i - \ru Индекс номера. + \en Index of number. \~ + \return \ru Номер сегмента из массива номеров. + \en Number of segment from the array of numbers. \~ + */ + size_t GetBaseNumber ( size_t i ) const; + + /** \brief \ru Добавить номер базового сегмента. + \en Add number of the base segment. \~ + \details \ru Добавить номер сегмента базового контура в конец массива.\n + \en Add segment number of base contour to the end of array. \n \~ + \param[in] number - \ru Номер для добавления. + \en Number to add. \~ + */ + void AddBaseSegNumber ( size_t number ); + + /** \brief \ru Добавить номер базового сегмента. + \en Add number of the base segment. \~ + \details \ru Добавить номер сегмента базового контура в начало массива.\n + \en Add segment number of base contour to the beginning of array. \n \~ + \param[in] number - \ru Номер для добавления. + \en Number to add. \~ + */ + void AddBaseSegNumberAtBegin ( size_t number ); + + /** \brief \ru Вставить номер после lastInd. + \en Insert number after the lastInd. \~ + \details \ru Вставить после элемента номер lastInd номер, соответствующий lastInd.\n + \en Insert number corresponding lastInd after element lastInd. \n \~ + \param[in] lastInd - \ru Индекс номера. + \en Index of number. \~ + */ + void InsertLastSegNumber ( size_t lastInd ); + + /** \brief \ru Удалить элемент по индексу. + \en Delete element by an index. \~ + \details \ru Удалить номер базового сегмента по индексу.\n + \en Remove number of the base segment by an index. \n \~ + \param[in] ind - \ru Индекс номера. + \en Index of number. \~ + */ + void DeleteBaseSegNumber ( size_t ind ); + + /// \ru Очистить массив с номерами базовых сегментов. \en Clear the array with numbers of base segments. + void ClearBaseSegNumbers () { baseSegNumbers.HardFlush(); } + + /** \brief \ru Изменить номера сегментов в разрывах. + \en Change numbers of segments in the breaks. \~ + \details \ru Изменить номера сегментов в разрывах.\n + Заданному номеру базового сегмента соответствует сегмент - эквидистанта на контуре. + Для всех разрывов: + если хотя бы часть разрыва находится на этом сегменте, + у всех его частей номер сегмента будет изменен. + \en Change numbers of segments in the breaks. \n + A given number of the base segments corresponds to the segment - equidistant on the contour. + For all breaks: + if at least one part of break is in this segment, + the segment number of all of its parts will be changed. \~ + \param[in] begBaseNumber - \ru Номер базового сегмента. + \en An index of the base segment. \~ + \param[in] deltaN - \ru Величина изменение номера сегмента частей разрывов. + \en The change value of segment number of breaks parts. \~ + */ + void ChangeBreaksSegNumbers ( size_t begBaseNumber, ptrdiff_t deltaN ); + + /** \brief \ru Изменить разрывы соотвестсвенно замкнутости. + \en Change breaks of closedness respectively. \~ + \details \ru Изменить разрывы соотвестсвенно замкнутости.\n + При изменении признака замкнутости контура нужно изменить разрывы + соответственно новому значению замкнутости.\n + Если контур стал разомкнутым - разрыв, находящийся на первом и последнем + сегменте одновременно, делится на 2 части.\n + Если контур стал замкнутым - разрывы, первый из который примыкает к левому краю + первого сегмента контура, а второй из которых примыкает к правому краю последнего + сегмента контура, объединяется в один. + \en Change breaks of closedness respectively. \n + When changing attribute of contour closedness breaks need to change + respectively to the new value of closedness. \n + If contour has become open - the break located on the first and last + segment is divided into 2 parts at the same time. \n + If contour has become closed - breaks, the first of which is adjacent to the left boundary + of the first contour segment and the second of which is adjacent to the right boundary of the last + contour segment are united into one. \~ + \param[in] newClosed - \ru Новый признак замкнутости контура. + \en The new closedness attribute of contour. \~ + */ + void ChangeBreaksAtClosed ( bool newClosed ); + + /** \brief \ru Изменить номера сегментов частей разрывов. + \en Change segments numbers of breaks parts. \~ + \details \ru Изменить номера сегментов частей разрывов.\n + \en Change segments numbers of breaks parts. \n \~ + \param[in] deltaN - \ru Величина изменения. + \en A change value. \~ + */ + void MoveBreaksSegNumbers ( ptrdiff_t deltaN ); + + /** \brief \ru Находится ли часть разрыва этого контура на сегменте обхода вершин. + \en Is the break part of this contour located on the segment of vertices traverse. \~ + \details \ru Находится ли часть разрыва этого контура на сегменте обхода вершин.\n + \en Is the break part of this contour located on the segment of vertices traverse. \n \~ + \param[in] part - \ru Часть разрыва этого контура. + \en A break part of this contour. \~ + \param[out] vertNumber - \ru В случае успеха вернет номер вершины. + \en In the case of success returns the vertex number. \~ + \return \ru true, если часть разрыва находится на сегменте обхода вершины. + \en true, if the break part is located on the segment of vertices traverse. \~ + */ + bool IsTrasingBreaksPart ( const MbBreaksPart & part, + size_t & vertNumber ) const; + + /** \brief \ru Поменяться разрывами. + \en Swap breaks. \~ + \details \ru Поменяться разрывами.\n + \en Swap breaks. \n \~ + \param[in] other - \ru Контур с разрывами для обмена. + \en A contour with breaks to swap. \~ + */ + void SwapBreaksAndBaseNumbers ( MbContourWithBreaks & other ); + + /** \brief \ru Добавить разрывы контура. + \en Add contour breaks. \~ + \details \ru Добавить разрывы контура.\n + \en Add contour breaks. \n \~ + \param[in] other - \ru Контур с разрывами для добавления разрывов. + \en A contour with breaks for adding breaks. \~ + */ + void AddContoursBreaks ( const MbContourWithBreaks & other ); + + /** \brief \ru Заменить номера базовых сегментов. + \en Replace the numbers of base segments. \~ + \details \ru Заменить номера базовых сегментов.\n + \en Replace the numbers of base segments. \n \~ + \param[in] other - \ru Контур с новыми номерами. + \en A contour with new numbers. \~ + */ + void ChangeBaseNumbers ( const MbContourWithBreaks & other ); + /** \} */ + +private: + void DeleteBreaks ( size_t segmentIndex, + bool delInLineSeg = true ); // \ru удалить разрывы на сегменте с номером index \en remove breaks from segment with number "index" + void DeleteBreaksPartAtSegNum ( size_t segNumber, size_t oldSegCount ); // \ru часть разрыва по номеру сегмента \en part of break by the segment number + bool DeleteBreaksPartOrBreak ( size_t breakIndex, size_t partIndex, + size_t oldSegCount ); // \ru часть разрыва или разрыв \en part of break or break +private: + void CalculateVisibleContours ( ); // \ru посчитать видимые части \en calculate visible parts + void CalculateInvisibleContours( RPArray & invisibleContours ); // \ru посчитать невидимые части \en calculate invisible parts + void CalculateContours ( const CSSArray & ranges, // \ru посчитать контуры по интервалам \en calculate contours by intervals + RPArray & contours, + bool visible ) const; // \ru для видимых частей вызывать AddRef() \en call AddRef() for visible parts + void CalculateInVisibleRanges ( CSSArray & breaksRanges ); // \ru посчитать невидимые интервалы \en calculate invisible intervals + void CalculateVisibleRanges ( CSSArray & breaksRanges, + CSSArray & visibleRanges ) const; // \ru посчитать видимые интервалы \en Calculate visible intervals + void CalculateRanges ( const CSSArray & startingInt, // \ru посчитать противоположные интервалы \en Calculate opposite intervals + CSSArray & resultInt ) const; // \ru второй массив по первому \en the second array by the first + void CalculateBreaksPart ( const MbRect1D & segParams, + size_t segNumber, MbBreak & brRange ) const; // \ru посчитать часть разрыва \en Calculate a part of the break + void CalculateBreak ( const MbRect1D & range, + MbBreak & brRange ) const; // \ru посчитать разрыв по интервалу \en Calculate the break by interval + MbRect1D GetLocalBreaksRange ( const MbBreaksPart & part, + double brParam, size_t segNumber ) const; // \ru интервал по параметру привязки \en interval by the binding parameter + void AddCalcBreak ( const MbRect1D & range ); + void ChangeBreaksSegNumbers ( const SArray & oldBaseNumbers, + SArray & oldEqCounts, + SArray & newEqCounts ); // \ru изменить номера сегментов у разрывов \en change segments numbers of breaks + void RedefineBreaksParts (); // \ru доопределить неопределенные части разрыва \en complete the definition of indefinite parts of the break + + // \ru для преобразования контуров \en for transformation of contours + void TransformBreaks ( const MbMatrix & matr ); // \ru преобразование в соответствии с матрицей \en transform according to the matrix + + size_t GetLocalBreaksRange ( const MbBreaksPart & part, + MbRect1D & localRect ) const; // \ru разрыв по номеру в параметрах сегмента \en a break by the number in segment parameters + + + void operator = ( const MbContourWithBreaks & ); // \ru не реализован \en not implemented + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbContourWithBreaks ) + +}; // MbContourWithBreaks + +IMPL_PERSISTENT_OPS( MbContourWithBreaks ) + +//------------------------------------------------------------------------------ +/** \brief \ru Часть разрыва. + \en Part of break. \~ + \details \ru Часть разрыва контура мультилинии. Относится к одному сегменту контура.\n + Для использования в разрыве MbBreak. + \en Part of multiline contour break. Applicable to one segment of the contour.\n + For using in the break MbBreak. \~ + \ingroup Algorithms_2D +*/ // --- +class MATH_CLASS MbBreaksPart { + +private : + size_t segNumber; // \ru Номер сегмента \en Number of the segment. + double fixedVar; // \ru Фиксированная переменная \en Fixed variable + // \ru ( tMax - tCentre ) / ( tCentre - tMin ), tCentre - параметр центра разрыва \en ( tMax - tCentre ) / ( tCentre - tMin ), tCentre - center of the break + // \ru для отрезка - расстояние до проекции неподвижной точки \en for segment - distance to projection of the fixed point + double length; // \ru Длина части разрыва (для отрезка) \en Length of the break part (for segment) + // \ru для дуг и по умолчанию сохраняем параметрическую длину \en For arcs and save parametric length by default. + // \ru (для дуги - угол) \en (for arc - angle) + MbCartPoint fixedPoint; // \ru неподвижная точка ( корректное значение для отрезка ) \en fixed point (correct value for segment) + +public: + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор по номеру сегмента, фиксированной переменной, длине, неподвижной точке. + \en Constructor by a number of segment, fixed variable, length, fixed point. \~ + \param[in] sNumber - \ru Номер сегмента контура, на котором находится часть разрыва. + \en Number of contour segment where a part of break is located. \~ + \param[in] fixVar - \ru Фиксированная переменная:\n + для отрезка - расстояние до проекции неподвижной точки,\n + в общем случае - величина, равная ( tMax - tCentre ) / ( tCentre - tMin ), где\n + tMin - минимальный параметр сегмента,\n + tMax - максимальный параметр сегмента,\n + tCentre - параметр центра части разрыва. + \en Fixed variable:\n + for segment - distance to projection of the fixed point,\n + In general case - a value which is equal to ( tMax - tCentre ) / ( tCentre - tMin ), where \n + tMin - minimal parameter of the segment,\n + tMax - maximal parameter of the segment,\n + tCentre - parameter of the break part center. \~ + \param[in] len - \ru Длина части разрыва:\n + для отрезка - метрическая длина,\n + в общем случае - параметрическая длина. + \en Length of the break part:\n + for segment - metric length, \n + In the general case - parametric length. \~ + \param[in] p - \ru Неподвижная точка:\n + для отрезка - используется для привязки части разрыва,\n + в общем случае - не имеет смысла. + \en Fixed point:\n + for segment - used to bind part of the break,\n + in general case - it is useless. \~ + */ + MbBreaksPart( size_t sNumber, double fixVar, double len, const MbCartPoint & p ) + : segNumber ( sNumber ), + fixedVar ( fixVar ), + length ( len ), + fixedPoint( p ) + { + } + + /// \ru Копирующий конструктор. \en Copy-constructor. + MbBreaksPart( const MbBreaksPart & other ) + : segNumber ( other.GetSegmentNumber() ), + fixedVar ( other.GetFixedVar() ), + length ( other.GetLength() ), + fixedPoint( other.GetFixedPoint() ) + { + } + + ~MbBreaksPart(){}; + + /**\ru \name Функции доступа к данным. + \en \name Functions for access to data. + \{ */ + size_t GetSegmentNumber() const { return segNumber; } ///< \ru Номер сегмента контура. \en A number of the contour segment. + double GetFixedVar() const { return fixedVar; } ///< \ru Фиксированная переменная. \en fixed variable + double GetLength() const { return length; } ///< \ru Длина части разрыва. \en Length of the break part. + const MbCartPoint & GetFixedPoint() const { return fixedPoint; } ///< \ru Фиксированная точка. \en Fixed point. + + /** \} */ + /**\ru \name Функции изменения данных. + \en \name Functions for changing data. + \{ */ + + /// \ru Изменить номер сегмента контура. \en Change a number of the contour segment. + void SetSegmentNumber( size_t newNumber ) { segNumber = newNumber; } + /// \ru Изменить фиксированную переменную. \en Change a fixed variable. + void SetFixedFar ( double newFixedVar ) { fixedVar = newFixedVar; } + /// \ru Изменить длину части разрыва. \en Change the length of the break part. + void SetLength ( double newLength ) { length = newLength; } + /// \ru Изменить фиксированную точку. \en Change a fixed point. + void SetFixedPoint ( const MbCartPoint & point ) { fixedPoint.Init( point ); } + + /** \brief \ru Переместить. + \en Move. \~ + \details \ru Переместить на вектор.\n + \en Move by vector.\n \~ + \param[in] to - \ru Вектор перемещения. + \en Movement vector. \~ + */ + void Move ( const MbVector & to ) { fixedPoint.Move( to ); } + + /** \brief \ru Повернуть. + \en Rotate. \~ + \details \ru Повернуть на угол вокруг точки.\n + \en Rotate at angle around a point.\n \~ + \param[in] pnt - \ru Точка - центр поворота. + \en A point is a rotation center. \~ + \param[in] angle - \ru Двумерный нормализованный вектор, задающий угол вращения. + \en A two-dimensional normalized vector which defines a rotation angle. \~ + */ + void Rotate ( const MbCartPoint & pnt, const MbDirection & angle ) { fixedPoint.Rotate( pnt, angle ); } + + /** \brief \ru Преобразование. + \en Transformation. \~ + \details \ru Преобразование в соответствии с матрицей.\n + \en Transform according to matrix.\n \~ + \param[in] matr - \ru Матрица трансформации. + \en Transformation matrix. \~ + */ + void Transform ( const MbMatrix & matr ) { fixedPoint.Transform( matr ); } + + /** \brief \ru Изменить номер сегмента. + \en Change a number of the segment. \~ + \details \ru Изменить номер сегмента на заданную величину.\n + Номер сегмента не изменится, если величина изменения будет отрицательной и большей по модулю, чем номер. + \en Change a number of the segment by a given value. \n + The number of the segment does not change if the amount of change is negative and greater in absolute value than the number. \~ + \param[in] deltaN - \ru Величина увеличения номера сегмента. + \en Increase value of the segment number. \~ + */ + void ChangeSegNumber ( ptrdiff_t deltaN ) { if( deltaN >= 0 || (ptrdiff_t)segNumber >= -deltaN ) segNumber += deltaN; } + /** \} */ +private: + void operator = ( const MbBreaksPart & ); // \ru не реализован \en not implemented +}; // MbBreaksPart + + +//------------------------------------------------------------------------------ +/** \brief \ru Разрыв. + \en Break. \~ + \details \ru Разрыв контура.\n + Для использования в контуре с разрывом MbContourWithBreaks.\n + Разрыв состоит из частей MbBreaksPart, каждая из которых находится на одном сегменте контура.\n + В разрыве может быть 1 или 2 части. + Если разрыв должен располагаться более чем на трех сегментах, то он имеет 2 части, + соответствующие первому и последнему сегментам. + \en Contour break.\n + For using in the contour with break MbContourWithBreaks.\n + The break consists of parts MbBreaksPart all of which are on the same segment of the contour. \n + The break can have 1 or 2 parts. + If the break must be located more than three segments it has two parts, + corresponding to the first and the last segments. \~ + \ingroup Algorithms_2D +*/ // --- +class MATH_CLASS MbBreak { + +private: + SArray parts; // \ru части разрыва: \en part of break: + // \ru одна, если разрыв на одном сегменте, \en one if the break is on the one segment, + // \ru две, если на нескольких сегментах - первая и последняя \en two if break is on the several segments - the first and the last + +public: + + /** \brief \ru Конструктор пустого разрыва. + \en Constructor of an empty break. \~ + \details \ru Конструктор пустого разрыва.\n + Такой разрыв не может находиться в контуре с разрывом MbContourWithBreaks. + Он будет удален при перестроении. + \en Constructor of an empty break. \n + Such break can not be in the contour with break MbContourWithBreaks. + It will be removed when rebuilding. \~ + */ + MbBreak(): parts () { } + + /// \ru Копирующий конструктор. \en Copy-constructor. + MbBreak( const MbBreak & other ): parts ( other.parts ) {} + + ~MbBreak() {} + +public: + + /**\ru \name Функции доступа к данным. + \en \name Functions for access to data. + \{ */ + + ///< \ru Количество частей. \en The number of parts. + size_t PartsCount () const { return parts.Count(); } + + /** \brief \ru Часть по номеру. + \en A part by the number. \~ + \details \ru Часть по номеру части разрыва.\n + Номер не проверяется на корректность. + \en A part by the number of the break part. \n + A number isn't checked for correctness. \~ + \param[in] number - \ru Номер части разрыва, должен быть меньше количества частей. + \en The number of break part must be less than the number of parts. \~ + \return \ru Ссылку на часть разрыва. + \en Reference to part of break. \~ + */ + MbBreaksPart & GetPart ( size_t number ) const { return parts[number]; } + + /** \} */ + /**\ru \name Функции изменения данных. + \en \name Functions for changing data. + \{ */ + + /// \ru Добавить часть разрыва. \en Add a part of break. + void AddPart ( MbBreaksPart part ) { parts.Add( part ); } + /// \ru Удалить все части разрыва. \en Remove all parts of break. + void DeleteParts () { parts.HardFlush(); } + + /** \brief \ru Удалить часть. + \en Remove a part. \~ + \details \ru Удалить часть разрыва по номеру.\n + Номер проверяется на корректность. + Если номер не меньше количества частей, то разрыв не изменится. + \en Remove a part of break by the number. \n + A number Is checked for correctness. + If the number isn't less than the number of parts the break doesn't change. \~ + \param[in] number - \ru Номер части разрыва, должен быть меньше количества частей. + \en The number of break part must be less than the number of parts. \~ + */ + void DeletePart ( size_t number ) { if( number < PartsCount() ) parts.RemoveInd( number ); } + + /** \brief \ru Переместить. + \en Move. \~ + \details \ru Переместить на вектор.\n + \en Move by vector.\n \~ + \param[in] to - \ru Вектор перемещения. + \en Movement vector. \~ + */ + void Move( const MbVector & to ) + { + for( size_t i = 0, count = parts.Count(); i < count; ++i ) + parts[i].Move( to ); + } + + /** \brief \ru Повернуть. + \en Rotate. \~ + \details \ru Повернуть на угол вокруг точки.\n + \en Rotate at angle around a point.\n \~ + \param[in] pnt - \ru Точка - центр поворота. + \en A point is a rotation center. \~ + \param[in] angle - \ru Двумерный нормализованный вектор, задающий угол вращения. + \en A two-dimensional normalized vector which defines a rotation angle. \~ + */ + void Rotate( const MbCartPoint & pnt, const MbDirection & angle ) + { + for( size_t i = 0, count = parts.Count(); i < count; ++i ) + parts[i].Rotate( pnt, angle ); + } + + /** \brief \ru Преобразовать. + \en Transform. \~ + \details \ru Преобразовать в соответствии с матрицей.\n + \en Transform according to matrix.\n \~ + \param[in] matr - \ru Матрица трансформации. + \en Transformation matrix. \~ + */ + void Transform( const MbMatrix & matr ) + { + for( size_t i = 0, count = parts.Count(); i < count; ++i ) + parts[i].Transform( matr ); + } + /** \} */ +private: + void operator =( const MbBreak & ); // \ru не реализован \en not implemented + +}; // MbBreak + + +//------------------------------------------------------------------------------ +/** \brief \ru Ближайшие проекции на контур. + \en Nearest projections on the contour. \~ + \details \ru Ближайшие проекции точки на контур.\n + \en Nearest projections of point on the contour. \n \~ + \param[in] contour - \ru Контур. + \en A contour. \~ + \param[in] pnt - \ru Проецируемая точка. + \en Projecting point. \~ + \param[out] tProjs - \ru Параметры ближайших проекций. + \en Parameters of nearest projections. \~ + \param[in] isNear - \ru Выбрать только проекции, + находящиеся от проецируемой точки не дальше заданной точности. + \en Select only the projections + which are located from projecting point within a given tolerance. \~ + \param[in] mEps - \ru Точность выбора ближайших точек. + \en A tolerance of nearest points selection. \~ + \ingroup Algorithms_2D +*/ +// --- +MATH_FUNC (void) NearPointProjections( const MbContour & contour, const MbCartPoint & pnt, + SArray & tProjs, bool isNear, double mEps = METRIC_REGION ); + + + +#endif // __CUR_CONTOUR_WITH_BREAKS_H diff --git a/C3d/Include/cur_cosinusoid.h b/C3d/Include/cur_cosinusoid.h index e331252..b9fb77b 100644 --- a/C3d/Include/cur_cosinusoid.h +++ b/C3d/Include/cur_cosinusoid.h @@ -26,7 +26,7 @@ class MbRegTransform; /** \brief \ru Косинусоида в двумерном пространстве. \en Cosinusoid in two-dimensional space. \~ \details \ru Косинусоида расположена вдоль оси X локальной системы координат. \n - Радиус-вектор кривой в методе PointOn(double&t,MbCartPoint3D&r) описывается векторной функцией\n + Радиус-вектор кривой в методе PointOn(double&t,MbCartPoint3D&r) описывается векторной функцией\n r(t) = position.origin + (position.axisX ((tmin + t) - phase) / frequency) + (amplitude cos(tmin + t) position.axisY).\n Косинусоида приведена на рисунке ниже. t = 0 @@ -39,7 +39,7 @@ class MbRegTransform; / | \/ y = amplitude cos(frequency x + phase) \en Cosinusoid located along the X-axis of the local coordinate system. \n - Radius-vector of the curve in the method PointOn(double&t,MbCartPoint3D&r) is described by the vector function\n + Radius-vector of the curve in the method PointOn(double&t,MbCartPoint3D&r) is described by the vector function\n r(t) = position.origin + (position.axisX ((tmin + t) - phase) / frequency) + (amplitude cos(tmin + t) position.axisY).\n Cosinusoid is shown in the figure below. t = 0 @@ -54,7 +54,7 @@ class MbRegTransform; \ingroup Curves_2D */ // --- -class MATH_CLASS MbCosinusoid: public MbCurve, public MbNestSyncItem { +class MATH_CLASS MbCosinusoid: public MbCurve { private : MbPlacement position; ///< \ru Локальная система координат. \en Local coordinate system. double frequency; ///< \ru Циклическая частота (angular frequency). \en Angular frequency. @@ -93,7 +93,7 @@ public: virtual void Transform ( const MbMatrix & matr, MbRegTransform * ireg = NULL, const MbSurface * newSurface = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix virtual void Move ( const MbVector & to, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Сдвиг \en Translation virtual void Rotate ( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Поворот \en Rotation - virtual bool IsSame ( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; + virtual bool IsSame ( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; virtual MbPlaneItem & Duplicate ( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element virtual void Refresh(); // \ru Сбросить все временные данные \en Reset all temporary data virtual void PrepareIntegralData( const bool forced ) const; // \ru Рассчитать временные (mutable) данные объекта. \en Calculate temporary (mutable) data of an object. diff --git a/C3d/Include/cur_crooked_spiral.h b/C3d/Include/cur_crooked_spiral.h index 8ed8ae9..a01898f 100644 --- a/C3d/Include/cur_crooked_spiral.h +++ b/C3d/Include/cur_crooked_spiral.h @@ -1,149 +1,151 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Спираль постоянного радиуса и осью, заданной произвольной кривой на плоскости XZ position. - \en Spiral with constant radius and axis defined by an arbitrary curve on the XZ plane "position". \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CUR_CROOKET_SPIRAL_H -#define __CUR_CROOKET_SPIRAL_H - - -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Спираль с криволинейной осью. - \en Spiral with a curvilinear axis. \~ - \details \ru Спираль постоянного радиуса и осью, заданной произвольной плоской кривой. - Ось спирали определяется кривой curve, располагающейся в плоскости ZX локальной системы координат спирали. - При этом ось Z локальной системы координат спирали служит осью X системы координат двумерной кривой curve, - а ось X локальной системы координат спирали служит осью Y системы координат двумерной кривой curve, - что приведено на рис. 1 ниже. \n - Радиус-вектор кривой в методе PointOn(double&t,MbCartPoint3D&r) описывается векторной функцией: \n - r(t) = position.origin + - (position.axisX (point.y + (radius cos(t) normal.ay)) + - (position.axisY radius sin(t)) + - (position.axisZ (point.x + (radius cos(t) normal.ax)), - где point - точка кривой curve, normal - нормаль кривой curve. - Рис. 1. - ^ Ось X локальной системы координат спирали является осью Y системы координат curve. - | - | curve(w) - | - +----> Ось Z локальной системы координат спирали является осью X системы координат curve. - \en Spiral with a constant radius and axis defined by an arbitrary plane curve. - Spiral axis is determined by the curve "curve" based in the ZX plane of the local coordinate system of spiral. - The Z-axis of the local coordinate system is the X-axis of coordinate system of two-dimensional uv-curve "curve", - and the X-axis of the local coordinate system is the Y-axis of the coordinate system of two-dimensional uv-curve "curve", - that is shown in fig. 1 below. \n - The radius-vector of curve in the method PointOn(double&t,MbCartPoint3D&r) is described by a vector function: \n - r(t) = position.origin + - (position.axisX (point.y + (radius cos(t) normal.ay)) + - (position.axisY radius sin(t)) + - (position.axisZ (point.x + (radius cos(t) normal.ax)), - where "point" is point of the curve "curve", "normal" is normal of the curve "curve". - Fig. 1 1. - ^ X-axis of the local coordinate system of the spiral is Y-axis of the coordinate system of the curve "curve". - | - | curve(w) - | - +----> Z-axis of the local coordinate system of the spiral is X-axis of the coordinate system of the curve "curve". \~ - \ingroup Curves_3D -*/ -// --- -class MATH_CLASS MbCrookedSpiral : public MbSpiral { - typedef std::vector CurveParams; -protected: - MbCurve * curve; ///< \ru Кривая, задающая ось спирали, (не может быть NULL). \en The curve which determines the axis of the spiral, (can not be NULL). - double radius; ///< \ru Радиус спирали. \en A spiral radius. - double wMin; ///< \ru Минимальное значение параметра curve. \en Minimal value of parameter "curve". - double wMax; ///< \ru Максимальное значение параметра curve. \en Maximal value of parameter "curve". - double t0; ///< \ru Начальный угол спирали. \en The initial angle of the spiral. - bool curveSense; ///< \ru Совпадение направления оси спирали с направлением кривой curve. \en The coincidence of the direction of the spiral axis with the direction of the curve "curve". - CurveParams curveParams; ///< \ru Параметры спирали (параметрические сдвиги от начала кривой) и параметры двумерной кривой. \en Parameters of spiral (parametric shifts from the beginning of the curve) and parameters of "curve". - -protected: - MbCrookedSpiral( const MbCrookedSpiral & init ); // \ru Не реализовано \en Not implemented - MbCrookedSpiral( const MbCrookedSpiral & init, MbRegDuplicate * iReg ); -public : - MbCrookedSpiral( const MbPlacement3D & pos, MbCurve & axisCurve, double radius, double step, bool same ); // \ru Спираль с кривой осью \en Spiral with a curvilinear axis - virtual ~MbCrookedSpiral(); - -public : - VISITING_CLASS( MbCrookedSpiral ); - - void Init( const MbCrookedSpiral & init ); - void Init( const MbPlacement3D & place ); - - // \ru Общие функции математического объекта \en Common functions of the mathematical object - - virtual MbeSpaceType IsA() const; // \ru Тип элемента \en Type of element - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element - virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; - virtual bool SetEqual( const MbSpaceItem & init ); // \ru Сделать равным \en Make equal - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object - - // \ru Общие функции кривой \en Common functions of curve - - virtual bool IsClosed() const; // \ru Проверка замкнутости кривой \en Check for curve closedness - // \ru Функции для работы внутри области определения кривой. \en Functions for working inside of the curve domain. \~ - virtual void PointOn ( double & t, MbCartPoint3D & pnt ) const; // \ru Точка на кривой \en Point on the curve - virtual void FirstDer ( double & t, MbVector3D & fd ) const; // \ru Первая производная \en First derivative - virtual void SecondDer( double & t, MbVector3D & sd ) const; // \ru Вторая производная \en Second derivative - virtual void ThirdDer ( double & t, MbVector3D & td ) const; // \ru Третья производная по t \en Third derivative with respect to t - // \ru Функции для работы внутри и вне области определения кривой. \en Functions for working inside and outside of the curve domain. \~ - virtual void _PointOn ( double t, MbCartPoint3D & pnt ) const; // \ru Точка на кривой \en Point on the curve - virtual void _FirstDer ( double t, MbVector3D & fd ) const; // \ru Первая производная \en First derivative - virtual void _SecondDer( double t, MbVector3D & sd ) const; // \ru Вторая производная \en Second derivative - virtual void _ThirdDer ( double t, MbVector3D & td ) const; // \ru Третья производная по t \en Third derivative with respect to t - // \ru Вычислить значения точки и производных для заданного параметра. \en Calculate point and derivatives of object for given parameter. \~ - virtual void Explore( double & t, bool ext, - MbCartPoint3D & pnt, MbVector3D & fir, MbVector3D * sec, MbVector3D * thir ) const; - - virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменить направление \en Change direction - - virtual MbCurve3D * Trimmed( double t1, double t2, int sense ) const; // \ru Создание усеченной кривой \en Creation of a trimmed curve - virtual double CalculateLength( double t1, double t2 ) const; - virtual void GetBasisItems( RPArray & ); - - // \ru Функции спирали \en Functions of spiral - - virtual void SetStep( double s ); // \ru Изменить шаг \en Change step - virtual double GetSpiralRadius( double t ) const; // \ru Выдать физический радиус спирали \en Get physical radius of spiral - const MbCurve & GetAxisCurve() const { return *curve; }; // \ru Выдать осевую кривую \en Get axial curve - double GetSpiralRadius() const { return radius; }; // \ru Выдать радиус \en Get radius - void SetSpiralRadius( double r ) { radius = r; }; // \ru Изменить радиус \en Change radius - bool GetCurveSense () const { return curveSense;};// \ru Выдать признак совпадения направления на спирали и оси (кривой) \en Get attribute of coincidence of the direction on the spiral and axis (curve) - -private: - void GetFirstDerNormW ( const MbVector & fDerW, const MbVector & sDerW, MbVector & fdNormW ) const; // \ru Выдать первую производную нормали по параметру кривой оси \en Get the first derivative of normal vector with respect to parameter of axis curve - void GetSecondDerNormW ( const MbVector & fDerW, const MbVector & sDerW, const MbVector & tDerW, MbVector & sdNormW ) const; // \ru Выдать вторую производную нормали по параметру кривой оси \en Get the second derivative of normal vector with respect to parameter of axis curve - void GetThirdDerNormW ( const MbVector & fDerW, const MbVector & sDerW, const MbVector & tDerW, MbVector & tdNormW ) const; // \ru Выдать третью производную нормали по параметру кривой оси \en Get the third derivative of normal vector with respect to parameter of axis curve - void GetFirstDerNormT ( const MbVector & fDerW, const MbVector & sDerW, MbVector & fdNorm ) const; // \ru Выдать первую производную нормали по параметру спирали \en Get the first derivative of normal vector with respect to parameter of the spiral - void GetSecondDerNormT ( const MbVector & fDerW, const MbVector & sDerW, const MbVector & tDerW, MbVector & sdNorm ) const; // \ru Выдать вторую производную нормали по параметру спирали \en Get the second derivative of normal vector with respect to parameter of the spiral - void GetThirdDerNormT ( const MbVector & fDerW, const MbVector & sDerW, const MbVector & tDerW, MbVector & tdNorm ) const; // \ru Выдать третью производную нормали по параметру спирали \en Get the third derivative of normal vector with respect to parameter of the spiral - void GetFirstDerT ( const MbVector & fDerW, MbVector & fd ) const; // \ru Выдать первую производную кривой оси по параметру спирали \en Get the first derivative of axis curve with respect to parameter of the spiral - void GetSecondDerT ( const MbVector & fDerW, const MbVector & sDerW, MbVector & sd ) const; // \ru Выдать вторую производную кривой оси по параметру спирали \en Get the second derivative of axis curve with respect to parameter of the spiral - void GetThirdDerT ( const MbVector & fDerW, const MbVector & sDerW, const MbVector & tDerW, MbVector & td ) const; // \ru Выдать третью производную кривой оси по параметру спирали \en Get the third derivative of axis curve with respect to parameter of the spiral - double GetFirstDerParamT ( const MbVector & fDerW ) const; // \ru Выдать первую производную параметра кривой оси по параметру спирали \en Get the first derivative of axis curve parameter with respect to parameter of the spiral - double GetSecondDerParamT( const MbVector & fDerW, const MbVector & sDerW ) const; // \ru Выдать вторую производную параметра кривой оси по параметру спирали \en Get the second derivative of axis curve parameter with respect to parameter of the spiral - double GetThirdDerParamT ( const MbVector & fDerW, const MbVector & sDerW, const MbVector & tDerW ) const; // \ru Выдать третью производную параметра кривой оси по параметру спирали \en Get the third derivative of axis curve parameter with respect to parameter of the spiral - void GetCurveParams ( double tSense, MbCartPoint & point, MbDirection & normal, - MbVector & fDerW, MbVector & sDerW, MbVector & tDerW ) const; // \ru Параметры кривой оси, соответствующие параметру спирали t \en Parameters of axis curve corresponding to the parameter t of the spiral - void CalculateParams (); // \ru Посчитать параметры спирали (параметрические сдвиги от начала кривой) и параметры кривой. \en Calculate parameters of spiral (parametric shifts from the beginning of the curve) and parameters of "curve". - bool NearestLeftParams ( double tSense, c3d::DoublePair & paramPair ) const; // \ru Ближайшая слева пара параметров спирали и кривой. \en Nearest left parameters pair of spiral and "curve". - -private: - void operator = ( const MbCrookedSpiral & ); // \ru Не реализовано \en Not implemented - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCrookedSpiral ) -}; // MbCrookedSpiral - -IMPL_PERSISTENT_OPS( MbCrookedSpiral ) - -#endif // __CUR_CROOKET_SPIRAL_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Спираль постоянного радиуса и осью, заданной произвольной кривой на плоскости XZ position. + \en Spiral with constant radius and axis defined by an arbitrary curve on the XZ plane "position". \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CUR_CROOKET_SPIRAL_H +#define __CUR_CROOKET_SPIRAL_H + + +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Спираль с криволинейной осью. + \en Spiral with a curvilinear axis. \~ + \details \ru Спираль постоянного радиуса и осью, заданной произвольной плоской кривой. + Ось спирали определяется кривой curve, располагающейся в плоскости ZX локальной системы координат спирали. + При этом ось Z локальной системы координат спирали служит осью X системы координат двумерной кривой curve, + а ось X локальной системы координат спирали служит осью Y системы координат двумерной кривой curve, + что приведено на рис. 1 ниже. \n + Радиус-вектор кривой в методе PointOn(double&t,MbCartPoint3D&r) описывается векторной функцией: \n + r(t) = position.origin + + (position.axisX (point.y + (radius cos(t) normal.ay)) + + (position.axisY radius sin(t)) + + (position.axisZ (point.x + (radius cos(t) normal.ax)), + где point - точка кривой curve, normal - нормаль кривой curve. + Рис. 1. + ^ Ось X локальной системы координат спирали является осью Y системы координат curve. + | + | curve(w) + | + +----> Ось Z локальной системы координат спирали является осью X системы координат curve. + \en Spiral with a constant radius and axis defined by an arbitrary plane curve. + Spiral axis is determined by the curve "curve" based in the ZX plane of the local coordinate system of spiral. + The Z-axis of the local coordinate system is the X-axis of coordinate system of two-dimensional uv-curve "curve", + and the X-axis of the local coordinate system is the Y-axis of the coordinate system of two-dimensional uv-curve "curve", + that is shown in fig. 1 below. \n + The radius-vector of curve in the method PointOn(double&t,MbCartPoint3D&r) is described by a vector function: \n + r(t) = position.origin + + (position.axisX (point.y + (radius cos(t) normal.ay)) + + (position.axisY radius sin(t)) + + (position.axisZ (point.x + (radius cos(t) normal.ax)), + where "point" is point of the curve "curve", "normal" is normal of the curve "curve". + Fig. 1 1. + ^ X-axis of the local coordinate system of the spiral is Y-axis of the coordinate system of the curve "curve". + | + | curve(w) + | + +----> Z-axis of the local coordinate system of the spiral is X-axis of the coordinate system of the curve "curve". \~ + \ingroup Curves_3D +*/ +// --- +class MATH_CLASS MbCrookedSpiral : public MbSpiral { + typedef std::vector CurveParams; +protected: + MbCurve * curve; ///< \ru Кривая, задающая ось спирали, (не может быть NULL). \en The curve which determines the axis of the spiral, (can not be NULL). + double radius; ///< \ru Радиус спирали. \en A spiral radius. + double wMin; ///< \ru Минимальное значение параметра curve. \en Minimal value of parameter "curve". + double wMax; ///< \ru Максимальное значение параметра curve. \en Maximal value of parameter "curve". + double t0; ///< \ru Начальный угол спирали. \en The initial angle of the spiral. + bool curveSense; ///< \ru Совпадение направления оси спирали с направлением кривой curve. \en The coincidence of the direction of the spiral axis with the direction of the curve "curve". + CurveParams curveParams; ///< \ru Параметры спирали (параметрические сдвиги от начала кривой) и параметры двумерной кривой. \en Parameters of spiral (parametric shifts from the beginning of the curve) and parameters of "curve". + +protected: + MbCrookedSpiral( const MbCrookedSpiral &, MbRegDuplicate * ); +public : + /// \ru Конструктор спирали с криволинейной осью. \en Spiral with a curvilinear axis. + MbCrookedSpiral( const MbPlacement3D & pos, MbCurve & axisCurve, double radius, double step, bool same ); +public: + virtual ~MbCrookedSpiral(); + +public : + VISITING_CLASS( MbCrookedSpiral ); + + /// \ru Инициализация спирали по спирали. \en Spiral initialization by spiral. + void Init( const MbCrookedSpiral & ); + /// \ru Инициализация спирали по основанию (локальной системе координат). \en Spiral initialization by base (local coordinate system). + void Init( const MbPlacement3D & ); + + // \ru Общие функции математического объекта \en Common functions of the mathematical object + + virtual MbeSpaceType IsA() const; // \ru Тип элемента \en Type of element + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element + virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; + virtual bool SetEqual( const MbSpaceItem & init ); // \ru Сделать равным \en Make equal + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + + // \ru Общие функции кривой \en Common functions of curve + + virtual bool IsClosed() const; // \ru Проверка замкнутости кривой \en Check for curve closedness + // \ru Функции для работы внутри области определения кривой. \en Functions for working inside of the curve domain. \~ + virtual void PointOn ( double & t, MbCartPoint3D & pnt ) const; // \ru Точка на кривой \en Point on the curve + virtual void FirstDer ( double & t, MbVector3D & fd ) const; // \ru Первая производная \en First derivative + virtual void SecondDer( double & t, MbVector3D & sd ) const; // \ru Вторая производная \en Second derivative + virtual void ThirdDer ( double & t, MbVector3D & td ) const; // \ru Третья производная по t \en Third derivative with respect to t + // \ru Функции для работы внутри и вне области определения кривой. \en Functions for working inside and outside of the curve domain. \~ + virtual void _PointOn ( double t, MbCartPoint3D & pnt ) const; // \ru Точка на кривой \en Point on the curve + virtual void _FirstDer ( double t, MbVector3D & fd ) const; // \ru Первая производная \en First derivative + virtual void _SecondDer( double t, MbVector3D & sd ) const; // \ru Вторая производная \en Second derivative + virtual void _ThirdDer ( double t, MbVector3D & td ) const; // \ru Третья производная по t \en Third derivative with respect to t + // \ru Вычислить значения точки и производных для заданного параметра. \en Calculate point and derivatives of object for given parameter. \~ + virtual void Explore( double & t, bool ext, + MbCartPoint3D & pnt, MbVector3D & fir, MbVector3D * sec, MbVector3D * thir ) const; + + virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменить направление \en Change direction + + virtual MbCurve3D * Trimmed( double t1, double t2, int sense ) const; // \ru Создание усеченной кривой \en Creation of a trimmed curve + virtual double CalculateLength( double t1, double t2 ) const; + virtual void GetBasisItems( RPArray & ); + + // \ru Функции спирали \en Functions of spiral + + virtual bool SetStep( double s ); // \ru Изменить шаг \en Change step + virtual double GetSpiralRadius( double t ) const; // \ru Выдать физический радиус спирали \en Get physical radius of spiral + + const MbCurve & GetAxisCurve() const { return *curve; } ///< \ru Выдать осевую кривую. \en Get axial curve. + double GetSpiralRadius() const { return radius; } ///< \ru Выдать радиус. \en Get radius. + void SetSpiralRadius( double r ) { radius = r; }; ///< \ru Изменить радиус. \en Change radius. + bool GetCurveSense () const { return curveSense; } ///< \ru Выдать признак совпадения направления на спирали и оси (кривой). \en Get attribute of coincidence of the direction on the spiral and axis (curve). + +private: + void GetFirstDerNormW ( const MbVector & fDerW, const MbVector & sDerW, MbVector & fdNormW ) const; // \ru Выдать первую производную нормали по параметру кривой оси \en Get the first derivative of normal vector with respect to parameter of axis curve + void GetSecondDerNormW ( const MbVector & fDerW, const MbVector & sDerW, const MbVector & tDerW, MbVector & sdNormW ) const; // \ru Выдать вторую производную нормали по параметру кривой оси \en Get the second derivative of normal vector with respect to parameter of axis curve + void GetThirdDerNormW ( const MbVector & fDerW, const MbVector & sDerW, const MbVector & tDerW, MbVector & tdNormW ) const; // \ru Выдать третью производную нормали по параметру кривой оси \en Get the third derivative of normal vector with respect to parameter of axis curve + void GetFirstDerNormT ( const MbVector & fDerW, const MbVector & sDerW, MbVector & fdNorm ) const; // \ru Выдать первую производную нормали по параметру спирали \en Get the first derivative of normal vector with respect to parameter of the spiral + void GetSecondDerNormT ( const MbVector & fDerW, const MbVector & sDerW, const MbVector & tDerW, MbVector & sdNorm ) const; // \ru Выдать вторую производную нормали по параметру спирали \en Get the second derivative of normal vector with respect to parameter of the spiral + void GetThirdDerNormT ( const MbVector & fDerW, const MbVector & sDerW, const MbVector & tDerW, MbVector & tdNorm ) const; // \ru Выдать третью производную нормали по параметру спирали \en Get the third derivative of normal vector with respect to parameter of the spiral + void GetFirstDerT ( const MbVector & fDerW, MbVector & fd ) const; // \ru Выдать первую производную кривой оси по параметру спирали \en Get the first derivative of axis curve with respect to parameter of the spiral + void GetSecondDerT ( const MbVector & fDerW, const MbVector & sDerW, MbVector & sd ) const; // \ru Выдать вторую производную кривой оси по параметру спирали \en Get the second derivative of axis curve with respect to parameter of the spiral + void GetThirdDerT ( const MbVector & fDerW, const MbVector & sDerW, const MbVector & tDerW, MbVector & td ) const; // \ru Выдать третью производную кривой оси по параметру спирали \en Get the third derivative of axis curve with respect to parameter of the spiral + double GetFirstDerParamT ( const MbVector & fDerW ) const; // \ru Выдать первую производную параметра кривой оси по параметру спирали \en Get the first derivative of axis curve parameter with respect to parameter of the spiral + double GetSecondDerParamT( const MbVector & fDerW, const MbVector & sDerW ) const; // \ru Выдать вторую производную параметра кривой оси по параметру спирали \en Get the second derivative of axis curve parameter with respect to parameter of the spiral + double GetThirdDerParamT ( const MbVector & fDerW, const MbVector & sDerW, const MbVector & tDerW ) const; // \ru Выдать третью производную параметра кривой оси по параметру спирали \en Get the third derivative of axis curve parameter with respect to parameter of the spiral + void GetCurveParams ( double tSense, MbCartPoint & point, MbDirection & normal, + MbVector & fDerW, MbVector & sDerW, MbVector & tDerW ) const; // \ru Параметры кривой оси, соответствующие параметру спирали t \en Parameters of axis curve corresponding to the parameter t of the spiral + void CalculateParams (); // \ru Посчитать параметры спирали (параметрические сдвиги от начала кривой) и параметры кривой. \en Calculate parameters of spiral (parametric shifts from the beginning of the curve) and parameters of "curve". + bool NearestLeftParams ( double tSense, c3d::DoublePair & paramPair ) const; // \ru Ближайшая слева пара параметров спирали и кривой. \en Nearest left parameters pair of spiral and "curve". + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCrookedSpiral ) +OBVIOUS_PRIVATE_COPY( MbCrookedSpiral ) +}; // MbCrookedSpiral + +IMPL_PERSISTENT_OPS( MbCrookedSpiral ) + +#endif // __CUR_CROOKET_SPIRAL_H diff --git a/C3d/Include/cur_cubic_spline.h b/C3d/Include/cur_cubic_spline.h index 36054e6..b8da59a 100644 --- a/C3d/Include/cur_cubic_spline.h +++ b/C3d/Include/cur_cubic_spline.h @@ -53,7 +53,7 @@ protected : MbCubicSpline(); ///< \ru Конструктор по умолчанию. \en Constructor by default. MbCubicSpline( const MbCubicSpline & other ); ///< \ru Дублирующий конструктор. \en Duplicating constructor. /// \ru Конструктор по заданной кривой. \en Constructor by a given curve. - MbCubicSpline( const MbCurve & ); + MbCubicSpline( const MbCurve &, VERSION ); /** \brief \ru Конструктор. \en Constructor. \~ @@ -90,6 +90,8 @@ public : \en Create copy of spline. \~ \details \ru Создать копию сплайна.\n \en Create copy of spline.\n \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbCubicSpline * Create( const MbCubicSpline & other ); /** \brief \ru Создать сплайн gj rhb. @@ -98,8 +100,10 @@ public : \en Create spline by a given curve and set parameters of spline.\n \~ \param[in] curve - \ru Заданная кривая. \en Given curve. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ - static MbCubicSpline * Create( const MbCurve & curve ); + static MbCubicSpline * Create( const MbCurve & curve, VERSION version ); /** \brief \ru Создать сплайн. \en Create spline. \~ \details \ru Создать сплайн и установить параметры сплайна.\n @@ -108,6 +112,8 @@ public : \en Set of control points. \~ \param[in] cls - \ru Признак замкнутости. \en A closedness attribute. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbCubicSpline * Create( const SArray & points, bool cls ); /** \brief \ru Создать сплайн. @@ -120,6 +126,8 @@ public : \en Set of second derivatives at the control points. \~ \param[in] cls - \ru Признак замкнутости. \en A closedness attribute. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbCubicSpline * Create( const SArray & points, const SArray & seconds, bool cls ); @@ -133,6 +141,8 @@ public : \en Set of parameters at the control points. \~ \param[in] cls - \ru Признак замкнутости. \en A closedness attribute. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbCubicSpline * Create( const SArray & points, const SArray & params, bool cls ); @@ -148,6 +158,8 @@ public : \en Set of parameters at the control points. \~ \param[in] cls - \ru Признак замкнутости. \en A closedness attribute. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbCubicSpline * Create( const SArray & points, const SArray & seconds, @@ -160,19 +172,19 @@ public : \en \name Spline initialization functions. \{ */ /// \ru Инициализатор по заданной кривой. \en Initializer by a given curve. - bool Init( const MbCurve & ); + bool Init( const MbCurve &, VERSION version ); /// \ru Инициализатор по точкам и признаку замкнутости. \en Initializer by points and an attribute of closedness. - bool Init( const SArray &, bool ); + bool Init( const SArray &, bool ); /// \ru Инициализатор по точкам, вторым производным и признаку замкнутости. \en Initializer by points, second derivatives and closedness attribute. - bool Init( const SArray &, - const SArray &, bool ); + bool Init( const SArray &, + const SArray &, bool ); /// \ru Инициализатор по точкам, параметрам и признаку замкнутости. \en Initializer by points, parameters and closedness attribute. - bool Init( const SArray &, - const SArray &, bool ); + bool Init( const SArray &, + const SArray &, bool ); /// \ru Инициализатор по точкам, вторым производным, параметрам и признаку замкнутости. \en Initializer by points, second derivatives, parameters and closedness attribute. - bool Init( const SArray &, - const SArray &, - const SArray &, bool ); + bool Init( const SArray &, + const SArray &, + const SArray &, bool ); /// \ru Дублирующий инициализатор. \en Duplicating initializer. void InitC( const MbCubicSpline & ); /** \} */ @@ -182,7 +194,7 @@ public : \{ */ virtual MbePlaneType IsA () const; // \ru Тип элемента \en Type of element virtual bool SetEqual ( const MbPlaneItem & ); // \ru Сделать элементы равными \en Make equal elements - virtual bool IsSame ( const MbPlaneItem &, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли кривая curve копией данной кривой \en Whether the curve "curve" is a copy of a given curve + virtual bool IsSame ( const MbPlaneItem &, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли кривая curve копией данной кривой \en Whether the curve "curve" is a copy of a given curve virtual void Transform( const MbMatrix &, MbRegTransform * ireg = NULL, const MbSurface * newSurface = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix virtual void Move ( const MbVector &, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Сдвиг \en Translation virtual void Rotate ( const MbCartPoint &, const MbDirection &, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Поворот \en Rotation @@ -218,9 +230,8 @@ public : virtual void Explore( double & t, bool ext, MbCartPoint & pnt, MbVector & fir, MbVector * sec, MbVector * thir ) const; - // \ru четвертая производная \en Fourth derivative - void FourDer ( double &, MbVector & ) const; ///< \ru Вычислить четвертую производную. \en Calculate the fourth derivative. - void PointOnLine ( double &, MbCartPoint & ); ///< \ru Вычислить точку на кривой при линейной аппроксимации. \en Calculate a point on the curve with a linear approximation. + void FourDer ( double &, MbVector & ) const; ///< \ru Оценить четвертую производную. \en Estimate the fourth derivative. + void PointOnLine( double &, MbCartPoint & ) const; ///< \ru Вычислить точку на кривой при линейной аппроксимации. \en Calculate a point on the curve with a linear approximation. /** \} */ /** \ru \name Функции движения по кривой diff --git a/C3d/Include/cur_cubic_spline3d.h b/C3d/Include/cur_cubic_spline3d.h index ab23832..dc387a8 100644 --- a/C3d/Include/cur_cubic_spline3d.h +++ b/C3d/Include/cur_cubic_spline3d.h @@ -67,7 +67,7 @@ protected: const MbVector3D & vectE, bool sInit, bool eInit ); - MbCubicSpline3D( const MbCurve3D & curve ); // \ru Конструктор по другой кривой \en Constructor by another curve + MbCubicSpline3D( const MbCurve3D & curve, VERSION version ); // \ru Конструктор по другой кривой \en Constructor by another curve // \ru Конструктор по двумерному сплайну на плоскости \en Constructor by a two-dimensional spline on the plane MbCubicSpline3D( const MbCubicSpline & initFlat, const MbPlacement3D & plane ); public: @@ -77,6 +77,8 @@ public : \en Create copy of spline. \~ \details \ru Создать копию сплайна.\n \en Create copy of spline.\n \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbCubicSpline3D * Create( const MbCubicSpline3D & other ); /** \brief \ru Создать сплайн. @@ -85,8 +87,12 @@ public : \en Create spline by a given curve and set parameters of spline.\n \~ \param[in] curve - \ru Заданная кривая. \en Given curve. \~ + \param[in] version - \ru Версия. + \en Version. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ - static MbCubicSpline3D * Create( const MbCurve3D & curve ); + static MbCubicSpline3D * Create( const MbCurve3D & curve, VERSION version ); /** \brief \ru Создать сплайн. \en Create spline. \~ \details \ru Создать сплайн и установить параметры сплайна.\n @@ -96,7 +102,9 @@ public : \param[in] cls - \ru Признак замкнутости. \en A closedness attribute. \~ \param[in] version - \ru Версия. - \en Version. \~ + \en Version. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbCubicSpline3D * Create( const SArray & points, bool cls, @@ -113,6 +121,8 @@ public : \en A closedness attribute. \~ \param[in] version - \ru Версия. \en Version. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbCubicSpline3D * Create( const SArray & points, const SArray & seconds, @@ -128,6 +138,8 @@ public : \en Set of parameters at the control points. \~ \param[in] cls - \ru Признак замкнутости. \en A closedness attribute. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbCubicSpline3D * Create( const SArray & points, const SArray & params, @@ -144,6 +156,8 @@ public : \en Set of parameters at the control points. \~ \param[in] cls - \ru Признак замкнутости. \en A closedness attribute. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbCubicSpline3D * Create( const SArray & points, const SArray & seconds, @@ -157,6 +171,8 @@ public : \en Plane spline. \~ \param[in] plane - \ru Плоскость кривой. \en Plane of curve. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbCubicSpline3D * Create( const MbCubicSpline & initFlat, const MbPlacement3D & plane ); /** \brief \ru Создать сплайн. @@ -173,6 +189,8 @@ public : \en Use or not first derivative at the begin. \~ \param[in] eInit - \ru Учитывать ли производную в конце. \en Use or not first derivative at the end. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ // \ru Конструктор по точкам, первым производным на краях (если их надо учитывать) \en Constructor by points, first derivatives at the edges (if they should be considered) static MbCubicSpline3D * Create( const SArray & points, @@ -180,6 +198,32 @@ public : const MbVector3D & vectE, bool sInit, bool eInit ); + /** \brief \ru Создать сплайн. + \en Create spline. \~ + \details \ru Создать сплайн и установить параметры сплайна.\n + \en Create spline and set parameters of spline.\n \~ + \param[in] points - \ru Набор контрольных точек. + \en Set of control points. \~ + \param[in] params - \ru Набор параметров в контрольных точках. + \en Set of parameters at the control points. \~ + \param[in] vectS - \ru Первая производная в начале. + \en First derivative at the begin. \~ + \param[in] vectE - \ru Первая производная в конце. + \en First derivative at the end. \~ + \param[in] sInit - \ru Учитывать ли производную в начале. + \en Use or not first derivative at the begin. \~ + \param[in] eInit - \ru Учитывать ли производную в конце. + \en Use or not first derivative at the end. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ + */ + // \ru Конструктор по точкам, первым производным на краях (если их надо учитывать) \en Constructor by points, first derivatives at the edges (if they should be considered) + static MbCubicSpline3D * Create( const SArray & points, + const SArray & params, + const MbVector3D & vectS, + const MbVector3D & vectE, + bool sInit, + bool eInit ); public: VISITING_CLASS( MbCubicSpline3D ); @@ -197,7 +241,7 @@ public: // \ru Инициализация по точкам, параметрам и краевым производным \en Initialization by points, parameters and boundary derivatives bool Init( const SArray &, const SArray &, const MbVector3D &, const MbVector3D &, bool, bool ); - bool Init( const MbCurve3D & ); // \ru Инициализатор по другой кривой \en Initializer by another curve + bool Init( const MbCurve3D &, VERSION version ); // \ru Инициализатор по другой кривой \en Initializer by another curve void InitC( const MbCubicSpline3D & ); // \ru Дублирующий инициализатор \en Duplicating initializer // \ru Инициализатор по двумерному сплайну на плоскости \en Initializer by a two-dimensional spline on the plane void Init( const MbCubicSpline &, const MbPlacement3D & ); @@ -227,7 +271,7 @@ public: virtual void Explore( double & t, bool ext, MbCartPoint3D & pnt, MbVector3D & fir, MbVector3D * sec, MbVector3D * thir ) const; - void FourDer ( double &, MbVector3D & ) const; // \ru четвертая производная \en Fourth derivative + void FourDer( double &, MbVector3D & ) const; ///< \ru Оценить четвертую производную. \en Estimate the fourth derivative. // \ru Построить NURBS-копию кривой \en Create a NURBS-copy of the curve virtual MbNurbs3D * NurbsCurve( const MbCurveIntoNurbsInfo & ) const; diff --git a/C3d/Include/cur_curve_spiral.h b/C3d/Include/cur_curve_spiral.h index ee06016..31e391a 100644 --- a/C3d/Include/cur_curve_spiral.h +++ b/C3d/Include/cur_curve_spiral.h @@ -42,20 +42,48 @@ protected: bool curveSense; ///< \ru Совпадение направления оси спирали с направлением кривой curve. \en The coincidence of the direction of the spiral axis with the direction of the curve "curve". public: - MbCurveSpiral( const MbPlacement3D & pl, double rad, double s, double t1, double t2 ); // \ru Цилиндрическая спираль \en Cylindrical spiral - MbCurveSpiral( const MbPlacement3D & pos, MbCurve & lawCurve, double s, bool same ); // \ru Спираль с образующей кривой \en Spiral with a generating curve - + /** \brief \ru Создать цилиндрическую спираль по радиусу основания, шагу, основанию, и двум параметрам. + \en Create cylindrical spiral by bottom radius, step, base and two parameters. \~ + \details \ru Создать цилиндрическую спираль по радиусу основания, шагу, основанию, и двум параметрам. \n + \en Create cylindrical spiral by bottom radius, step, base and starting and ending parameters. \n \~ + \param[in] place - \ru Локальная система координат. + \en A local coordinate system. \~ + \param[in] radius - \ru Радиус основания спирали. + \en Radius of spiral. \~ + \param[in] st - \ru Шаг между витками спирали. + \en Step between coils of spiral. \~ + \param[in] t1 - \ru Начальный параметр. + \en Starting parameter. \~ + \param[in] t2 - \ru Конечный параметр. + \en Ending parameter. \~ + */ + MbCurveSpiral( const MbPlacement3D & place, double radius, double st, double t1, double t2 ); + /** \brief \ru Конструктор спирали по её основанию, закону изменения радиуса и шагу. + \en The constructor of spiral by its base, the law of radius change and step. \~ + \details \ru Конструктор спирали по её основанию (локальной системе координат), закону изменения радиуса в виде образующей кривой и шагу между витками спирали. \n + \en The constructor of spiral by its base (local coordinate system), the law of radius change (in the form of the 2D-curve) and step between coils of spiral. \n \~ + \param[in] place - \ru Локальная система координат. + \en A local coordinate system. \~ + \param[in] lawCurve - \ru Закон изменения радиуса. + \en The law of radius change. \~ + \param[in] st - \ru Шаг между витками спирали. + \en Step between coils of spiral. \~ + \param[in] sameLawCurve - \ru Флаг использования оригинала кривой. + \en Flag of original curve use. \~ + */ + MbCurveSpiral( const MbPlacement3D & place, MbCurve & lawCurve, double st, bool sameLawCurve ); protected: - MbCurveSpiral( const MbCurveSpiral & init ); - + MbCurveSpiral( const MbCurveSpiral & ); public : virtual ~MbCurveSpiral(); public: VISITING_CLASS( MbCurveSpiral ); - void Init( const MbCurveSpiral & init ); - void Init( const MbPlacement3D & place ); + /// \ru Инициализация спирали по спирали. \en Spiral initialization by spiral. + void Init( const MbCurveSpiral & ); + /// \ru Инициализация спирали по основанию (локальной системе координат). \en Spiral initialization by base (local coordinate system). + void Init( const MbPlacement3D & ); // \ru Общие функции математического объекта \en Common functions of the mathematical object @@ -90,7 +118,7 @@ public: // \ru Функции спирали \en Functions of spiral - virtual void SetStep( double s ); // \ru Изменить шаг \en Change step + virtual bool SetStep( double s ); // \ru Изменить шаг \en Change step virtual double GetSpiralRadius ( double t ) const; // \ru Выдать физический радиус спирали \en Get physical radius of spiral protected: diff --git a/C3d/Include/cur_hermit.h b/C3d/Include/cur_hermit.h index 1196811..bab0288 100644 --- a/C3d/Include/cur_hermit.h +++ b/C3d/Include/cur_hermit.h @@ -88,6 +88,8 @@ public : \en Create copy of spline. \~ \details \ru Создать копию сплайна.\n \en Create copy of spline.\n \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbHermit * Create( const MbHermit & ); /** \brief \ru Создать сплайн. @@ -98,6 +100,8 @@ public : \en Set of control points. \~ \param[in] cls - \ru Признак замкнутости. \en A closedness attribute. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbHermit * Create( const SArray & initList, bool cls ); /** \brief \ru Создать сплайн. @@ -110,6 +114,8 @@ public : \en Set of control points. \~ \param[in] cls - \ru Признак замкнутости. \en A closedness attribute. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbHermit * Create( const SArray & initParams, const SArray & initPoints, bool cls ); @@ -125,6 +131,8 @@ public : \en Set of derivatives at the control points. \~ \param[in] cls - \ru Признак замкнутости. \en A closedness attribute. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbHermit * Create( const SArray & initParams, const SArray & initPoints, const SArray & initVectors, bool cls ); @@ -140,6 +148,8 @@ public : \en Array, containing indexes of points with same derivative. \~ \param[in] cls - \ru Признак замкнутости. \en A closedness attribute. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbHermit * Create( const SArray & initParams, const SArray & initPoints, const SArray & vLabels, bool cls ); @@ -151,6 +161,8 @@ public : \en Start point of curve. \~ \param[in] p2 - \ru Конечная точка кривой. \en End point of curve. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbHermit * Create( const MbCartPoint & p1, const MbCartPoint & p2 ); /** \brief \ru Создать сплайн. @@ -169,6 +181,8 @@ public : \en End point of curve. \~ \param[in] v2 - \ru Касательный вектор к кривой в конечной точке. \en A tangent vector to the curve at the end point. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbHermit * Create( double t1, const MbCartPoint & p1, const MbVector & v1, double t2, const MbCartPoint & p2, const MbVector & v2 ); @@ -217,7 +231,7 @@ public : // \ru Создать NURBS представление кривой \en Create a NURBS representation of the curve virtual MbNurbs * NurbsCurve( const MbCurveIntoNurbsInfo & ) const; virtual MbCurve * NurbsCurve( const MbNurbsParameters & ) const; // \ru Построить NURBS-копию кривой \en Create a NURBS-copy of the curve - virtual MbContour * NurbsContour() const; + virtual MbContour * NurbsContour() const; virtual double GetTMin() const; // \ru Вернуть минимальное значение параметра \en Get the minimum value of parameter virtual double GetTMax() const; // \ru Вернуть максимальное значение параметра \en Get the maximum value of parameter @@ -254,7 +268,7 @@ public : virtual size_t GetCount() const; // \ru Выдать индекс точки, ближайшей к заданной \en Get index of the nearest point to the given one - virtual ptrdiff_t GetNearPointIndex( const MbCartPoint & pnt ) const; + virtual ptrdiff_t GetNearPointIndex( const MbCartPoint & pnt ) const; virtual void Rebuild(); // \ru Пересчитать кривую \en Rebuild the curve virtual void SetClosed( bool cls ); // \ru Установить признак замкнутости. \en Set attribute of closedness. @@ -286,7 +300,7 @@ public : void SetLimitVector( int n, const MbVector & v ); /// \ru Создать кривую путём сращивания части данной кривой с частью кривой init. \en Create a curve by joining a part of this curve with a part of "init" curve. MbHermit * CurvesCombine( double t0, double w0, bool add, - const MbHermit & init, double t1, double w1, double koef ) const; + const MbHermit & init, double t1, double w1, double koef, bool checkClosed ) const; size_t GetVectorListCount() const { return vectorList.Count(); } void GetVectorList( SArray & vectors ) const { vectors = vectorList; } @@ -302,6 +316,9 @@ public : // \ru Вставить точки и параметры в перед кривой в заданной последовательности. \en Parameters and points insetr to beg successively. bool InsertPoints( SArray & params, SArray & points ); + // \ru При линейном расположении нескольких точек согласовать производные на краях участка. \en Aligning the derivatives on the group if several points are located linearly. + bool DerivativesCorrection( double accuracy ); + /// \ru Определение максимального индекса массива параметров слева. \en Determination of the maximum index of parameter array on the left. ptrdiff_t GetIndex( double t ) const; @@ -328,6 +345,7 @@ private: IMPL_PERSISTENT_OPS( MbHermit ) + //------------------------------------------------------------------------------ /// \ru Определение местных координат области поверхности \en Definition of local coordinates in a surface region // --- diff --git a/C3d/Include/cur_hermit3d.h b/C3d/Include/cur_hermit3d.h index 6a48a28..6a58df8 100644 --- a/C3d/Include/cur_hermit3d.h +++ b/C3d/Include/cur_hermit3d.h @@ -73,6 +73,8 @@ public: \en Create copy of spline. \~ \details \ru Создать копию сплайна.\n \en Create copy of spline.\n \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbHermit3D * Create( const MbHermit3D & ); /** \brief \ru Создать сплайн. @@ -83,6 +85,8 @@ public: \en Set of control points. \~ \param[in] cls - \ru Признак замкнутости. \en A closedness attribute. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbHermit3D * Create( const SArray & initList, bool cls ); /** \brief \ru Создать сплайн. @@ -95,6 +99,8 @@ public: \en Set of control points. \~ \param[in] cls - \ru Признак замкнутости. \en A closedness attribute. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbHermit3D * Create( const SArray & initParams, const SArray & initPoints, bool cls ); @@ -110,6 +116,8 @@ public: \en Set of derivatives at the control points. \~ \param[in] cls - \ru Признак замкнутости. \en A closedness attribute. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbHermit3D * Create( const SArray & initParams, const SArray & initPoints, const SArray & initVectors, bool cls ); @@ -125,6 +133,8 @@ public: \en Array, containing indexes of points with same derivative. \~ \param[in] cls - \ru Признак замкнутости. \en A closedness attribute. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbHermit3D * Create( const SArray & initParams, const SArray & initPoints, const SArray & vLabels, bool cls ); @@ -136,6 +146,8 @@ public: \en The two-dimensional spline. \~ \param[in] place - \ru Локальная система координат сплайна. \en Local coordinate system of spline. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbHermit3D * Create( const MbHermit & init, const MbPlacement3D & plane ); /** \brief \ru Создать сплайн. @@ -146,6 +158,8 @@ public: \en Start point of curve. \~ \param[in] p2 - \ru Конечная точка кривой. \en End point of curve. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbHermit3D * Create( const MbCartPoint3D & p1, const MbCartPoint3D & p2 ); /** \brief \ru Создать сплайн. @@ -164,6 +178,8 @@ public: \en End point of curve. \~ \param[in] v2 - \ru Касательный вектор к кривой в конечной точке. \en A tangent vector to the curve at the end point. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbHermit3D * Create( double t1, const MbCartPoint3D & p1, const MbVector3D & v1, double t2, const MbCartPoint3D & p2, const MbVector3D & v2 ); @@ -171,18 +187,24 @@ public: public : VISITING_CLASS( MbHermit3D ); - // \ru Установить параметры сплайна \en Set parameters of spline + /// \ru Установить параметры сплайна по точкам и флагу замкнутости. \en Set parameters of spline by points and closeness flag. bool Init( const SArray & initPoints, bool cls ); + /// \ru Установить параметры сплайна по параметрам, точкам и флагу замкнутости. \en Set parameters of spline by parameters, points and closeness flag. bool Init( const SArray & initParams, const SArray & initPoints, bool cls ); + /// \ru Установить параметры сплайна. \en Set parameters of spline. bool Init( const SArray & initParams, const SArray & initPoints, const SArray & initVectors, bool cls ); + /// \ru Установить параметры сплайна. \en Set parameters of spline. bool Init( const SArray & initParams, const SArray & initPoints, const SArray & vLabels, bool cls ); + /// \ru Установить параметры сплайна по другому сплайну Эрмита. \en Set parameters of spline by another spline of Hermit. void Init( const MbHermit3D & ); + /// \ru Установить параметры сплайна по двумерному сплайну Эрмита. \en Set parameters of spline by two-dimensional spline of Hermit. void Init( const MbHermit &, const MbPlacement3D & ); + /// \ru Установить параметры сплайна. \en Set parameters of spline. void Init( double t1, const MbCartPoint3D & p1, const MbVector3D & v1, double t2, const MbCartPoint3D & p2, const MbVector3D & v2 ); @@ -271,14 +293,14 @@ public : void CalculateDerivatives(); void SetLimitVector( ptrdiff_t n, const MbVector3D & v ); - size_t GetVectorListCount() const { return vectorList.size(); } - void GetVectorList( SArray & vectors ) const { vectors = vectorList; } + size_t GetVectorListCount() const { return vectorList.size(); } + void GetVectorList( SArray & vectors ) const { vectors = vectorList; } const MbVector3D & _GetVectorList( size_t i ) const { return vectorList[i]; } MbVector3D & _SetVectorList( size_t i ) { MbPolyCurve3D::Refresh(); return vectorList[i]; } - size_t GetTListCount() const { return tList.size(); } - void GetTList( SArray & params ) const { params = tList; } - double _GetTList( size_t i ) const { return tList[i]; } + size_t GetTListCount() const { return tList.size(); } + void GetTList( SArray & params ) const { params = tList; } + double _GetTList( size_t i ) const { return tList[i]; } void LocalCoordinate( double & t, ptrdiff_t & index1, ptrdiff_t & index2, @@ -286,6 +308,10 @@ public : double & paramD, double & paramW, double & quota1, double & quota2 ) const; ptrdiff_t GetIndex( double t ) const; + + // \ru При линейном расположении нескольких точек согласовать производные на краях участка. \en Aligning the derivatives on the group if several points are located linearly. + bool DerivativesCorrection( double accuracy ); + private: bool Break( MbHermit3D & trimPart, double t1, double t2 ) const; // \ru Разбить на две части \en Split into two parts bool SetCorrection( size_t ind, double tDelta ); // \ru Скорректировать кривую по индексу. \en Curve correction by index. @@ -298,6 +324,7 @@ private: IMPL_PERSISTENT_OPS( MbHermit3D ) + //------------------------------------------------------------------------------ // \ru Определение местных координат области поверхности \en Definition of local coordinates in a surface region // --- diff --git a/C3d/Include/cur_line.h b/C3d/Include/cur_line.h index 0baa480..8008f95 100644 --- a/C3d/Include/cur_line.h +++ b/C3d/Include/cur_line.h @@ -61,7 +61,7 @@ public : void Init( const MbCartPoint & pnt, const MbDirection & dir ) { origin = pnt; direction = dir; } void Init( const MbCartPoint & pnt, const MbVector & dir ) { origin = pnt; direction = dir; } void Init( const MbCartPoint & p1, const MbCartPoint & p2 ) { origin = p1; direction.Calculate( p1, p2 ); } - void Init( double a, double b, double c ); // \ru Инициализация прямой по коэффициентам \en Initialization of a line by coefficients + void Init( double a, double b, double c ); // \ru Инициализация прямой по коэффициентам \en Initialization of a line by coefficients /** \} */ /** \ru \name Общие функции геометрического объекта. @@ -84,7 +84,8 @@ public : virtual bool IsVisibleInRect( const MbRect &, bool exact = false ) const; // \ru Виден ли объект в заданном прям-ке \en Whether the object is visible in the given rectangle using MbCurve::IsVisibleInRect; - virtual double DistanceToPoint( const MbCartPoint & ) const; // \ru Расстояние до точки \en Distance to a point + virtual double DistanceToPoint( const MbCartPoint & ) const; // \ru Расстояние до точки \en Distance to a point + virtual bool DistanceToPointIfLess( const MbCartPoint & toP, double & d ) const; // \ru Вычислить расстояние до точки, если оно меньше d. \en Calculate the distance to the point if it is less than d. /** \} */ /** \ru \name Функции описания области определения кривой. @@ -134,7 +135,7 @@ public : /** \ru \name Общие функции кривой \en \name Common functions of curve \{ */ - virtual MbCurve * Offset( double rad ) const; // \ru Смещение прямой \en Shift of line + virtual MbCurve * Offset( double rad ) const; // \ru Смещение прямой \en Shift of line // \ru Удалить часть прямой между параметрами t1 и t2 \en Remove a part of the line between t1 and t2 parameters virtual MbeState DeletePart( double t1, double t2, MbCurve *& part2 ); @@ -289,7 +290,8 @@ inline void MbLine::Implicit( double & A, double & B, double & C ) const { //------------------------------------------------------------------------------ -// \ru Определение параметров t1, t2 точки пересечения прямых, заданных точкой и вектором направления \en Definition of parameters t1, t2 of the intersection point of lines given a point and a direction vector +// \ru Определение параметров t1, t2 точки пересечения прямых, заданных точкой и вектором направления. +// \en Definition of parameters t1, t2 of the intersection point of lines given a point and a direction vector. // --- inline bool LineLineCrossParams( const MbCartPoint & origin1, const MbVector & direction1, const MbCartPoint & origin2, const MbVector & direction2, @@ -297,7 +299,7 @@ inline bool LineLineCrossParams( const MbCartPoint & origin1, const MbVector & d { double d = direction1.y * direction2.x - direction1.x * direction2.y; - if ( ::fabs( d ) > EPSILON ) { + if ( ::fabs( d ) > EPSILON ) { // \ru Прямые не параллельны. \en Lines not parallel. double dx = origin2.x - origin1.x; double dy = origin2.y - origin1.y; @@ -306,6 +308,18 @@ inline bool LineLineCrossParams( const MbCartPoint & origin1, const MbVector & d return true; } + // \ru Для параллельных прямых определяем параметры точки по середине между origin1 и origin2. \en Definition the parameters for the middle point between origin 1 and origin 2 for parallel lines. + MbCartPoint middle; + middle.Set( origin1, 0.5, origin2, 0.5 ); + t1 = ( (middle.x - origin1.x) * direction1.x + (middle.y - origin1.y) * direction1.y ); + t2 = ( (middle.x - origin2.x) * direction2.x + (middle.y - origin2.y) * direction2.y ); + d = ( direction1.x * direction1.x ) + ( direction1.y * direction1.y ); + if ( d > NULL_EPSILON ) + t1 /= d; + d = ( direction2.x * direction2.x ) + ( direction2.y * direction2.y ); + if ( d > NULL_EPSILON ) + t2 /= d; + return false; // \ru Прямые совпадают или параллельны \en Lines coincide or are parallel } diff --git a/C3d/Include/cur_line3d.h b/C3d/Include/cur_line3d.h index fe39407..900a684 100644 --- a/C3d/Include/cur_line3d.h +++ b/C3d/Include/cur_line3d.h @@ -109,7 +109,6 @@ public : // \ru Вычислить габарит в локальной системе координат. \en Calculate bounding box in the local coordinate system. virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & ) const; - // \ru Все проекции точки на кривую \en All point projections on the curve // \ru Ближайшая проекция точки на кривую \en The nearest point projection to the curve virtual bool NearPointProjection( const MbCartPoint3D &, double & t, bool ext, MbRect1D * tRange = NULL ) const; diff --git a/C3d/Include/cur_line_segment.h b/C3d/Include/cur_line_segment.h index d9a02c6..e1bd852 100644 --- a/C3d/Include/cur_line_segment.h +++ b/C3d/Include/cur_line_segment.h @@ -23,14 +23,14 @@ class DiskreteLengthData; /** \brief \ru Отрезок прямой в двумерном пространстве. \en Line segment in two-dimensional space. \~ \details \ru Отрезок прямой описывается начальной точкой point1 и конечной точкой point2.\n - Область определения параметра отрезка располагается в пределах от нуля до единицы. + Область определения параметра отрезка располагается в пределах от нуля до единицы. Начальной точке отрезка point1 соответствует параметр tmin=0, конечной точке отрезка point2 соответствует параметр tmax=1.\n - Радиус-вектор отрезка описывается векторной функцией\n + Радиус-вектор отрезка описывается векторной функцией\n r(t) = ((1 - t) point1) + (t point2).\n \en Line segment is described by the start point "point1" and the end point "point2". \n - Domain of a line segment is the range [0, 1]. + Domain of a line segment is the range [0, 1]. The start point of line segment corresponds to parameter tmin=0, the end point of line segment corresponds to parameter tmax=1.\n - Radius-vector of line segment is described by the vector function \n + Radius-vector of line segment is described by the vector function \n r(t) = ((1 - t) point1) + (t point2).\n \~ \ingroup Curves_2D */ diff --git a/C3d/Include/cur_nurbs.h b/C3d/Include/cur_nurbs.h index 6a64f65..337d711 100644 --- a/C3d/Include/cur_nurbs.h +++ b/C3d/Include/cur_nurbs.h @@ -62,7 +62,7 @@ class MATH_CLASS MbContour; class MATH_CLASS MbNurbs : public MbPolyCurve { private: - ptrdiff_t degree; ///< \ru Порядок В-сплайна (порядок = степень + 1). \en Order of B-spline (order = degree + 1). + size_t degree; ///< \ru Порядок В-сплайна (порядок = степень + 1). \en Order of B-spline (order = degree + 1). ptrdiff_t uppKnotsIndex; ///< \ru Последний индекс узлового вектора. \en Last index of knot vector. SArray knots; ///< \ru Узловой вектор. \en Knot vector. SArray weights; ///< \ru Множество весов контрольных точек. \en Set of weights of the control points. @@ -125,7 +125,7 @@ protected: */ template - MbNurbs( ptrdiff_t degree, bool cls, const PointsVector & points, + MbNurbs( size_t degree, bool cls, const PointsVector & points, const DoubleVector * weights, const DoubleVector * knots ); MbNurbs( const MbNurbs & ); public : @@ -138,6 +138,8 @@ public: \en Create copy of spline. \~ \details \ru Создать копию сплайна.\n \en Create copy of spline.\n \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbNurbs * Create( const MbNurbs & ); /** \brief \ru Создать сплайн. @@ -150,6 +152,8 @@ public: \en Set of control points. \~ \param[in] initClosed - \ru Признак замкнутости. \en A closedness attribute. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ template static MbNurbs * Create( ptrdiff_t initDegree, const PointsVector & initPoints, bool initClosed ) @@ -171,6 +175,8 @@ public: \en A closedness attribute. \~ \param[in] initWeights - \ru Набор весов для контрольных точек. \en Set of weights for control points. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ template static MbNurbs * Create( ptrdiff_t initDegree, const PointsVector & initPoints, bool initClosed, @@ -197,6 +203,8 @@ public: \en Non-decreasing sequence of weights. \~ \param[in] initForm - \ru Тип построения. \en Type of construction. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ template static MbNurbs * Create( ptrdiff_t initDegree, bool initClosed, const PointsVector & initPoints, @@ -223,8 +231,10 @@ public: \en Set of points which the spline passes through. \~ \param[in] params - \ru Последовательность узловых параметров. \en Sequence of knot parameters. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ - static MbNurbs * CreateThrough( ptrdiff_t degree, bool cls, const SArray & points, + static MbNurbs * CreateThrough( size_t degree, bool cls, const SArray & points, const SArray & params ); /** \brief \ru Заполнить NURBS по данным parasolid. \en Fill NURBS by parasolid data. \~ @@ -254,8 +264,10 @@ public: \en Count of elements in 'knots' array. \~ \param[in] scl - \ru Коэффициент масштабирования. \en A scale factor. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ - static MbNurbs * CreateParasolid( ptrdiff_t degree, bool closed, bool rational, ptrdiff_t count, + static MbNurbs * CreateParasolid( size_t degree, bool closed, bool rational, ptrdiff_t count, const CcArray & verts, ptrdiff_t vertsCount, const CcArray & mul, ptrdiff_t mulCount, const CcArray & knots, ptrdiff_t knotsCount, @@ -272,6 +284,8 @@ public: \en The final point the spline passes through. \~ \param[in] v2 - \ru Касательный вектор к кривой в конечной точке. \en A tangent vector to the curve at the end point. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbNurbs * CreateCube( const MbCartPoint & p1, const MbVector & v1, const MbCartPoint & p2, const MbVector & v2 ); /** \brief \ru Создать сплайн. @@ -282,6 +296,8 @@ public: Первая и последняя точки определяют начало и конец дуги, соответственно. \en A set of four points the section passes through. The first and last points define the beginning and ending of the arc respectively. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbNurbs * CreateArc( const SArray & points ); /** \brief \ru Создать сплайн. @@ -294,6 +310,8 @@ public: \en The starting point of the arc. \~ \param[in] p2 - \ru Конечная точка дуги. \en The end point of the arc. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbNurbs * CreateArc( double a2, const MbCartPoint & p1, const MbCartPoint & p2 ); /** \brief \ru Создать сплайн. @@ -308,6 +326,8 @@ public: \en Height of the wave. \~ \param[in] periode - \ru Период волны. \en Period of the wave. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbNurbs * CreateWavyLine( const MbCartPoint & p1, const MbCartPoint & p2, double height, double periode ); @@ -333,7 +353,7 @@ public : \en A closedness attribute. \~ */ template - bool Init( ptrdiff_t initDegree, const PointsVector & initPoints, bool initClosed ) + bool Init( size_t initDegree, const PointsVector & initPoints, bool initClosed ) { if ( ::IsValidNurbsParams( initDegree, initClosed, initPoints.size() ) ) { @@ -366,7 +386,7 @@ public : \en Set of weights for control points. \~ */ template - bool Init( ptrdiff_t initDegree, const PointsVector & initPoints, bool initClosed, + bool Init( size_t initDegree, const PointsVector & initPoints, bool initClosed, const DoubleVector * initWeights ) { if ( ::IsValidNurbsParamsExt( initDegree, initClosed, initPoints, initWeights ) ) @@ -415,7 +435,7 @@ public : \en Type of construction. \~ */ template - bool Init( ptrdiff_t initDegree, bool initClosed, const PointsVector & initPoints, + bool Init( size_t initDegree, bool initClosed, const PointsVector & initPoints, const DoubleVector & initWeights, const DoubleVector & initKnots, MbeNurbsCurveForm initForm = ncf_Unspecified ) { @@ -455,7 +475,7 @@ public : \param[in] nKnots - \ru Количество узлов. \en Count of knots. \~ */ - bool Init( ptrdiff_t degree, bool cls, const CcArray & points, + bool Init( size_t degree, bool cls, const CcArray & points, const CcArray & knots, ptrdiff_t nPoints, ptrdiff_t nKnots ); /** \brief \ru Инициализация. \en Initialization. \~ @@ -472,7 +492,7 @@ public : \param[in] params - \ru Последовательность узловых параметров. \en Sequence of knot parameters. \~ */ - bool InitThrough( ptrdiff_t degree, bool cls, const SArray & points, + bool InitThrough( size_t degree, bool cls, const SArray & points, const SArray & params ); /** \brief \ru Инициализация. @@ -580,7 +600,7 @@ public : /** \brief \ru Интерполяция. \en Interpolation. \~ \details \ru Создать плоский сплайн четвертого порядка по составному сплайну Безье четвертого порядка.\n - Внимание! Парамеризация отлична от параметризации исходной кривой Безье. + Внимание! Параметризация отлична от параметризации исходной кривой Безье. \en Create a planar spline of fourth order by composite Bezier spline of fourth order.\n If closedness is necessary - call UnClamped( bezier.IsClosed() ). \~ */ @@ -590,15 +610,15 @@ public : bool AttachG( MbPntMatingData & connectData, bool beg ); /// \ru Увеличить порядок кривой, не изменяя ее геометрическую форму и параметризацию. \en Increase order of curve without changing its geometric shape and parametrization. - bool RaiseDegree ( ptrdiff_t, double relEps = Math::paramEpsilon ); + bool RaiseDegree ( size_t, double relEps = Math::paramEpsilon ); /// \ru Уменьшить порядок кривой на 1, не изменяя ее геометрическую форму и параметризацию. \en Decrease order of curve by 1 without changing its geometric shape and parametrization. bool ReductionDegree( double relEps = Math::paramEpsilon ); /// \ru Задать порядок сплайна. \en Set the spline order. - void SetDegree( ptrdiff_t newDegree ); + void SetDegree( size_t newDegree ); /// \ru Увеличить порядок на 1. \en Increase the order by 1. void DegreeIncrease(); /// \ru Установить тип формы. \en Set the type of shape. - void SetFormType( MbeNurbsCurveForm f ) { form = f; } + void SetFormType( MbeNurbsCurveForm f ) { form = f; } /// \ru Точка на кратном узле. \en The point on a multiple knot. bool PointOnMultipleKnot( const MbCartPoint & point ) const; @@ -608,7 +628,7 @@ public : \{ */ virtual MbePlaneType IsA() const; // \ru Тип элемента. \en A type of element. virtual MbPlaneItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element - virtual bool IsSame( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли кривая other копией данной кривой? \en Whether the curve is duplicate of current curve. + virtual bool IsSame( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли кривая other копией данной кривой? \en Whether the curve is duplicate of current curve. virtual bool SetEqual( const MbPlaneItem & ); // \ru Сделать элементы равными. \en Make elements equal. virtual void Transform( const MbMatrix & matr, MbRegTransform * ireg = NULL, const MbSurface * newSurface = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. virtual void Move( const MbVector & to, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Сдвиг. \en Translation. @@ -680,7 +700,7 @@ public : virtual MbNurbs * NurbsCurve( const MbCurveIntoNurbsInfo & ) const; virtual MbCurve * NurbsCurve( const MbNurbsParameters & tParameters ) const; // \ru Построить NURBS копию кривой. \en Construct a NURBS copy of a curve. - virtual MbContour * NurbsContour() const; + virtual MbContour * NurbsContour() const; virtual MbCurve * Trimmed( double t1, double t2, int sense ) const; @@ -749,7 +769,8 @@ public : double & t, bool ext, MbRect1D * tRange = NULL ) const; virtual bool IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = NULL, double epsilon = EPSILON ) const; // \ru Непрерывна ли первая производная? \en Have the first derivative the continuous? - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); virtual void GetStartPoint( MbCartPoint & ) const; virtual void GetEndPoint ( MbCartPoint & ) const; @@ -772,21 +793,30 @@ public : MbeNurbsCurveForm GetFormType() const { return form; } /// \ru Выдать порядок сплайна. \en Get the spline order. - ptrdiff_t GetDegree() const { return degree; } + size_t GetDegree() const { return degree; } /// \ru Вернуть признак рациональности, но не регулярности кривой. \en Get attribute of rationality, but no regularity of curve. bool IsRational() const; - template - void GetWeights( Weights & wts ) const { std::copy( weights.begin(), weights.end(), std::back_inserter(wts) ); } + /// \ru Получить размер весового вектора. \en Get a size of weights vector. size_t GetWeightsCount() const { return weights.size(); } + /// \ru Получить весовой вектор. \en Get a weights vector. + template + void GetWeights( WeightsVector & wts, bool justSet = true ) const { if ( justSet ) { wts.clear(); }; std::copy( weights.begin(), weights.end(), std::back_inserter( wts ) ); } + /// \ru Получить значение элемента весового вектора по индексу. \en Get a weights vector element value by index. double GetWeight( size_t ind ) const { return weights[ind]; } + /// \ru Получить значение элемента весового вектора по индексу. \en Get a weights vector element value by index. double & SetWeight( size_t ind ) { return weights[ind]; } - template - void GetKnots( Knots & kts ) const { std::copy( knots.begin(), knots.end(), std::back_inserter(kts) ); } + /// \ru Получить размер узлового вектора. \en Get a size of knots vector. size_t GetKnotsCount() const { return knots.size(); } + /// \ru Получить узловой вектор. \en Get a knots vector. + template + void GetKnots( KnotsVector & kts, bool justSet = true ) const { if ( justSet ) { kts.clear(); }; std::copy( knots.begin(), knots.end(), std::back_inserter( kts ) ); } + /// \ru Получить значение элемента узлового вектора по индексу. \en Get a knots vector element value by index. double GetKnot( size_t ind ) const { return knots[ind]; } + /// \ru Получить значение элемента узлового вектора по индексу. \en Get a knots vector element value by index. double & SetKnot( size_t ind ) { return knots[ind]; } + /// \ru Вернуть максимальный индекс узлового вектора. \en Get the maximal index of knots vector. ptrdiff_t GetUppKnotsIndex() const { return uppKnotsIndex; } // \ru BEG: для библиотеки (хорошо бы избавиться) \en BEG: for the library (it would be good to get rid of this) @@ -797,15 +827,15 @@ public : /// \ru Добавить узел в конец узлового вектора. \en Add a knot to the end of knot vector. void LtAddKnot ( double knot ) { C3D_ASSERT_UNCONDITIONAL( false ); knots.push_back( knot ); } /// \ru Задать порядок сплайна. \en Set the spline order. - void LtSetDegree( ptrdiff_t newDegree ) { C3D_ASSERT_UNCONDITIONAL( false ); if ( newDegree >= 2 && form == ncf_Unspecified ) { degree = newDegree; } } + void LtSetDegree( size_t newDegree ) { C3D_ASSERT_UNCONDITIONAL( false ); if ( newDegree >= 2 && form == ncf_Unspecified ) { degree = newDegree; } } /// \ru Установить признак замкнутости. \en Set the closedness attribute. void LtSetClosed( bool cls ) { C3D_ASSERT_UNCONDITIONAL( false ); if ( form == ncf_Unspecified ) { closed = cls; } } /// \ru Изменить степень, замкнутость и тип формы. \en Change degree, closedness and type of shape. - void LtSetData( ptrdiff_t d, bool c, MbeNurbsCurveForm f ); + void LtSetData( size_t d, bool c, MbeNurbsCurveForm f ); /// \ru Перестроить сплайн после накачки из библиотеки. \en Rebuild the spline. bool LtRebuild(); /// \ru Инициализация. \en Initialization. - void LtInit(); + void LtInit(); // \ru Преобразование кусочно степенной формы в NURBS-кривую. \en Convert a piecewise exponential form to a NURBS-curve. bool LtInitPowerArc(); bool LtTrimmed( double t1, double t2, int sense = 1 ); @@ -820,7 +850,7 @@ public : void SetWeight( ptrdiff_t pointNumber, double newWeight ); /// \ru Получить кратность узла. \en Get the knot multiplicity. - ptrdiff_t KnotMultiplicity( ptrdiff_t knotIndex ) const; + size_t KnotMultiplicity( ptrdiff_t knotIndex ) const; /// \ru Определение базисного узлового вектора. \en Determination of basis knot vector. void DefineKnotsVector(); /// \ru Переопределение базисного узлового вектора из Close в Open. \en Redetermination of the basis knot vector from Close to Open. @@ -841,7 +871,7 @@ public : // \ru Базовые операции над NURBS-кривой. \en Base operation with NURBS curve. /// \ru Добавление нового узла; возвращает количество узлов, которые удалось вставить. \en Addition of a new knots; returns the number of knots which have been inserted. - ptrdiff_t InsertKnots( double & newKnot, ptrdiff_t multiplicity, double relEps ); + size_t InsertKnots( double & newKnot, size_t multiplicity, double relEps ); /// \ru Удалить кратный внутренний узел id, num раз; вернуть количество удалений, которое удалось сделать. \en Remove multiple internal 'id' knot 'num' times, return count of removals was successfully made. ptrdiff_t RemoveKnot( ptrdiff_t id, ptrdiff_t num, double relEps = Math::paramEpsilon, double absEps = Math::lengthEpsilon ); /// \ru Удалить все внутренние узлы, если это возможно. \en Remove all internal knots if it is possible. @@ -931,25 +961,25 @@ IMPL_PERSISTENT_OPS( MbNurbs ) //------------------------------------------------------------------------------ -// конструктор математического В-сплайн +// Конструктор математического В-сплайна. // --- template -MbNurbs::MbNurbs( ptrdiff_t initDegree, bool initClosed, const PointsVector & initPoints, +MbNurbs::MbNurbs( size_t initDegree, bool initClosed, const PointsVector & initPoints, const DoubleVector * initWeights, const DoubleVector * initKnots ) : MbPolyCurve ( ) , degree ( 0 ) - , uppKnotsIndex( SYS_MAX_T ) // максимальный индекс узлового вектора + , uppKnotsIndex( SYS_MAX_T ) // Максимальный индекс узлового вектора. , knots ( ) , weights ( ) - , form ( ncf_Unspecified ) // форма В-сплайна + , form ( ncf_Unspecified ) // Форма В-сплайна. , cache ( ) { if ( ::IsValidNurbsParamsExt( initDegree, initClosed, initPoints, initWeights, initKnots ) ) { pointList.assign( initPoints.begin(), initPoints.end() ); - uppIndex = (ptrdiff_t)pointList.size() - 1; // количество точек + uppIndex = (ptrdiff_t)pointList.size() - 1; // Количество точек. closed = initClosed; - degree = initDegree; // степень В-сплайна + degree = initDegree; // Степень В-сплайна. if ( initWeights != NULL ) weights.assign( initWeights->begin(), initWeights->end() ); diff --git a/C3d/Include/cur_nurbs3d.h b/C3d/Include/cur_nurbs3d.h index e4dd3a7..e441b7a 100644 --- a/C3d/Include/cur_nurbs3d.h +++ b/C3d/Include/cur_nurbs3d.h @@ -19,6 +19,8 @@ #include #include #include +#include +#include class MATH_CLASS MbNurbs; @@ -57,7 +59,7 @@ class MATH_CLASS MbBezier3D; // --- class MATH_CLASS MbNurbs3D : public MbPolyCurve3D { private : - ptrdiff_t degree; ///< \ru Порядок В-сплайна (порядок = степень + 1). \en Order of B-spline (order = degree + 1). + size_t degree; ///< \ru Порядок В-сплайна (порядок = степень + 1). \en Order of B-spline (order = degree + 1). ptrdiff_t uppKnotsIndex; ///< \ru Последний индекс узлового вектора. \en Last index of knot vector. SArray weights; ///< \ru Множество весов контрольных точек. \en Set of weights of the control points. SArray knots; ///< \ru Узловой вектор сплайна. \en Knot vector of the spline. @@ -124,7 +126,7 @@ protected: \en Sequence of knot parameters. \~ */ - MbNurbs3D( ptrdiff_t deg, bool cls, const SArray & points, + MbNurbs3D( size_t deg, bool cls, const SArray & points, const SArray * weights = NULL, const SArray * knots = NULL ); MbNurbs3D( const MbNurbs3D & ); public : @@ -137,6 +139,8 @@ public : \en Create copy of spline. \~ \details \ru Создать копию сплайна.\n \en Create copy of spline.\n \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbNurbs3D * Create( const MbNurbs3D & ); /** \brief \ru Создать сплайн. @@ -147,6 +151,8 @@ public : \en The two-dimensional spline. \~ \param[in] place - \ru Локальная система координат сплайна. \en Local coordinate system of spline. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ static MbNurbs3D * Create( const MbNurbs & nurbs, const MbPlacement3D & place ); /** \brief \ru Создать сплайн. @@ -161,8 +167,10 @@ public : \en Closedness attribute. \~ \param[in] weights - \ru Набор весов для контрольных точек. \en Set of weights for control points. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ - static MbNurbs3D * Create( ptrdiff_t degree, const SArray & points, bool closed, + static MbNurbs3D * Create( size_t degree, const SArray & points, bool closed, const SArray * weights = NULL ); /** \brief \ru Создать сплайн. \en Create spline. \~ @@ -176,9 +184,10 @@ public : \en Set of control points. \~ \param[in] knots - \ru Неубывающая последовательность узлов. \en Nondecreasing sequence of knots. \~ - + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ - static MbNurbs3D * Create( ptrdiff_t degree, bool closed, const SArray & points, + static MbNurbs3D * Create( size_t degree, bool closed, const SArray & points, const SArray & knots ); /** \brief \ru Создать сплайн. \en Create spline. \~ @@ -196,10 +205,11 @@ public : \en Nondecreasing sequence of knots. \~ \param[in] initForm - \ru Тип построения. \en Type of construction. \~ - + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ template - static MbNurbs3D * Create( ptrdiff_t initDegree, bool initClosed, const PointsVector & initPoints, + static MbNurbs3D * Create( size_t initDegree, bool initClosed, const PointsVector & initPoints, const DoubleVector & initWeights, const DoubleVector & initKnots, MbeNurbsCurveForm initForm = ncf_Unspecified ) { @@ -224,8 +234,10 @@ public : \en Sequence of knot parameters. \~ \param[in] aKnots - \ru Неубывающая последовательность узлов. \en Nondecreasing sequence of knots. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ - static MbNurbs3D * CreateThrough( ptrdiff_t degree, bool cls, const SArray & points, + static MbNurbs3D * CreateThrough( size_t degree, bool cls, const SArray & points, const SArray & params, SArray * aKnots = NULL ); /** \brief \ru Заполнить NURBS по данным parasolid. \en Fill NURBS by parasolid data. \~ @@ -255,8 +267,10 @@ public : \en Count of elements in 'knots' array. \~ \param[in] scl - \ru Коэффициент масштабирования. \en Scale factor. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ */ - static MbNurbs3D * CreateParasolid( ptrdiff_t degree, bool closed, bool rational, ptrdiff_t count, + static MbNurbs3D * CreateParasolid( size_t degree, bool closed, bool rational, ptrdiff_t count, const CcArray & verts, ptrdiff_t vertsCount, const CcArray & mul, ptrdiff_t mulCount, const CcArray & knots, ptrdiff_t knotsCount, @@ -279,7 +293,7 @@ public: \param[in] closed - \ru Признак замкнутости. \en Closedness attribute. \~ */ - bool Init( ptrdiff_t degree, const SArray & points, bool closed, + bool Init( size_t degree, const SArray & points, bool closed, const SArray * weights = NULL ); /** \brief \ru Инициализация. \en Initialization. \~ @@ -295,7 +309,7 @@ public: \en Nondecreasing sequence of knots. \~ */ - bool Init( ptrdiff_t degree, bool closed, const SArray & points, + bool Init( size_t degree, bool closed, const SArray & points, const SArray & knots ); /** \brief \ru Инициализация. \en Initialization. \~ @@ -316,7 +330,7 @@ public: */ template - bool Init( ptrdiff_t initDegree, bool initClosed, const PointsVector & initPoints, + bool Init( size_t initDegree, bool initClosed, const PointsVector & initPoints, const DoubleVector & initWeights, const DoubleVector & initKnots, MbeNurbsCurveForm initForm = ncf_Unspecified ) { @@ -372,7 +386,7 @@ public: \param[in] endData - \ru Параметр сопряжения в конечной точке сплайна. \en Parameter of conjugation at the end point of the spline. \~ */ - bool Init( ptrdiff_t degree, const SArray & points, const SArray & weights, + bool Init( size_t degree, const SArray & points, const SArray & weights, MbPntMatingData & begData, MbPntMatingData & endData ); /** \brief \ru Инициализация. @@ -392,8 +406,8 @@ public: \param[in] endData - \ru Количество узлов. \en Count of knots. \~ */ - bool Init( ptrdiff_t degree, bool closed, const CcArray & points, - const CcArray & knots, ptrdiff_t nPoints, ptrdiff_t nKnots ); + bool Init( size_t degree, bool closed, const CcArray & points, + const CcArray & knots, size_t nPoints, size_t nKnots ); /** \brief \ru Инициализация. \en Initialization. \~ \details \ru Сплайн, проходящий через заданные точки при заданных параметрах.\n @@ -411,10 +425,10 @@ public: \param[in] aKnots - \ru Неубывающая последовательность узлов. \en Nondecreasing sequence of knots. \~ */ - bool InitThrough( ptrdiff_t degree, bool cls, const SArray & points, + bool InitThrough( size_t degree, bool cls, const SArray & points, const SArray & params, SArray * aKnots = NULL ); /// \ru Установить тип формы. \en Set the type of shape. - void SetFormType( MbeNurbsCurveForm f ) { form = f; } + void SetFormType( MbeNurbsCurveForm f ) { form = f; } // \ru Общие функции математического объекта. \en The common functions of the mathematical object. @@ -508,7 +522,8 @@ public: virtual void ResetTCalc() const; // \ru Сбросить текущее значение параметра \en Reset the current value of the parameter virtual bool IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = NULL, double epsilon = EPSILON ) const; // \ru Непрерывна ли первая производная? \en Have the first derivative the continuous? - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); // \ru Функции B-сплайн кривой. \en Functions of B-spline curve. /// \ru Выделить часть кривой. \en Extract a piece of a curve. @@ -520,7 +535,7 @@ public: // \ru Функции B-сплайна. \en Functions of B-spline. /// \ru Добавление нового узла с заданной кратностью. \en Add a new knot with the given multiplicity. - void InsertKnots ( double & newKnot, ptrdiff_t multiplicity, double relEps = Math::paramEpsilon ); + void InsertKnots ( double & newKnot, size_t multiplicity, double relEps = Math::paramEpsilon ); /// \ru Добавление новых узлов равномерно в промежуток от idBegin до idBegin+1. \en Add new equally spaced knots into the range from idBegin to idBegin+1. void InsertKnotsInRegion( ptrdiff_t idBegin ); @@ -536,12 +551,12 @@ public: bool DecomposeCurve(); /// \ru Увеличить порядок NURBS-кривой, не меняя ее геометрическую форму и парамеризацию. \en Increase the order of a NURBS-curve without changing its geometric shape and parameterization. - bool RaiseDegree ( ptrdiff_t, double relEps = Math::paramEpsilon ); + bool RaiseDegree ( size_t, double relEps = Math::paramEpsilon ); /// \ru Уменьшить порядок кривой на 1. \en Decrease the order of a curve by 1. bool ReductionDegree( double relEps = Math::paramEpsilon ); /// \ru Получить кратность узла с заданным номером. \en Get multiplicity of a knot with a given index. - ptrdiff_t KnotMultiplicity( ptrdiff_t knotIndex ) const; + size_t KnotMultiplicity( size_t knotIndex ) const; /// \ru Определение базисного узлового вектора. \en Definition of the basis knot vector. void DefineKnotsVector(); /// \ru Переопределение базисного узлового вектора из Close в Open. \en Redefine the basis knot vector from Close to Open. @@ -550,31 +565,42 @@ public: bool CloseKnotsVector (); /// \ru Установить область изменения параметра. \en Set the range of parameter. - bool SetLimitParam ( double pmin, double pmax ); + bool SetLimitParam( double pmin, double pmax ); /// \ru Добавить кривую в конец. \en Add a curve to the end. - void AddCurve ( MbNurbs3D &, bool bmerge = true ); + void AddCurve ( MbNurbs3D &, bool bmerge = true ); /// \ru Добавить кривые в конец. \en Add curves to the end. - void AddCurves ( const RPArray & ); + void AddCurves ( const RPArray & ); /// \ru Репераметризовать кривую в соответствии с длиной в случае, если кривая получена из набора кривых Безье. \en Reparameterize a curve according to the length if the curve is obtained from a set of Bezier curves. bool ReparamCurveInBezierForm(); /// \ru Получить форму В-сплайна. \en Get the form of B-spline. - MbeNurbsCurveForm GetFormType() const { return form; } + MbeNurbsCurveForm GetFormType() const { return form; } /// \ru Получить порядок В-сплайна. \en Get the order of B-spline. - ptrdiff_t GetDegree() const { return degree; } + size_t GetDegree() const { return degree; } /// \ru Вернуть признак рациональности, но не регулярности кривой. \en Get the attribute of rationality, but not regularity of a curve. bool IsRational() const; - size_t GetWeightsCount() const { return weights.Count(); } - void GetWeights( SArray & wts ) const { wts = weights; } + /// \ru Получить размер весового вектора. \en Get a size of weights vector. + size_t GetWeightsCount() const { return weights.size(); } + /// \ru Получить весовой вектор. \en Get a weights vector. + template + void GetWeights( WeightsVector & wts, bool justSet = true ) const { if ( justSet ) { wts.clear(); }; std::copy( weights.begin(), weights.end(), std::back_inserter( wts ) ); } + /// \ru Получить значение элемента весового вектора по индексу. \en Get a weights vector element value by index. double GetWeight( size_t ind ) const { return weights[ind]; } + /// \ru Получить значение элемента весового вектора по индексу. \en Get a weights vector element value by index. double & SetWeight( size_t ind ) { return weights[ind]; } - size_t GetKnotsCount() const { return knots.Count(); } - void GetKnots( SArray & knts ) const { knts = knots; } + /// \ru Получить размер узлового вектора. \en Get a size of knots vector. + size_t GetKnotsCount() const { return knots.size(); } + /// \ru Получить узловой вектор. \en Get a knots vector. + template + void GetKnots( KnotsVector & kts, bool justSet = true ) const { if ( justSet ) { kts.clear(); }; std::copy( knots.begin(), knots.end(), std::back_inserter( kts ) ); } + /// \ru Получить значение элемента узлового вектора по индексу. \en Get a knots vector element value by index. double GetKnot ( size_t ind ) const { return knots[ind]; } + /// \ru Получить значение элемента узлового вектора по индексу. \en Get a knots vector element value by index. double & SetKnot ( size_t ind ) { return knots[ind]; } + /// \ru Вернуть максимальный индекс узлового вектора. \en Get the maximal index of knots vector. ptrdiff_t GetUppKnotsIndex() const { return uppKnotsIndex; } // \ru Функции только 3D кривой. \en Functions of 3D curve only. @@ -659,6 +685,14 @@ public: Attention! Parameterization is different from the parameterization of the source Bezier curve. \~ */ static MbNurbs3D * CreateNURBS4( const MbBezier3D & ); + /** \brief \ru Создать сплайн. + \en Create NURBS. \~ + \details \ru Создать сплайн произвольного порядка через точки, с управлением касательными и кривизной в этих точках. + \en Create a spline of any order containing the given points with managing of tangent and curvature at these points.\~ + */ + static MbNurbs3D * CreateNURBS( size_t initDegree, const SArray & initPoints, + const SArray & initParams, bool initClosed, + RPArray> & matingData ); /** \brief \ru Разбить кривую. \en Split the curve. \~ \details \ru Разбить недифференцируемую NURBS-кривую четвертой степени в трижды кратном внутреннем узле.\n @@ -736,7 +770,7 @@ private: RPArray< MbPntMatingData > & inferredData, const SArray & arParams, const SArray & arKnots, - ptrdiff_t addCount, + size_t addCount, bool cls, MbeSplineCreateType useInitThrough, size_t deg = 4 ); diff --git a/C3d/Include/cur_nurbs_vector3d.h b/C3d/Include/cur_nurbs_vector3d.h index 869d4dd..0476a22 100644 --- a/C3d/Include/cur_nurbs_vector3d.h +++ b/C3d/Include/cur_nurbs_vector3d.h @@ -27,11 +27,15 @@ struct DoubleTriple double y; double z; + DoubleTriple() : x(0.0), y(0.0), z(0.0) {} + DoubleTriple( double xx, double yy, double zz ) : x( xx ), y( yy ), z( zz ) {} + // \ru Инициализация. \en Initialization. void Init( double xx, double yy, double zz ) { x = xx; y = yy; z = zz; } // \ru Инициализация. \en Initialization. - void Init( const DoubleTriple & ip ) { Init( ip.x, ip.y, ip.z ); } + template + void Init( const Array & o ) { x = o[0]; y = o[1]; z = o[2]; } // \ru Инициализация. \en Initialization. void Init( const DoubleTriple & ip, double iw ) { Init( ip.x * iw, ip.y * iw, ip.z * iw ); } @@ -44,8 +48,40 @@ struct DoubleTriple Init( ( p2.x - p1.x ) * kk, ( p2.y - p1.y ) * kk, ( p2.z - p1.z ) * kk ); } + // \ru Присвоение значений. \en Values assignment. + template + const DoubleTriple & operator=( const Array & o ) { + Init( o ); + return *this; + } + + /// \ru Доступ к координате по индексу. \en Access to coordinate by an index. + double & operator[]( size_t i ) { + return i < 2 ? ( i == 1 ? y : x) : z; + } + double operator[]( size_t i ) const { + return i < 2 ? ( i == 1 ? y : x) : z; + } + + /// \ru Сложить с точкой. \en Add a vector. + template + inline DoubleTriple operator + ( const Point & v ) const { + return DoubleTriple( x + v.x, y + v.y, z + v.z ); + } + + // \ru Вычесть точку. \en Subtract a point. + template + inline DoubleTriple operator - ( const Point & p ) const { + return DoubleTriple( x - p.x, y - p.y, z - p.z ); + } + }; +/// \ru Умножить DoubleTriple на число. \en Multiply DoubleTriple by number. +inline DoubleTriple operator * ( const DoubleTriple & p, const double & factor ) { + return DoubleTriple( p.x * factor, p.y * factor, p.z * factor ); +} + //------------------------------------------------------------------------------ /** \brief \ru Nurbs-вектор. \en Nurbs-vector. \~ @@ -73,6 +109,7 @@ public: void Dec( ptrdiff_t i, const MbNURBSVector & p1, ptrdiff_t ip1, const MbNURBSVector & p2, ptrdiff_t ip2, double kk ); void Set( ptrdiff_t i, const MbNURBSVector & p, ptrdiff_t ip, double kk ); void Set( ptrdiff_t i, const DoubleTriple * t, double * ww, ptrdiff_t ip ); + void Set( ptrdiff_t i, const DoubleTriple & t, double ww ); void Set( ptrdiff_t i, const DoubleTriple & t ); double& x( ptrdiff_t i ) { return _vec[i].x; } @@ -179,6 +216,13 @@ inline void MbNURBSVector::Set( ptrdiff_t i, const DoubleTriple * t, double * ww w(i) = ww[ip]; } +inline void MbNURBSVector::Set( ptrdiff_t i, const DoubleTriple & t, double ww ) +{ + _vec[i].Init( t.x , t.y , t.z ); + if ( useWeights ) + w(i) = ww; +} + inline void MbNURBSVector::Set( ptrdiff_t i, const DoubleTriple & t ) { _vec[i].Init( t.x , t.y , t.z ); diff --git a/C3d/Include/cur_offset_curve.h b/C3d/Include/cur_offset_curve.h index 81cabed..04dd602 100644 --- a/C3d/Include/cur_offset_curve.h +++ b/C3d/Include/cur_offset_curve.h @@ -25,13 +25,13 @@ class MbRegTransform; \en Offset extended curve. \~ \details \ru Эквидистантная продолженная кривая строится смещением точек базовой кривой вдоль нормали к ней. \n Параметры "offsetTmin, offsetTmax" задают смещение точек базовой кривой в точках tmin, tmax. - Радиус-вектор кривой в методе PointOn(double&t,MbCartPoint3D&r) описывается векторной функцией \n + Радиус-вектор кривой в методе PointOn(double&t,MbCartPoint3D&r) описывается векторной функцией \n r(t) = basisCurve(t) + (Offset0(t) * normal(t)), где normal(t) - нормаль базовой кривой. \n Базовой кривой для эквидистантной кривой не может служить другая эквидистантная кривая. В подобной ситуации выполняется переход к первичной базовой кривой. \en Offset extended curve is constructed by shifting points of the base curve along a normal to it. \n The "offsetTmin, offsetTmax" parameters set shift of base curve on begin and end points. - Radius-vector of the curve in the method PointOn(double&t,MbCartPoint3D&r) is described by the function \n + Radius-vector of the curve in the method PointOn(double&t,MbCartPoint3D&r) is described by the function \n r(t) = basisCurve(t) + (Offset0(t) * normal(t)), where normal(t) - normal of base curve. \n Base curve for offset curve can not be other offset curve. In such situation it changes to the initial base curve. \~ @@ -106,7 +106,7 @@ public : virtual void Transform( const MbMatrix & matr, MbRegTransform * ireg = NULL, const MbSurface * newSurface = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix virtual void Move( const MbVector & to, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Сдвиг \en Translation virtual void Rotate( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Поворот \en Rotation - virtual bool IsSame( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли кривая curve копией данной кривой ? \en Whether the 'curve' curve is duplicate of current curve. + virtual bool IsSame( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли кривая curve копией данной кривой ? \en Whether the 'curve' curve is duplicate of current curve. virtual void AddYourGabaritTo( MbRect & ) const; // \ru Добавь свой габарит в прямой прям-к \en Add bounding box into a straight box /** \} */ @@ -191,7 +191,7 @@ public : \en \name Common function of curve. \{ */ virtual bool IsStraight() const; // \ru Является ли линия прямолинейной \en Whether the line is straight - virtual MbCurve * Offset( double rad ) const; // \ru Смещение смещенной кривой \en Offset of the offset curve + virtual MbCurve * Offset( double rad ) const; // \ru Смещение смещенной кривой \en Offset of the offset curve virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменение направления кривой на противоположное \en Change to the opposite direction of a curve virtual void Refresh(); // \ru Сбросить все временные данные \en Reset all temporary data virtual void PrepareIntegralData( const bool forced ) const; // \ru Рассчитать временные (mutable) данные объекта. \en Calculate temporary (mutable) data of an object. @@ -238,7 +238,8 @@ public : virtual void SetBasisPoints( const MbControlData & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. virtual bool IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = NULL, double epsilon = EPSILON ) const; // \ru Непрерывна ли первая производная? \en Have the first derivative the continuous? - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); void SetBasisCurve( MbCurve & ); // \ru Установить базовую кривую \en Set the base curve // \ru Тип смещения точек. \en The type of points offset. @@ -246,20 +247,29 @@ public : // \ru Постоянное ли смещение точек? \en Is const the offset type? bool IsConstOffset() const { return ( (type == off_Empty) || (type == off_Const) ); } // \ru Величина смещения. \en The offset distance. - double GetDistance( size_t i = 0 ) const { - if ( i == 1 ) return offsetTmax; - return offsetTmin; + double GetDistance( size_t i ) const { + i = i % 2; + if ( i == 0 ) + return offsetTmin; + return offsetTmax; } + // \ru Средняя величина смещения. \en The average offset distance. + double GetDistance() const { return ( offsetTmin + offsetTmax ) / 2; } /** \brief \ru Установить величины смещения. \en Set offset distances. \~ \param[in] d - \ru Новая величина смещения - \en New offset distance \~ + \en New offset distance. \~ */ - void SetDistance( double d, size_t i = 0 ); + void SetDistance( double d, size_t i ); + // \ru Установить постоянную величину смещения. Set new constant offset distance. + void SetDistance( double d ); - const MbRect & GetGabarit() const { if ( rect.IsEmpty() ) CalculateGabarit( rect ); return rect; } - void SetDirtyGabarit() const { rect.SetEmpty(); } + virtual void CalculateGabarit( MbRect & ) const; // \ru Определить габаритный прямоугольник кривой. \en Detect the bounding box of a curve. + virtual double CalculateMetricLength() const; // \ru Вычислить метрическую длину кривой. \en Calculate the metric length of a curve. + + const MbRect & GetGabarit() const; + void SetDirtyGabarit() const; const double & GetDmin() const { return deltaTmin; } // \ru Дать расширение начала \en Get extension of start const double & GetDmax() const { return deltaTmax; } // \ru Дать расширение конца \en Get extension of end void SetDmin( double d ) { deltaTmin = d; } // \ru Установить расширение начала \en Set extension of start diff --git a/C3d/Include/cur_offset_curve3d.h b/C3d/Include/cur_offset_curve3d.h index f86fdf3..559d18a 100644 --- a/C3d/Include/cur_offset_curve3d.h +++ b/C3d/Include/cur_offset_curve3d.h @@ -1,214 +1,221 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Эквидистантная кривая в трехмерном пространстве. - \en Offset curve in three-dimensional space. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __CUR_OFFSET_CURVE3D_H -#define __CUR_OFFSET_CURVE3D_H - - -#include -#include -#include - - -class MATH_CLASS MbSpine; - - -//------------------------------------------------------------------------------ -/** \brief \ru Эквидистантная кривая в трехмерном пространстве. - \en Offset curve in three-dimensional space. \~ - \details \ru Эквидистантная кривая строится смещением точек базовой кривой вдоль некоторого вектора, - направление которого может меняться вдоль кривой. \n - Вектор offset задаёт смещение начальной точки базовой криаой. - В процессе движения вдоль кривой вектор offset сохраняет своё положение в движущейся локальной системе координат, - начало которой совпадает с текущей точкой базовой кривой. - Одна из осей движущейся локальной системы координат всегда совпадает с касательной базовой кривой, - а две другие оси ортогональны ей. - Базовой кривой для эквидистантной кривой не может служить другая эквидистантная кривая. - В подобной ситуации выполняется переход к первичной базовой кривой. - \en Offset curve is constructed by shifting points of the base curve along some vector, - direction of which can be changed along the curve. \n - Vector "offset" sets the offset of start point of the base curve. - While moving along a curve the vector "offset" keeps the position in the moving local coordinate system, - origin coincides with the current point of the base curve. - One of the axes of the moving local coordinate system is always the same as the tangent of the base curve, - and the other two axes are orthogonal to it. - Base curve for offset curve can not be other offset curve. - In this situation it changes to the initial base curve. \~ - \ingroup Curves_3D -*/ -// --- -class MATH_CLASS MbOffsetCurve3D : public MbCurve3D { -protected : - MbSpine * basisCurve; ///< \ru Базовая кривая. \en The base curve. - double tmin; ///< \ru Начальный параметр basisCurve. \en Start parameter of basisCurve. - double tmax; ///< \ru Конечный параметр basisCurve. \en End parameter of basisCurve. - bool closed; ///< \ru Замкнутость basisCurve. \en Closedness of basisCurve. - MbVector3D offset; ///< \ru Смещение в начальной точке. \en Offset in start point. - double factorTmin; ///< \ru Множитель смещения offset в точке tmin базовой кривой. \en The offset multiplier in point tmin of base curve. - double factorTmax; ///< \ru Множитель смещения offset в точке tmax базовой кривой. \en The offset multiplier in point tmax of base curve. - MbeOffsetType type; ///< \ru Тип смещения: константный, линейный или кубический. \en The type of offset: constant, or linear, or cubic. - double deltaTmin; ///< \ru Увеличение tmin параметра базовой кривой. \en Increase of tmin of base curve parameter. - double deltaTmax; ///< \ru Увеличение tmax параметра базовой кривой. \en Increase of tmax of base curve parameter. - mutable MbCube cube; ///< \ru Габаритный куб. \en Bounding box. - -public : - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор эквидистантной кривой по спайну и вектору.\n - \en Constructor by a curve and offset vector in start point.\n \~ - \param[in] c - \ru Базовая кривая. \en The base curve. \~ - \param[in] off - \ru Вектор смещения начальной точки кривой. \en Offset in start point. \~ - \param[in] same - \ru Использовать присланную кривую (true) или ее копию (false). - \en Use same curve (true) or copy (false). \~ - \param[in] ort - \ru Ортогонализовать вектор к касательной кривой в начальной точке. - \en Ortogonalize offset vector (true) or same vector (false). \~ - */ - MbOffsetCurve3D( const MbCurve3D & c, const MbVector3D & off, bool same, bool ort, VERSION version = Math::DefaultMathVersion() ); -private : - MbOffsetCurve3D( const MbOffsetCurve3D & ); // \ru Не реализовано. \en Not implemented. -protected: - MbOffsetCurve3D( const MbOffsetCurve3D & init, MbRegDuplicate * ireg ); - -public : - virtual ~MbOffsetCurve3D(); - -public: - VISITING_CLASS( MbOffsetCurve3D ); - - /** \brief \ru Инициализация по смещению и приращениям параметров. - \en Initialization by offset and increments of parameters. \~ - \details \ru Смещение задано на краях параметрической области базовой кривой и может изменяться по константному, линейному и кубическому законам.\n - Приращение параметров нужно использовать для изменения области определения кривой относительно базовой кривой. - \en The offset displacement is defined in the begin and the end of the parametric region of the base curve and can be changed by constant, linear and cubic laws.\n - Increment of parameters needs to be used for change of curve domain relative to base curve. \~ - \param[in] d1 - \ru Смещение в точке Tmin базовой кривой. - \en Offset distance on point Tmin of base curve. \~ - \param[in] d2 - \ru Смещение в точке Tmax базовой кривой. - \en Offset distance on point Tmax of base curve. \~ - \param[in] t - \ru Тип смещения точек: константный, линейный или кубический. - \en The offset type: constant, or linear, or cubic. \~ - \param[in] dt1 - \ru Изменение tmin параметра - \en The change of tmin parameter \~ - \param[in] dt2 - \ru Изменение tmax параметра - \en The change of tmax parameter \~ - */ - void Init( double d1, double d2, MbeOffsetType t, double dt1, double dt2 ); - - // \ru Общие функции математического объекта \en Common functions of the mathematical object - - virtual MbeSpaceType IsA() const; // \ru Тип элемента \en Type of element - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; - virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным \en Make equal - virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar - virtual void Transform( const MbMatrix3D &, MbRegTransform * ireg ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * ireg ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * ireg ); // \ru Повернуть вокруг оси \en Rotate about an axis - virtual void Refresh (); // \ru Сбросить все временные данные \en Reset all temporary data - virtual void PrepareIntegralData( const bool forced ) const; // \ru Рассчитать временные (mutable) данные объекта. \en Calculate temporary (mutable) data of an object. - virtual void AddYourGabaritTo( MbCube & ) const; // \ru Добавь свой габарит в куб \en Add bounding box into a cube - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - // \ru Общие функции кривой \en Common functions of curve - - virtual double GetTMin() const; - virtual double GetTMax() const; - virtual bool IsClosed() const; // \ru Замкнутость кривой \en A curve closedness - virtual double GetPeriod() const; // \ru Период кривой \en Curve period - // \ru Функции кривой для работы в области определения параметрической кривой \en Functions of curve for working at parametric curve domain - virtual void PointOn ( double & t, MbCartPoint3D & p ) const; // \ru Точка на кривой \en Point on curve - virtual void FirstDer ( double & t, MbVector3D & fd ) const; // \ru Первая производная \en First derivative - virtual void SecondDer( double & t, MbVector3D & sd ) const; // \ru Вторая производная \en Second derivative - virtual void ThirdDer ( double & t, MbVector3D & td ) const; // \ru Третья производная по t \en Third derivative with respect to t - - // \ru ЗАКОМЕНТАРЕНО в связи с необходимостью использовать строгое продолжение по касательной \en COMMENTED because it is necessary to use a strong extension by the tangent - //virtual void _PointOn ( double t, MbCartPoint3D &p ) const; // \ru Точка на расширенной кривой \en Point on the extended curve - //virtual void _FirstDer ( double t, MbVector3D &fd ) const; // \ru Первая производная \en The first derivative - //virtual void _SecondDer( double t, MbVector3D &sd ) const; // \ru Вторая производная \en The second derivative - //virtual void _ThirdDer ( double t, MbVector3D &td ) const; // \ru Третья производная по t \en The third derivative with respect to t - // \ru Вычислить значения точки и производных для заданного параметра. \en Calculate point and derivatives of object for given parameter. \~ - virtual void Explore( double & t, bool ext, - MbCartPoint3D & pnt, MbVector3D & fir, MbVector3D * sec, MbVector3D * thir ) const; - - virtual double Step( double t, double sag ) const; // \ru Вычисление шага аппроксимации \en Calculation of approximation step - virtual double DeviationStep( double t, double angle ) const; - - virtual const MbCurve3D & GetBasisCurve() const; - virtual MbCurve3D & SetBasisCurve(); -//virtual MbCurve3D * Trimmed( double t1, double t2, int sense ) const; // \ru Создание усеченной кривой \en Creation of a trimmed curve - virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменить направление \en Change direction - - virtual size_t GetCount() const; - virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменение носителя \en Changing of carrier - virtual bool IsStraight() const; // \ru Является ли линия прямолинейной \en Whether the line is straight - virtual bool IsPlanar () const; // \ru Является ли кривая плоской \en Whether a curve is planar - - virtual bool GetPlacement( MbPlacement3D & place, VERSION version = Math::DefaultMathVersion() ) const; // \ru Заполнить плейсемент, ести кривая плоская \en Fill the placement if curve is planar - - virtual bool IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = NULL, double epsilon = EPSILON ) const; // \ru Непрерывна ли первая производная? \en Have the first derivative the continuous? - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. - - /// \ru Смещение в начальной точке. \en Offset in the start point. - const MbVector3D & GetOffsetVector() const { return offset; } - // \ru Тип смещения точек. \en The type of points offset. - MbeOffsetType GetOffsetType() const { return type; } - // \ru Постоянное ли смещение точек? \en Is const the offset type? - bool IsConstOffset() const { return ( (type == off_Empty) || (type == off_Const) ); } - // \ru Множитель смещения. \en The offset multiplier. - double GetFactor( size_t i = 0 ) const { - if ( i == 1 ) return factorTmax; - return factorTmin; - } - - /** \brief \ru Установить множитель смещения. \en Set offset multiplier. \~ - \param[in] d - \ru Новый множитель смещения. \en New offset multiplier. \~ - */ - void SetFactor( double d, size_t i = 0 ); - // \ru Проверить факторы и тип. \en Check factors and typr. - void CheckFactor(); - - const MbCube & GetGabarit() const { if ( cube.IsEmpty() ) CalculateGabarit( cube ); return cube; } // \ru Выдать габарит кривой \en Get the bounding box of curve - bool IsSelfIntersect() const; - /** \brief \ru Поиск точек излома оффсетной кривой. - \en Search of break points of the offset curve. \~ - \details \ru Для нахождения точек точек излома используется характеристическая функция Ratio(), - представляющая собой разность аналитически и численно посчитанной производной деленную - на модуль аналитической производной и величину шага, использованного для численного рассчета производной. - Увеличение этой функции на порядок по сравнению с ее значением в гладкой области означает точку излома. \n - \en To find the break points using the characteristic function Ratio(), - which represents a difference between the analytical and numerical calculated derivative divided - by module of analytical derivative and step used for numerical calculation of the derivative. - Increase of this function on the order in comparison with its value in smooth region is a break point. \n \~ - \param[out] breakParams - \ru Массив параметров точек излома - \en Parameter array of break points \~ - */ - void FindBreakParams( SArray & breakParams ) const; - int ExtendedParam( double &t ) const; // \ru Проверка, лежит ли параметр в пределах \en Check if parameter is in range - -private: - // \ru Вычисление множителя смещения и его производных. \en The offset multiplier and it derivatives. - double Factor0 ( double t ) const; - double FactorT ( double t ) const; - double FactorTT ( double t ) const; - double FactorTTT( double t ) const; - - void operator = ( const MbOffsetCurve3D & ); // \ru Не реализовано. \en Not implemented. - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbOffsetCurve3D ) -}; - -IMPL_PERSISTENT_OPS( MbOffsetCurve3D ) - - -#endif // __CUR_OFFSET_CURVE3D_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Эквидистантная кривая в трехмерном пространстве. + \en Offset curve in three-dimensional space. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CUR_OFFSET_CURVE3D_H +#define __CUR_OFFSET_CURVE3D_H + + +#include +#include +#include + + +class MATH_CLASS MbSpine; + + +//------------------------------------------------------------------------------ +/** \brief \ru Эквидистантная кривая в трехмерном пространстве. + \en Offset curve in three-dimensional space. \~ + \details \ru Эквидистантная кривая строится смещением точек базовой кривой вдоль некоторого вектора, + направление которого может меняться вдоль кривой. \n + Вектор offset задаёт смещение начальной точки базовой криаой. + В процессе движения вдоль кривой вектор offset сохраняет своё положение в движущейся локальной системе координат, + начало которой совпадает с текущей точкой базовой кривой. + Одна из осей движущейся локальной системы координат всегда совпадает с касательной базовой кривой, + а две другие оси ортогональны ей. + Базовой кривой для эквидистантной кривой не может служить другая эквидистантная кривая. + В подобной ситуации выполняется переход к первичной базовой кривой. + \en Offset curve is constructed by shifting points of the base curve along some vector, + direction of which can be changed along the curve. \n + Vector "offset" sets the offset of start point of the base curve. + While moving along a curve the vector "offset" keeps the position in the moving local coordinate system, + origin coincides with the current point of the base curve. + One of the axes of the moving local coordinate system is always the same as the tangent of the base curve, + and the other two axes are orthogonal to it. + Base curve for offset curve can not be other offset curve. + In this situation it changes to the initial base curve. \~ + \ingroup Curves_3D +*/ +// --- +class MATH_CLASS MbOffsetCurve3D : public MbCurve3D { +protected : + MbSpine * basisCurve; ///< \ru Базовая кривая. \en The base curve. + double tmin; ///< \ru Начальный параметр basisCurve. \en Start parameter of basisCurve. + double tmax; ///< \ru Конечный параметр basisCurve. \en End parameter of basisCurve. + bool closed; ///< \ru Замкнутость basisCurve. \en Closedness of basisCurve. + MbVector3D offset; ///< \ru Смещение в начальной точке. \en Offset in start point. + double factorTmin; ///< \ru Множитель смещения offset в точке tmin базовой кривой. \en The offset multiplier in point tmin of base curve. + double factorTmax; ///< \ru Множитель смещения offset в точке tmax базовой кривой. \en The offset multiplier in point tmax of base curve. + MbeOffsetType type; ///< \ru Тип смещения: константный, линейный или кубический. \en The type of offset: constant, or linear, or cubic. + double deltaTmin; ///< \ru Увеличение tmin параметра базовой кривой. \en Increase of tmin of base curve parameter. + double deltaTmax; ///< \ru Увеличение tmax параметра базовой кривой. \en Increase of tmax of base curve parameter. + mutable MbCube cube; ///< \ru Габаритный куб. \en Bounding box. + +public : + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор эквидистантной кривой по спайну и вектору.\n + \en Constructor by a curve and offset vector in start point.\n \~ + \param[in] c - \ru Базовая кривая. \en The base curve. \~ + \param[in] off - \ru Вектор смещения начальной точки кривой. \en Offset in start point. \~ + \param[in] same - \ru Использовать присланную кривую (true) или ее копию (false). + \en Use same curve (true) or copy (false). \~ + \param[in] ort - \ru Ортогонализовать вектор к касательной кривой в начальной точке. + \en Ortogonalize offset vector (true) or same vector (false). \~ + */ + MbOffsetCurve3D( const MbCurve3D & c, const MbVector3D & off, bool same, bool ort, VERSION version = Math::DefaultMathVersion() ); +private : + MbOffsetCurve3D( const MbOffsetCurve3D & ); // \ru Не реализовано. \en Not implemented. +protected: + MbOffsetCurve3D( const MbOffsetCurve3D & init, MbRegDuplicate * ireg ); + +public : + virtual ~MbOffsetCurve3D(); + +public: + VISITING_CLASS( MbOffsetCurve3D ); + + /** \brief \ru Инициализация по смещению и приращениям параметров. + \en Initialization by offset and increments of parameters. \~ + \details \ru Смещение задано на краях параметрической области базовой кривой и может изменяться по константному, линейному и кубическому законам.\n + Приращение параметров нужно использовать для изменения области определения кривой относительно базовой кривой. + \en The offset displacement is defined in the begin and the end of the parametric region of the base curve and can be changed by constant, linear and cubic laws.\n + Increment of parameters needs to be used for change of curve domain relative to base curve. \~ + \param[in] d1 - \ru Смещение в точке Tmin базовой кривой. + \en Offset distance on point Tmin of base curve. \~ + \param[in] d2 - \ru Смещение в точке Tmax базовой кривой. + \en Offset distance on point Tmax of base curve. \~ + \param[in] t - \ru Тип смещения точек: константный, линейный или кубический. + \en The offset type: constant, or linear, or cubic. \~ + \param[in] dt1 - \ru Изменение tmin параметра + \en The change of tmin parameter \~ + \param[in] dt2 - \ru Изменение tmax параметра + \en The change of tmax parameter \~ + */ + void Init( double d1, double d2, MbeOffsetType t, double dt1, double dt2 ); + + // \ru Общие функции математического объекта \en Common functions of the mathematical object + + virtual MbeSpaceType IsA() const; // \ru Тип элемента \en Type of element + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; + virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным \en Make equal + virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar + virtual void Transform( const MbMatrix3D &, MbRegTransform * ireg ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * ireg ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * ireg ); // \ru Повернуть вокруг оси \en Rotate about an axis + virtual void Refresh (); // \ru Сбросить все временные данные \en Reset all temporary data + virtual void PrepareIntegralData( const bool forced ) const; // \ru Рассчитать временные (mutable) данные объекта. \en Calculate temporary (mutable) data of an object. + virtual void AddYourGabaritTo( MbCube & ) const; // \ru Добавь свой габарит в куб \en Add bounding box into a cube + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the basis objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + // \ru Общие функции кривой \en Common functions of curve + + virtual double GetTMin() const; + virtual double GetTMax() const; + virtual bool IsClosed() const; // \ru Замкнутость кривой \en A curve closedness + virtual double GetPeriod() const; // \ru Период кривой \en Curve period + // \ru Функции кривой для работы в области определения параметрической кривой \en Functions of curve for working at parametric curve domain + virtual void PointOn ( double & t, MbCartPoint3D & p ) const; // \ru Точка на кривой \en Point on curve + virtual void FirstDer ( double & t, MbVector3D & fd ) const; // \ru Первая производная \en First derivative + virtual void SecondDer( double & t, MbVector3D & sd ) const; // \ru Вторая производная \en Second derivative + virtual void ThirdDer ( double & t, MbVector3D & td ) const; // \ru Третья производная по t \en Third derivative with respect to t + + // \ru ЗАКОМЕНТАРЕНО в связи с необходимостью использовать строгое продолжение по касательной \en COMMENTED because it is necessary to use a strong extension by the tangent + //virtual void _PointOn ( double t, MbCartPoint3D &p ) const; // \ru Точка на расширенной кривой \en Point on the extended curve + //virtual void _FirstDer ( double t, MbVector3D &fd ) const; // \ru Первая производная \en The first derivative + //virtual void _SecondDer( double t, MbVector3D &sd ) const; // \ru Вторая производная \en The second derivative + //virtual void _ThirdDer ( double t, MbVector3D &td ) const; // \ru Третья производная по t \en The third derivative with respect to t + // \ru Вычислить значения точки и производных для заданного параметра. \en Calculate point and derivatives of object for given parameter. \~ + virtual void Explore( double & t, bool ext, + MbCartPoint3D & pnt, MbVector3D & fir, MbVector3D * sec, MbVector3D * thir ) const; + + virtual double Step( double t, double sag ) const; // \ru Вычисление шага аппроксимации \en Calculation of approximation step + virtual double DeviationStep( double t, double angle ) const; + + virtual const MbCurve3D & GetBasisCurve() const; + virtual MbCurve3D & SetBasisCurve(); +//virtual MbCurve3D * Trimmed( double t1, double t2, int sense ) const; // \ru Создание усеченной кривой \en Creation of a trimmed curve + virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменить направление \en Change direction + + virtual size_t GetCount() const; + virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменение носителя \en Changing of carrier + virtual bool IsStraight() const; // \ru Является ли линия прямолинейной \en Whether the line is straight + virtual bool IsPlanar () const; // \ru Является ли кривая плоской \en Whether a curve is planar + + virtual bool GetPlacement( MbPlacement3D & place, VERSION version = Math::DefaultMathVersion() ) const; // \ru Заполнить плейсемент, ести кривая плоская \en Fill the placement if curve is planar + + virtual bool IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = NULL, double epsilon = EPSILON ) const; // \ru Непрерывна ли первая производная? \en Have the first derivative the continuous? + // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); + + /// \ru Смещение в начальной точке. \en Offset in the start point. + const MbVector3D & GetOffsetVector() const { return offset; } + // \ru Тип смещения точек. \en The type of points offset. + MbeOffsetType GetOffsetType() const { return type; } + // \ru Постоянное ли смещение точек? \en Is const the offset type? + bool IsConstOffset() const { return ( (type == off_Empty) || (type == off_Const) ); } + // \ru Множитель смещения. \en The offset multiplier. + double GetFactor( size_t i ) const { + i = i % 2; + if ( i == 0 ) + return factorTmin; + return factorTmax; + } + // \ru Средний множитель смещения. \en The average offset multiplier. + double GetFactor() const { return ( factorTmin + factorTmax ) / 2; } + + /** \brief \ru Установить множитель смещения. \en Set offset multiplier. \~ + \param[in] d - \ru Новый множитель смещения. \en New offset multiplier. \~ + */ + void SetFactor( double d, size_t i ); + // \ru Установить постоянный множитель смещения. Set new constant offset multiplier. + void SetFactor( double d ); + // \ru Проверить факторы и тип. \en Check factors and typr. + void CheckFactor(); + + const MbCube & GetGabarit() const; // \ru Выдать габарит кривой \en Get the bounding box of curve + bool IsSelfIntersect() const; + /** \brief \ru Поиск точек излома оффсетной кривой. + \en Search of break points of the offset curve. \~ + \details \ru Для нахождения точек точек излома используется характеристическая функция Ratio(), + представляющая собой разность аналитически и численно посчитанной производной деленную + на модуль аналитической производной и величину шага, использованного для численного рассчета производной. + Увеличение этой функции на порядок по сравнению с ее значением в гладкой области означает точку излома. \n + \en To find the break points using the characteristic function Ratio(), + which represents a difference between the analytical and numerical calculated derivative divided + by module of analytical derivative and step used for numerical calculation of the derivative. + Increase of this function on the order in comparison with its value in smooth region is a break point. \n \~ + \param[out] breakParams - \ru Массив параметров точек излома + \en Parameter array of break points \~ + */ + void FindBreakParams( SArray & breakParams ) const; + int ExtendedParam( double &t ) const; // \ru Проверка, лежит ли параметр в пределах \en Check if parameter is in range + +private: + // \ru Вычисление множителя смещения и его производных. \en The offset multiplier and it derivatives. + double Factor0 ( double t ) const; + double FactorT ( double t ) const; + double FactorTT ( double t ) const; + double FactorTTT( double t ) const; + + void operator = ( const MbOffsetCurve3D & ); // \ru Не реализовано. \en Not implemented. + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbOffsetCurve3D ) +}; + +IMPL_PERSISTENT_OPS( MbOffsetCurve3D ) + + +#endif // __CUR_OFFSET_CURVE3D_H diff --git a/C3d/Include/cur_plane_curve.h b/C3d/Include/cur_plane_curve.h index 30c34cc..298a424 100644 --- a/C3d/Include/cur_plane_curve.h +++ b/C3d/Include/cur_plane_curve.h @@ -151,7 +151,8 @@ public : MbCurve * MakeCurve( const MbPlacement3D & ) const; MbCurve3D * MakeCurve() const; // \ru Дать пространственную кривую \en Get the spatial curve - void SetCurve( const MbCurve & ); // \ru Заменить плоскую кривую \en Replace the plane curve + void SetCurve( const MbCurve & ); // \ru Заменить плоскую кривую \en Replace the plane curve + bool SetLimitParam( double newTMin, double newTMax ); // \ru Установить область изменения параметра. \en Set range of parameter. void SetOrigin( const MbCartPoint3D & org ) { position.SetOrigin(org); } const MbPlacement3D & GetPlacement() const { return position; } @@ -163,7 +164,8 @@ public : virtual bool IsSimilarToCurve( const MbCurve3D & curve, double precision = METRIC_PRECISION ) const; // \ru Подобные ли кривые для объединения (слива) \en Whether the curves are similar for merge (joining) virtual bool IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = NULL, double epsilon = EPSILON ) const; // \ru Непрерывна ли первая производная? \en Have the first derivative the continuous? - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); private: void operator = ( const MbPlaneCurve & ); // \ru Не реализовано. \en Not implemented. diff --git a/C3d/Include/cur_point_curve.h b/C3d/Include/cur_point_curve.h index f306747..802c30f 100644 --- a/C3d/Include/cur_point_curve.h +++ b/C3d/Include/cur_point_curve.h @@ -72,7 +72,7 @@ public : virtual void AddYourGabaritTo ( MbRect & ) const; // \ru Добавь свой габарит в прямой прям-к \en Add bounding box into a straight box virtual void CalculateLocalGabarit( const MbMatrix & into, MbRect & local ) const; // \ru Добавь в прям-к свой габарит с учетом матрицы \en Add bounding box into a box with consideration of the matrix virtual double DistanceToPoint( const MbCartPoint & to ) const; // \ru Расстояние до точки \en Distance to a point - virtual bool IsSame( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли кривая curve копией данной кривой ? \en Whether the 'curve' curve is duplicate of current curve. + virtual bool IsSame( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли кривая curve копией данной кривой ? \en Whether the 'curve' curve is duplicate of current curve. virtual bool IsVisibleInRect( const MbRect & r, bool exact = false ) const; // \ru Виден ли объект в заданном прямоугольнике \en Whether the object is visible in the given rectangle /** \} */ diff --git a/C3d/Include/cur_polycurve.h b/C3d/Include/cur_polycurve.h index 7c51bab..8ea51df 100644 --- a/C3d/Include/cur_polycurve.h +++ b/C3d/Include/cur_polycurve.h @@ -13,6 +13,7 @@ #include #include +#include class MbRegDuplicate; @@ -29,7 +30,7 @@ class MbRegTransform; \ingroup Curves_2D */ // --- -class MATH_CLASS MbPolyCurve : public MbCurve, public MbNestSyncItem { +class MATH_CLASS MbPolyCurve : public MbCurve { protected : SArray pointList; ///< \ru Множество контрольных точек. \en Set of control points. ptrdiff_t uppIndex; ///< \ru Количество участков кривой (равно количество контрольных точек минус единица). \en Count of curve pieces (is equal to count of control points minus one). @@ -52,7 +53,7 @@ public : virtual MbePlaneType IsA() const = 0; // \ru Тип элемента \en Type of element virtual MbePlaneType Type() const; // \ru Тип элемента \en Type of element virtual bool SetEqual( const MbPlaneItem & ) = 0; // \ru Сделать элементы равными \en Make the elements equal - virtual bool IsSame( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const = 0; // \ru Является ли кривая curve копией данной кривой ? \en Whether curve 'curve' is a duplicate of the current curve. + virtual bool IsSame( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const = 0; // \ru Является ли кривая curve копией данной кривой ? \en Whether curve 'curve' is a duplicate of the current curve. virtual void Transform( const MbMatrix & matr, MbRegTransform * ireg = NULL, const MbSurface * newSurface = NULL ) = 0; // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix virtual void Move( const MbVector & to, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ) = 0; // \ru Сдвиг \en Translation virtual void Rotate( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ) = 0; // \ru Поворот \en Rotation @@ -115,7 +116,7 @@ public : /** \ru \name Общие функции полигональной кривой \en \name Common functions of polygonal curve \{ */ - virtual size_t GetPointsCount() const; ///< \ru Выдать количество контрольных точек. \en Get count of control points. + virtual size_t GetPointsCount() const; ///< \ru Выдать количество контрольных точек. \en Get count of control points. /** \brief \ru Выдать характерную точку. \en Get control point. \~ @@ -132,7 +133,7 @@ public : */ virtual void GetPoint( ptrdiff_t index, MbCartPoint & pnt ) const; // \ru Выдать точку \en Get point - virtual ptrdiff_t GetNearPointIndex( const MbCartPoint & pnt ) const; ///< \ru Выдать индекс точки, ближайшей к заданной. \en Get index of the point nearest to the given one. + virtual ptrdiff_t GetNearPointIndex( const MbCartPoint & pnt ) const; ///< \ru Выдать индекс точки, ближайшей к заданной. \en Get index of the point nearest to the given one. /** \brief \ru Вернуть интервал влияния точки кривой. \en Get the range of influence of point of the curve. \~ @@ -301,6 +302,7 @@ public : void GetPoints( Points & pnts ) const { std::copy( pointList.begin(), pointList.end(), std::back_inserter( pnts ) ); } ///< \ru Вернуть массив контрольных точек. \en Get array of control points. void GetPointList( SArray & pnts ) const { pnts = pointList; } ///< \ru Вернуть массив контрольных точек. \en Get array of control points. bool ReplacePoints( const SArray & pnts ); ///< \ru Заменить набор контрольных точек. \en Replace the set of control points. + bool ReplacePoints( const std::vector & pnts ); ///< \ru Заменить набор контрольных точек. \en Replace the set of control points. const MbCartPoint & GetPointList( size_t i ) const { return pointList[i]; } ///< \ru Вернуть характерную точку с заданным индексом. \en Get control point with the given index. MbCartPoint & SetPointList( size_t i ) { Refresh(); return pointList[i]; } ///< \ru Вернуть характерную точку с заданным индексом. \en Get control point with the given index. diff --git a/C3d/Include/cur_polycurve3d.h b/C3d/Include/cur_polycurve3d.h index d068a46..a5bdf62 100644 --- a/C3d/Include/cur_polycurve3d.h +++ b/C3d/Include/cur_polycurve3d.h @@ -25,7 +25,7 @@ \ingroup Curves_3D */ // --- -class MATH_CLASS MbPolyCurve3D : public MbCurve3D, public MbNestSyncItem { +class MATH_CLASS MbPolyCurve3D : public MbCurve3D { protected : ptrdiff_t uppIndex; ///< \ru Количество участков кривой (равно количество контрольных точек минус единица). \en Count of curve pieces (is equal to count of control points minus one). SArray pointList; ///< \ru Множество контрольных точек. \en Set of control points. @@ -115,7 +115,7 @@ public : ptrdiff_t & endPointNumber, // \ru Номер последней точки \en Index of the last point ptrdiff_t & period ) const; // \ru Количество точек в периоде \en Count of points in period - const MbCube & GetGabarit() const { if ( cube.IsEmpty() ) CalculateGabarit( cube ); return cube; } // \ru Выдать габарит кривой \en Get bounding box of curve + const MbCube & GetGabarit() const; // \ru Выдать габарит кривой \en Get bounding box of curve size_t GetPointListCount() const { return pointList.Count(); } ptrdiff_t GetPointListMaxIndex() const { return pointList.MaxIndex(); } diff --git a/C3d/Include/cur_polyline.h b/C3d/Include/cur_polyline.h index fc3b585..c28e5cd 100644 --- a/C3d/Include/cur_polyline.h +++ b/C3d/Include/cur_polyline.h @@ -86,7 +86,7 @@ public : virtual MbePlaneType IsA() const; // \ru Тип элемента \en Type of element virtual bool SetEqual( const MbPlaneItem & ); // \ru Сделать элементы равными \en Make the elements equal - virtual bool IsSame( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли кривая curve копией данной кривой ? \en Whether curve 'curve' is a duplicate of the current curve. + virtual bool IsSame( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли кривая curve копией данной кривой ? \en Whether curve 'curve' is a duplicate of the current curve. virtual void Transform( const MbMatrix & matr, MbRegTransform * ireg = NULL, const MbSurface * newSurface = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix virtual void Move( const MbVector & to, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Сдвиг \en Translation virtual void Rotate( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Поворот \en Rotation @@ -176,11 +176,11 @@ public : /** \ru \name Общие функции кривой \en \name Common functions of curve \{ */ - virtual MbCurve * Offset( double rad ) const; // \ru Смещение полилинии \en Shift of polyline + virtual MbCurve * Offset( double rad ) const; // \ru Смещение полилинии \en Shift of polyline virtual MbNurbs * NurbsCurve( const MbCurveIntoNurbsInfo & ) const; virtual MbCurve * NurbsCurve( const MbNurbsParameters & ) const; - virtual MbContour * NurbsContour() const; + virtual MbContour * NurbsContour() const; virtual MbCurve * Trimmed( double t1, double t2, int sense ) const; @@ -252,7 +252,8 @@ public : virtual bool IsSmoothConnected( double angleEps ) const; // \ru Являются ли стыки контура\кривой гладкими? \en Whether the joints of a contour\curve are smooth. virtual bool IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = NULL, double epsilon = EPSILON ) const; // \ru Непрерывна ли первая производная? \en Have the first derivative the continuous? - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object diff --git a/C3d/Include/cur_polyline3d.h b/C3d/Include/cur_polyline3d.h index 0100dd0..6c4fba2 100644 --- a/C3d/Include/cur_polyline3d.h +++ b/C3d/Include/cur_polyline3d.h @@ -168,7 +168,8 @@ public : virtual bool IsSmoothConnected( double angleEps ) const; // \ru Являются ли стыки контура\кривой гладкими? \en Whether the joints of a contour\curve are smooth. virtual bool IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = NULL, double epsilon = EPSILON ) const; // \ru Непрерывна ли первая производная? \en Have the first derivative the continuous? - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); bool UnClamped( bool ); void DeleteEqPoints( double absEps ); // \ru Удалить одинаковые точки \en Remove equal points diff --git a/C3d/Include/cur_projection_curve.h b/C3d/Include/cur_projection_curve.h index 4a1a994..a49155c 100644 --- a/C3d/Include/cur_projection_curve.h +++ b/C3d/Include/cur_projection_curve.h @@ -211,6 +211,8 @@ public : virtual double GetLengthEvaluation() const; // \ru Оценка метрической длины кривой. \en Estimation of metric length of the curve. // \ru Вычислить метрическую длину кривой от параметра t1 до t2. \en Calculate the metric length of unclosed curve from parameter t1 to parameter t2. virtual double CalculateLength( double t1, double t2 ) const; + /// \ru Вычислить метрическую длину кривой. \en Calculate the metric length of a curve. + virtual double CalculateMetricLength() const; virtual bool GetMiddlePoint( MbCartPoint & ) const; // \ru Вычислить среднюю точку кривой. \en Calculate mid-point of curve. diff --git a/C3d/Include/cur_reparam_curve.h b/C3d/Include/cur_reparam_curve.h index af0579a..7d93efb 100644 --- a/C3d/Include/cur_reparam_curve.h +++ b/C3d/Include/cur_reparam_curve.h @@ -16,6 +16,7 @@ class MbRegDuplicate; class MbRegTransform; +class MbFunction; //------------------------------------------------------------------------------ @@ -40,25 +41,23 @@ class MbRegTransform; // --- class MATH_CLASS MbReparamCurve : public MbCurve { enum MbeReparamType { - rt_Linear, ///< \ru Линейная репараметризация. \en Linear reparametrization. - rt_Quadratic, ///< \ru Квадратичная репараметризация. \en Quadratic reparametrization. - rt_Cubic ///< \ru Кубическая репараметризация. \en Quadratic reparametrization. + rt_Linear, ///< \ru Линейная репараметризация. \en Linear reparametrization. + rt_Quadratic, ///< \ru Квадратичная репараметризация. \en Quadratic reparametrization. + rt_ScaledEndDers ///< \ru Репараметризация с заданными масштабами производной на концах. \en Reparametrization with a given scale of the derivative at the ends. }; protected : - MbCurve * basisCurve; ///< \ru Базовая кривая. \en The base curve. - MbeReparamType reparamType; ///< \ru Способ репараметризации. \en Way of repatametrization. - double tmin; ///< \ru Начальный параметр. \en Start parameter. - double tmax; ///< \ru Конечный параметр. \en End parameter. - double q; ///< \ru Коэффициент при кубическом члене репараметризующего многочлена. \en The coefficient of the cubic term of the reparametrizing polynomial. - double a; ///< \ru Коэффициент при квадратичном члене репараметризующего многочлена. \en The coefficient of the quadratic term of the reparametrizing polynomial. - double b; ///< \ru Коэффициент при линейном члене репараметризующего многочлена. \en The coefficient of the linear term of the reparametrizing polynomial. - double c; ///< \ru Свободный коэффициент репараметризующего многочлена. \en The free coefficient of the reparametrizing polynomial. + MbCurve * basisCurve; ///< \ru Базовая кривая. \en The base curve. + MbeReparamType reparamType; ///< \ru Способ репараметризации. \en Way of repatametrization. + SPtr rFunc; ///< \ru Репараметризующая функция, всегда не нулевая. \en Reparametric function, always non-zero. public : + /// \ru Конструктор по кривой и новым параметрам. \en Constructor by curve and by new parametric limits. MbReparamCurve( const MbCurve &, double t1, double t2 ); + /// \ru Конструктор по кривой и новым параметрам. \en Constructor by curve and by new parametric limits. MbReparamCurve( const MbCurve &, const double t1, const double t2, const double begFirstDerValue ); - MbReparamCurve( const MbCurve &, double t1, double t2, double derBeg, double derEnd ); + /// \ru Конструктор по кривой и производным параметра на ее концах. \en Constructor for the curve and derivatives of the parameter at its ends + MbReparamCurve( double dt1, double dt2, const MbCurve & curve ); protected: MbReparamCurve( const MbReparamCurve & ); public : @@ -67,28 +66,31 @@ public : public : VISITING_CLASS( MbReparamCurve ); + /// \ru Установить параметрическую область кривой. \en Set curve parametric range. void Init( double t1, double t2 ); + /// \ru Установить параметрическую область кривой. \en Set curve parametric range. void Init( double t1, double t2, double begFirstDerValue ); - void Init( double t1, double t2, double der1, double der2 ); + /// \ru Установить параметрическую область кривой. \en Set curve parametric range. + void InitScaledEnds( double scaleDer1, double scaleDer2 ); /** \ru \name Общие функции геометрического объекта. \en \name Common functions of a geometric object. \{ */ virtual MbePlaneType IsA() const; // \ru Тип элемента \en Type of element - virtual bool IsSimilar ( const MbPlaneItem & ) const; // \ru Являются ли элементы подобными \en Whether the elements are similar - virtual bool SetEqual( const MbPlaneItem & ); // \ru Сделать элементы равными \en Make the elements equal - virtual void Transform( const MbMatrix & matr, MbRegTransform * ireg = NULL, const MbSurface * newSurface = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move( const MbVector & to, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Поворот \en Rotation - virtual bool IsSame( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; - virtual MbCurve * Offset( double rad ) const; // \ru Смещение усеченной кривой \en Shift of a trimmed curve + virtual bool IsSimilar( const MbPlaneItem & ) const; // \ru Являются ли элементы подобными \en Whether the elements are similar + virtual bool SetEqual ( const MbPlaneItem & ); // \ru Сделать элементы равными \en Make the elements equal + virtual void Transform( const MbMatrix &, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move( const MbVector &, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate( const MbCartPoint &, const MbDirection & angle, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Поворот \en Rotation + virtual bool IsSame( const MbPlaneItem &, double accuracy = LENGTH_EPSILON ) const; + virtual MbCurve * Offset( double rad ) const; // \ru Смещение усеченной кривой \en Shift of a trimmed curve virtual MbPlaneItem & Duplicate( MbRegDuplicate * = NULL ) const; virtual MbCurve * Trimmed( double t1, double t2, int sense ) const; - virtual MbContour * NurbsContour() const; // \ru Построить контур \en Create a contour - virtual void AddYourGabaritTo( MbRect & r ) const; // \ru Добавь свой габарит в прямой прям-к \en Add your own gabarit into the given bounding rectangle - virtual void CalculateGabarit( MbRect & r ) const; // \ru Определить габариты кривой \en Determine the bounding box of a curve - virtual bool IsVisibleInRect( const MbRect & r, bool exact = false ) const; // \ru Виден ли объект в заданном прямоугольнике \en Whether the object is visible in the given rectangle + virtual MbContour * NurbsContour() const; // \ru Построить контур \en Create a contour + virtual void AddYourGabaritTo( MbRect & ) const; // \ru Добавь свой габарит в прямой прям-к \en Add your own gabarit into the given bounding rectangle + virtual void CalculateGabarit( MbRect & ) const; // \ru Определить габариты кривой \en Determine the bounding box of a curve + virtual bool IsVisibleInRect( const MbRect &, bool exact = false ) const; // \ru Виден ли объект в заданном прямоугольнике \en Whether the object is visible in the given rectangle using MbCurve::IsVisibleInRect; /** \} */ @@ -207,7 +209,7 @@ public : virtual double GetRadius() const; // \ru Дать физический радиус объекта или ноль, если это невозможно. \en Get the physical radius of the object or null if it impossible. virtual bool DistanceAlong( double & t, double len, int curveDir, double eps = Math::LengthEps, VERSION version = Math::DefaultMathVersion() ) const; // \ru Сдвинуть параметр t на расстояние len по направлению \en Translate parameter 't' by distance 'len' along the direction - virtual bool GetAxisPoint( MbCartPoint & p ) const; // \ru Точка для построения оси \en Point for the axis construction + virtual bool GetAxisPoint( MbCartPoint & ) const; // \ru Точка для построения оси \en Point for the axis construction virtual bool IsSimilarToCurve( const MbCurve & curve, double precision = PARAM_PRECISION ) const; // \ru Подобные ли кривые для объединения (слива) \en Whether the curves for union (joining) are similar virtual size_t GetCount() const; // \ru Количество разбиений для прохода в операциях \en Count of subdivisions for pass in operations /// \ru Получить границы участков кривой, на которых сохраняется непрерывность кривизны. @@ -225,13 +227,13 @@ public : virtual const MbCurve & GetBasisCurve() const; virtual MbCurve & SetBasisCurve(); - void SetBasisCurve( MbCurve & ); // \ru Заменить плоскую кривую \en Replace the planar curve - double Tmin() const { return tmin; } // \ru Начальный параметр \en Start parameter - double Tmax() const { return tmax; } // \ru Конечный параметр \en End parameter - double Dt() const { return b; } // \ru Производная параметра кривой basisCurve по параметру \en Derivative of parameter of 'basisCurve' curve by parameter - void SetTmin( double t ); - void SetTmax( double t ); - void SetDt ( double d ); + bool SetBasisCurve( const MbCurve &, const MbRect1D * tRange = NULL ); ///< \ru Заменить плоскую кривую \en Replace the planar curve + double Tmin() const; ///< \ru Начальный параметр. \en Start parameter. + double Tmax() const; ///< \ru Конечный параметр. \en End parameter. + double Dt() const; ///< \ru Производная параметра кривой basisCurve по параметру. \en Derivative of parameter of 'basisCurve' curve by parameter. + bool SetTmin( double t ); + bool SetTmax( double t ); + bool SetDt ( double d ); MbeReparamType GetReparamType() const { return reparamType; } // \ru Тип параметризации. \en Parameterization type. @@ -242,13 +244,14 @@ public : virtual void SubstrateToCurve( double & ) const; // \ru Преобразовать параметр подложки в параметр кривой \en Transform a substrate parameter to the curve parameter virtual void CurveToSubstrate( double & ) const; // \ru Преобразовать параметр кривой в параметр подложки \en Transform a curve parameter to the substrate parameter - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object virtual void GetBasisPoints( MbControlData & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. virtual void SetBasisPoints( const MbControlData & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. virtual bool IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = NULL, double epsilon = EPSILON ) const; // \ru Непрерывна ли первая производная? \en Have the first derivative the continuous? - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); bool IsLinear() const { return (reparamType == rt_Linear); } // \ru Является ли репараметризация линейной? \en Is the re-parametrization linear? @@ -256,117 +259,14 @@ public : private: void operator = ( const MbReparamCurve & ); // \ru Не реализовано. \en Not implemented. - double CubicRoot( double t ) const; // \ru Решение кубического уравнения. \en Solution of cubic equation. - void Explore( double t, double * par, double * dpar, double * ddpar, double * dddpar ) const; // \ru Параметр базовой кривой и его производные. \en The base curve parameter and its derivatives. + // \ru Параметр базовой кривой и его производные. \en The base curve parameter and its derivatives. + void Explore( double & t, bool ext, double & par, double & dpar, double * ddpar, double * dddpar ) const; DECLARE_PERSISTENT_CLASS_NEW_DEL( MbReparamCurve ) }; // MbReparamCurve IMPL_PERSISTENT_OPS( MbReparamCurve ) -//------------------------------------------------------------------------------ -// \ru Перевод параметра базовой кривой в новый параметр \en Transformation of the base curve parameter to a new parameter -// --- -inline void MbReparamCurve::ParameterInto( double & t ) const { - if ( reparamType == rt_Linear ) - t = ( t - c ) / b; - else if ( reparamType == rt_Quadratic ) { - C3D_ASSERT( ::fabs(a) > NULL_EPSILON ); - const double discriminant = b * b + 4.0 * a * (t - c); - if ( discriminant > EXTENT_EQUAL ) - t = 0.5 * ( ::sqrt(discriminant) - b ) / a; - else - t = -0.5 * b / a; - } - else if( reparamType == rt_Cubic ) { - bool close = basisCurve->IsClosed(); - double bTmin = basisCurve->GetTMin(), bTmax = basisCurve->GetTMax(); - if ( !close && ( t < bTmin || t > bTmax ) ) { - double t0 = ( t < bTmin ) ? tmin : tmax; - double par = ( t < bTmin ) ? bTmin : bTmax; - double dpar = 3.0 * q * t0 * t0 + 2.0 * a * t0 + b; - t = t0 + ( t - par ) / dpar; - } - else { - double n = 0.0; - if ( close ) { - double period = bTmax - bTmin; - n = ::floor( (t - bTmin) / period ); - t -= n * period; - } - t = CubicRoot( t ); - if( n != 0.0 ) - t += n * ( tmax - tmin ); - } - } -} - - -//------------------------------------------------------------------------------ -// \ru Перевод нового параметра в параметр базовой кривой \en Transformation of a new parameter to the base curve parameter -// --- -inline void MbReparamCurve::ParameterFrom( double & t ) const { - if ( reparamType == rt_Linear ) - t = b * t + c; - else if ( reparamType == rt_Quadratic ) - t = a * t * t + b * t + c; - else if ( reparamType == rt_Cubic ) - Explore( t, &t, 0, 0, 0 ); -} - - -//------------------------------------------------------------------------------ -// \ru Перевод точности параметра базовой кривой в точность локального параметра \en Transformation of the base curve parameter tolerance to a local parameter tolerance -// --- -inline double MbReparamCurve::EpsilonInto( double eps ) const { - double res = 0.0; - - if ( reparamType == rt_Linear ) { - if ( ::fabs(b) > Math::paramEpsilon ) - res = eps / b; - } - else if ( reparamType == rt_Quadratic ) { - const double bmin = 2.0 * a * tmin + b; - const double bmax = 2.0 * a * tmax + b; - const double div = std_max( bmin, bmax ); - if ( ::fabs(div) > Math::paramEpsilon ) - res = eps / div; - } - else if ( reparamType == rt_Cubic ) { - const double bmin = 3.0 * q * tmin * tmin + 2.0 * a * tmin + b; - const double bmax = 3.0 * q * tmax * tmax + 2.0 * a * tmax + b; - const double div = std_max( bmin, bmax ); - if ( ::fabs( div ) > Math::paramEpsilon ) - res = eps / div; - } - - return res; -} - - -//------------------------------------------------------------------------------ -// \ru Перевод точности локального параметра в точность параметра базовой кривой \en Transformation of a local parameter tolerance to the base curve parameter tolerance -// --- -inline double MbReparamCurve::EpsilonFrom( double eps ) const { - double res = 0.0; - - if ( reparamType == rt_Linear ) - return eps * b; - else if ( reparamType == rt_Quadratic ) { - const double bmin = 2.0 * a * tmin + b; - const double bmax = 2.0 * a * tmax + b; - const double mul = std_min( bmin, bmax ); - res = eps * mul; - } - else if ( reparamType == rt_Cubic ) { - const double bmin = 3.0 * q * tmin * tmin + 2.0 * a * tmin + b; - const double bmax = 3.0 * q * tmax * tmax + 2.0 * a * tmax + b; - const double mul = std_min( bmin, bmax ); - res = eps * mul; - } - - return res; -} #endif // __CUR_REPARAM_CURVE_H diff --git a/C3d/Include/cur_reparam_curve3d.h b/C3d/Include/cur_reparam_curve3d.h index f8d8e1c..caccd3c 100644 --- a/C3d/Include/cur_reparam_curve3d.h +++ b/C3d/Include/cur_reparam_curve3d.h @@ -13,6 +13,7 @@ #include +class MbFunction; //------------------------------------------------------------------------------ /** \brief \ru Репараметризованная кривая в трехмерном пространстве. @@ -40,29 +41,27 @@ // --- class MATH_CLASS MbReparamCurve3D : public MbCurve3D { enum MbeReparamType { - rt_Linear, ///< \ru Линейная репараметризация. \en Linear reparametrization. - rt_Quadratic, ///< \ru Квадратичная репараметризация. \en Quadratic reparametrization. - rt_Cubic ///< \ru Кубическая репараметризация. \en Quadratic reparametrization. + rt_Linear, ///< \ru Линейная репараметризация. \en Linear reparametrization. + rt_Quadratic, ///< \ru Квадратичная репараметризация. \en Quadratic reparametrization. + rt_ScaledEndDers ///< \ru Репараметризация с заданными масштабами производной на концах. \en Reparametrization with a given scale of the derivative at the ends. }; protected: - MbCurve3D * basisCurve; ///< \ru Базовая кривая. \en The base curve. - MbeReparamType reparamType; ///< \ru Способ репараметризации. \en Way of repatametrization. - double tmin; ///< \ru Начальный параметр. \en Start parameter. - double tmax; ///< \ru Конечный параметр. \en End parameter. - double q; ///< \ru Коэффициент при кубическом члене репараметризующего многочлена. \en The coefficient of the cubic term of the reparametrizing polynomial. - double a; ///< \ru Коэффициент при квадратичном члене репараметризующего многочлена. \en The coefficient of the quadratic term of the reparametrizing polynomial. - double b; ///< \ru Коэффициент при линейном члене репараметризующего многочлена. \en The coefficient of the linear term of the reparametrizing polynomial. - double c; ///< \ru Свободный коэффициент репараметризующего многочлена. \en The free coefficient of the reparametrizing polynomial. + MbCurve3D * basisCurve; ///< \ru Базовая кривая. \en The base curve. + MbeReparamType reparamType; ///< \ru Способ репараметризации. \en Way of repatametrization. + SPtr rFunc; ///< \ru Репараметризующая функция, всегда не нулевая. \en Reparametric function, always non-zero. protected: MbReparamCurve3D( const MbReparamCurve3D &, MbRegDuplicate * ); private : MbReparamCurve3D( const MbReparamCurve3D & ); // \ru Не реализовано. \en Not implemented. public : + /// \ru Конструктор по кривой и новым параметрам. \en Constructor by curve and by new parametric limits. MbReparamCurve3D( const MbCurve3D &, double t1, double t2 ); + /// \ru Конструктор по кривой и новым параметрам. \en Constructor by curve and by new parametric limits. MbReparamCurve3D( const MbCurve3D &, double t1, double t2, double begFirstDerValue ); - MbReparamCurve3D( const MbCurve3D &, double t1, double t2, double derBeg, double derEnd ); + /// \ru Конструктор по кривой и производным параметра на ее концах. \en Constructor for the curve and derivatives of the parameter at its ends + MbReparamCurve3D( double dt1, double dt2, const MbCurve3D & curve ); virtual ~MbReparamCurve3D(); @@ -71,7 +70,7 @@ public : void Init( double t1, double t2 ); void Init( double t1, double t2, double begFirstDerValue ); - void Init( double t1, double t2, double der1, double der2 ); + void InitScaledEnds( double scaleDer1, double scaleDer2 ); // \ru Общие функции математического объекта \en Common functions of the mathematical object @@ -175,9 +174,9 @@ public : void ParameterFrom( double & ) const; // \ru Перевод локального параметра в параметр базовой кривой \en Transformation of a local parameter to the base curve parameter void SetBasisCurve( MbCurve3D & ); // \ru Заменить плоскую кривую \en Replace the planar curve - double Tmin() const { return tmin; } // \ru Начальный параметр \en Start parameter - double Tmax() const { return tmax; } // \ru Конечный параметр \en End parameter - double Dt() const { return b; } // \ru Производная параметра кривой basisCurve по параметру \en Derivative of parameter of 'basisCurve' curve by parameter + double Tmin() const; ///< \ru Начальный параметр. \en Start parameter. + double Tmax() const; ///< \ru Конечный параметр. \en End parameter. + double Dt() const; ///< \ru Производная параметра кривой basisCurve по параметру. \en Derivative of parameter of 'basisCurve' curve by parameter. void SetTmin( double t ); void SetTmax( double t ); void SetDt ( double d ); @@ -200,68 +199,20 @@ public : virtual bool IsSimilarToCurve( const MbCurve3D & curve, double precision = METRIC_PRECISION ) const; // \ru Подобные ли кривые для объединения (слива) \en Whether the curves for union (joining) are similar virtual bool IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = NULL, double epsilon = EPSILON ) const; // \ru Непрерывна ли первая производная? \en Have the first derivative the continuous? - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); bool IsLinear() const { return (reparamType == rt_Linear); } // \ru Является ли репараметризация линейной? \en Is the re-parametrization linear? private: void operator = ( const MbReparamCurve3D & ); // \ru Не реализовано. \en Not implemented. - double CubicRoot( double t ) const; // \ru Решение кубического уравнения. \en Solution of cubic equation. - void Explore( double t, double * par, double * dpar, double * ddpar, double * dddpar ) const; // \ru Параметр базовой кривой и его производные. \en The base curve parameter and its derivatives. + // \ru Параметр базовой кривой и его производные. \en The base curve parameter and its derivatives. + void Explore( double & t, bool ext, double & par, double & dpar, double * ddpar, double * dddpar ) const; DECLARE_PERSISTENT_CLASS_NEW_DEL( MbReparamCurve3D ) }; // MbReparamCurve3D IMPL_PERSISTENT_OPS( MbReparamCurve3D ) -//------------------------------------------------------------------------------ -/// \ru Перевод параметра базовой кривой в новый параметр. \en Transformation of the base curve parameter to a new parameter. -// --- -inline void MbReparamCurve3D::ParameterInto( double & t ) const { - if ( reparamType == rt_Linear ) - t = ( t - c ) / b; - else if( reparamType == rt_Quadratic ){ - C3D_ASSERT( ::fabs(a) > NULL_EPSILON ); - const double discriminant = b * b + 4.0 * a * (t - c); - if ( discriminant > EXTENT_EQUAL ) - t = 0.5 * ( ::sqrt(discriminant) - b ) / a; - else - t = -0.5 * b / a; - } - else if ( reparamType == rt_Cubic ) { - bool close = basisCurve->IsClosed(); - double bTmin = basisCurve->GetTMin(), bTmax = basisCurve->GetTMax(); - if ( !close && ( t < bTmin || t > bTmax ) ) { - double t0 = ( t < bTmin ) ? tmin : tmax; - double par = ( t < bTmin ) ? bTmin : bTmax; - double dpar = 3.0 * q * t0 * t0 + 2.0 * a * t0 + b; - t = t0 + ( t - par ) / dpar; - } - else { - double n = 0.0; - if ( close ) { - double period = bTmax - bTmin; - n = ::floor( (t - bTmin) / period ); - t -= n * period; - } - t = CubicRoot( t ); - if ( n != 0.0 ) - t += n * ( tmax - tmin ); - } - } -} - -//------------------------------------------------------------------------------ -/// \ru Перевод нового параметра в параметр базовой кривой. \en Transformation of a new parameter to the base curve parameter. -// --- -inline void MbReparamCurve3D::ParameterFrom( double & t ) const { - if ( reparamType == rt_Linear ) - t = b * t + c; - else if( reparamType == rt_Quadratic ) - t = a * t * t + b * t + c; - else if ( reparamType == rt_Cubic ) - Explore( t, &t, 0, 0, 0 ); -} - #endif // __CUR_REPARAM_CURVE3D_H diff --git a/C3d/Include/cur_silhouette_curve.h b/C3d/Include/cur_silhouette_curve.h index abee7d9..fff5ad0 100644 --- a/C3d/Include/cur_silhouette_curve.h +++ b/C3d/Include/cur_silhouette_curve.h @@ -11,12 +11,9 @@ #define __CUR_SILHOUETTE_CURVE_H -#include #include -struct AuxiliarySilhouetteData; -struct CurvaturePointData; //------------------------------------------------------------------------------ /** \brief \ru Линия очерка или силуэтная кривая поверхности. @@ -74,8 +71,6 @@ protected: MbCurve3D * approxCurve; ///< \ru Пространственное представление линии очерка. \en The spatial representation of isocline curve. bool approxExact; ///< \ru Точная ли кривая approxCurve. \en Is exact approxCurven. - AuxiliarySilhouetteData * silhData; ///< \ru Общие параметры силуэтной линии, используемые в алгоритмах ее точного представления. - ///< \en General parameters of a silhouette line used in its exact presentation algorithms. public : /// \ru Конструктор по поверхности, двумерной кривой, типу кривой, матрице и флагу перспективы. \en Constructor by surface, two-dimensional curve, type of curve, matrix and flag of perspective. MbSilhouetteCurve( const MbSurface & surf, const MbCurve & crv, MbeCurveBuildType _species, @@ -114,20 +109,14 @@ public: virtual void FirstDer ( double & t, MbVector3D & ) const; // \ru Вычислить первую производную. \en Calculate the first derivative. virtual void SecondDer( double & t, MbVector3D & ) const; // \ru Вычислить вторую производную. \en Calculate the second derivative. virtual void ThirdDer ( double & t, MbVector3D & ) const; // \ru Вычислить третью производную по t. \en Calculate the third derivative by t. - /// \ru Вычислить вектор главной нормали (нормализованный) на кривой и её продолжении. \en Calculate main normal vector (normalized) at curve and its extension. - virtual void Normal( double & t, MbVector3D & ) const; // \ru Функции для работы вне области определения. \en Functions for working outside of definition domain. virtual void _PointOn ( double t, MbCartPoint3D & ) const; // \ru Вычислить точку на расширенной кривой. \en Calculate a point on the extended curve. virtual void _FirstDer ( double t, MbVector3D & ) const; // \ru Вычислить первую производную. \en Calculate the first derivative. virtual void _SecondDer( double t, MbVector3D & ) const; // \ru Вычислить вторую производную. \en Calculate the second derivative. virtual void _ThirdDer ( double t, MbVector3D & ) const; // \ru Вычислить третью производную по t. \en Calculate the third derivative by t. - /// \ru Вычислить вектор главной нормали (нормализованный) на кривой и её продолжении. \en Calculate main normal vector (normalized) at curve and its extension. - virtual void _Normal( double t, MbVector3D & ) const; // \ru Вычислить значения точки и производных для заданного параметра. \en Calculate point and derivatives of object for given parameter. \~ virtual void Explore( double & t, bool ext, MbCartPoint3D & pnt, MbVector3D & fir, MbVector3D * sec, MbVector3D * thir ) const; - /// \ru Вычислить кривизну кривой. \en Calculate curvature of curve. - virtual double Curvature( double t ) const; // \ru Функции приближённого быстрого вычисления точки и производных на кривой. \en Functions of approximate fast calculation of point and derivatives on the curve. virtual void FastApproxExplore( double & t, MbCartPoint3D & pnt, MbVector3D & fir, MbVector3D * sec ) const; @@ -141,6 +130,9 @@ public: virtual bool GetPlacement( MbPlacement3D & place, VERSION version = Math::DefaultMathVersion() ) const; // \ru Дать плоскую кривую и плейсмент, если пространственная кривая плоская (после использования вызывать DeleteItem на двумерную кривую). \en Get the planar curve and placement if the spatial curve is planar (call DeleteItem for two-dimensional curve after using ). virtual bool GetPlaneCurve( MbCurve *& curve2d, MbPlacement3D & place3d, bool saveParams, VERSION version = Math::DefaultMathVersion() ) const; + // \ru Дать поверхностную кривую, если пространственная кривая поверхностная (после использования вызывать DeleteItem на аргументы). \en Get a surface curve if a spatial curve is on a surface (call DeleteItem for arguments after use). + virtual bool GetSurfaceCurve( MbCurve *& curve2d, MbSurface *& surface, VERSION version = Math::DefaultMathVersion() ) const; + // \ru Создать усеченную кривую. \en Create a trimmed curve virtual MbCurve3D * Trimmed( double t1, double t2, int sense ) const; // \ru Дать плоскую проекцию кривой(локальная система координат, шаг, параметрическая область). \en Get the planar projection of a curve (local coordinate system, step, parametric region). @@ -177,42 +169,7 @@ public: // \ru Получить текущую точку на кривой по параметру. \en Get current point on a curve by a parameter. virtual bool GetCurvePoint( double & t, MbCartPoint & cPoint ) const; - // \ru Найти все особые точки функции кривизны кривой. \en Find all the special points of the curvature function of the curve. - virtual void GetCurvatureSpecialPoints( std::vector & points ) const; - private: - // Расчитать точку обрезки луча pnt0 - pnt границами поверхности surface. - bool IsCutBounds( const MbCartPoint * pnt0, MbCartPoint & pnt ) const; - // Вычислить параметр видимости в точке и его градиент. - void CalcVisibilityParam( const MbCartPoint & p, double & val, MbVector * der = NULL ) const; - // Ищем интервал между точками p1 и p2, на котором произойдет смена видимости, последовательно прибавляя в заданную сторону вектор w. - void GoToOneSide( const MbVector & w, MbCartPoint & p1, MbCartPoint & p2, double & vis1, double & vis2, MbVector &der1, MbVector & der2, int nIter, bool move1 ) const; - // Найти точку на силуэтной линии на отрезке [pnt0 - w, pnt0 + w]. - bool GetPointOnSilhouette( const MbCartPoint &pnt0, const MbVector & w, int nIter, double zEps, double & zEpsOut, MbCartPoint & silhPoint ) const; - // Найти проекцию точки на аппроксимационном сплайне на силуэтную линию по нормали к аппроксимационному сплайну. - void ExactSilhouettePoint( double h, double t, int pos, MbCartPoint & point2D ) const; - // Расчет производной через конечные разности. - void CalcFiniteDifference( double tmin, double tmax, double h, double t, int pos, int order, - std::map & points2D, std::map & points3D, std::map( &ders )[3] ) const; - // Суммирование слагаемых конечной разности. - void CalcDifference( double tmin, double tmax, double h, double t, int pos0, int order, int pos, const double( &kf )[5], double zn, - std::map & points2D, std::map & points3D, std::map( &ders )[3] ) const; - // Расчет по параметру на сплайне точной точки на силуэтной линии, а также первой и второй производной, вычисленных через конечные разности. - void AccurateExplore( double t, MbCartPoint & pnt, MbVector3D & fir, MbVector3D & sec ) const; - // Расчет параметров, используемых в алгоритмах для кривизны, в точке (положение на кривой, кривизна, нормаль, шаг). - void CurvatureExplore( double t, CurvaturePointData & cpd ) const; - // Расчет на поверхности кривизны в направлении seg. - void CurvatureOnSurfaceLine( const MbVector & seg, const MbCartPoint & p, double & curv, double & dcurv ) const; - // Анализ разрыва кривизны на поверхности на отрезке между точками p1 и p2. - bool IsCurvatureRapture( const MbCartPoint & p1, const MbCartPoint & p2 ) const; - // Поиск экстремума на интервале pd1 - pd2 методом золотого сечения. Начиная с точки prev до точки pd2, кривизна монотоно убывает/возрастает. - void CurvatureExtremeBinarySearch( const CurvaturePointData & pd1, const CurvaturePointData & pd2, CurvaturePointData & prev, - bool isMax, double eps, std::vector & spPoints ) const; - // Проверка интервала на кривой на разрыв кривизны. - void CheckRapture( double t1, double t2, std::vector & spPoints ) const; - // Найти разрывы кривизны на участке кривой pd1 - pd2. Начиная с точки prev до точки pd2, кривизна монотоно убывает/возрастает. - void FindCurvatureRaptures( const CurvaturePointData & pd1, const CurvaturePointData & pd2, CurvaturePointData & prev, - double paramAccuracy, std::vector & points ) const; // \ru Проверить параметр и вычислить параметрическую точки. \en Check parameter and calculate parametric points. bool CorrectPoint( double & t, bool ext, MbCartPoint & cPoint, MbVector & cFirst, MbVector * cSecond, MbVector * cThird ) const; void CalculatePoint ( double & t, bool ext, MbCartPoint3D & ) const; // \ru Вычислить точку на расширенной кривой. \en Calculate a point on the extended curve. diff --git a/C3d/Include/cur_spiral.h b/C3d/Include/cur_spiral.h index d5aa2c7..bca6c0d 100644 --- a/C3d/Include/cur_spiral.h +++ b/C3d/Include/cur_spiral.h @@ -33,50 +33,79 @@ protected: /** \brief \ru Метрическая длина кривой. \en Metric length of a curve. \~ - \details \ru Метрическая длина кривой расчитывается только при запросе длины объекта. Метрическая длина кривой в конструкторе кривой и после модификации кривой принимает отрицательное значение. + \details \ru Метрическая длина кривой, рассчитывается только при запросе длины объекта. Метрическая длина кривой в конструкторе кривой и после модификации кривой принимает отрицательное значение. \en Metric length of a curve is calculated only at the request. Metric length of a curve is undefined (negative) after object constructor and after object modifications. \n \~ */ mutable double metricLength; /** \brief \ru Габаритный куб кривой. \en Bounding box of a curve. \~ - \details \ru Габаритный куб кривой расчитывается только при запросе габарита объекта. Габаритный куб в конструкторе кривой и после модификации кривой принимает неопределенное значение. + \details \ru Габаритный куб кривой, рассчитывается только при запросе габарита объекта. Габаритный куб в конструкторе кривой и после модификации кривой принимает неопределенное значение. \en Bounding box of a curve is calculated only at the request. Bounding box of a curve is undefined after object constructor and after object modifications. \n \~ */ mutable MbCube cube; protected: - MbSpiral() : position(), step( 1.0 ), tmin( 0.0 ), tmax( M_PI2 ), metricLength( -1.0 ), cube() {} - MbSpiral( const MbPlacement3D & pl ) : position( pl ), step( 0.0 ), tmin( 0.0 ), tmax( 0.0 ), metricLength( -1.0 ), cube() {} - MbSpiral( const MbPlacement3D & pl, double height, double st ); // \ru По высоте и шагу \en By height and pitch - MbSpiral( const MbPlacement3D & pl, double s, double t1, double t2 ); - MbSpiral( const MbCartPoint3D & p0, const MbCartPoint3D & p1, const MbCartPoint3D & p2, double st, bool left = false ); - MbSpiral( const MbSpiral & init ); + /// \ru Конструктор по умолчанию. \en The default constructor. + MbSpiral() : position(), step( 1.0 ), tmin( 0.0 ), tmax( M_PI2 ), metricLength( -1.0 ), cube() {} + /// \ru Конструктор по локальной системе координат. \en The constructor by local coordinate system. + MbSpiral( const MbPlacement3D & pl ) : position( pl ), step( 1.0 ), tmin( 0.0 ), tmax( M_PI2 ), metricLength( -1.0 ), cube() {} +protected: + /// \ru Конструктор по локальной системе координат, высоте и шагу между витками. \en The constructor by local coordinate system, height and step between coils of spiral. + MbSpiral( const MbPlacement3D &, double height, double step ); + /** \brief \ru Конструктор по локальной системе координат, шагу между витками и параметрам. + \en The constructor by local coordinate system, step between coils of spiral and parameters. \~ + \details \ru Конструктор по локальной системе координат, шагу между витками, начальному и конечному параметрам. + \en The constructor by local coordinate system, step between coils of spiral, starting and ending parameters. \n \~ + */ + MbSpiral( const MbPlacement3D &, double step, double t1, double t2 ); + /** \brief \ru Конструктор по трем разным точкам и ненулевому шагу. + \en The constructor by three different points and non-zero step. \~ + \details \ru Конструктор по трем разным точкам и ненулевому шагу. \n + \en The constructor by three different points and non-zero step. \n \~ + \param[in] p0 - \ru Начало локальной системы координат. + \en Origin point of the local coordinate system. \~ + \param[in] p1 - \ru Точка в направлении оси Z, определяющая высоту спирали. + \en Point in the direction of the Z axis determining the height of the spiral. \~ + \param[in] p2 - \ru Точка в направлении оси X. + \en Point in the direction of the X axis. \~ + \param[in] st - \ru Шаг между витками спирали. + \en Step between coils of spiral. \~ + \param[in] left - \ru Признак левой системы координат. + \en Flag of the left coordinate system. \~ + */ + MbSpiral( const MbCartPoint3D & p0, const MbCartPoint3D & p1, const MbCartPoint3D & p2, double step, bool left = false ); + /// \ru Конструктор по другой спирали. \en Constructor by another spiral. + MbSpiral( const MbSpiral & ); public : virtual ~MbSpiral(); public : VISITING_CLASS( MbSpiral ); - void Init( const MbSpiral & init ); - void Init( const MbPlacement3D & place ); - void Init( double height, double st ); // \ru Установить высоту и шаг \en Set height and pitch - void Init( const MbPlacement3D & place, double height, double st ); + /// \ru Установить параметры спирали по другой спирали. \en Set spiral parameters by another spiral. + void Init( const MbSpiral & ); + /// \ru Установить другую локальную систему координат. \en Replace local coordinate system. + void Init( const MbPlacement3D & ); + /// \ru Установить высоту и шаг. \en Set height and step between coils of spiral. + bool Init( double height, double st ); + /// \ru Установить локальную систему координат, высоту и шаг спирали. \en Set local coordinates system, height and step of spiral. + bool Init( const MbPlacement3D & place, double height, double st ); // \ru Общие функции математического объекта \en Common functions of the mathematical object virtual MbeSpaceType IsA() const = 0; // \ru Тип элемента \en Type of element virtual MbeSpaceType Type() const; // \ru Тип элемента \en Type of element virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const = 0; // \ru Сделать копию элемента \en Create a copy of the element - virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; - virtual bool SetEqual( const MbSpaceItem & init ); // \ru Сделать равным \en Make equal - virtual void Transform( const MbMatrix3D & matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move( const MbVector3D & to, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate( const MbAxis3D & axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis + virtual bool IsSame( const MbSpaceItem &, double accuracy = LENGTH_EPSILON ) const; + virtual bool SetEqual( const MbSpaceItem & ); // \ru Сделать равным \en Make equal + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate about an axis virtual void AddYourGabaritTo( MbCube & ) const; // \ru Добавь свой габарит в куб \en Add your own bounding box into the cube virtual void PrepareIntegralData( const bool forced ) const; // \ru Рассчитать временные (mutable) данные объекта. \en Calculate temporary (mutable) data of an object. - virtual void GetProperties( MbProperties & properties ) = 0; // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ) = 0; // \ru Записать свойства объекта \en Set properties of the object + virtual void GetProperties( MbProperties & ) = 0; // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ) = 0; // \ru Записать свойства объекта \en Set properties of the object virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. @@ -110,7 +139,7 @@ public : virtual size_t GetCount() const; double GetSpiralPeriod() const; // \ru Вернуть период \en Get period - // \ru Заполнить плейсемент, ести кривая плоская \en Fill the placement if the curve is planar + // \ru Заполнить плейсемент, если кривая плоская \en Fill the placement if the curve is planar virtual bool GetPlacement( MbPlacement3D & place, VERSION version = Math::DefaultMathVersion() ) const; /// \ru Является ли объект смещением \en Whether the object is a shift @@ -119,33 +148,76 @@ public : // \ru Функции спирали \en Functions of spiral - virtual void SetStep( double s ) = 0; // \ru Изменить шаг \en Change step + virtual bool SetStep( double s ) = 0; // \ru Изменить шаг \en Change step virtual double GetSpiralRadius ( double t ) const = 0; // \ru Выдать физический радиус спирали \en Get physical radius of spiral - inline void CheckParam( double & t ) const; + + void CheckParam( double & t ) const; + /// \ru Дать направление спирали. \en Get direction of spiral. void GetDirection ( MbVector3D & v ) const { v = position.GetAxisZ(); } - bool GetAxis( MbAxis3D & axis ) const; // \ru Дать ось спирали \en Get axis of spiral - double GetStep() const { return step; } // \ru Выдать шаг \en Get step - double GetSpiralStep() const; // \ru Выдать физический шаг спирали \en Get physical pitch of spiral - double GetAngle() const { return tmax-tmin; } // \ru Выдать полный угол спирали \en Get full angle of spiral - void SetTMin( double t ) { tmin = t; Refresh(); } // \ru Изменить граничный угол \en Change boundary angle - void SetTMax( double t ) { tmax = t; Refresh(); } // \ru Изменить граничный угол \en Change boundary angle - void SetLimit( double t1, double t2 ) { tmin = std_min( t1, t2 ); tmax = std_max( t1, t2 ); Refresh(); } + /// \ru Дать ось спирали. \en Get axis of spiral. + bool GetAxis( MbAxis3D & axis ) const; + /// \ru Выдать шаг. \en Get step. + double GetStep() const { return step; } + /// \ru Выдать физический шаг спирали. \en Get physical pitch of spiral. + double GetSpiralStep() const; + /// \ru Выдать полный угол спирали \en Get full angle of spiral + double GetAngle() const { return tmax-tmin; } + /// \ru Изменить граничный угол. \en Change boundary angle. + bool SetTMin( double t ) + { + C3D_ASSERT( t < tmax ); + if ( t < tmax ) { + tmin = t; + Refresh(); + return true; + } + return false; + } + /// \ru Изменить граничный угол. \en Change boundary angle. + bool SetTMax( double t ) + { + C3D_ASSERT( t > tmin ); + if ( t > tmin ) { + tmax = t; + Refresh(); + return true; + } + return false; + } + /// \ru Изменить граничные углы. \en Change boundary angles. + bool SetLimit( double t1, double t2 ) + { + if ( t1 > t2 ) + std::swap( t1, t2 ); + C3D_ASSERT( t1 < t2 ); + if ( t1 < t2 ) { + tmin = t1; + tmax = t2; + Refresh(); + return true; + } + return false; + } const MbPlacement3D & GetPlacement() const { return position; } bool IsPositionNormal() const { return ( !position.IsAffine() ); } +protected: + static bool IsNonZeroStep( double s ) { return (::fabs( s ) >= METRIC_EPSILON); } private: void operator = ( const MbSpiral & ); // \ru Не реализовано. \en Not implemented. - DECLARE_PERSISTENT_CLASS( MbSpiral ) +DECLARE_PERSISTENT_CLASS( MbSpiral ) }; IMPL_PERSISTENT_OPS( MbSpiral ) + //------------------------------------------------------------------------------ // \ru Проверка параметра кривой \en Check parameter of curve //--- -inline void MbSpiral::CheckParam( double & t ) const +inline +void MbSpiral::CheckParam( double & t ) const { if ( t < tmin ) t = tmin; @@ -157,7 +229,8 @@ inline void MbSpiral::CheckParam( double & t ) const //------------------------------------------------------------------------------ // \ru Выдать физический шаг спирали \en Get physical pitch of spiral //--- -inline double MbSpiral::GetSpiralStep() const +inline +double MbSpiral::GetSpiralStep() const { if ( position.IsNormal() ) return step; diff --git a/C3d/Include/cur_surface_curve.h b/C3d/Include/cur_surface_curve.h index 774ec95..c2a3425 100644 --- a/C3d/Include/cur_surface_curve.h +++ b/C3d/Include/cur_surface_curve.h @@ -35,7 +35,7 @@ class MbCurveIntoNurbsInfo; \details \ru Кривая на поверхности строится путём введения зависимости параметров поверхности u и v от некоторого общего для них параметра t: u=u(t), v=v(t). \n Параметры поверхности u и v являются координатами двумерной точки в пространстве параметров поверхности. - Кривая на поверхности описывается поверхностью surface и двумерной кривой в пространстве параметров curve. + Кривая на поверхности описывается поверхностью surface и двумерной кривой в пространстве параметров curve. Поверхностью surface может быть любая поверхность, кроме MbCurveBoundedSurface. \n Для заданного параметра t кривой curve вычисляется двумерная точка w=[u v] области параметров поверхности, далее для параметров u и v поверхностью surface вычисляется точка кривой на поверхности. @@ -47,7 +47,7 @@ class MbCurveIntoNurbsInfo; \en Curve on surface is constructed by introduction of dependence of u and v surface parameters from some parameter t common for them: u=u(t), v=v(t). \n u and v surface parameters are coordinates of two-dimensional point in space of surface parameters. - Curve on surface is described by 'surface' surface and two-dimensional curve 'curve' in space of parameters. + Curve on surface is described by 'surface' surface and two-dimensional curve 'curve' in space of parameters. Any surface except MbCurveBoundedSurface can be surface 'surface'. \n two-dimensional point w=[u v] of region of surface parameters is calculated for a given parameter t of curve 'curve', further, a point of curve on surface is calculated for u and v parameters of 'surface' surface. @@ -105,7 +105,7 @@ protected : public : /// \ru Конструктор кривой на поверхности. \en Constructor of curve on surface. - MbSurfaceCurve( const MbSurface &, const MbCurve &, bool same, MbRegDuplicate * iReg = NULL ); + MbSurfaceCurve( const MbSurface &, const MbCurve &, bool sameCurve, MbRegDuplicate * iReg = NULL ); /// \ru Конструктор отрезка прямой на поверхности. \en Constructor of a line segment on surface. MbSurfaceCurve( const MbSurface &, const MbCartPoint & p0, const MbCartPoint & p1, MbePlaneType type = pt_Curve ); /// \ru Конструктор граничной кривой поверхности. \en Constructor of boundary curve of surface. @@ -243,7 +243,8 @@ public: virtual void GetCurvatureContinuityBounds( std::vector & params ) const; virtual bool IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = NULL, double epsilon = EPSILON ) const; // \ru Непрерывна ли первая производная? \en Have the first derivative the continuous? - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length. + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); /** \} */ @@ -251,6 +252,10 @@ public: bool IsSameCurvePoints( const MbSurfaceCurve * scurve, double accuracy, bool sameSense ) const; /// \ru Вычислить нормаль к поверхности. \en Calculate surface normal. void SurfaceNormal( double & t, MbVector3D &, bool ext = false ) const; + /// \ru Нормаль к поверхности, первая и вторая производная нормали поверхности по t. \en Calculation of the normal, first, and second derivative of the surface normal. + void SurfaceNormal( double & t, MbVector3D & n, MbVector3D & fd, MbVector3D & sd, bool ext = false ) const; + /// \ru Кривизна поверхности в поперечном направлении к вектору tau. \en The surface curvature in the transverse direction to the vector tau. + double SurfaceTransversalCurvature( double t, const MbVector3D & tau ) const; /// \ru Получить параметры поверхности по параметру на кривой. \en Get surface parameters by a parameter on the curve. void SurfaceParams( double & t, double & u, double & v, bool ext = false ) const; /// \ru Вычислить параметрический габарит кривой. \en Calculate parametric bounding box of the curve. @@ -261,9 +266,9 @@ public: void GetVPairs( double u, SArray & v, SArray & t ) const; /// \ru Построить участок пространственной копии кривой. \en Construct a piece of a spatial curve copy. - MbCurve3D * MakeCurve( double t1, double t2 ) const; + MbCurve3D * MakeCurve( double t1, double t2, VERSION version ) const; /// \ru Построить пространственную копию кривой. \en Construct a spatial curve copy. - MbCurve3D * MakeCurve() const; + MbCurve3D * MakeCurve( VERSION version ) const; /// \ru Построить точную пространственную копию кривой. \en Construct the exact spatial curve copy. MbCurve3D * CreateCurve() const; /// \ru Создать пространственную кривую по линии u, v. \en Create a spatial curve by u, v lines. diff --git a/C3d/Include/cur_surface_intersection.h b/C3d/Include/cur_surface_intersection.h index 7a2a469..4bf85ea 100644 --- a/C3d/Include/cur_surface_intersection.h +++ b/C3d/Include/cur_surface_intersection.h @@ -368,9 +368,9 @@ public: virtual void CalculateGabarit( MbCube & c ) const; // \ru Вычислить габарит кривой. \en Calculate bounding box of a curve. // \ru Получить габарит кривой. \en Get bounding box of curve. - const MbCube & GetGabarit() const { if ( cube.IsEmpty() ) CalculateGabarit( cube ); return cube; } + const MbCube & GetGabarit() const; // \ru Сбросить габаритный куб. \en Reset bounding box. - void SetDirtyGabarit() const { cube.SetEmpty(); } + void SetDirtyGabarit() const; virtual bool DistanceAlong( double & t, double len, int curveDir, double eps = Math::metricPrecision, VERSION version = Math::DefaultMathVersion() ) const; // \ru Сдвинуть параметр t на расстояние len по направлению. \en Translate parameter 't' on the distance 'len' by the direction. @@ -414,7 +414,7 @@ public: /// \ru Установить тип кривой по топологии. \en Set a curve type by topology. void SetGlueType( MbeCurveGlueType type ) { glueType = type; } /// \ru Установить тип кривой по топологии. \en Set a curve type by topology. - void SetPoleGlueType() const { glueType = cgt_Pole; } + void SetPoleGlueType() const; /// \ru Получить тип кривой по построению. \en Get a curve type by construction. MbeCurveBuildType GetBuildType() const { return buildType; } diff --git a/C3d/Include/cur_trimmed_curve.h b/C3d/Include/cur_trimmed_curve.h index 4890dcf..a5915a0 100644 --- a/C3d/Include/cur_trimmed_curve.h +++ b/C3d/Include/cur_trimmed_curve.h @@ -74,12 +74,13 @@ public : virtual MbePlaneType Type() const; // \ru Вернуть тип кривой \en Get type of curve virtual bool IsSimilar( const MbPlaneItem & ) const; // \ru Являются ли элементы подобными \en Whether the elements are similar virtual bool SetEqual ( const MbPlaneItem & ); // \ru Сделать элементы равными \en Make the elements equal - virtual bool IsSame ( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли кривая curve копией данной кривой ? \en Whether curve 'curve' is a duplicate of the current curve. + virtual bool IsSame ( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли кривая curve копией данной кривой ? \en Whether curve 'curve' is a duplicate of the current curve. virtual void Transform( const MbMatrix &, MbRegTransform * ireg = NULL, const MbSurface * newSurface = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix virtual void Move ( const MbVector &, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Сдвиг \en Translation virtual void Rotate ( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Поворот \en Rotation virtual MbPlaneItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element virtual void AddYourGabaritTo( MbRect & ) const; // \ru Добавь свой габарит в прямой прям-к \en Add your own gabarit into the given bounding rectangle + virtual void CalculateGabarit( MbRect & ) const; // \ru Определить габаритный прямоугольник кривой. \en Detect the bounding box of a curve. virtual bool IsInRectForDeform( const MbRect & r ) const; // \ru Виден ли объект в заданном прямоугольнике для деформации \en Whether the object is visible in the given rectangle for deformation virtual void Refresh(); // \ru Сбросить все временные данные \en Reset all temporary data virtual void PrepareIntegralData( const bool forced ) const; // \ru Рассчитать временные (mutable) данные объекта. \en Calculate temporary (mutable) data of an object. @@ -172,7 +173,7 @@ public : virtual MbeState DeletePart( double t1, double t2, MbCurve *& part2 ); // \ru Удалить часть усеченной кривой между параметрами t1 и t2 \en Delete a part of a trimmed curve between parameters t1 and t2 virtual MbeState TrimmPart ( double t1, double t2, MbCurve *& part2 ); // \ru Оставить часть усеченной кривой между параметрами t1 и t2 \en Keep a part of the trimmed curve between parameters t1 and t2 virtual MbCurve * Trimmed( double t1, double t2, int sense ) const; - virtual MbCurve * Offset( double rad ) const; // \ru Смещение усеченной кривой \en Shift of a trimmed curve + virtual MbCurve * Offset( double rad ) const; // \ru Смещение усеченной кривой \en Shift of a trimmed curve virtual MbNurbs * NurbsCurve( const MbCurveIntoNurbsInfo & ) const; virtual MbCurve * NurbsCurve( const MbNurbsParameters & ) const; // \ru Построить NURBS-копию кривой \en Create a NURBS-copy of the curve diff --git a/C3d/Include/curve.h b/C3d/Include/curve.h index 2eee6c9..886baf7 100644 --- a/C3d/Include/curve.h +++ b/C3d/Include/curve.h @@ -73,7 +73,7 @@ typedef std::vector ConstPlaneCurvesSPtrVector; \ingroup Curves_2D */ // --- -class MATH_CLASS MbCurve : public MbPlaneItem { +class MATH_CLASS MbCurve : public MbPlaneItem, public MbNestSyncItem { protected: SimpleName name; ///< \ru Имя кривой. \en A curve name. @@ -1271,8 +1271,10 @@ public : \en Eliminate the discontinuities of the first derivatives of the length. \n \~ \param[in] epsilon - \ru Погрешность вычисления. \en The accuracy of the calculation. \~ + \param[in] version - \ru Версия математики. + \en Math version. \~ */ - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); /** \brief \ru Определить, близки ли две кривые метрически. \en Check whether the two curves are metrically close. \~ diff --git a/C3d/Include/curve3d.h b/C3d/Include/curve3d.h index 8a4fbdd..e345f21 100644 --- a/C3d/Include/curve3d.h +++ b/C3d/Include/curve3d.h @@ -41,14 +41,24 @@ struct MbNurbsParameters; class MATH_CLASS MbCurve3D; namespace c3d // namespace C3D { -typedef SPtr SpaceCurveSPtr; -typedef SPtr ConstSpaceCurveSPtr; +typedef SPtr SpaceCurveSPtr; +typedef SPtr ConstSpaceCurveSPtr; -typedef std::vector SpaceCurvesVector; -typedef std::vector ConstSpaceCurvesVector; +typedef std::vector SpaceCurvesVector; +typedef std::vector ConstSpaceCurvesVector; -typedef std::vector SpaceCurvesSPtrVector; -typedef std::vector ConstSpaceCurvesSPtrVector; +typedef std::vector SpaceCurvesSPtrVector; +typedef std::vector ConstSpaceCurvesSPtrVector; + +typedef std::set SpaceCurvesSet; +typedef SpaceCurvesSet::iterator SpaceCurvesSetIt; +typedef SpaceCurvesSet::const_iterator SpaceCurvesSetConstIt; +typedef std::pair SpaceCurvesSetRet; + +typedef std::set ConstSpaceCurvesSet; +typedef ConstSpaceCurvesSet::iterator ConstSpaceCurvesSetIt; +typedef ConstSpaceCurvesSet::const_iterator ConstSpaceCurvesSetConstIt; +typedef std::pair ConstSpaceCurvesSetRet; } @@ -68,7 +78,7 @@ typedef std::vector ConstSpaceCurvesSPtrVector; \ingroup Curves_3D */ // --- -class MATH_CLASS MbCurve3D : public MbSpaceItem { +class MATH_CLASS MbCurve3D : public MbSpaceItem, public MbNestSyncItem { protected: SimpleName name; ///< \ru Имя кривой. \en A curve name. @@ -781,8 +791,10 @@ public : \en Eliminate the discontinuities of the first derivatives of the length. \n \~ \param[in] epsilon - \ru Погрешность вычисления. \en The accuracy of the calculation. \~ + \param[in] version - \ru Версия математики. + \en Math version. \~ */ - virtual bool SetContinuousDerivativeLength( double epsilon = EPSILON ); + virtual bool SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ); /** \brief \ru Определить, близки ли две кривые метрически. \en Check whether the two curves are metrically close. \~ @@ -848,7 +860,7 @@ public : Point with greater curvature is inserted with a plus sign, a point with a lower curvature is inserted with a minus sign. \n \ingroup Curves_3D */ - virtual void GetCurvatureSpecialPoints( std::vector & points ) const; + virtual void GetCurvatureSpecialPoints( std::vector & points ) const; /** \brief \ru Получить границы участков кривой, на которых сохраняется непрерывность кривизны. \en Get the boundaries of the sections of the curve on which the continuity of curvature is preserved. \~ @@ -860,7 +872,7 @@ public : \en The points at which the curvature has a discontinuity. \~ \ingroup Curves_3D */ - virtual void GetCurvatureContinuityBounds( std::vector & params ) const; + virtual void GetCurvatureContinuityBounds( std::vector & params ) const; /** \brief \ru Вычислить граничную точку. \en Calculate the boundary point. \~ @@ -872,7 +884,7 @@ public : \en A calculated point. \~ \ingroup Curves_3D */ - MbCartPoint3D GetLimitPoint( ptrdiff_t number ) const; // \ru number <= 1 : в начале, инача - в конце \en Number <= 1 : at start, otherwise - at end + MbCartPoint3D GetLimitPoint( ptrdiff_t number ) const; // \ru number <= 1 : в начале, иначе - в конце \en Number <= 1 : at start, otherwise - at end /** \brief \ru Вычислить граничную точку. \en Calculate the boundary point. \~ @@ -915,7 +927,7 @@ public : \return \ru true, если точки равны. \en Returns true if points are equal. \~ */ - bool AreLimitPointsEqual() const { return GetLimitPoint( 1 ) == GetLimitPoint( 2 ); } + bool AreLimitPointsEqual() const { return GetLimitPoint( 1 ) == GetLimitPoint( 2 ); } /// \ru Загнать в параметрическую область. \en Move to the parametric region. bool SetInParamRegion( double & t ) const; @@ -1084,15 +1096,18 @@ MATH_FUNC (MbeNewtonResult) NearestPoints( const MbCurve3D & curve1, bool ext1, \en Parameter of the curve 1 for the intersection point (the initial approximation at input). \~ \param[in, out] t2 - \ru Параметр кривой 2 для точки пересечения (начальное приближение на входе). \en Parameter of the curve 2 for the intersection point (the initial approximation at input). \~ + \param[in] correctNewtonParam - \ru Выполнять корректировку приращения параметра по предыщушему приращению параметра. + \en Modify current parameter changes by previous parameter changes. \~ \return \ru Код ошибки: случае успешного определения nr_Success (+1), nr_Special(0) или nr_Failure(-1) - в случае неудачи. \en Error code: in a case of successful defining nr_Success (+1), nr_Special(0) or nr_Failure(-1) - in a case of failure. \~ \ingroup Curves_3D */ // --- -MATH_FUNC (MbeNewtonResult) CurveCrossNewton( const MbCurve3D & curve1, bool ext1, - const MbCurve3D & curve2, bool ext2, +MATH_FUNC (MbeNewtonResult) CurveCrossNewton( const MbCurve3D & curve1, bool ext1, + const MbCurve3D & curve2, bool ext2, double funcEpsilon, size_t iterLimit, - double & t1, double & t2 ); + double & t1, double & t2, + bool correctNewtonParam = true ); //------------------------------------------------------------------------------ diff --git a/C3d/Include/dxf_data.h b/C3d/Include/dxf_data.h index 3f5f03d..fe772cf 100644 --- a/C3d/Include/dxf_data.h +++ b/C3d/Include/dxf_data.h @@ -9,12 +9,12 @@ #ifndef __DXF_DATA_H #define __DXF_DATA_H -#include #include #include #include #include #include +#include #include diff --git a/C3d/Include/func_analytical_function.h b/C3d/Include/func_analytical_function.h index 4bc8804..8b19060 100644 --- a/C3d/Include/func_analytical_function.h +++ b/C3d/Include/func_analytical_function.h @@ -43,9 +43,13 @@ private : public : /// \ru Конструктор. \en Constructor. - MdCharacterFunction( const MbMathematicalNode & expression_, const MbListVars & vars, - const c3d::string_t & data_, const c3d::string_t & argument_, - double tmin_, double tmax_, bool sense_ ); + MdCharacterFunction( const MbMathematicalNode & expression, + const MbListVars & vars, + const c3d::string_t & data, + const c3d::string_t & argument, + double tmin, + double tmax, + bool sense ); virtual ~MdCharacterFunction(); private: @@ -132,7 +136,7 @@ private: public : // \ru Конструктор. \en Constructor. - MdAnalyticalFunction( MbUserFunc & ufunc, double tmin_, double tmax_, bool sense_ = true ); + MdAnalyticalFunction( const MbUserFunc & ufunc, bool sameFunc, double tmin, double tmax, bool sense ); public: virtual ~MdAnalyticalFunction(); @@ -181,12 +185,12 @@ public : // \ru Разбить функцию точкой с параметром t и вернуть отрезанную часть. \en Function break by the parameter t, and cut off part of the function: begs == true - save the initial half, beg == false - save the final half. virtual MbFunction * BreakFunction( double t, bool beg ); - virtual bool IsCos ( double &a, double& b ) const; ///< \ru Имеет ли функция вид a * cos() + b. \en Function looks like a * cos() + b. + virtual bool IsCos ( double & a, double & b ) const; ///< \ru Имеет ли функция вид a * cos() + b. \en Function looks like a * cos() + b. private: /// \ru Производные по параметру: параметр, значение, первая, вторая и третья производные \en Derivatives with respect to the parameter: parameter, value, first, second and third derivatives - void Derivates ( double & t, DerivateData* data ) const; + void Derivates ( double & t, DerivateData * data ) const; void CheckParam( double & t ) const; void ResetTCalc() const; // \ru Сбросить временные данные \en Reset temporary data void operator = ( const MdAnalyticalFunction & ); // \ru Не реализовано. \en NOT ALLOWED !!! diff --git a/C3d/Include/func_cubic_function.h b/C3d/Include/func_cubic_function.h index 14a14d5..7e4d4a4 100644 --- a/C3d/Include/func_cubic_function.h +++ b/C3d/Include/func_cubic_function.h @@ -15,7 +15,7 @@ #include -#define FUNC_NUMB 4 ///< \ru Количество элементов расчетного массива кубической функции Эрмита. \en The number of elements of calculation array of a cubic Hermite function. +const_expr size_t FUNC_NUMB = 4; ///< \ru Количество элементов расчетного массива кубической функции Эрмита. \en The number of elements of calculation array of a cubic Hermite function. //------------------------------------------------------------------------------ @@ -80,6 +80,8 @@ public: // \ru Вычислить значение и производные. \en Calculate value and derivatives of object for given parameter. \~ virtual void Explore( double & t, bool ext, double & val, double & fir, double * sec, double * thr ) const; + // \ru Вычислить аргумент t по значению функции. \en Calculate the argument t by the function value. + virtual double Argument( double & val ) const; virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменить направление \en Change direction virtual double Step( double t, double sag ) const; @@ -112,6 +114,10 @@ public: size_t GetValuesCount() const; // \ru Выдать количество опорных точек \en Get the number of control points double GetParam( size_t index ) const; // \ru Дать значение параметра точки по номеру \en Get the value of point parameter by its number double GetValue( size_t index ) const; // \ru Дать значение точки по номеру \en Get the value of point by its number + double GetDerive( size_t index ) const; // \ru Дать значение производной по номеру \en Get the value of derivative by its number + // \ru Получить коэффициенты кубической функции на указанном интервале. + // \en Get the coefficients of the cubic function on the specified interval. + void GetCubicFactors( size_t index, double & a, double & b, double & c, double & d ) const; private: bool CalculateDerivatives(); // \ru Расчет производных. \en Calculation of derivatives diff --git a/C3d/Include/func_line_function.h b/C3d/Include/func_line_function.h index ad9432c..137012d 100644 --- a/C3d/Include/func_line_function.h +++ b/C3d/Include/func_line_function.h @@ -64,6 +64,8 @@ public: // \ru Вычислить значение и производные. \en Calculate value and derivatives of object for given parameter. \~ virtual void Explore( double & t, bool ext, double & val, double & fir, double * sec, double * thr ) const; + // \ru Вычислить аргумент t по значению функции. \en Calculate the argument t by the function value. + virtual double Argument( double & val ) const; virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменить направление \en Change direction virtual double Step( double t, double sag ) const; @@ -95,4 +97,5 @@ private: IMPL_PERSISTENT_OPS( MbLineFunction ) + #endif // __FUNC_LINE_FUNCTION_H diff --git a/C3d/Include/func_serve_function.h b/C3d/Include/func_serve_function.h new file mode 100644 index 0000000..3c4072d --- /dev/null +++ b/C3d/Include/func_serve_function.h @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Служебная функция. + \en Service function. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __FUNC_SERVE_FUNCTION_H +#define __FUNC_SERVE_FUNCTION_H + + +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Монотонно возрастающая служебная функция. + \en The monotonically increasing service function. \~ + \details \ru Функция служит для изменения параметризации кривых, обеспечивающей непрерывномть производных составных кривых. \n + \en The function is used to change the parameterization of curves, which provides a continuous flow of derivatives of composite curves. \n \~ + \ingroup Functions +*/ +// --- +class MATH_CLASS MbServeFunction : public MbFunction { +public : + double a; ///< \ru Коэффициент при квадратичном члене репараметризующего многочлена. \en The coefficient of the quadratic term of the reparametrizing polynomial. + double b; ///< \ru Коэффициент при линейном члене репараметризующего многочлена. \en The coefficient of the linear term of the reparametrizing polynomial. + double c; ///< \ru Свободный коэффициент репараметризующего многочлена. \en The free coefficient of the reparametrizing polynomial. + double tmin; ///< \ru Начальный параметр. \en Start parameter. + double tmax; ///< \ru Конечный параметр. \en End parameter. + +public : + ///< \ru Конструктор по умолчанию. \en Default constructor. + MbServeFunction(); +private: + ///< \ru Конструктор по параметрам. \en Constructor by parameters. + MbServeFunction( double ka, double kb, double kc, double t1, double t2 ); + ///< \ru Конструктор копировния. \en Copy constructor. + MbServeFunction( const MbServeFunction & ); +public : + virtual ~MbServeFunction(); +public: + /** \brief \ru Инициализация переменных для линейной репараметризации. + \en Initialization of variables for linear reparameterization. \~ + \param[in] basisTMin, basisTMin - \ru Область определения базовой кривой. + \en Parametric region of the base curve.. \~ + \param[in] t1, t2 - \ru Область определения репараметризованной кривой + \en Parametric region of the reparameterized curve. \~ + */ + void InitLinear( double basisTMin, double basisTMax, double t1, double t2 ); + + /** \brief \ru Инициализация переменных для репараметризации с заданной производной в начале. + \en Initialization of variables for reparameterization with a given derivative at the beginning. \~ + \param[in] basisTMin, basisTMin - \ru Область определения базовой кривой. + \en Parametric region of the base curve.. \~ + \param[in] t1, t2 - \ru Область определения репараметризованной кривой + \en Parametric region of the reparameterized curve. \~ + \param[in] begDer - \ru Производная параметра базовой кривой в начале кривой. + \en The derivative of the base curve parameter at the beginning of the curve. \~ + \return - \ru true - если репараметризация выполнена успешно, + false - если репараметризация оказалась вырожденной и была сведена к линейной. + \en true - if reparameterization is successful, + false - if the reparametrization is degenerate and reduced to linear. \~ + */ + bool InitQuadratic( double basisTMin, double basisTMax, double t1, double t2, double begDer ); + + /** \brief \ru Репараметризация, обеспечивающая на концах новой кривой указаные производные параметра. + \en Reparametrization providing the indicated derivatives of the parameter at the ends of the new curve. \~ + \details \ru Параметрическая ширина будет подобрана автоматически, исходя из значений производных. + \en The parametric width will be automatically selected based on the values of the derivatives. \~ + \param[in] basisTMin, basisTMin - \ru Область определения базовой кривой. + \en Parametric region of the base curve.. \~ + \param[in] dt1, dt2 - \ru Производные параметра базовой кривой в начале и в конце кривой. + \en Derivatives of the base curve parameter at the beginning and end of the curve. \~ + \return - \ru true - репараметризация выполнена успешно, + false - репараметризация оказалась вырожденной и была сведена к линейной. + \en true - reparameterization is successful, + false - reparametrization is degenerate and reduced to linear. \~ + */ + bool InitScaledEnds( double basisTMin, double basisTMax, double dt1, double dt2); +public: + // \ru Общие функции математического объекта \en Common functions of mathematical object + virtual MbeFunctionType IsA() const; // \ru Тип элемента \en A type of element + virtual MbFunction & Duplicate() const; // \ru Сделать копию элемента \en Create a copy of the element + virtual bool IsSame ( const MbFunction & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными \en Determine whether objects are equal + virtual bool SetEqual ( const MbFunction & ); // \ru Сделать равным \en Make equal + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + + virtual double GetTMax() const; // \ru Вернуть максимальное значение параметра \en Get the maximum value of parameter + virtual double GetTMin() const; // \ru Вернуть минимальное значение параметра \en Get the minimum value of parameter + virtual bool IsClosed() const; // \ru Замкнутость кривой \en A curve closedness + virtual void SetClosed( bool cl ); // \ru Замкнутость функции \en A function closedness + + virtual double Value ( double & t ) const; // \ru Значение функции для t \en The value of function for a given t + virtual double FirstDer ( double & t ) const; // \ru Первая производная по t \en The first derivative with respect to t + virtual double SecondDer ( double & t ) const; // \ru Вторая производная по t \en The second derivative with respect to t + virtual double ThirdDer ( double & t ) const; // \ru Третья производная по t \en The third derivative with respect to t + + virtual double _Value ( double t ) const; // \ru Значение функции для t \en The value of function for a given t + virtual double _FirstDer ( double t ) const; // \ru Первая производная по t \en The first derivative with respect to t + virtual double _SecondDer ( double t ) const; // \ru Вторая производная по t \en The second derivative with respect to t + virtual double _ThirdDer ( double t ) const; // \ru Третья производная по t \en The third derivative with respect to t + // \ru Вычислить значение и производные. \en Calculate value and derivatives of object for given parameter. \~ + virtual void Explore( double & t, bool ext, + double & val, double & fir, double * sec, double * thr ) const; + // \ru Вычислить аргумент t по значению функции. \en Calculate the argument t by the function value. + virtual double Argument( double & val ) const; + + virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменить направление \en Change direction + virtual double Step( double t, double sag ) const; + virtual double DeviationStep( double t, double angle ) const; + + virtual double MinValue ( double & t ) const; // \ru Минимальное значение функции \en The minimum value of function + virtual double MaxValue ( double & t ) const; // \ru Максимальное значение функции \en The maximum value of function + virtual double MidValue () const; // \ru Среднее значение функции \en The middle value of function + virtual bool IsGood () const; // \ru Корректность функции \en Correctness of function + + virtual bool IsConst() const; + virtual bool IsLine () const; + + // \ru Создать функцию из части функции между параметрами t1 и t2 c выбором направления sense. \en Create a function in part of the function between the parameters t1 and t2 choosing the direction. + virtual MbFunction * Trimmed( double t1, double t2, int sense ) const; + // \ru Разбить функцию точкой с параметром t и вернуть отрезанную часть. \en Function break by the parameter t, and cut off part of the function: begs == true - save the initial half, beg == false - save the final half. + virtual MbFunction * BreakFunction( double t, bool beg ); + + virtual void SetOffsetFunc( double distOld, double distNew ); // \ru Сместить функцию \en Shift a function + virtual bool SetLimitParam( double newTMin, double newTMax ); // \ru Установить область изменения параметра \en Set range of parameter + virtual void SetLimitValue( size_t n, double newValue ); // \ru Установить значение на конце ( 1 - в начале, 2 - в конце) \en Set the value at the end (1 - at beginning, 2 - at ending) + virtual double GetLimitValue( size_t n ) const; // \ru Дать значение на конце ( 1 - в начале, 2 - в конце) \en Get the value at the end (1 - at beginning, 2 - at ending) + +private: + void operator = ( const MbServeFunction & ); // \ru Не реализовано \en Not implemented + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbServeFunction ) +}; + +IMPL_PERSISTENT_OPS( MbServeFunction ) + + +#endif // __FUNC_SERVE_FUNCTION_H diff --git a/C3d/Include/function.h b/C3d/Include/function.h index 4a46e7c..59e88eb 100644 --- a/C3d/Include/function.h +++ b/C3d/Include/function.h @@ -40,6 +40,7 @@ enum MbeFunctionType { ft_CubicSplineFunction = 5, ///< \ru Кубическая сплайновая функция. \en A cubic spline function. ft_PowerFunction = 6, ///< \ru Степенная функция. \en Power function. ft_SinusFunction = 7, ///< \ru Синусоидальная функция. \en Sinusoidal function. + ft_ServeFunction = 8, ///< \ru Служебная функция. \en Service function. ft_CharacterFunction = 101, ///< \ru Символьная функция. \en A symbolic function. ft_AnalyticalFunction = 102, ///< \ru Символьная функция на модельном выражении. \en A symbolic function in model expression. @@ -136,6 +137,11 @@ public: */ virtual void Explore( double & t, bool ext, double & val, double & fir, double * sec, double * thr ) const; + // Hахождениe аргумента t по значению функции value. \en Calculate the argument t by the function value. + MbeNewtonResult ArgumentNewton( double value, bool ext, double funcEpsilon, + size_t iterLimit, double & t ) const; + // \ru Вычислить аргумент t по значению функции. \en Calculate the argument t by the function value. + virtual double Argument( double & val ) const; /// \ru Изменить направление. \en Change direction. virtual void Inverse( MbRegTransform * iReg = NULL ) = 0; @@ -190,7 +196,7 @@ public: /// \ru Находится ли параметр в области определения функции. \en Whether the parameter belongs to the function domain. bool IsParamOn( double t, double eps ) const { return ( GetTMin()-eps <= t && t <= GetTMax()+eps ); } /// \ru Подготовить к записи регистрируемый объект. \en Prepare for writing the registered object. - void PrepareWrite() { SetRegistrable( (GetUseCount() > 1) ? registrable : noRegistrable ); } + void PrepareWrite() const { SetRegistrable( (GetUseCount() > 1) ? registrable : noRegistrable ); } private: void operator = ( const MbFunction & ); // \ru Не реализовано \en Not implemented diff --git a/C3d/Include/function_factory.h b/C3d/Include/function_factory.h index 74f757f..fda2a4d 100644 --- a/C3d/Include/function_factory.h +++ b/C3d/Include/function_factory.h @@ -30,7 +30,7 @@ class MATH_CLASS MbMathematicalNode; \ingroup Functions */ // --- -class MATH_CLASS MbFunctionFactory { +class MATH_CLASS MbFunctionFactory : public MbSyncItem { private: mutable MbResultType status; ///< \ru Результат создания. \en The result of creation. diff --git a/C3d/Include/gc_api.h b/C3d/Include/gc_api.h index 26df62d..896687c 100644 --- a/C3d/Include/gc_api.h +++ b/C3d/Include/gc_api.h @@ -169,6 +169,22 @@ GCE_FUNC(constraint_item) GCE_FormCirDimension( GCE_system gcContext, geom_item // --- GCE_FUNC(void) GCE_ResetMovingMode( GCE_system ); +//---------------------------------------------------------------------------------------- +/** + \details + \ru По этому вызову солвер запоминает текущее состояние замороженных объектов, + как обязательное к исполнению и перераспределяет их начальное приближение для + минимизации неудовлетворенных ограничений. После успешного вызова GCE_Evaluate + замороженные объекты снова займут свое обязательное положение. Незамороженные + объекты подстроятся под замороженные в соотвествии с заданными ограничениями. + + \note + \ru Данный вызов обеспечивает поддержку старого поведения при переоценке координат замороженных объектов. + \en The call is intended to replay an old-version evaluation for the start values of the frozen coordinates. +*/ +//--- +GCE_FUNC( bool ) GCE_InitFrozenCoords( GCE_system gSys ); + /** \} Constraints2D_API @@ -271,6 +287,13 @@ GCE_FUNC(GCE_system) GCE_RestoreFromJournal( const char * fName ); //--- GCE_FUNC(const GCE_diagnostic_pars &) GCE_DiagnosticPars( GCE_system gSys ); +//---------------------------------------------------------------------------------------- +/** + \note Used only for testing +*/ +// --- +GCE_FUNC(size_t) GCT_InConstraintsFullCount( GCE_system gSys ); + //---------------------------------------------------------------------------------------- // Measure a dimension value (it used for testing purposes only) //--- diff --git a/C3d/Include/gce_api.h b/C3d/Include/gce_api.h index 8a90053..7231e8d 100644 --- a/C3d/Include/gce_api.h +++ b/C3d/Include/gce_api.h @@ -124,12 +124,12 @@ GCE_FUNC(void) GCE_ClearSystem( GCE_system gSys ); //---------------------------------------------------------------------------------------- /** \brief \ru Удалить систему ограничений. \en Delete system of constraints. \~ - \details \ru Данный метод делает систему ограничений недействительной. - Осуществляется освобождение ОЗУ от внутренних структур данных, обслуживающих - систему ограничений. - \en This method makes the constraint system invalid. - Deallocation of RAM from the internal data structures maintaining - the system of constraints is performed. \~ + \details \ru Данный метод освобождает память от внутренних структур данных, обслуживающих + систему ограничений. Удаляемая система ограничений становится недействительной после + данного вызова. + \en This method releases memory from internal data structures maintaining + the constraint system. The removed constraint system is invalidated after this call. + \~ \param[in] gSys - \ru Система ограничений. \en System of constraints. \~ \sa #GCE_ClearSystem @@ -1186,6 +1186,27 @@ GCE_FUNC(constraint_item) GCE_AddRadiusDimension( GCE_system gSys, geom_item cir //--- GCE_FUNC(constraint_item) GCE_AddDiameter( GCE_system gSys, geom_item cir, GCE_dim_pars dPar ); +//---------------------------------------------------------------------------------------- +/** \brief \ru Задать ограничение "Длина кривой". + \en Specify a "Curve Length" constraint. \~ + \param[in] gSys - \ru Система ограничений. + \en System of constraints. \~ + \param[in] curve - \ru Дескриптор кривой. + \en Descriptor of curve. \~ + \param[in] dPar - \ru Параметры линейного размера (подробности см.#GCE_dim_pars). + \en Parameters of linear dimension (see #GCE_dim_pars). \~ + + \return \ru Дескриптор нового ограничения. + \en Descriptor of a new constraint. \~ + + \details \ru У кривой должны быть начальная и конечная точки (#GCE_FIRST_END и #GCE_SECOND_END). + Ограничение поддерживается для линейных объектов и дуг окружностей. + \en The curve must have a start and end points (#GCE_FIRST_END и #GCE_SECOND_END). + Constraint is supported for linear objects and circular arcs.\~ +*/ +//--- +GCE_FUNC(constraint_item) GCE_AddLength( GCE_system gSys, geom_item curve, GCE_dim_pars dPar ); + //---------------------------------------------------------------------------------------- /** \brief \ru Задать ограничение "Управляющий параметр" или "Фиксация переменной" \en Set the constraint "Driving parameter" or "Fixation of variable" \~ @@ -1227,8 +1248,11 @@ GCE_FUNC(constraint_item) GCE_FixGeom( GCE_system gSys, geom_item g ); \en Descriptor of segment. \~ \return \ru Дескриптор нового ограничения. \en Descriptor of a new constraint. \~ - \details \ru Ограничение применимо пока только для отрезков. - \en The constraint is applicable only for segments so far. \~ + + \details \ru У кривой должны быть начальная и конечная точки (#GCE_FIRST_END и #GCE_SECOND_END). + Ограничение поддерживается для линейных объектов и дуг окружностей. + \en The curve must have a start and end points (#GCE_FIRST_END и #GCE_SECOND_END). + Constraint is supported for linear objects and circular arcs.\~ */ //--- GCE_FUNC(constraint_item) GCE_FixLength( GCE_system gSys, geom_item ls ); @@ -1725,6 +1749,21 @@ GCE_FUNC(GCE_result) GCE_MovePoint( GCE_system gcSys, GCE_point curXY ); \param[in] mat - \ru Матрица преобразования. \en Transformation matrix. \~ \return \ru Код ошибки. \en Error code. \~ + + \details \ru Режим динамической трансформации для данного набора геометрических объектов + включается при первом вызове функции #GCE_DynamicTransform. При этом запоминаются начальные + положения геометрических объектов и далее трансформации при каждом новом вызове + #GCE_DynamicTransform выполняются относительно их первоначального положения до тех пор, пока + режим динамической трансформации не будет выключен. Выключается режим динамической + трансформации геометрических объектов при вызове любой другой функции API или при вызове + функции #GCE_DynamicTransform для другого набора геометрических объектов. + \en The dynamic transformation mode for given set of geometric objects is turned on + after the first time the #GCE_DynamicTransform function is called. In this case, the initial + positions of geometric objects are remembered and then the transformations for each new call + of the #GCE_DynamicTransform are performed relative to their initial position until the dynamic + transformation mode is turned off. The mode of dynamic transformation of geometric objects is + turned off when calling any other API function or when calling the #GCE_DynamicTransform + function for another set of geometric objects. \~ */// --- GCE_FUNC(GCE_result) GCE_DynamicTransform( GCE_system gSys, const std::vector & geoms, const MbMatrix & mat ); @@ -1732,6 +1771,16 @@ GCE_FUNC(GCE_result) GCE_DynamicTransform( GCE_system gSys, const std::vector #include -#define GC_GOLDEN_SECTION 0.381966011250105 // \ru Золотое сечение. \en Golden ratio. +const_expr double GC_GOLDEN_SECTION = 0.381966011250105; // \ru Золотое сечение. \en Golden ratio. /** \addtogroup Constraints2D_API @@ -37,38 +37,37 @@ struct MbGeomTol ////////////////////////////////////////////////////////////////////////////////////////// // -/** \brief \ru Фиксированные точности решения задач двумерной параметризации и иные константы. - \en Fixed tolerances of two-dimensional parametrization problems solving and other constants \~ - \details \ru Класс инкапсулирует все точности и прочие константы, распространяющиеся на задачи - двумерной параметризации. - \n - 1. При проверке метрических величин или инцидентности точек в 2D-пространстве +/** \brief \ru Точности для решения двухмерных ограничений и иные константы. + \en Tolerances of 2D-constraint solving and other constants. \~ + + \details \ru Класс инкапсулирует все точности и прочие константы для решения систем + 2D-ограничений. \n + + 1. При проверке линейных размеров или совпадения точек в 2D-пространстве используется точность GcPrecision::lengthRegion. Т.е. любые две точки, не совпадающие с погрешностью GcPrecision::lengthRegion считаютя разными.\n 2. Для итерационных решателей используется точность параметра GcPrecision::newtonEpsilon, эта величина на 1-2 порядка выше, чем неразличимая область (lengthRegion, angleRegion).\n - 3. Любое угловое, метрическое или параметрическое значение меньшее - GcPrecision::tolerance считается нулем.\n - 4. Величина newtonEpsilon всегда меньше либо равна lengthEpsilon и angleEpsilon.\n + 3. Любое угловое, метрическое или параметрическое значение, меньшее GcPrecision::tolerance + считается нулем.\n + 4. Величина newtonEpsilon всегда меньше либо равна min(lengthEpsilon, angleEpsilon).\n 5. Любой элемент матрицы меньший по модулю, чем GcPrecision::m_null - считается нулем. Барьер GcPrecision::m_null влияет на быстродействие и точность разложения - и перемножения матриц. Чем выше m_null, тем быстрее может работать разложение, в ущерб - вычислительной устойчивости.\n + и перемножения матриц. \n + \en The class encapsulates all tolerances and other constants related to - two-dimensional parametrization problems. - \n - 1. For check of metric values or points coincidence in 2D space - tolerance GcPrecision::lengthRegion is used. I.e. any two points - which are not coincident with tolerance GcPrecision::lengthRegion are considered to be different.\n + two-dimensional constraint solving.\n + + 1. For check of distance values or points coincidence in 2D space tolerance + GcPrecision::lengthRegion is used. I.e. any two points which are not coincident with + tolerance GcPrecision::lengthRegion are considered to be different.\n 2. For iterative solvers tolerance of parameter GcPrecision::newtonEpsilon is used. This value is 1-2 orders higher than undistinguishable domain (lengthRegion, angleRegion).\n - 3. Any angular,metric or parametric value less than - GcPrecision::tolerance is considered to be zero.\n - 4. Value newtonEpsilon is always less or equal to lengthEpsilon and angleEpsilon.\n + 3. Any angular, length or parametric value less than GcPrecision::tolerance + is considered to be zero.\n + 4. Value newtonEpsilon is always less or equal to (lengthEpsilon, angleEpsilon).\n 5. Any element of matrix less than GcPrecision::m_null by absolute value is considered - to be zero. Threshold GcPrecision::m_null influences on performance and accuracy of decomposition - and multiplication of matrices. The higher m_null the faster the decomposition can work, to the prejudice of - computational stability.\n \~ + to be zero. \n \~ \nosubgrouping */ // @@ -82,20 +81,20 @@ struct GCE_CLASS GcPrecision \{ */ - const static double m_null; ///< \ru Абсолютная точность, с которой определяется четкий нуль в матрице; \en Absolute tolerance with which the clear zero is determined in matrix; - const static double grshTol; ///< \ru Относительная точность для оценки линейной зависимости при ортогонализации (Грамм-Шмидт) \en Relative tolerance for estimation of linear dependence during orthogonalizaiton (Gram-Schmidt) - const static double tolerance; ///< \ru Точность для проверки на нуль вещественного числа (углового или метрического); \en Tolerance for checking a real number (angular or metric) for zero; - const static double degMetricEps; ///< \ru Метрическая точность для проверки вырожденной геометрии; \en Metric tolerance for checking the geometry for degeneracy; - const static double lengthEpsilon; ///< \ru Вычислительная точность итерационных решателей и конструирования локусов пересечения ( линейная ); \en Computational tolerance of iterative solvers and intersection loci construction (linear); - const static double angleEpsilon; ///< \ru Вычислительная точность итерационных решателей и конструирования локусов пересечения ( угловая ); \en Computational tolerance of iterative solvers and intersection loci construction (angular); + const static double m_null; ///< \ru Абсолютная точность, с которой определяется четкий нуль в матрице. \en Absolute tolerance with which the clear zero is determined in matrix. + const static double grshTol; ///< \ru Относительная точность для оценки линейной зависимости при ортогонализации (Грамм-Шмидт). \en Relative tolerance for estimation of linear dependence during orthogonalizaiton (Gram-Schmidt). + const static double tolerance; ///< \ru Точность для проверки на нуль вещественного числа (углового или метрического). \en Tolerance for checking a real number (angular or metric) for zero. + const static double degMetricEps; ///< \ru Метрическая точность для проверки вырожденной геометрии. \en Metric tolerance for checking the geometry for degeneracy. + const static double lengthEpsilon; ///< \ru Вычислительная точность итерационных решателей в единицах длины. \en Computational tolerance of iterative solvers in the length units. + const static double angleEpsilon; ///< \ru Угловая точность вычислений для итерационных решателей в радианах. \en Angular tolerance in radians for iterative computation. const static double lengthRegion; ///< \ru Неразличимая метрическая область (проверочная точность). \en Indistinguishable metric domain (satisfactory tolerance). const static double angleRegion; ///< \ru Неразличимая угловая область (проверочная точность). \en Indistinguishable angular domain (satisfactory tolerance). - const static double newtonEpsilon; ///< \ru Целевая точность итерационного решателя. \en Target accuracy of the iterative solver. + const static double newtonEpsilon; ///< \ru Целевая точность результата итерационных вычислений. \en Target accuracy of the iterative solution result. const static double newtonRegion; ///< \ru Удовлетворительная точность итерационного решения. \en Satisfactory accuracy of the iterative solution. - const static double paramRegion; ///< \ru Проверочная точность параметра \en Parameter checking tolerance - const static double maxRadius; ///< \ru Максимально возможный радиус для 2d-эскиза \en Maximal possible radius for cutting of 2d-sketch - const static MbGeomTol satisfying; ///< \ru Точность удовлетворенных ограничений; \en Satisfactory accuracy; - const static MbGeomTol newtonTol; ///< \ru Точность решения системы уравнений итерационными методами; \en Tolerance of solving the equation system by iterative methods; + const static double paramRegion; ///< \ru Проверочная точность параметра. \en Parameter checking tolerance. + const static double maxRadius; ///< \ru Максимально возможный радиус для 2d-эскиза. \en Maximal possible radius to deal with a circle in 2d-sketch. + const static MbGeomTol satisfying; ///< \ru Точность удовлетворенных ограничений. \en Satisfactory tolerance. + const static MbGeomTol newtonTol; ///< \ru Точность решения системы уравнений итерационными методами. \en Tolerance of solving the equation system by iterative methods. /** \} @@ -105,10 +104,10 @@ struct GCE_CLASS GcPrecision */ const static double pi2; ///< 2*M_PI (6.18...) - const static double lastReal; ///< \ru "Самое большое" число с плавающей точкой \en "The greatest" float number - const static int iterLimitCount; ///< \ru Предельное количество итераций в численном методе Ньютона \en Limit of iterations in numerical Newton's method + const static double lastReal; ///< \ru "Самое большое" число с плавающей точкой. \en "The greatest" float number. + const static int iterLimitCount; ///< \ru Предельное количество итераций в численном методе Ньютона. \en Limit of iterations in numerical Newton's method. const static int wellIterLimit; ///< \ru Предельное количество итераций в хорошо-сходящихся процессах. \en Limit number of iterations in well-convergent processes. - static TCHAR buff_512[512]; ///< \ru Текстовый буфер размером в 512 байт \en Text buffer of 512 bytes + static TCHAR buff_512[512]; ///< \ru Текстовый буфер размером в 512 байт. \en Text buffer of 512 bytes. /** \} */ diff --git a/C3d/Include/gce_types.h b/C3d/Include/gce_types.h index 39f0c4d..37ffdb4 100644 --- a/C3d/Include/gce_types.h +++ b/C3d/Include/gce_types.h @@ -226,7 +226,7 @@ typedef enum GCE_RESULT_Aborted = 13, ///< \ru Процесс вычислений был прерван по запросу приложения. \en The evaluation process aborted by the application. \~ GCE_RESULT_IsNotDrivingDimension = 14, ///< \ru Данное ограничение должно быть управляющим размером. \en Given constraint should be a driving dimension. GCE_RESULT_UnsupportedConstraint = 15, ///< \ru На геометрические объекты было наложено невозможное ограничение. \en An impossible constraint was set on geometric objects. - GCE_RESULT_NonUnitScalingFactor = 16, ///< \ru Неединичный коэффициент масштабирования. \en Non unit scaling factor. + GCE_RESULT_AnisotropicScaling = 16, ///< \ru Анизотропное масштабирование. \en Anisotropic scaling. } GCE_result; //---------------------------------------------------------------------------------------- @@ -254,10 +254,9 @@ typedef enum The status of a constraint implies the division of the system into subsets, which are labeled as follows: \n - Constraints marked with the GCE_STATUS_WellConditioned and GCE_STATUS_IllConditioned - statuses together form a group of interrelated constraints, which are usually resolved - without contradiction, but can potentially contradict each other with the presence of - dimensions. \n + Constraints marked by GCE_STATUS_WellTreated and GCE_STATUS_WellConditioned together form + well-resolved part that does not contain overdefining constraints and usually computed + without contradiction. \n Constraints marked with GCE_STATUS_WellConditioned and GCE_STATUS_IllConditioned statuses together form a group of interrelated constraints that are usually resolved without contradiction, but potentially contradictory with dimensions. @@ -376,7 +375,7 @@ struct GCE_circle }; //---------------------------------------------------------------------------------------- -/// \ru Координаты эллипса. \en Coordinates of a ellipse. +/// \ru Координаты эллипса. \en Coordinates of an ellipse. //--- struct GCE_ellipse { @@ -623,7 +622,7 @@ struct GCE_diagnostic_pars size_t consCount; // A number of registered constraints. size_t inConsCount; // A number of internal constraints. double reductCoef; // Reduction ration of decomposition methods [percentage]. - GCE_diagnostic_pars() : consCount( 0 ), inConsCount( 0 ), reductCoef( .0 ) {} + GCE_diagnostic_pars() : consCount( 0 ), inConsCount( 0 ), reductCoef( 0. ) {} }; //---------------------------------------------------------------------------------------- diff --git a/C3d/Include/gcm_blackbox.h b/C3d/Include/gcm_blackbox.h index 52ad5a6..681c31f 100644 --- a/C3d/Include/gcm_blackbox.h +++ b/C3d/Include/gcm_blackbox.h @@ -40,11 +40,11 @@ //--- struct ItGCBlackbox { - /// \ru Выдать независимые геометрические объекты. \en The function collects in the array independent geoms of a blackbox. - virtual void CollectMyInGeoms( IFC_Array & ) const = 0; - /// \ru Выдать зависимые геометрические объекты. \en The function collects in the array dependent geoms of a blackbox. - virtual void CollectMyOutGeoms( IFC_Array & ) const = 0; - /** \brief \ru Рассчитать положение зависимого объекта. + /// \ru Выдать независимые геометрические объекты. \en The function collects in the array independent geoms of a blackbox. + virtual void CollectMyInGeoms( IFC_Array & ) const = 0; + /// \ru Выдать зависимые геометрические объекты. \en The function collects in the array dependent geoms of a blackbox. + virtual void CollectMyOutGeoms( IFC_Array & ) const = 0; + /** \brief \ru Рассчитать положение зависимого объекта. \en Calculate position of a dependent geometric object. \~ \param[in] inPlaces - \ru Позиции независимых объектов, получаемых методом #ItGCBlackbox::CollectMyInGeoms. \en Positions of independed geoms, which are got by #ItGCBlackbox::CollectMyInGeoms.\~ @@ -55,11 +55,11 @@ struct ItGCBlackbox \return \ru true, если функция корректно исполнена. \en true if the function performed succeeded. \~ */ - virtual bool Calculate( const SArray & inPlaces + virtual bool Calculate( const SArray & inPlaces , const ItGeom & depGeom , MbPlacement3D & depPlace ) const = 0; - /// \ru Является ли данный объект зависимым для черного ящика? \en Check if the given geometric item is dependent - virtual bool IsMyOutGeom( const ItGeom & ) const = 0; + /// \ru Является ли данный объект зависимым для черного ящика? \en Check if the given geometric item is dependent + virtual bool IsMyOutGeom( const ItGeom & ) const = 0; /** \brief \ru Сформулировать ограничения для зависимого геометрического объекта. \en Formulate constraints for the dependent geometric object. \~ diff --git a/C3d/Include/gcm_constraint.h b/C3d/Include/gcm_constraint.h index f6e01d2..1a76ff3 100644 --- a/C3d/Include/gcm_constraint.h +++ b/C3d/Include/gcm_constraint.h @@ -229,6 +229,9 @@ public: // (!) The constants below will be removed in a future version (V17 or l static const GCM_angle_type at_Planar = GCM_2D_ANGLE; static const GCM_angle_type at_3D = GCM_3D_ANGLE; +protected: + virtual GCM_scale _ScaleType() const { return GCM_NO_SCALE; } + protected: ItConstraintItem() : m_args() {} ~ItConstraintItem() {} @@ -298,6 +301,7 @@ inline void ItConstraintItem::GetParams( GCM_c_params & pars ) const pars.m_TanChoice = TangencyChoice(); pars.m_AngType = AngleType(); pars.m_RealPar = DimParameter(); + pars.m_scale = _ScaleType(); } //---------------------------------------------------------------------------------------- diff --git a/C3d/Include/gcm_manager.h b/C3d/Include/gcm_manager.h index 96ba797..a7c3f59 100644 --- a/C3d/Include/gcm_manager.h +++ b/C3d/Include/gcm_manager.h @@ -330,7 +330,14 @@ public: /// \ru Добавить геометрический объект в паттерн. \en Add a geometric object to the pattern. MtConstraintId AddGeomToPattern( MtPatternId ptrn, MtArgument ptrnObj, MtParVariant par1 = MtParVariant::undef, MtParVariant par2 = MtParVariant::undef, GCM_scale scale=GCM_RIGID ); - /// \ru Добавить ограничение. \en Add constraint. + /** + \brief \ru Добавить ограничение. + \en Add constraint. + \return \ru Вернет GCM_RESULT_Ok, если ограничение успешно добавлено, + в противном случае вернет код ошибки. + \en Returns GCM_RESULT_Ok if the constraint is successfully added, + otherwise it will return an error code. + */ MtResultCode3D AddConstraintItem( ItConstraintItem & ); /** \brief \ru Добавить сопряжение для пары геометрических объектов(ограничение). @@ -408,7 +415,7 @@ public: */ ItGeom * CreateCluster( std::vector & ); /// \ru Зафиксировать геометрический объект в ГСК. \en Fix geometric object in the WCS. - MtResultCode3D FixGeom( ItGeom & ); + MtResultCode3D FixGeom( const ItGeom * ); /// \ru Узнать зафиксирован ли геометрический объект? \en Check if a geometric object is fixed? bool IsFixed( const ItGeom * ); /// \ru Очистить систему ограничений. \en Clear the system constraint. @@ -620,14 +627,17 @@ public: */ /// \ru Функция будет удалена из API. Использовать ChangeDefinition(). \en The call is deprecated. Use ChangeDefinition() instead this. - MtResultCode3D ChangeAlignCondition( ItConstraintItem & ); - /// \ru Функция будет удалена из API. Использовать Evalute(). \en The call is deprecated. Use ChangeDefinition() instead this. - MtResultCode3D Solve( bool diagQuery ); + MtResultCode3D ChangeAlignCondition( ItConstraintItem & ); + MtResultCode3D FixGeom( ItGeom & ); /** \} */ +//protected: + /// \ru Функция будет удалена из API. Использовать Evalute(). \en The call is deprecated. Use ChangeDefinition() instead this. + MtResultCode3D Solve( bool diagQuery ); + protected: MtGeomSolver(); ~MtGeomSolver(); diff --git a/C3d/Include/gcm_reposition.h b/C3d/Include/gcm_reposition.h index 80a1b2f..b9dc5f3 100644 --- a/C3d/Include/gcm_reposition.h +++ b/C3d/Include/gcm_reposition.h @@ -79,6 +79,9 @@ struct ItPositionManager virtual refcount_t AddRef() const = 0; /// \ru Удалить ссылку \en Release reference virtual refcount_t Release() const = 0; + +protected: + ~ItPositionManager() {} }; /** \} */ // GCM_3D_ObjectAPI diff --git a/C3d/Include/gcm_res_code.h b/C3d/Include/gcm_res_code.h index 8f33b3a..71eb7e4 100644 --- a/C3d/Include/gcm_res_code.h +++ b/C3d/Include/gcm_res_code.h @@ -53,7 +53,7 @@ inline int PriorityLevel( GCM_result resCode ) case GCM_RESULT_MultiDependedGeom: // \ru Задана входящая зависимость для выходного объекта черного ящика; \en Given an incoming dependence of the output object of a black box; case GCM_RESULT_OverconstrainingDependedGeoms: // \ru Задана зависимость между экземплярами массива (выходными); \en Given dependence between copies of the pattern (output); - case GCM_RESULT_DependedGeomCantBeFixed: // The depended geom can't be fixed. + case GCM_RESULT_DependedGeomCantBeFixed: // The depended geom can't be fixed. return 8; case GCM_RESULT_CyclicDependence: // \ru Задана циклическая зависимость \en Given a cyclic dependence diff --git a/C3d/Include/generic_utility.h b/C3d/Include/generic_utility.h index 569bab2..1f5712b 100644 --- a/C3d/Include/generic_utility.h +++ b/C3d/Include/generic_utility.h @@ -1111,6 +1111,15 @@ bool is_exist( _Iterator begIt, _Iterator endIt, const _Element & elem ) return std::find( begIt, endIt, elem ) != endIt; } +//---------------------------------------------------------------------------------------- +// +// --- +template +bool is_exist_if( _Iterator begIt, _Iterator endIt, _UnaryPredicate _Pred ) +{ + return std::find_if( begIt, endIt, _Pred ) != endIt; +} + namespace c3d { struct color_label @@ -1120,6 +1129,20 @@ namespace c3d bool operator == ( color_code col ) const { return col == val; } color_label & operator = ( color_code col ) { val = col; return *this; } }; + +//---------------------------------------------------------------------------------------- +// +//--- +template +struct _IterTraits { + typedef typename Iterator::value_type value_type; +}; + +template +struct _IterTraits { + typedef T value_type; +}; + //---------------------------------------------------------------------------------------- // Диапазон итераторов //--- @@ -1127,18 +1150,20 @@ template struct range : public std::pair { typedef std::pair _Pair; - typedef typename Iterator::value_type value_type; + typedef typename _IterTraits::value_type value_type; range( const Iterator & iter, const Iterator & last ) :_Pair( iter, last ) {} range( const _Pair & other ) :_Pair( other ) {} range() :_Pair() {} - Iterator begin() { return _Pair::first; } - Iterator end() { return _Pair::second; } + Iterator begin() const { return _Pair::first; } + Iterator end() const { return _Pair::second; } bool empty() const { return _Pair::first == _Pair::second; } size_t size() const { return (size_t)std::distance( _Pair::first, _Pair::second ); } void clear() { _Pair::first = _Pair::second; } - //value_type & front() { return *first; } - const value_type & front() const { return *_Pair::first; } + range & move_front() { ++_Pair::first; return *this; } + + const value_type & front() const { return (*begin()); } + const value_type & back() const { return (*(end() - 1)); } }; //---------------------------------------------------------------------------------------- @@ -1151,6 +1176,16 @@ range range_of( const _Cont & list ) return rng; } +//---------------------------------------------------------------------------------------- +// Get a range of iterators +//--- +template +range<_Iterator> make_range( _Iterator first, _Iterator last ) +{ + c3d::range<_Iterator> rng( first, last ); + return rng; +} + }; #endif // __GENERIC_UTILITY_H diff --git a/C3d/Include/hash32.h b/C3d/Include/hash32.h index be2eeda..992f359 100644 --- a/C3d/Include/hash32.h +++ b/C3d/Include/hash32.h @@ -19,11 +19,12 @@ class reader; //////////////////////////////////////////////////////////////////////////////// // -// \ru Oпределение простого имени \en Definition of simple name +// \ru Определение простого имени \en Definition of simple name // //////////////////////////////////////////////////////////////////////////////// -// Activate to check compilation // #define SIMPLENAME_AS_CLASS +// Activate to check compilation +// #define SIMPLENAME_AS_CLASS #ifndef SIMPLENAME_AS_CLASS @@ -43,7 +44,10 @@ typedef uint32 SimpleName; \ingroup Base_Tools */ // --- -inline int SimpleNameCompare( const SimpleName & h1, const SimpleName & h2 ) { return ( (h1 > h2) ? 1 : ( (h1 < h2) ? -1 : 0 ) ); } +inline +int SimpleNameCompare( const SimpleName & h1, const SimpleName & h2 ) { + return ( (h1 > h2) ? 1 : ( (h1 < h2) ? -1 : 0 ) ); +} //------------------------------------------------------------------------------ @@ -59,7 +63,9 @@ bool IsGoodSimpleName( const SimpleName & s ) { #else // SIMPLENAME_AS_CLASS -// \ru При активации этой ветки обязательно собрать проект с активацией максимального уровеня предупреждений \en When this branch is activated, the project must be rebuilt with the highest level of warnings activated +#include + +// \ru При активации этой ветки обязательно собрать проект с активацией максимального уровня предупреждений \en When this branch is activated, the project must be rebuilt with the highest level of warnings activated class SimpleName { protected: @@ -71,7 +77,7 @@ public: SimpleName( const SimpleName & other ) : body( SYS_MAX_UINT32 ) { body = other.body; } SimpleName( size_t other ) : body( SYS_MAX_UINT32 ) { body = other; } - // \ru операторы копирования \en copy operators + // \ru операторы копирования \en copy operators SimpleName & operator = ( const SimpleName & other ) { body = other.body; return *this; } SimpleName & operator = ( size_t other ) { body = other; return *this; } @@ -84,16 +90,23 @@ public: bool operator <= ( const SimpleName & other ) const { return Сompare( other.body ) <= 0; } SimpleName operator + ( size_t other ) const { return SimpleName( body + (size_t)other ); } - SimpleName operator * ( size_t other ) const { return SimpleName( body * (size_t)other ); } + SimpleName operator - ( size_t other ) const + { + C3D_ASSERT( body >= other ); + return SimpleName( body - (size_t)other ); + } + + SimpleName operator * ( size_t other ) const { return SimpleName( body * other ); } SimpleName & operator += ( size_t other ) { body += (size_t)other; return *this; } SimpleName & operator *= ( size_t other ) { body *= (size_t)other; return *this; } SimpleName & operator |= ( size_t other ) { body |= (size_t)other; return *this; } - SimpleName & operator ++ () { ++body; return *this; } // pre increment - SimpleName operator ++ ( int ) { size_t _tmp = body; ++body; return _tmp; } // post increment + SimpleName & operator ++ () { ++body; return *this; } // pre increment + SimpleName operator ++ ( int ) { size_t _tmp = body; ++body; return _tmp; } // post increment // \ru доступ к данным \en access to data operator bool () const { return body != 0 && body != SYS_MAX_UINT32; } operator size_t() const { return (size_t)body; } + operator ptrdiff_t() const { return (ptrdiff_t)body; } // service int Сompare( const SimpleName & other ) const { return Сompare( other.body ); } // return value [-1; 0; +1] protected: @@ -110,6 +123,9 @@ inline void SwapIT( SimpleName & a, SimpleName & b ) { SimpleName tmp = a; a = b #endif // SIMPLENAME_AS_CLASS +namespace c3d // namespace C3D +{ + /** \addtogroup Base_Tools \{ */ @@ -119,21 +135,21 @@ inline void SwapIT( SimpleName & a, SimpleName & b ) { SimpleName tmp = a; a = b \en Maximum allowable simple name. \~ */ //--- -const SimpleName SIMPLENAME_MAX = SYS_MAX_UINT32; +const SimpleName SIMPLENAME_MAX = size_t(SYS_MAX_UINT32); //------------------------------------------------------------------------------ /** \brief \ru Значение используемое, в качестве "неопределенного", еще не назначенного имени. \en A value is used as "undefined", not yet assigned name. \~ */ //--- -const SimpleName UNDEFINED_SNAME = SYS_MAX_UINT32; +const SimpleName UNDEFINED_SNAME = size_t(SYS_MAX_UINT32); //------------------------------------------------------------------------------ /** \brief \ru Начальное число для хэш-функции. \en The initial value for the hash-function. \~ */ // --- -const SimpleName INIT_HASH32_VAL = 31415926; +const SimpleName INIT_HASH32_VAL = size_t( 31415926 ); //------------------------------------------------------------------------------ @@ -141,7 +157,7 @@ const SimpleName INIT_HASH32_VAL = 31415926; \en Golden section - an arbitrary number for hash-function. \~ */ // --- -#define GOLDENRATIO 0x9e3779b9 +const_expr uint GOLDENRATIO = 0x9e3779b9; /** \} @@ -158,7 +174,8 @@ const SimpleName INIT_HASH32_VAL = 31415926; // \ru Обратимая 32-битная смесь для любых трёх 32-битных чисел a, b, c. \en Invertible 32-bit mixture for any three 32-bit numbers a, b, c. // \ru Вероятность изменения значений в любом случае равна как минимум одной четвёртой. \en In any case the probability of values modification is equal to 1/4 at least. // --- -static void mix( uint & a, uint & b, uint & c ) +inline +void mix( uint & a, uint & b, uint & c ) { a -= b; a -= c; a ^= (c >> 13); b -= c; b -= a; b ^= (a << 8 ); @@ -175,7 +192,7 @@ static void mix( uint & a, uint & b, uint & c ) //------------------------------------------------------------------------------ /** \brief \ru Хэш-функция. \en Hash-function. \~ - \details \ru Хэш по последовательности байти и предыдущему хэшу. \n + \details \ru Хэш по последовательности байт и предыдущему хэшу. \n Каждый бит k влияет на возвращаемое значение. \n Функция плохо подходит для использования в криптографии. \n k - Указатель на начало последовательности байт. \n @@ -192,7 +209,8 @@ static void mix( uint & a, uint & b, uint & c ) \ingroup Base_Tools */ // --- -inline SimpleName Hash32( uint8 * k, size_t length, SimpleName _c = INIT_HASH32_VAL ) +inline +SimpleName Hash32( uint8 * k, size_t length, SimpleName _c = INIT_HASH32_VAL ) { PRECONDITION( HiUint32( length ) == 0 ); @@ -244,9 +262,6 @@ inline SimpleName Hash32( uint8 * k, size_t length, SimpleName _c = INIT_HASH32_ } -#undef GOLDENRATIO - - //------------------------------------------------------------------------------ /** \brief \ru Хэш указателя. \en Hash of the pointer. \~ @@ -256,7 +271,7 @@ inline SimpleName Hash32( uint8 * k, size_t length, SimpleName _c = INIT_HASH32_ */ // --- template -SimpleName Hash32Ptr( T * k ) { return ::Hash32( reinterpret_cast(&k), sizeof(T*) ); } +SimpleName Hash32Ptr( T * k ) { return c3d::Hash32( reinterpret_cast(&k), sizeof(T*) ); } //------------------------------------------------------------------------------ @@ -267,8 +282,9 @@ SimpleName Hash32Ptr( T * k ) { return ::Hash32( reinterpret_cast(&k), \ingroup Base_Tools */ // --- -inline SimpleName HashStr( const c3d::string_t & str ) { - return ::Hash32( (uint8*)str.c_str(), str.length() * sizeof(TCHAR) ); +inline +SimpleName HashStr( const c3d::string_t & str ) { + return c3d::Hash32( (uint8*)str.c_str(), str.length() * sizeof(TCHAR) ); } @@ -284,7 +300,7 @@ inline SimpleName HashStr( const char * c_str ) { PRECONDITION( c_str ); - return ::Hash32( (uint8*)c_str, strlen(c_str) * sizeof(char) ); + return c3d::Hash32( (uint8*)c_str, strlen(c_str) * sizeof(char) ); } @@ -301,11 +317,11 @@ SimpleName HashStr( const wchar_t * w_str ) { PRECONDITION( w_str ); #ifndef __MOBILE_VERSION__ - return ::Hash32( (uint8*)w_str, wcslen(w_str) * sizeof(wchar_t) ); + return c3d::Hash32( (uint8*)w_str, wcslen(w_str) * sizeof(wchar_t) ); #else // __MOBILE_VERSION__ uint16 * hashBuf = Ucs4ToUtf16((uint32*)w_str); uint16 * hashBufPointer = hashBuf; - SimpleName hash = ::Hash32( (uint8*)hashBufPointer, wcslen(w_str) * 2 ); + SimpleName hash = c3d::Hash32( (uint8*)hashBufPointer, wcslen(w_str) * 2 ); delete[] hashBuf; return hash; #endif // __MOBILE_VERSION__ @@ -322,7 +338,7 @@ SimpleName HashStr( const wchar_t * w_str ) \ingroup Base_Tools */ // --- -class StrHash { +class StrHash { public: /// \ru Тип строки-источника имени. \en Type of the source string of name. enum StrHashType { @@ -342,13 +358,13 @@ public: {} /// \ru Конструктор по строке. \en Constructor by string. StrHash ( const char * str ) - : m_val ( ::HashStr(str) ) - , m_type( htp_char ) + : m_val ( c3d::HashStr(str) ) + , m_type( htp_char ) {} /// \ru Конструктор по строке. \en Constructor by string. StrHash ( const wchar_t * str ) - : m_val ( ::HashStr(str) ) - , m_type( htp_wchar ) + : m_val ( c3d::HashStr(str) ) + , m_type( htp_wchar ) {} SimpleName GetVal() const { return m_val; } ///< \ru Получить значение. \en Get the value. @@ -359,8 +375,8 @@ public: int operator == ( const wchar_t * with ) const; ///< \ru Оператор равенства. \en Equality operator. // \ru Чтение-запись \en Reading-writing - friend writer & operator << ( writer &, const StrHash & strHash ); ///< \ru Оператор записи. \en Write operator. - friend reader & operator >> ( reader &, StrHash & strHash ); ///< \ru Оператор чтения. \en Read operator. + friend writer & operator << ( writer &, const c3d::StrHash & strHash ); ///< \ru Оператор записи. \en Write operator. + friend reader & operator >> ( reader &, c3d::StrHash & strHash ); ///< \ru Оператор чтения. \en Read operator. }; @@ -372,7 +388,7 @@ public: \ingroup Base_Tools */ // --- -#define NullStrHash StrHash( 0, StrHash::htp_undef ) +const StrHash NullStrHash( 0, StrHash::htp_undef ); //------------------------------------------------------------------------------ @@ -383,7 +399,7 @@ public: \ingroup Base_Tools */ // --- -#define UndefStrHash StrHash( -1, StrHash::htp_undef ) +const StrHash UndefStrHash( -1, StrHash::htp_undef ); //------------------------------------------------------------------------------ @@ -396,7 +412,8 @@ public: \ingroup Base_Tools */ // --- -inline SimpleName Hash32SN( SimpleName k1, SimpleName k2 ) +inline +SimpleName Hash32SN( SimpleName k1, SimpleName k2 ) { //SimpleName array[] = { k1, k2 }; uint arr[2]; @@ -407,7 +424,7 @@ inline SimpleName Hash32SN( SimpleName k1, SimpleName k2 ) arr[0] = LoUint32( (size_t)k1 ); arr[1] = LoUint32( (size_t)k2 ); #endif // SIMPLENAME_AS_CLASS - return ::Hash32( (uint8*)arr, 2 * sizeof(uint) ); // \ru длина - 4 * 2 = 8 \en length - 4 * 2 = 8 + return c3d::Hash32( (uint8*)arr, 2 * sizeof(uint) ); // \ru длина - 4 * 2 = 8 \en length - 4 * 2 = 8 } @@ -451,6 +468,8 @@ inline int StrHash::operator == ( const wchar_t * with ) const return *this == StrHash( with ); } +} // namespace C3D + #endif // __HASH32_H diff --git a/C3d/Include/iges_basic.h b/C3d/Include/iges_basic.h index d09ffea..e1bcdaf 100644 --- a/C3d/Include/iges_basic.h +++ b/C3d/Include/iges_basic.h @@ -217,7 +217,7 @@ struct DirEntryParameter { } bool operator == ( DirEntryParameter & o ) const { return numbString == o.numbString; } - bool operator < ( DirEntryParameter & o ) const { return numbString < o.numbString; } + bool operator < ( DirEntryParameter & o ) const { return numbString < o.numbString; } }; diff --git a/C3d/Include/instance.h b/C3d/Include/instance.h index e608a0e..4f883dd 100644 --- a/C3d/Include/instance.h +++ b/C3d/Include/instance.h @@ -1,185 +1,185 @@ -////////////////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Вставка объекта. - \en Instance of object. \~ - \details \ru Вставка объекта геометрической модели MbItem в локальной системе координат MbPlacement3D - позволяет накладывать геометрические ограничения на объект в сборке и позиционоровать объект. - Вставка не может содержать другую вставку. \n - \en The instance of geometric model MbItem object in local coordinate system MbPlacement3D - allows to define constraints for an object in the assembly and to locate the object. - The instance cannot contain another instance. \n \~ -*/ -////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef __INSTANCE_H -#define __INSTANCE_H - -#include -#include - - -class MATH_CLASS MbSolid; -class MATH_CLASS MbInstance; -class MATH_CLASS MbAssembly; - -namespace c3d // namespace C3D -{ -typedef SPtr InstanceSPtr; -typedef SPtr ConstInstanceSPtr; - -typedef std::vector InstancesVector; -typedef std::vector ConstInstancesVector; - -typedef std::vector InstancesSPtrVector; -typedef std::vector ConstInstancesSPtrVector; -} - - -//---------------------------------------------------------------------------------------- -/** \brief \ru Вставка объекта. - \en Instance of object. \~ - \details \ru Вставка объекта геометрической модели MbItem в локальной системе координат MbPlacement3D - позволяет накладывать геометрические ограничения на объект в сборке и позиционоровать объект. - Вставка не может содержать другую вставку. \n - \en The instance of geometric model MbItem object in local coordinate system MbPlacement3D - allows to define constraints for an object in the assembly and to locate the object. - The instance cannot contain another instance. \n \~ - \ingroup Model_Items -*/ -// --- -class MATH_CLASS MbInstance : public MbItem -{ - MbPlacement3D place; ///< \ru Локальная система координат объекта. \en Local coordinate system of the object. - SPtr item; ///< \ru Геометрический объект. \en A geometric object. - -protected : - /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. - explicit MbInstance( const MbInstance &, MbRegDuplicate * ); - -public : - /// \ru Конструктор по объекту и его системе координат. \en The constructor by an object and its coordinate system. - MbInstance( MbItem &, const MbPlacement3D & ); - /// \ru Деструктор. \en Destructor. - virtual ~MbInstance(); - -public : - VISITING_CLASS( MbInstance ); - - // \ru Общие функции геометрического объекта. \en Common functions of a geometric object. - virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en An object type. - virtual MbSpaceItem & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Создать копию. \en Create a copy. - virtual void Transform( const MbMatrix3D &, MbRegTransform * iReg = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. - virtual void Move ( const MbVector3D &, MbRegTransform * iReg = NULL ); // \ru Сдвинуть вдоль вектора. \en Move along a vector. - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * iReg = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Whether the objects are equal? - virtual bool IsSimilar( const MbSpaceItem & init ) const; // \ru Являются ли объекты подобными? \en Whether the objects are similar? - virtual bool SetEqual ( const MbSpaceItem & init ); // \ru Сделать объекты равными. \en Make the objects equal. - virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate distance to point. - virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Добавь свой габарит в куб. \en Add own bounding box to the bounding box. - virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate the bounding box in a local coordinate system. - virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. - - virtual MbProperty & CreateProperty( MbePrompt n ) const; // \ru Создать собственное свойство. \en Create own property. - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & properties ); // \ru Установить свойства объекта. \en Set properties of the object. - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - // \ru Получить систему координат объекта. \en Get the coordinate system of an item. - virtual bool GetPlacement( MbPlacement3D & ) const; - // \ru Установить систему координат объекта. \en Set the coordinate system of an item. - virtual bool SetPlacement( const MbPlacement3D & ); - - // \ru Перестроить объект по журналу построения. \en Reconstruct object according to the history tree. - virtual bool RebuildItem( MbeCopyMode sameShell, RPArray * items, IProgressIndicator * progInd ); - - // \ru Создать полигональный объект - упрощенную копию данного объекта. \en Create a polygonal object - a simplified copy of the given object. - virtual MbItem * CreateMesh( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * iReg ) const; - // \ru Добавить полигонную сетку объекта. \en Add a polygon mesh of the object. - virtual bool AddYourMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; - // \ru Разрезать полигональный объект одной или двумя параллельными плоскостями. \en Cut the polygonal object by one or two parallel planes. - virtual MbItem * CutMesh( const MbPlacement3D & cutPlace, double distance ) const; - // \ru Найти ближайший объект или имя ближайшего объекта. \en Find the closest object or its name. - virtual bool NearestMesh( MbeSpaceType sType, MbeTopologyType tType, MbePlaneType pType, - const MbAxis3D & axis, double maxDistance, bool gridPriority, double & t, double & dMin, - MbItem *& find, SimpleName & findName, - MbRefItem *& element, SimpleName & elementName, - MbPath & path, MbMatrix3D & from ) const; - // \ru Дать все объекты указанного типа. \en Get all objects by type. \~ - virtual bool GetItems( MbeSpaceType type, const MbMatrix3D & from, - RPArray & items, SArray & matrs ); - // \ru Дать все уникальные объекты указанного типа. \en Get all unique objects by type . \~ - virtual bool GetUniqItems( MbeSpaceType type, CSSArray & items ) const; - // \ru Дать объект по его пути положения в модели и матрицу преобразования объекта в глобальную систему координат. \en Get the object by its path in the model and get the matrix of transformation of the object to the global coordinate system. - virtual const MbItem * GetItemByPath( const MbPath & path, size_t ind, MbMatrix3D & from, size_t currInd = 0 ) const; - - // \ru Найти объект по геометрическому объекту (MbSpaceItem). \en Find the object by a geometric object (MbSpaceItem). - virtual const MbItem * FindItem( const MbSpaceItem * s, MbPath & path, MbMatrix3D & from ) const; - // \ru Найти объект по геометрическому объекту (MbPlaneItem). \en Find the object by a geometric object (MbSpaceItem). - virtual const MbItem * FindItem( const MbPlaneItem * s, MbPath & path, MbMatrix3D & from ) const; - // \ru Найти объект и матрицу его преобразования в глобальную систему координат. \en Find the object and the matrix of its transformation to the global coordinate system. - virtual const MbItem * FindItem( const MbItem * s, MbPath & path, MbMatrix3D & from ) const; - // \ru Дать объект с заданным именем и матрицу его преобразования в глобальную систему координат. \en Get the object with the specified name and the matrix of its transformation to the global coordinate system. - virtual const MbItem * GetItemByName( SimpleName n, MbPath & path, MbMatrix3D & from ) const; - - // \ru Преобразовать согласно матрице c использованием регистратора содержимый объект, если он селектирован. \en Transform the contained object according to the matrix using the registrator if the object selected. - virtual void TransformSelected( const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); - // \ru Сдвинуть вдоль вектора с использованием регистратора содержимый объект, если он селектирован. \en Translate the contained object along the vector according to the matrix using the registrator if the object selected. - virtual void MoveSelected( const MbVector3D & to, MbRegTransform * iReg = NULL ); - // \ru Повернуть вокруг оси на заданный угол с использованием регистратора содержимый объект, если он селектирован. \en Translate the contained object about the axis according to the matrix using the registrator if the object selected. - virtual void RotateSelected( const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); - - /// \ru Дать матрицу преобразования из локальной системы объекта. \en Get transform matrix from local coordinate system of object. - virtual bool GetMatrixFrom( MbMatrix3D & from ) const; - /// \ru Дать матрицу преобразования в локальную систему объекта. \en Get transform matrix into local coordinate system of object. - virtual bool GetMatrixInto( MbMatrix3D & into ) const; - - /** \ru \name Функции вставки объекта. - \en \name Functions of the object instance. - \{ */ - /// \ru Выдать геометрический объект. \en Get the geometric object. - const MbItem * GetItem() const { return item; } - /// \ru Выдать геометрический объект для модификации. \en Get the geometric object for modification. - MbItem * SetItem() { return item; } - /// \ru Установить другой геометрический объект. \en Set another geometric object. - void SetItem( MbItem * init ); - - /** \brief \ru Заменить объект. - \en Replace an item. \~ - \details \ru Заменить объект новым. - \en Replace an item by a new one. \~ - \param[in] item - \ru Заменяемый объект. - \en An item to be replaced. \~ - \param[in] newItem - \ru Новый объект. - \en A new item. \~ - \return \ru Возвращает true, если замена была выполнена. - \en Returns true if the replacement has been performed. \~ - */ - bool ReplaceItem( const MbItem & item, MbItem & newItem, bool saveName = false ); - - /// \ru Выдать количество граней. \en Get the number of faces. - size_t GetFacesCount() const; - /// \ru Заполнить контейнер гранями тела. \en Fill container by solid faces. - template - void GetFacesSet( FacesVector & faces ) const; - /// \ru Выдать систему координат объекта. \en Get the coordinate system of an item. - const MbPlacement3D & GetPlacement() const { return place; } - /// \ru Выдать систему координат объекта для редактирования. \en Get the coordinate system of an assembly item for editing. - MbPlacement3D & SetPlacement() { return place; } - - /** \} */ - -private: - // Найти объект по геометрическому объекту - template - const MbItem * _FindItem( const _ItemType * s, MbPath & path, MbMatrix3D & from ) const; - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbInstance ) ; - OBVIOUS_PRIVATE_COPY( MbInstance ); -}; - -IMPL_PERSISTENT_OPS( MbInstance ) - - -#endif +////////////////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Вставка объекта. + \en Instance of object. \~ + \details \ru Вставка объекта геометрической модели MbItem в локальной системе координат MbPlacement3D + позволяет накладывать геометрические ограничения на объект в сборке и позиционоровать объект. + Вставка не может содержать другую вставку. \n + \en The instance of geometric model MbItem object in local coordinate system MbPlacement3D + allows to define constraints for an object in the assembly and to locate the object. + The instance cannot contain another instance. \n \~ +*/ +////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef __INSTANCE_H +#define __INSTANCE_H + +#include +#include + + +class MATH_CLASS MbSolid; +class MATH_CLASS MbInstance; +class MATH_CLASS MbAssembly; + +namespace c3d // namespace C3D +{ +typedef SPtr InstanceSPtr; +typedef SPtr ConstInstanceSPtr; + +typedef std::vector InstancesVector; +typedef std::vector ConstInstancesVector; + +typedef std::vector InstancesSPtrVector; +typedef std::vector ConstInstancesSPtrVector; +} + + +//---------------------------------------------------------------------------------------- +/** \brief \ru Вставка объекта. + \en Instance of object. \~ + \details \ru Вставка объекта геометрической модели MbItem в локальной системе координат MbPlacement3D + позволяет накладывать геометрические ограничения на объект в сборке и позиционоровать объект. + Вставка не может содержать другую вставку. \n + \en The instance of geometric model MbItem object in local coordinate system MbPlacement3D + allows to define constraints for an object in the assembly and to locate the object. + The instance cannot contain another instance. \n \~ + \ingroup Model_Items +*/ +// --- +class MATH_CLASS MbInstance : public MbItem +{ + MbPlacement3D place; ///< \ru Локальная система координат объекта. \en Local coordinate system of the object. + SPtr item; ///< \ru Геометрический объект. \en A geometric object. + +protected : + /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. + explicit MbInstance( const MbInstance &, MbRegDuplicate * ); + +public : + /// \ru Конструктор по объекту и его системе координат. \en The constructor by an object and its coordinate system. + MbInstance( MbItem &, const MbPlacement3D & ); + /// \ru Деструктор. \en Destructor. + virtual ~MbInstance(); + +public : + VISITING_CLASS( MbInstance ); + + // \ru Общие функции геометрического объекта. \en Common functions of a geometric object. + virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en An object type. + virtual MbSpaceItem & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Создать копию. \en Create a copy. + virtual void Transform( const MbMatrix3D &, MbRegTransform * iReg = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. + virtual void Move ( const MbVector3D &, MbRegTransform * iReg = NULL ); // \ru Сдвинуть вдоль вектора. \en Move along a vector. + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * iReg = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Whether the objects are equal? + virtual bool IsSimilar( const MbSpaceItem & init ) const; // \ru Являются ли объекты подобными? \en Whether the objects are similar? + virtual bool SetEqual ( const MbSpaceItem & init ); // \ru Сделать объекты равными. \en Make the objects equal. + virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate distance to point. + virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Добавь свой габарит в куб. \en Add own bounding box to the bounding box. + virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate the bounding box in a local coordinate system. + virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. + + virtual MbProperty & CreateProperty( MbePrompt n ) const; // \ru Создать собственное свойство. \en Create own property. + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & properties ); // \ru Установить свойства объекта. \en Set properties of the object. + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + // \ru Получить систему координат объекта. \en Get the coordinate system of an item. + virtual bool GetPlacement( MbPlacement3D & ) const; + // \ru Установить систему координат объекта. \en Set the coordinate system of an item. + virtual bool SetPlacement( const MbPlacement3D & ); + + // \ru Перестроить объект по журналу построения. \en Reconstruct object according to the history tree. + virtual bool RebuildItem( MbeCopyMode sameShell, RPArray * items, IProgressIndicator * progInd ); + + // \ru Создать полигональный объект - упрощенную копию данного объекта. \en Create a polygonal object - a simplified copy of the given object. + virtual MbItem * CreateMesh( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * iReg ) const; + // \ru Добавить полигонную сетку объекта. \en Add a polygon mesh of the object. + virtual bool AddYourMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; + // \ru Разрезать полигональный объект одной или двумя параллельными плоскостями. \en Cut the polygonal object by one or two parallel planes. + virtual MbItem * CutMesh( const MbPlacement3D & cutPlace, double distance ) const; + // \ru Найти ближайший объект или имя ближайшего объекта. \en Find the closest object or its name. + virtual bool NearestMesh( MbeSpaceType sType, MbeTopologyType tType, MbePlaneType pType, + const MbAxis3D & axis, double maxDistance, bool gridPriority, double & t, double & dMin, + MbItem *& find, SimpleName & findName, + MbRefItem *& element, SimpleName & elementName, + MbPath & path, MbMatrix3D & from ) const; + // \ru Дать все объекты указанного типа. \en Get all objects by type. \~ + virtual bool GetItems( MbeSpaceType type, const MbMatrix3D & from, + RPArray & items, SArray & matrs ); + // \ru Дать все уникальные объекты указанного типа. \en Get all unique objects by type . \~ + virtual bool GetUniqItems( MbeSpaceType type, CSSArray & items ) const; + // \ru Дать объект по его пути положения в модели и матрицу преобразования объекта в глобальную систему координат. \en Get the object by its path in the model and get the matrix of transformation of the object to the global coordinate system. + virtual const MbItem * GetItemByPath( const MbPath & path, size_t ind, MbMatrix3D & from, size_t currInd = 0 ) const; + + // \ru Найти объект по геометрическому объекту (MbSpaceItem). \en Find the object by a geometric object (MbSpaceItem). + virtual const MbItem * FindItem( const MbSpaceItem * s, MbPath & path, MbMatrix3D & from ) const; + // \ru Найти объект по геометрическому объекту (MbPlaneItem). \en Find the object by a geometric object (MbSpaceItem). + virtual const MbItem * FindItem( const MbPlaneItem * s, MbPath & path, MbMatrix3D & from ) const; + // \ru Найти объект и матрицу его преобразования в глобальную систему координат. \en Find the object and the matrix of its transformation to the global coordinate system. + virtual const MbItem * FindItem( const MbItem * s, MbPath & path, MbMatrix3D & from ) const; + // \ru Дать объект с заданным именем и матрицу его преобразования в глобальную систему координат. \en Get the object with the specified name and the matrix of its transformation to the global coordinate system. + virtual const MbItem * GetItemByName( SimpleName n, MbPath & path, MbMatrix3D & from ) const; + + // \ru Преобразовать согласно матрице c использованием регистратора содержимый объект, если он селектирован. \en Transform the contained object according to the matrix using the registrator if the object selected. + virtual void TransformSelected( const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); + // \ru Сдвинуть вдоль вектора с использованием регистратора содержимый объект, если он селектирован. \en Translate the contained object along the vector according to the matrix using the registrator if the object selected. + virtual void MoveSelected( const MbVector3D & to, MbRegTransform * iReg = NULL ); + // \ru Повернуть вокруг оси на заданный угол с использованием регистратора содержимый объект, если он селектирован. \en Translate the contained object about the axis according to the matrix using the registrator if the object selected. + virtual void RotateSelected( const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); + + /// \ru Дать матрицу преобразования из локальной системы объекта. \en Get transform matrix from local coordinate system of object. + virtual bool GetMatrixFrom( MbMatrix3D & from ) const; + /// \ru Дать матрицу преобразования в локальную систему объекта. \en Get transform matrix into local coordinate system of object. + virtual bool GetMatrixInto( MbMatrix3D & into ) const; + + /** \ru \name Функции вставки объекта. + \en \name Functions of the object instance. + \{ */ + /// \ru Выдать геометрический объект. \en Get the geometric object. + const MbItem * GetItem() const { return item; } + /// \ru Выдать геометрический объект для модификации. \en Get the geometric object for modification. + MbItem * SetItem() { return item; } + /// \ru Установить другой геометрический объект. \en Set another geometric object. + void SetItem( MbItem * init ); + + /** \brief \ru Заменить объект. + \en Replace an item. \~ + \details \ru Заменить объект новым. + \en Replace an item by a new one. \~ + \param[in] item - \ru Заменяемый объект. + \en An item to be replaced. \~ + \param[in] newItem - \ru Новый объект. + \en A new item. \~ + \return \ru Возвращает true, если замена была выполнена. + \en Returns true if the replacement has been performed. \~ + */ + bool ReplaceItem( const MbItem & item, MbItem & newItem, bool saveName = false ); + + /// \ru Выдать количество граней. \en Get the number of faces. + size_t GetFacesCount() const; + /// \ru Заполнить контейнер гранями тела. \en Fill container by solid faces. + template + void GetFacesSet( FacesVector & faces ) const; + /// \ru Выдать систему координат объекта. \en Get the coordinate system of an item. + const MbPlacement3D & GetPlacement() const { return place; } + /// \ru Выдать систему координат объекта для редактирования. \en Get the coordinate system of an assembly item for editing. + MbPlacement3D & SetPlacement() { return place; } + + /** \} */ + +private: + // Найти объект по геометрическому объекту + template + const MbItem * _FindItem( const _ItemType * s, MbPath & path, MbMatrix3D & from ) const; + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbInstance ) +OBVIOUS_PRIVATE_COPY( MbInstance ) +}; + +IMPL_PERSISTENT_OPS( MbInstance ) + + +#endif diff --git a/C3d/Include/io_memory_buffer.h b/C3d/Include/io_memory_buffer.h index ececdfb..5a19467 100644 --- a/C3d/Include/io_memory_buffer.h +++ b/C3d/Include/io_memory_buffer.h @@ -25,13 +25,6 @@ class reader; class writer; -//------------------------------------------------------------------------------ -/// \ru Размер кластера по умолчанию. \en Cluster size by default. \~ \ingroup Base_Tools_IO -//--- -const uint16 DEFCLSIZE = 0x1000; -// \ru САА K13 12.5.2011 Увеличил размер кластера по умолчанию - параллельное перестроение видов. \en САА K13 12.5.2011 Increased cluster size by default - parallel rebuilding of views. -// \ru САА K13 12.5.2011 const uint16 DEFCLSIZE = 256; \en САА K13 12.5.2011 const uint16 DEFCLSIZE = 256; - //------------------------------------------------------------------------------ /** \brief \ru Потоковый буфер памяти. @@ -103,7 +96,7 @@ public: /// The meaning of addSize depends on the initial value of the parameter 'memory': /// if memory != 0 (i.e. the memory is already allocated), then addSize should be equal to memory size (addSize >= getMemLen() !!!). /// if memory == 0, then addSize defines a number of bytes to be added (and zeroed) at the beginning when allocating memory. - size_t toMemory( const char *& memory, size_t addSize = 0 ) const; + size_t toMemory( const char *& memory, size_t addSize = 0 ) const; /// \ru Прочитать из непрерывной памяти. \en Read from the contiguous memory. bool fromMemory( const char * memory ); /// \ru Вычислить необходимую длину непрерывного куска памяти для буфера. \en Compute the necessary length of the contiguous memory block for a buffer. diff --git a/C3d/Include/io_tape.h b/C3d/Include/io_tape.h index b0cc193..9639de6 100644 --- a/C3d/Include/io_tape.h +++ b/C3d/Include/io_tape.h @@ -1252,40 +1252,7 @@ void WriteVBase( writer & out, const Base * base ) \ingroup Base_Tools_IO */ // --- -#ifdef __BORLANDC__ -#define IMPL_PERSISTENT_OPS( Class ) \ - inline reader & CALL_DECLARATION operator >> ( reader & in, Class & ref ) { \ - in.readObject( dynamic_cast(&ref) ); \ - return in; \ - } \ - inline reader & CALL_DECLARATION operator >> ( reader & in, Class *& ptr ) { \ - ptr = dynamic_cast( in.readObjectPointer() ); \ - return in; \ - } \ - inline reader & CALL_DECLARATION operator >> ( reader & in, const Class *& ptr ) \ - { \ - ptr = dynamic_cast( in.readObjectPointer() ); \ - return in; \ - } \ - inline writer & CALL_DECLARATION operator << ( writer & out, const Class & ref ) { \ - out.writeObject( dynamic_cast(&ref) ); \ - return out; \ - } \ - inline writer & CALL_DECLARATION operator << ( writer & out, const Class * ptr ) { \ - out.writeObjectPointer( dynamic_cast(ptr) ); \ - return out; \ - } \ - inline writer & CALL_DECLARATION operator << ( writer & out, Class & ref ) { \ - out.writeObject( dynamic_cast(&ref) ); \ - return out; \ - } \ - inline writer & CALL_DECLARATION operator << ( writer& out, Class * ptr ) { \ - out.writeObjectPointer( dynamic_cast(ptr) ); \ - return out; \ - } -#else #define IMPL_PERSISTENT_OPS( Class ) -#endif //---------------------------------------------------------------------------------------- /** @@ -1492,7 +1459,21 @@ ClassDescriptor TapeClassForNewObjects::GetPackedClassNameForWrite( long version // \ru или ptr->operator Class*()->F(); \n \en or ptr->operator Class*()->F(); \n // \ru Для ссылок так же. \en Similarly for references. // --- - #define DECLARE_NEW_DELETE_CLASS( Class ) \ + #define DECLARE_NEW_DELETE_CLASS( Class ) + + //-------------------------------------------------------------------------------------- + /// \ru Реализация функций new, delete и операторов доступа. \en Implementation of functions new, delete and access operators. \~ \ingroup Base_Tools_IO + // --- + #define IMP_PERSISTENT_NEW_DELETE_CLASS( Class ) + + //-------------------------------------------------------------------------------------- + /// \ru Объявление операторов new и delete, обеспечивающих последовательное обращение + /// к функциям выделения/освобождения памяти из разных потоков. + /// \en Declaration of new and delete operators which provide sequential access + /// to the allocation/deallocation functions from different threads. \~ + /// \ingroup Base_Tools_IO + // --- + #define DECLARE_NEW_DELETE_CLASS_EX( Class ) \ public: \ void * operator new ( size_t ); \ void operator delete ( void *, size_t ); \ @@ -1500,9 +1481,13 @@ ClassDescriptor TapeClassForNewObjects::GetPackedClassNameForWrite( long version void operator delete [] ( void * ); //-------------------------------------------------------------------------------------- - /// \ru Реализация функций new, delete и операторов доступа. \en Implementation of functions new, delete and access operators. \~ \ingroup Base_Tools_IO + /// \ru Реализация операторов new и delete, обеспечивающих последовательное обращение + /// к функциям выделения/освобождения памяти из разных потоков. + /// \en Implementation of new and delete operators which provide sequential access + /// to the allocation/deallocation functions from different threads. \~ + /// \ingroup Base_Tools_IO // --- - #define IMP_PERSISTENT_NEW_DELETE_CLASS( Class ) \ + #define IMP_PERSISTENT_NEW_DELETE_CLASS_EX( Class ) \ void * Class::operator new( size_t size ) { \ return ::Allocate( size, typeid(Class).name() ); } \ void Class::operator delete ( void *ptr, size_t size ) { \ @@ -1513,24 +1498,6 @@ ClassDescriptor TapeClassForNewObjects::GetPackedClassNameForWrite( long version void Class::operator delete[] ( void *ptr ) { \ ::FreeArray( ptr, typeid(Class[]).name() ); } - //-------------------------------------------------------------------------------------- - /// \ru Объявление операторов new и delete, обеспечивающих последовательное обращение - /// к функциям выделения/освобождения памяти из разных потоков. - /// \en Declaration of new and delete operators which provide sequential access - /// to the allocation/deallocation functions from different threads. \~ - /// \ingroup Base_Tools_IO - // --- - #define DECLARE_NEW_DELETE_CLASS_EX( Class ) - - //-------------------------------------------------------------------------------------- - /// \ru Реализация операторов new и delete, обеспечивающих последовательное обращение - /// к функциям выделения/освобождения памяти из разных потоков. - /// \en Implementation of new and delete operators which provide sequential access - /// to the allocation/deallocation functions from different threads. \~ - /// \ingroup Base_Tools_IO - // --- - #define IMP_PERSISTENT_NEW_DELETE_CLASS_EX( Class ) - #else // __DEBUG_MEMORY_ALLOCATE_FREE_ //-------------------------------------------------------------------------------------- /// \ru Объявление функций new, delete и операторов доступа. \en Declaration of functions new, delete and access operators. \~ \ingroup Base_Tools_IO @@ -1663,8 +1630,6 @@ ClassDescriptor TapeClassForNewObjects::GetPackedClassNameForWrite( long version \ingroup Base_Tools_IO */ // --- -#ifndef __BORLANDC__ - #define DECLARE_PERSISTENT_CLASS( Class ) \ DECLARE_PERSISTENT_FUNCS( Class ); \ DECLARE_PERSISTENT_OPS( Class ); \ @@ -1672,17 +1637,6 @@ ClassDescriptor TapeClassForNewObjects::GetPackedClassNameForWrite( long version DECLARE_NEW_DELETE_CLASS( Class ); \ DECLARE_CLASS_DESC_FUNC(Class) -#else // __BORLAND__ - -#define DECLARE_PERSISTENT_CLASS( Class ) \ - DECLARE_PERSISTENT_FUNCS( Class ); \ - DECLARE_PERSISTENT_OPS_B( Class ); \ - DECLARE_PERSISTENT_CTOR( Class ); \ - DECLARE_NEW_DELETE_CLASS( Class ); \ - DECLARE_CLASS_DESC_FUNC(Class) - -#endif // __BORLAND__ - /** \brief \ru Аналог макроса DECLARE_PERSISTENT_CLASS с возможностью перегрузки операторов new/delete, обеспечивающий последовательное обращение к функциям @@ -1958,7 +1912,6 @@ ClassDescriptor TapeClassForNewObjects::GetPackedClassNameForWrite( long version // \ru Выдаваемые значения \en Returned values // MS Visual C++ 6.0 ... 2010: "class CLASS_A" // gcc (Linux): "7CLASS_A" -// BORLAND C++ 5.0: "CLASS_A" // Embarcadero C++ 7.20 for Win32 "$CLASS_A" // \ru Интересующая нас функция должна выдавать "CLASS_A" \en The desired function must write "CLASS_A" // --- @@ -1971,11 +1924,6 @@ inline const char * pureName( const char * name ) ptrdiff_t i = strlen(name) - 1; for ( ; i >= 0 && name[i] != ' '; i-- ); return ((i >= 0) && (name[i] == ' ')) ? &(name[i+1]) : name; -#elif __BORLANDC__ - // \ru убираем "$" в начале строки \en remove "$" at the beginning of the string - for ( size_t i = 0, c = strlen(name); i < c; i++ ) - if ( name[i] != '$' ) - return &(name[i]); #else // _MSC_VER // \ru убираем длину имени в начале строки \en remove the name length at the beginning of the string for ( size_t i = 0, c = strlen(name); i < c; i++ ) @@ -2098,9 +2046,9 @@ inline reader & __readWchar( reader & ps, TCHAR * & s ) #if __SIZEOF_WCHAR_T__ == 2 // sizeof(wchar_t) == sizeof(uint16) s = wcsnewmbs( (const wchar_t*)readBuf ); // \ru Конвертировать WCHAR в CHAR строку. \en Convert WCHAR-string to CHAR-string. #else // sizeof(wchar_t) == sizeof(uint32) - uint32* readBuf32 = Utf16ToUcs4(readBuf); // \ru Конвертировать UTF-16 в WCHAR \en Convert from UTF-16 to WCHAR - s = wcsnewmbs( (const wchar_t*)readBuf32 ); // \ru Конвертировать WCHAR в CHAR строку. \en Convert WCHAR-string to CHAR-string. - delete [] readBuf32; + uint32* readBuf32 = Utf16ToUcs4(readBuf); // \ru Конвертировать UTF-16 в WCHAR \en Convert from UTF-16 to WCHAR + s = wcsnewmbs( (const wchar_t*)readBuf32 ); // \ru Конвертировать WCHAR в CHAR строку. \en Convert WCHAR-string to CHAR-string. + delete [] readBuf32; #endif delete [] readBuf; #endif // _UNICODE @@ -3000,7 +2948,7 @@ inline size_t ReadCOUNT ( void * in, VERSION version ) MATH_FUNC (ClassDescriptor) GetPackedClassName( const ClassDescriptor &, const VersionContainer & ver ); -#ifdef STANDARD_C11 +#ifdef C3D_STANDARD_CXX_11_PARTIAL //---------------------------------------------------------------------------------------- /** Добавить новое соответствие значения хэша записанного в поток упакованному имени класса @@ -3012,44 +2960,7 @@ MATH_FUNC (ClassDescriptor) GetPackedClassName( const ClassDescriptor &, const V */ // --- MATH_FUNC (void) AddPackedClassNameForVersion( const ClassDescriptor & newClassName, const ClassDescriptor & oldClassName, uint appIndex, VERSION lowVersion, VERSION highVersion ); -#endif //STANDARD_C11 - - -//////////////////////////////////////////////////////////////////////////////// -// -// \ru для получения дампа значений Hash Value в файле hash_GRP.txt(hash_VIE.txt) \en for getting dump of Hash Value values in file hash_GRP.txt(hash_VIE.txt) -// \ru необходимо раскоментарить этот комментарий \en this comment should be uncommented -// -//////////////////////////////////////////////////////////////////////////////// -#ifdef C3D_DEBUG -//#define HASH_DOCUMENTATION -#endif - -#ifdef HASH_DOCUMENTATION - -#include - -inline void HashDocumentationOut( uint16 hashValue, const char * name, bool unique ) -{ - static uint st_calls = 0; - - const char * outName = "C:\\Logs\\hash_GRP.txt"; - std::ofstream out( outName, st_calls ? (std::ios::out|std::ios::app) : std::ios::out ); - - std::string sname( name ); - - out << _T("\t") << uint16(hashValue) << _T("\t\t") << sname.c_str(); - if ( !unique ) - out << _T("\tNOT UNIQUE!!!"); - out << _T("\n"); - - st_calls++; -} - -#endif // HASH_DOCUMENTATION - -#undef HASH_DOCUMENTATION - +#endif // C3D_STANDARD_CXX_11_PARTIAL #ifndef SIMPLENAME_AS_CLASS @@ -3069,7 +2980,8 @@ inline SimpleName ReadSimpleName( reader & in ) { SimpleName s = 0; in >> s; ret //---------------------------------------------------------------------------------------- /// \ru Запись простого имени. \en Writing of a simple name. \~ \ingroup Base_Tools_IO // --- -inline void WriteSimpleName( writer & out, const SimpleName & s ) +inline +void WriteSimpleName( writer & out, const SimpleName & s ) { size_t sn = s; WriteCOUNT( out, sn ); @@ -3078,7 +2990,8 @@ inline void WriteSimpleName( writer & out, const SimpleName & s ) //---------------------------------------------------------------------------------------- /// \ru Чтение простого имени. \en Reading of a simple name. \~ \ingroup Base_Tools_IO // --- -inline SimpleName ReadSimpleName( reader & in ) +inline +SimpleName ReadSimpleName( reader & in ) { size_t sn = ReadCOUNT( in ); return SimpleName(sn); @@ -3086,6 +2999,8 @@ inline SimpleName ReadSimpleName( reader & in ) #endif // SIMPLENAME_AS_CLASS +namespace c3d // namespace C3D +{ //---------------------------------------------------------------------------------------- /// \ru Оператор записи хэша. \en Operator of writing hash. \~ \ingroup Base_Tools_IO @@ -3093,7 +3008,7 @@ inline SimpleName ReadSimpleName( reader & in ) inline writer & operator << ( writer & out, const StrHash & strHash ) { // \ru Нельзя допускать хеш с неопределенным типом и с определенным значением \en Hash with undefined type and with specified value cannot be allowed - PRECONDITION( !(IsGoodSimpleName(strHash.m_val) && strHash.m_type == StrHash::htp_undef) ); + PRECONDITION( !(IsGoodSimpleName(strHash.m_val) && strHash.m_type == c3d::StrHash::htp_undef) ); WriteSimpleName( out, strHash.m_val ); @@ -3102,11 +3017,10 @@ inline writer & operator << ( writer & out, const StrHash & strHash ) return out; } - //---------------------------------------------------------------------------------------- /// \ru Оператор чтения хэша. \en Operator of hash reading. \~ \ingroup Base_Tools_IO //--- -inline reader & operator >> ( reader& in, StrHash & strHash ) +inline reader & operator >> ( reader & in, c3d::StrHash & strHash ) { strHash.m_val = ReadSimpleName( in ); @@ -3114,16 +3028,18 @@ inline reader & operator >> ( reader& in, StrHash & strHash ) if ( in.MathVersion() >= 0x0A001005L /*\ru введена запись типа хеша \en record of hash type is set */) in >> strHash.m_type; else - strHash.m_type = StrHash::htp_wchar; // \ru !!! Переходный период, могут быть разные \en !!! Period of transition, may be different + strHash.m_type = c3d::StrHash::htp_wchar; // \ru !!! Переходный период, могут быть разные \en !!! Period of transition, may be different else - strHash.m_type = StrHash::htp_char; + strHash.m_type = c3d::StrHash::htp_char; // \ru Нельзя допускать хеш с неопределенным типом и с определенным значением \en Hash with undefined type and with specified value cannot be allowed - PRECONDITION( !(IsGoodSimpleName(strHash.m_val) && strHash.m_type == StrHash::htp_undef) ); + PRECONDITION( !(IsGoodSimpleName(strHash.m_val) && strHash.m_type == c3d::StrHash::htp_undef) ); return in; } +} // namespace C3D + //---------------------------------------------------------------------------------------- /// \ru Запись строки в поток. \en Writing a string to the stream. \~ \ingroup Base_Tools_IO @@ -3372,3 +3288,4 @@ inline size_t ReadClusterBody( void * in, VERSION version, Cluster & obj, uint16 } #endif // __IO_TAPE_H + diff --git a/C3d/Include/io_tree.h b/C3d/Include/io_tree.h index ad57c61..581989d 100644 --- a/C3d/Include/io_tree.h +++ b/C3d/Include/io_tree.h @@ -1,301 +1,301 @@ -//////////////////////////////////////////////////////////////////////////////// -/** -\file -\brief \ru Дерево геометрической модели. - \en Tree of geometric model. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __IO_TREE_H -#define __IO_TREE_H - -#include -#include -#include -#include - -class reader; -class writer; -struct ClusterReference; -class TapeBase; - -namespace c3d // namespace C3D -{ - -//---------------------------------------------------------------------------------------- -// \ru Предварительное объявление структуры для данных узла дерева. -// \en The forward declaration of a structure for the tree node data. \~ -// --- -struct MbItemData; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Узел дерева модели. - \en Model tree node. \~ - \details \ru Узел дерева модели (может иметь несколько потомков). - Умеет записывать в поток и читаться из потока. \n - \en Model tree node (can have several children). - Can be written to a stream and read from a stream. \n \~ - \ingroup Base_Tools_IO -*/ -// --- -class IModelTreeNode -{ -protected: - // \ru Непосредственные потомки узла. \en The immediate children of the node. - std::set m_children; - // \ru Непосредственные предки узла. \en The immediate parents of the node. - std::set m_parents; -public: - IModelTreeNode() {} - virtual ~IModelTreeNode() {} - - // \ru Доступ к непосредственным предкам узла. \en Access to the immediate node parents. - std::set& GetParents() { return m_parents; } - const std::set& GetParents() const { return m_parents; } - - // \ru Доступ к непосредственным потомкам узла. \en Access to the immediate node children. - std::set& GetChildren() { return m_children; } - const std::set& GetChildren() const { return m_children; } - - // \ru Добавить предка. \en Add a parent. - void AddParent( IModelTreeNode* parent ) { if (parent) m_parents.insert(parent); } - - // \ru Добавить потомка. \en Add a child. - void AddChild( IModelTreeNode* child ) { if (child) { m_children.insert(child); child->AddParent(this); } } - - // \ru Доступ к данным узла. \en Access to the node data. - virtual MbItemData& GetData() = 0; - virtual const MbItemData& GetData() const = 0; - - // \ru Доступ к позиции чтения/записи узла. \en Access to the node read/write position. - virtual ClusterReference& GetPosition() = 0; - virtual const ClusterReference& GetPosition() const = 0; - - // \ru Признак частичного или полного чтения узла. - // При чтении объекта может возникнуть необходимость чтения некоторых данных его родителя. - // В этом случае объект родителя читается частично и имеет соответствующий флаг. - // \en Indicator of partial reading of the current node. - // While reading an object there can be a need to read some data from its parent. - // In this case the parent object is read partially and has a corresponding flag. - // \ru Узнать, читать ли только часть узла. - // \en Check whether to read the node partially. - virtual bool PartialRead() const = 0; - // \ru Установить признак частичного или полного чтения узла. - // \en Set indication of full or partial node reading. - virtual void SetPartialRead ( bool partial ) const = 0; - - // \ru Записать узел. \en Write the node. - virtual writer & operator >> ( writer & ) = 0; - // \ru Прочитать узел. \en Read the node. - virtual reader & operator << ( reader & ) = 0; - - // \ru Операторы для записи узла дерева поток в xml формате. \en Operators to output tree node to a stream in xml format. - friend MATH_FUNC( c3d::t_ofstream & ) operator << ( c3d::t_ofstream& file, IModelTreeNode& node ); - friend MATH_FUNC( c3d::t_ofstream & ) operator << ( c3d::t_ofstream& file, const IModelTreeNode& node ); - -OBVIOUS_PRIVATE_COPY(IModelTreeNode) -}; - -//---------------------------------------------------------------------------------------- -// \ru Предварительное объявление Дерева Исполнений. -// \en The forward declaration of Embodiments Tree. \~ -// --- -class IEmbodimentTree; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Обобщенное дерево модели. - \en Generic model tree. \~ - \details \ru Обобщенное дерево модели (может иметь несколько корней). - Умеет записываться в поток и читаться из потока. \n - Дерево Модели отражает иерархию стандартной модели (объекта MbModel). - \en Generic model tree (can have several roots). - Can be written to a stream and read from a stream. \n \~ - Model Tree presents a hierarchy of a standard model (MbModel object). - \ingroup Base_Tools_IO -*/ -// --- -class IModelTree -{ -public: - enum TreeType - { - mtt_Model, // \ru Дерево содержит модель. \en Tree contains model. - mtt_Embodiment // \ru Дерево содержит исполнения. \en Tree contains embodiments. - }; - - // \ru Тип, представляющий листовой узел с ветвью дерева, ведущей к нему, начиная с корневого узла дерева. - // \en A type which represents a leaf node with the tree branch, leading to it, starting from the root of the tree. - typedef std::pair > NodeBranch; - - // \ru Тип функции для выбора узлов дерева по фильтрам. - // \en The type of a function for selecting tree nodes by filters. - typedef bool ( CALL_DECLARATION * FilterNodesFunc ) ( std::vector&, const std::vector&, const IModelTree* ); - - // \ru Тип функции для определения, нужно ли добавлять объект в дерево модели, и заполнения данных узла. - // \en The type of a function for determining, whether to add the object to the model tree, and filling the node data. - typedef bool ( CALL_DECLARATION *NodeToAddFunc ) ( const TapeBase* mem, MbItemData& data ); - -protected: - // \ru Тип дерева. \en Tree type. - TreeType m_type; - - // \ru Функция для определения, нужно ли добавлять объект в дерево модели, и заполнения данных узла. - // \en A function for determining, whether to add the object to the model tree, and filling the node data. - NodeToAddFunc m_nodeToAddFunc; - - // \ru Функция для выбора объектов по фильтрам. - // \en A function for for selecting objects by filters. - FilterNodesFunc m_filterFunc; - - // \ru Корни дерева. \en The tree roots. - std::vector m_roots; - -public: - - IModelTree() : m_type ( mtt_Model ), m_nodeToAddFunc( NULL ), m_filterFunc( NULL ) {} - virtual ~IModelTree() {} - - // \ru Выдать тип дерева. \en Get the tree type. - TreeType GetType() const { return m_type; } - // \ru Установить тип дерева. \en Set the tree type. - void SetType( TreeType type ) { m_type = type; } - - // \ru Построить дерево из узлов, выбранных по фильтрам. В случае дерева исполнений, функция работает с первым исполнением. - // \en Build a tree with nodes, selected by filters. In case of embodiment tree, the function works with the first embodiment. - virtual std_unique_ptr GetFilteredTree ( const std::vector& filters ) const = 0; - - // \ru Построить дерево по заданным узлам. Не применимо к дереву исполнений (в этом случае возвращает NULL). - // \en Build a tree for given nodes. Not applicable to embodiment tree (in this case, returns NULL). - virtual std_unique_ptr GetFilteredTree ( std::vector& nodes ) const = 0; - - // \ru Выдать указатель на дерево исполнений. Выдает NULL, если не применимо (нет исполнений). - // \en Get pointer to embodiments tree. Return NULL if not applicable (no embodiments). - virtual const IEmbodimentTree* GetEmbodimentsTree() const = 0; - - // \ru Добавить узел. \en Add a node. - virtual void AddNode ( const TapeBase* mem, const ClusterReference& ref ) = 0; - - // \ru Нотификация об окончании чтения/записи текущего узла. - // \en Notification about the end of current node writing/reading. - virtual void CloseNode( const TapeBase* mem ) = 0; - - // \ru Установить функцию для выбора геометрического объекта для добавления в дерево модели, и заполнения данных узла. - // \en Define a function for selecting a geometric object for adding to the model tree, and filling the node data. - virtual void SetNodeToAddFunction( NodeToAddFunc callback ) { if ( callback ) m_nodeToAddFunc = callback; } - - // \ru Установить функцию для выбора узлов из дерева модели. - // \en Define a function for selecting nodes from the model tree. - virtual void SetFilterFunction( FilterNodesFunc callback ) { if ( callback ) m_filterFunc = callback; } - - // \ru Записать дерево. \en Write the tree. - virtual writer & operator >> ( writer & ) = 0; - // \ru Прочитать дерево. \en Read the tree. - virtual reader & operator << ( reader & ) = 0; - - - // \ru Доступ к корням дерева. - // Узел дерева может быть рекурсивно вложен - // (например, Instance может содержать сборку, которая содержит другой Instance, ссылающийся на эту же сборку). - // \en Access to the tree roots. - // Tree node could be nested recursively - // (e.g. Instance can contain an Assembly which contains another Instance which includes this Assembly). - const std::vector& GetRoots() const { return m_roots; } - std::vector& GetRoots() { return m_roots; } - - // \ru Версия дерева. \en Tree version. - virtual VERSION GetVersion() = 0; - virtual void SetVersion( VERSION ) = 0; - - // \ru Построить поддерево из потомков заданного узла. - // \en Build a tree from children of a given node. - static EXPORT_DECLARATION std_unique_ptr GetSubtree ( const IModelTreeNode* node ); - - // \ru Получить значимые узлы поддерева с указанным корнем (исключив узлы, которые добавлены для восстановления иерархии дерева). - // Возвращает количество значимых узлов. - // \en Get significant nodes in a subtree with the given root (excluding nodes that were added to reconstruct the tree hierarchy). - // Return a number of significant nodes. - static EXPORT_DECLARATION size_t GetSubtreeSignificantNodes ( const c3d::IModelTreeNode* node, std::set& nodes ); - - // \ru Создать экземпляр дерева. \en Create a tree instance. - static EXPORT_DECLARATION IModelTree* CreateModelTree(); - - // \ru Операторы для записи дерева в поток в xml формате. \en Operators to output a tree to a stream in xml format. - friend MATH_FUNC( c3d::t_ofstream & ) operator << ( c3d::t_ofstream& file, IModelTree& tree ); - friend MATH_FUNC( c3d::t_ofstream & ) CALL_DECLARATION operator << ( c3d::t_ofstream& file, const IModelTree& tree ); - -OBVIOUS_PRIVATE_COPY(IModelTree) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Узел дерева исполнений. - \en Embodiments tree node. \~ - \details \ru Узел дерева исполнений (может иметь несколько потомков). - \en Embodiments tree node (can have several children). - \ingroup Base_Tools_IO -*/ -// --- -class IEmbodimentNode -{ -protected: - // \ru Непосредственные потомки узла. \en The immediate children of the node. - std::set m_children; -public: - IEmbodimentNode() {} - virtual ~IEmbodimentNode() { - for ( std::set::iterator i = m_children.begin(); i != m_children.end(); ++i ) - if ( *i != NULL ) delete *i; - } - - // \ru Построить поддерево модели, содержащееся в данном исполнении. - // \en Build a subtree of a model tree which is contained in a given embodiment. - virtual std_unique_ptr GetEmbodiment() const = 0; - - // \ru Выдать узел дерева модели, соответствующий данному исполнению. - // \en Get a model tree node which corresponds to a given embodiment. - virtual const IModelTreeNode * GetModelTreeNode() const = 0; - - // \ru Доступ к информации об исполнении. \en Access to the embodiment info. - virtual const MbItemData& GetEmbodimentData() const = 0; - - // \ru Доступ к непосредственным потомкам узла. \en Access to the immediate node children. - std::set& GetChildren() { return m_children; } - const std::set& GetChildren() const { return m_children; } - - // \ru Добавить потомка. \en Add a child. - void AddChild( IEmbodimentNode* child ) { if ( child ) m_children.insert( child ); } - - OBVIOUS_PRIVATE_COPY( IEmbodimentNode ) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Дерево Исполнений. - \en Embodiment Tree. \~ - \details \ru Дерево Исполнений отражает иерархию вариантов реализации модели (исполнений). - Каждый узел дерева представляет одно исполнение. - Подразумевается, что в геометрической модели исполнение хранится, - как объект MbAssembly с выставленным атрибутом типа at_Embodiment.\n - \en Embodiment Tree presents a hierarchy of variants of model implementation (embodiments). - Each node of the tree presents an embodiment. - It is assumed that in a geometric model an embodiment is stored as - MbAssembly object with an attribute of type at_Embodiment. \n \~ - \ingroup Base_Tools_IO -*/ -class IEmbodimentTree -{ -protected: - std::vector m_roots; - -public: - IEmbodimentTree() {} - virtual ~IEmbodimentTree() {} - - // \ru Доступ к корням дерева. \en Access to the tree roots. - const std::vector& GetRoots() const { return m_roots; } - std::vector& GetRoots() { return m_roots; } - -}; - -} //namespace c3d - -#endif // __IO_TREE_H +//////////////////////////////////////////////////////////////////////////////// +/** +\file +\brief \ru Дерево геометрической модели. + \en Tree of geometric model. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __IO_TREE_H +#define __IO_TREE_H + +#include +#include +#include +#include + +class reader; +class writer; +struct ClusterReference; +class TapeBase; + +namespace c3d // namespace C3D +{ + +//---------------------------------------------------------------------------------------- +// \ru Предварительное объявление структуры для данных узла дерева. +// \en The forward declaration of a structure for the tree node data. \~ +// --- +struct MbItemData; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Узел дерева модели. + \en Model tree node. \~ + \details \ru Узел дерева модели (может иметь несколько потомков). + Умеет записывать в поток и читаться из потока. \n + \en Model tree node (can have several children). + Can be written to a stream and read from a stream. \n \~ + \ingroup Base_Tools_IO +*/ +// --- +class IModelTreeNode +{ +protected: + // \ru Непосредственные потомки узла. \en The immediate children of the node. + std::set m_children; + // \ru Непосредственные предки узла. \en The immediate parents of the node. + std::set m_parents; +public: + IModelTreeNode() {} + virtual ~IModelTreeNode() {} + + // \ru Доступ к непосредственным предкам узла. \en Access to the immediate node parents. + std::set& GetParents() { return m_parents; } + const std::set& GetParents() const { return m_parents; } + + // \ru Доступ к непосредственным потомкам узла. \en Access to the immediate node children. + std::set& GetChildren() { return m_children; } + const std::set& GetChildren() const { return m_children; } + + // \ru Добавить предка. \en Add a parent. + void AddParent( IModelTreeNode* parent ) { if (parent) m_parents.insert(parent); } + + // \ru Добавить потомка. \en Add a child. + void AddChild( IModelTreeNode* child ) { if (child) { m_children.insert(child); child->AddParent(this); } } + + // \ru Доступ к данным узла. \en Access to the node data. + virtual MbItemData& GetData() = 0; + virtual const MbItemData& GetData() const = 0; + + // \ru Доступ к позиции чтения/записи узла. \en Access to the node read/write position. + virtual ClusterReference& GetPosition() = 0; + virtual const ClusterReference& GetPosition() const = 0; + + // \ru Признак частичного или полного чтения узла. + // При чтении объекта может возникнуть необходимость чтения некоторых данных его родителя. + // В этом случае объект родителя читается частично и имеет соответствующий флаг. + // \en Indicator of partial reading of the current node. + // While reading an object there can be a need to read some data from its parent. + // In this case the parent object is read partially and has a corresponding flag. + // \ru Узнать, читать ли только часть узла. + // \en Check whether to read the node partially. + virtual bool PartialRead() const = 0; + // \ru Установить признак частичного или полного чтения узла. + // \en Set indication of full or partial node reading. + virtual void SetPartialRead ( bool partial ) const = 0; + + // \ru Записать узел. \en Write the node. + virtual writer & operator >> ( writer & ) = 0; + // \ru Прочитать узел. \en Read the node. + virtual reader & operator << ( reader & ) = 0; + + // \ru Операторы для записи узла дерева поток в xml формате. \en Operators to output tree node to a stream in xml format. + friend MATH_FUNC( c3d::t_ofstream & ) operator << ( c3d::t_ofstream& file, IModelTreeNode& node ); + friend MATH_FUNC( c3d::t_ofstream & ) operator << ( c3d::t_ofstream& file, const IModelTreeNode& node ); + +OBVIOUS_PRIVATE_COPY(IModelTreeNode) +}; + +//---------------------------------------------------------------------------------------- +// \ru Предварительное объявление Дерева Исполнений. +// \en The forward declaration of Embodiments Tree. \~ +// --- +class IEmbodimentTree; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Обобщенное дерево модели. + \en Generic model tree. \~ + \details \ru Обобщенное дерево модели (может иметь несколько корней). + Умеет записываться в поток и читаться из потока. \n + Дерево Модели отражает иерархию стандартной модели (объекта MbModel). + \en Generic model tree (can have several roots). + Can be written to a stream and read from a stream. \n \~ + Model Tree presents a hierarchy of a standard model (MbModel object). + \ingroup Base_Tools_IO +*/ +// --- +class IModelTree +{ +public: + enum TreeType + { + mtt_Model, // \ru Дерево содержит модель. \en Tree contains model. + mtt_Embodiment // \ru Дерево содержит исполнения. \en Tree contains embodiments. + }; + + // \ru Тип, представляющий листовой узел с ветвью дерева, ведущей к нему, начиная с корневого узла дерева. + // \en A type which represents a leaf node with the tree branch, leading to it, starting from the root of the tree. + typedef std::pair > NodeBranch; + + // \ru Тип функции для выбора узлов дерева по фильтрам. + // \en The type of a function for selecting tree nodes by filters. + typedef bool ( CALL_DECLARATION * FilterNodesFunc ) ( std::vector&, const std::vector&, const IModelTree* ); + + // \ru Тип функции для определения, нужно ли добавлять объект в дерево модели, и заполнения данных узла. + // \en The type of a function for determining, whether to add the object to the model tree, and filling the node data. + typedef bool ( CALL_DECLARATION *NodeToAddFunc ) ( const TapeBase* mem, MbItemData& data ); + +protected: + // \ru Тип дерева. \en Tree type. + TreeType m_type; + + // \ru Функция для определения, нужно ли добавлять объект в дерево модели, и заполнения данных узла. + // \en A function for determining, whether to add the object to the model tree, and filling the node data. + NodeToAddFunc m_nodeToAddFunc; + + // \ru Функция для выбора объектов по фильтрам. + // \en A function for for selecting objects by filters. + FilterNodesFunc m_filterFunc; + + // \ru Корни дерева. \en The tree roots. + std::vector m_roots; + +public: + + IModelTree() : m_type ( mtt_Model ), m_nodeToAddFunc( NULL ), m_filterFunc( NULL ) {} + virtual ~IModelTree() {} + + // \ru Выдать тип дерева. \en Get the tree type. + TreeType GetType() const { return m_type; } + // \ru Установить тип дерева. \en Set the tree type. + void SetType( TreeType type ) { m_type = type; } + + // \ru Построить дерево из узлов, выбранных по фильтрам. В случае дерева исполнений, функция работает с первым исполнением. + // \en Build a tree with nodes, selected by filters. In case of embodiment tree, the function works with the first embodiment. + virtual std_unique_ptr GetFilteredTree ( const std::vector& filters ) const = 0; + + // \ru Построить дерево по заданным узлам. Не применимо к дереву исполнений (в этом случае возвращает NULL). + // \en Build a tree for given nodes. Not applicable to embodiment tree (in this case, returns NULL). + virtual std_unique_ptr GetFilteredTree ( std::vector& nodes ) const = 0; + + // \ru Выдать указатель на дерево исполнений. Выдает NULL, если не применимо (нет исполнений). + // \en Get pointer to embodiments tree. Return NULL if not applicable (no embodiments). + virtual const IEmbodimentTree* GetEmbodimentsTree() const = 0; + + // \ru Добавить узел. \en Add a node. + virtual void AddNode ( const TapeBase* mem, const ClusterReference& ref ) = 0; + + // \ru Нотификация об окончании чтения/записи текущего узла. + // \en Notification about the end of current node writing/reading. + virtual void CloseNode( const TapeBase* mem ) = 0; + + // \ru Установить функцию для выбора геометрического объекта для добавления в дерево модели, и заполнения данных узла. + // \en Define a function for selecting a geometric object for adding to the model tree, and filling the node data. + virtual void SetNodeToAddFunction( NodeToAddFunc callback ) { if ( callback ) m_nodeToAddFunc = callback; } + + // \ru Установить функцию для выбора узлов из дерева модели. + // \en Define a function for selecting nodes from the model tree. + virtual void SetFilterFunction( FilterNodesFunc callback ) { if ( callback ) m_filterFunc = callback; } + + // \ru Записать дерево. \en Write the tree. + virtual writer & operator >> ( writer & ) = 0; + // \ru Прочитать дерево. \en Read the tree. + virtual reader & operator << ( reader & ) = 0; + + + // \ru Доступ к корням дерева. + // Узел дерева может быть рекурсивно вложен + // (например, Instance может содержать сборку, которая содержит другой Instance, ссылающийся на эту же сборку). + // \en Access to the tree roots. + // Tree node could be nested recursively + // (e.g. Instance can contain an Assembly which contains another Instance which includes this Assembly). + const std::vector& GetRoots() const { return m_roots; } + std::vector& GetRoots() { return m_roots; } + + // \ru Версия дерева. \en Tree version. + virtual VERSION GetVersion() = 0; + virtual void SetVersion( VERSION ) = 0; + + // \ru Построить поддерево из потомков заданного узла. + // \en Build a tree from children of a given node. + static EXPORT_DECLARATION std_unique_ptr GetSubtree ( const IModelTreeNode* node ); + + // \ru Получить значимые узлы поддерева с указанным корнем (исключив узлы, которые добавлены для восстановления иерархии дерева). + // Возвращает количество значимых узлов. + // \en Get significant nodes in a subtree with the given root (excluding nodes that were added to reconstruct the tree hierarchy). + // Return a number of significant nodes. + static EXPORT_DECLARATION size_t GetSubtreeSignificantNodes ( const c3d::IModelTreeNode* node, std::set& nodes ); + + // \ru Создать экземпляр дерева. \en Create a tree instance. + static EXPORT_DECLARATION IModelTree* CreateModelTree(); + + // \ru Операторы для записи дерева в поток в xml формате. \en Operators to output a tree to a stream in xml format. + friend MATH_FUNC( c3d::t_ofstream & ) operator << ( c3d::t_ofstream& file, IModelTree& tree ); + friend MATH_FUNC( c3d::t_ofstream & ) CALL_DECLARATION operator << ( c3d::t_ofstream& file, const IModelTree& tree ); + +OBVIOUS_PRIVATE_COPY(IModelTree) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Узел дерева исполнений. + \en Embodiments tree node. \~ + \details \ru Узел дерева исполнений (может иметь несколько потомков). + \en Embodiments tree node (can have several children). + \ingroup Base_Tools_IO +*/ +// --- +class IEmbodimentNode +{ +protected: + // \ru Непосредственные потомки узла. \en The immediate children of the node. + std::set m_children; +public: + IEmbodimentNode() {} + virtual ~IEmbodimentNode() { + for ( std::set::iterator i = m_children.begin(); i != m_children.end(); ++i ) + if ( *i != NULL ) delete *i; + } + + // \ru Построить поддерево модели, содержащееся в данном исполнении. + // \en Build a subtree of a model tree which is contained in a given embodiment. + virtual std_unique_ptr GetEmbodiment() const = 0; + + // \ru Выдать узел дерева модели, соответствующий данному исполнению. + // \en Get a model tree node which corresponds to a given embodiment. + virtual const IModelTreeNode * GetModelTreeNode() const = 0; + + // \ru Доступ к информации об исполнении. \en Access to the embodiment info. + virtual const MbItemData& GetEmbodimentData() const = 0; + + // \ru Доступ к непосредственным потомкам узла. \en Access to the immediate node children. + std::set& GetChildren() { return m_children; } + const std::set& GetChildren() const { return m_children; } + + // \ru Добавить потомка. \en Add a child. + void AddChild( IEmbodimentNode* child ) { if ( child ) m_children.insert( child ); } + + OBVIOUS_PRIVATE_COPY( IEmbodimentNode ) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Дерево Исполнений. + \en Embodiment Tree. \~ + \details \ru Дерево Исполнений отражает иерархию вариантов реализации модели (исполнений). + Каждый узел дерева представляет одно исполнение. + Подразумевается, что в геометрической модели исполнение хранится, + как объект MbAssembly с выставленным атрибутом типа at_Embodiment.\n + \en Embodiment Tree presents a hierarchy of variants of model implementation (embodiments). + Each node of the tree presents an embodiment. + It is assumed that in a geometric model an embodiment is stored as + MbAssembly object with an attribute of type at_Embodiment. \n \~ + \ingroup Base_Tools_IO +*/ +class IEmbodimentTree +{ +protected: + std::vector m_roots; + +public: + IEmbodimentTree() {} + virtual ~IEmbodimentTree() {} + + // \ru Доступ к корням дерева. \en Access to the tree roots. + const std::vector& GetRoots() const { return m_roots; } + std::vector& GetRoots() { return m_roots; } + +}; + +} //namespace c3d + +#endif // __IO_TREE_H diff --git a/C3d/Include/io_version_container.h b/C3d/Include/io_version_container.h index 379869c..96cb5d4 100644 --- a/C3d/Include/io_version_container.h +++ b/C3d/Include/io_version_container.h @@ -1,75 +1,75 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Контейнер версий. - \en Container of versions. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __IO_VERSION_CONTAINER_H -#define __IO_VERSION_CONTAINER_H - - -#include -#include - - -//----------------------------------------------------------------------------- -/** \brief \ru Контейнер версий. - \en Container of versions. \~ - \details \ru Контейнер версий объектов. \n - \en Container of versions of objects. \n \~ - \ingroup Base_Tools_IO -*/ -// --- -class MATH_CLASS VersionContainer -{ -private: - // \ru static VersionContainer iobuf_defaultVersionCont; // Версия по умолчанию. \en static VersionContainer iobuf_defaultVersionCont; // Default version. - static VersionContainer & StaticVersionContainer(); -protected: - SArray m_self; ///< \ru Массив версий. \en Array of versions. -protected: - /// \ru Конструктор. \en Constructor. - VersionContainer( VERSION/*firstInit*/ ); -public: - /// \ru Конструктор. \en Constructor. - VersionContainer(); - /// \ru Конструктор копирования. \en Copy-constructor. - VersionContainer( const VersionContainer & other ); - /// \ru Деструктор. \en Destructor. - virtual ~VersionContainer(); - - /// \ru Получить экземпляр контейнера версий. \en Get the instance of version container. - static const VersionContainer & defaultVersionContainer(); - /// \ru Установить версии из другого контейнера \en Set versions from another container - static void SetDefaultVersion( const VersionContainer & v ); - - // \ru Вернуть главную версию (математического ядра). \en Return the main version (of the mathematical kernel). - VERSION GetMathVersion() const; - // \ru Вернуть дополнительную версию (конечного приложения). \en Return the additional version (of the target application). - VERSION GetAppVersion ( size_t ind = -1 ) const; - // \ru Очистить контейнер. \en Flush the container. - void Flush (); - // \ru Установить версию по индексу. \en Set the version by the index. - void SetVersion ( size_t index, VERSION ver ); - // \ru Записать в кусок памяти. \en Write to the memory block. - size_t ToMemory ( const char *& memory ) const; - // \ru Прочитать из куска памяти. \en Read from the memory block. - size_t FromMemory ( const char * memory ); - - /// \ru Оператор присваивания. \en An assignment operator. - VersionContainer & operator = ( const VersionContainer & other ); - - /// \ru Оператор чтения. \en Read operator. - friend MATH_FUNC (reader &) operator >> ( reader &, VersionContainer & ); - /// \ru Оператор записи. \en Write operator. - friend MATH_FUNC (writer &) operator << ( writer &, const VersionContainer & ); - -protected: - /// \ru Функция инициализации по другому контейнеру. \en Function of initialization by another container. - void Init( const VersionContainer & ); -}; - -#endif //__IO_VERSION_CONTAINER_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Контейнер версий. + \en Container of versions. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __IO_VERSION_CONTAINER_H +#define __IO_VERSION_CONTAINER_H + + +#include +#include + + +//----------------------------------------------------------------------------- +/** \brief \ru Контейнер версий. + \en Container of versions. \~ + \details \ru Контейнер версий объектов. \n + \en Container of versions of objects. \n \~ + \ingroup Base_Tools_IO +*/ +// --- +class MATH_CLASS VersionContainer +{ +private: + // \ru static VersionContainer iobuf_defaultVersionCont; // Версия по умолчанию. \en static VersionContainer iobuf_defaultVersionCont; // Default version. + static VersionContainer & StaticVersionContainer(); +protected: + SArray m_self; ///< \ru Массив версий. \en Array of versions. +protected: + /// \ru Конструктор. \en Constructor. + VersionContainer( VERSION/*firstInit*/ ); +public: + /// \ru Конструктор. \en Constructor. + VersionContainer(); + /// \ru Конструктор копирования. \en Copy-constructor. + VersionContainer( const VersionContainer & other ); + /// \ru Деструктор. \en Destructor. + virtual ~VersionContainer(); + + /// \ru Получить экземпляр контейнера версий. \en Get the instance of version container. + static const VersionContainer & defaultVersionContainer(); + /// \ru Установить версии из другого контейнера \en Set versions from another container + static void SetDefaultVersion( const VersionContainer & v ); + + // \ru Вернуть главную версию (математического ядра). \en Return the main version (of the mathematical kernel). + VERSION GetMathVersion() const; + // \ru Вернуть дополнительную версию (конечного приложения). \en Return the additional version (of the target application). + VERSION GetAppVersion ( size_t ind = -1 ) const; + // \ru Очистить контейнер. \en Flush the container. + void Flush (); + // \ru Установить версию по индексу. \en Set the version by the index. + void SetVersion ( size_t index, VERSION ver ); + // \ru Записать в кусок памяти. \en Write to the memory block. + size_t ToMemory ( const char *& memory ) const; + // \ru Прочитать из куска памяти. \en Read from the memory block. + size_t FromMemory ( const char * memory ); + + /// \ru Оператор присваивания. \en An assignment operator. + VersionContainer & operator = ( const VersionContainer & other ); + + /// \ru Оператор чтения. \en Read operator. + friend MATH_FUNC (reader &) operator >> ( reader &, VersionContainer & ); + /// \ru Оператор записи. \en Write operator. + friend MATH_FUNC (writer &) operator << ( writer &, const VersionContainer & ); + +protected: + /// \ru Функция инициализации по другому контейнеру. \en Function of initialization by another container. + void Init( const VersionContainer & ); +}; + +#endif //__IO_VERSION_CONTAINER_H diff --git a/C3d/Include/io_version_container_rw.h b/C3d/Include/io_version_container_rw.h index edb672f..e1aa717 100644 --- a/C3d/Include/io_version_container_rw.h +++ b/C3d/Include/io_version_container_rw.h @@ -1,29 +1,29 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Контейнер версий. Чтение/запись. - \en Container of versions. Reading/writing. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __IO_VERSION_CONTAINER_RW_H -#define __IO_VERSION_CONTAINER_RW_H - -#include -#include - - -//----------------------------------------------------------------------------- -/// \ru Оператор чтения контейнера версий. \en Operator of version container reading. \~ \ingroup Base_Tools_IO -// --- -MATH_FUNC (reader &) operator >> ( reader &, VersionContainer & ); - - -//----------------------------------------------------------------------------- -/// \ru Оператор записи контейнера версий. \en Operator of version container writing. \~ \ingroup Base_Tools_IO -// --- -MATH_FUNC (writer &) operator << ( writer &, const VersionContainer & ); - - -#endif //__IO_VERSION_CONTAINER_RW_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Контейнер версий. Чтение/запись. + \en Container of versions. Reading/writing. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __IO_VERSION_CONTAINER_RW_H +#define __IO_VERSION_CONTAINER_RW_H + +#include +#include + + +//----------------------------------------------------------------------------- +/// \ru Оператор чтения контейнера версий. \en Operator of version container reading. \~ \ingroup Base_Tools_IO +// --- +MATH_FUNC (reader &) operator >> ( reader &, VersionContainer & ); + + +//----------------------------------------------------------------------------- +/// \ru Оператор записи контейнера версий. \en Operator of version container writing. \~ \ingroup Base_Tools_IO +// --- +MATH_FUNC (writer &) operator << ( writer &, const VersionContainer & ); + + +#endif //__IO_VERSION_CONTAINER_RW_H diff --git a/C3d/Include/item_registrator.h b/C3d/Include/item_registrator.h index 423cc2f..53ee5d6 100644 --- a/C3d/Include/item_registrator.h +++ b/C3d/Include/item_registrator.h @@ -55,7 +55,7 @@ if ( iReg == NULL || !iReg->IsReg( this, copyItem ) ) { \ Registrar is used to prevent multiple copying of the object. Registrator consists of two synchronous arrays. The first array contains pointers to copied objects, the second array contains pointers to their copies. \n - When copying the object using the registrator, the existance of the copied object inside the first array is verified. + When copying the object using the registrator, the existence of the copied object inside the first array is verified. If such object exists, then the pointer to its copy is given from the second array. If such objects is absent, then it is stored in the first array, after that its copy is stored in the second array and then this copy is returned. \~ \ingroup Base_Algorithms @@ -135,7 +135,7 @@ OBVIOUS_PRIVATE_COPY( MbAutoRegDuplicate ) which contain pointers to other geometries. \n Object pointer can be contained in several other objects for transformations. Registrar is used to prevent multiple transformation of object. - When transforming the object with registrator, the existance of the object inside the registrator is verified. + When transforming the object with registrator, the existence of the object inside the registrator is verified. If such object is absent, it is stored to the registrator and transformed, otherwise, a transformation of the object is not performed. \~ \ingroup Base_Algorithms diff --git a/C3d/Include/last.h b/C3d/Include/last.h index 57daf20..da20d66 100644 --- a/C3d/Include/last.h +++ b/C3d/Include/last.h @@ -1,38 +1,38 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Контроль утечек памяти. - \en Control of memory leaks. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef _LAST_H_ -#define _LAST_H_ - -#include "math_cfg.h" // There are modules without defined paths to C3D - -#ifdef ENABLE_VLD - #include // \ru KVA V14 16.1.2012 Для компиляции КОМПАС \en KVA V14 16.1.2012 To compile the COMPAS - #include -#else - #ifdef C3D_WINDOWS //_MSC_VER - #ifdef __AFX_H__ - #ifdef _DEBUG - #define new DEBUG_NEW - #undef THIS_FILE - #define THIS_FILE __FILE__ - #endif - #else // __AFX_H__ - #define _CRTDBG_MAP_ALLOC - #include - #include - - #ifdef _DEBUG - #define new new(_NORMAL_BLOCK, __FILE__, __LINE__) - #endif - #endif // __AFX_H__ - #endif // C3D_WINDOWS -#endif // ENABLE_VLD - -#endif // _LAST_H_ +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Контроль утечек памяти. + \en Control of memory leaks. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _LAST_H_ +#define _LAST_H_ + +#include "math_cfg.h" // There are modules without defined paths to C3D + +#ifdef ENABLE_VLD + #include // \ru KVA V14 16.1.2012 Для компиляции КОМПАС \en KVA V14 16.1.2012 To compile the COMPAS + #include +#else + #ifdef C3D_WINDOWS //_MSC_VER + #ifdef __AFX_H__ + #ifdef _DEBUG + #define new DEBUG_NEW + #undef THIS_FILE + #define THIS_FILE __FILE__ + #endif + #else // __AFX_H__ + #define _CRTDBG_MAP_ALLOC + #include + #include + + #if defined(_DEBUG) && !defined(__DEBUG_MEMORY_ALLOCATE_FREE_) + #define new new(_NORMAL_BLOCK, __FILE__, __LINE__) + #endif + #endif // __AFX_H__ + #endif // C3D_WINDOWS +#endif // ENABLE_VLD + +#endif // _LAST_H_ diff --git a/C3d/Include/legend.h b/C3d/Include/legend.h index fcd271a..f70eb86 100644 --- a/C3d/Include/legend.h +++ b/C3d/Include/legend.h @@ -1,64 +1,64 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Вспомогательный геометрический объект в трехмерном пространстве. - \en Auxiliary geometric object in the three-dimensional space. \~ - \details \ru Базовый абстрактный класс вспомогательного геометрического объекта. \n - \en Base abstract class of auxiliary geometric object. \n \~ -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __LEGEND_H -#define __LEGEND_H - -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Вспомогательный геометрический объект. - \en Auxiliary geometric object. \~ - \details \ru Базовый абстрактный класс вспомогательного геометрического объекта. \n - Вспомогательные объекты описывают базовые точки других объектов, резьбу, выносные линии, шероховатости и условные обозначения.\n - \en Base abstract class of auxiliary geometric object. \n - Auxiliary objects describe base points of other objects: thread, extension lines, roughness and notation conventions. \n \~ - \ingroup Legend -*/ -// --- -class MATH_CLASS MbLegend : public MbSpaceItem -{ -protected : - /// \ru Конструктор. \en Constructor. - MbLegend(); -public: - /// \ru Деструктор. \en Destructor. - virtual ~MbLegend(); - -public: /* \ru Общие функции геометрического объекта. \en Common functions of a geometric object. */ - - virtual MbeSpaceType IsA() const = 0; // \ru Тип объекта. \en Type of the object. - virtual MbeSpaceType Type() const = 0; // \ru Тип объекта. \en Type of the object. - virtual MbeSpaceType Family() const; // \ru Семейство элемента. \en Family of the element. - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const = 0; // \ru Создать копию. \en Create a copy. - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ) = 0; // \ru Преобразовать согласно матрице. \en Transform according to the matrix. - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ) = 0; // \ru Сдвинуть вдоль вектора. \en Translate along a vector. - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ) = 0; // \ru Повернуть вокруг оси. \en Rotate around an axis. - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const = 0; // \ru Являются ли объекты равными? \en Determine whether the objects are equal. - virtual bool IsSimilar( const MbSpaceItem & init ) const = 0; // \ru Являются ли объекты подобными? \en Determine whether the objects are similar. - virtual bool SetEqual ( const MbSpaceItem & init ) = 0; // \ru Сделать объекты равным. \en Make the objects equal. - virtual double DistanceToPoint ( const MbCartPoint3D & ) const = 0; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. - virtual void AddYourGabaritTo( MbCube & r ) const = 0; // \ru Добавь свой габарит в куб. \en Add bounding box into a cube. - virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate the bounding box in a local coordinate system. - virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const = 0; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. - - virtual MbProperty & CreateProperty( MbePrompt n ) const = 0; // \ru Создать собственное свойство. \en Create a custom property. - virtual void GetProperties( MbProperties & properties ) = 0; // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & properties ) = 0; // \ru Установить свойства объекта. \en Set properties of the object. - - DECLARE_PERSISTENT_CLASS( MbLegend ); - OBVIOUS_PRIVATE_COPY( MbLegend ); -}; - -IMPL_PERSISTENT_OPS( MbLegend ) - - -#endif // __LEGEND_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Вспомогательный геометрический объект в трехмерном пространстве. + \en Auxiliary geometric object in the three-dimensional space. \~ + \details \ru Базовый абстрактный класс вспомогательного геометрического объекта. \n + \en Base abstract class of auxiliary geometric object. \n \~ +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __LEGEND_H +#define __LEGEND_H + +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Вспомогательный геометрический объект. + \en Auxiliary geometric object. \~ + \details \ru Базовый абстрактный класс вспомогательного геометрического объекта. \n + Вспомогательные объекты описывают базовые точки других объектов, резьбу, выносные линии, шероховатости и условные обозначения.\n + \en Base abstract class of auxiliary geometric object. \n + Auxiliary objects describe base points of other objects: thread, extension lines, roughness and notation conventions. \n \~ + \ingroup Legend +*/ +// --- +class MATH_CLASS MbLegend : public MbSpaceItem +{ +protected : + /// \ru Конструктор. \en Constructor. + MbLegend(); +public: + /// \ru Деструктор. \en Destructor. + virtual ~MbLegend(); + +public: /* \ru Общие функции геометрического объекта. \en Common functions of a geometric object. */ + + virtual MbeSpaceType IsA() const = 0; // \ru Тип объекта. \en Type of the object. + virtual MbeSpaceType Type() const = 0; // \ru Тип объекта. \en Type of the object. + virtual MbeSpaceType Family() const; // \ru Семейство элемента. \en Family of the element. + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const = 0; // \ru Создать копию. \en Create a copy. + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ) = 0; // \ru Преобразовать согласно матрице. \en Transform according to the matrix. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ) = 0; // \ru Сдвинуть вдоль вектора. \en Translate along a vector. + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ) = 0; // \ru Повернуть вокруг оси. \en Rotate around an axis. + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const = 0; // \ru Являются ли объекты равными? \en Determine whether the objects are equal. + virtual bool IsSimilar( const MbSpaceItem & init ) const = 0; // \ru Являются ли объекты подобными? \en Determine whether the objects are similar. + virtual bool SetEqual ( const MbSpaceItem & init ) = 0; // \ru Сделать объекты равным. \en Make the objects equal. + virtual double DistanceToPoint ( const MbCartPoint3D & ) const = 0; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. + virtual void AddYourGabaritTo( MbCube & r ) const = 0; // \ru Добавь свой габарит в куб. \en Add bounding box into a cube. + virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate the bounding box in a local coordinate system. + virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const = 0; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. + + virtual MbProperty & CreateProperty( MbePrompt n ) const = 0; // \ru Создать собственное свойство. \en Create a custom property. + virtual void GetProperties( MbProperties & properties ) = 0; // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & properties ) = 0; // \ru Установить свойства объекта. \en Set properties of the object. + + DECLARE_PERSISTENT_CLASS( MbLegend ); + OBVIOUS_PRIVATE_COPY( MbLegend ); +}; + +IMPL_PERSISTENT_OPS( MbLegend ) + + +#endif // __LEGEND_H diff --git a/C3d/Include/lump.h b/C3d/Include/lump.h index ef64638..1219c08 100644 --- a/C3d/Include/lump.h +++ b/C3d/Include/lump.h @@ -68,9 +68,9 @@ typedef std::pair ConstLumpsSPtrSetRet; struct MATH_CLASS MbLump: public MbRefItem { protected: c3d::ConstSolidSPtr solid; ///< \ru Тело (всегда не NULL). \en Solid (always not NULL). + MbMatrix3D from; ///< \ru Матрица преобразования из локальной системы координат. \en A transformation matrix from the local coordinate system. uint component; ///< \ru Идентификатор компонента, в котором определено тело. \en An identifier of a component which a solid is defined in. size_t identifier; ///< \ru Идентификатор нити. \en A thread identifier. - MbMatrix3D from; ///< \ru Матрица преобразования из локальной системы координат. \en A transformation matrix from the local coordinate system. private: bool changed; ///< \ru Флаг необходимости обработки компонента. \en Component processing flag. @@ -79,7 +79,7 @@ private: MbLump( const MbLump & other, MbRegDuplicate * iReg ); public: /// \ru Пустой конструктор. \en Empty constructor. - MbLump() : solid( NULL ), component( 0 ), identifier( SYS_MAX_T ), from(), changed( true ) {} + MbLump() : solid( NULL ), from(), component( 0 ), identifier( SYS_MAX_T ), changed( true ) {} /// \ru Конструктор по данным. \en Constructor by data. MbLump( const MbSolid & _solid, const MbMatrix3D & _from, uint _comp = 0, size_t _ident = SYS_MAX_T, bool _changed = true ); /// \ru Деструктор. \en Destructor. diff --git a/C3d/Include/m2b_mesh_curvature.h b/C3d/Include/m2b_mesh_curvature.h index 86f9244..cf269b9 100644 --- a/C3d/Include/m2b_mesh_curvature.h +++ b/C3d/Include/m2b_mesh_curvature.h @@ -1,40 +1,40 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Структура для хранения кривизн, их направлений и нормалей в вершине сетки. - \en Struct to store curvatures, principal curvature directions and normals at mesh vertex. \~ -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __M2B_MESH_CURVATURE_H -#define __M2B_MESH_CURVATURE_H - -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Данные о кривизне и главных направлениях изменений кривизны. - \en Curvature and principal curvature direction data. \~ - \details \ru Структура для хранения информации о кривизне и главных направлениях изменений кривизны поверхности, - рассчитанной в вершине полигональной сетки. - \en Structure for store curvature and principal curvature direction data calculated at the polygon vertex. \~ - \ingroup Polygonal_Objects -*/ -// --- -struct MATH_CLASS MbCurvature -{ - double k_h; ///< \ru Средняя кривизна. \en Mean curvature. - double k_g; ///< \ru Гауссова кривизна. \en Gaussian curvature. - double k1; ///< \ru Максимальная кривизна. \en Maximum principal curvature. - double k2; ///< \ru Минимальная кривизна. \en Minimum principal curvature. - MbVector3D normal; ///< \ru Нормаль (вычислена по оператору кривизны). \en Normal (calculated by curvature operator). - MbVector3D meanNormal; ///< \ru Нормаль (вычислена как взвешенное среднее нормалей соседних граней). \en Normal (calculated as weighted mean of the normals of neighboring faces). - MbVector3D cdir1; ///< \ru Направление максимальной кривизны. \en Maximum principal curvature direction. - MbVector3D cdir2; ///< \ru Направление минимальной кривизны. \en Minimum principal curvature direction. - /// \ru Конструктор по умолчанию. \en Default constructor. - MbCurvature() : k_h( 0.0 ), k_g( 0.0 ), k1 ( 0.0 ), k2 ( 0.0 ) {} -}; - - -#endif // __M2B_MESH_CURVATURE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Структура для хранения кривизн, их направлений и нормалей в вершине сетки. + \en Struct to store curvatures, principal curvature directions and normals at mesh vertex. \~ +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __M2B_MESH_CURVATURE_H +#define __M2B_MESH_CURVATURE_H + +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Данные о кривизне и главных направлениях изменений кривизны. + \en Curvature and principal curvature direction data. \~ + \details \ru Структура для хранения информации о кривизне и главных направлениях изменений кривизны поверхности, + рассчитанной в вершине полигональной сетки. + \en Structure for store curvature and principal curvature direction data calculated at the polygon vertex. \~ + \ingroup Polygonal_Objects +*/ +// --- +struct MATH_CLASS MbCurvature +{ + double k_h; ///< \ru Средняя кривизна. \en Mean curvature. + double k_g; ///< \ru Гауссова кривизна. \en Gaussian curvature. + double k1; ///< \ru Максимальная кривизна. \en Maximum principal curvature. + double k2; ///< \ru Минимальная кривизна. \en Minimum principal curvature. + MbVector3D normal; ///< \ru Нормаль (вычислена по оператору кривизны). \en Normal (calculated by curvature operator). + MbVector3D meanNormal; ///< \ru Нормаль (вычислена как взвешенное среднее нормалей соседних граней). \en Normal (calculated as weighted mean of the normals of neighboring faces). + MbVector3D cdir1; ///< \ru Направление максимальной кривизны. \en Maximum principal curvature direction. + MbVector3D cdir2; ///< \ru Направление минимальной кривизны. \en Minimum principal curvature direction. + /// \ru Конструктор по умолчанию. \en Default constructor. + MbCurvature() : k_h( 0.0 ), k_g( 0.0 ), k1 ( 0.0 ), k2 ( 0.0 ) {} +}; + + +#endif // __M2B_MESH_CURVATURE_H diff --git a/C3d/Include/map_create.h b/C3d/Include/map_create.h index c6a4170..49aecba 100644 --- a/C3d/Include/map_create.h +++ b/C3d/Include/map_create.h @@ -268,18 +268,21 @@ OBVIOUS_PRIVATE_COPY( GetVestigesTransData ) \en Merge same curves (default true). \~ \param[in] prevCubes - \ru Габариты тел до изменений. \en Bounding boxes of solids before changes. \~ + \result \ru Возвращает результат операции. + \en Returns the result of the operation. \~ + \ingroup Mapping */ // --- -MATH_FUNC (void) GetVestiges ( const MbPlacement3D & place, - double znear, - const RPArray & lumps, - const MbProjectionsObjects & objects, - MbVEFVestiges & result, - const MbMapVisibilityMode & visMode, - VERSION version = Math::DefaultMathVersion(), - bool merge = true, - const std::vector * prevCubes = NULL ); +MATH_FUNC (MbResultType) GetVestiges ( const MbPlacement3D & place, + double znear, + const RPArray & lumps, + const MbProjectionsObjects & objects, + MbVEFVestiges & result, + const MbMapVisibilityMode & visMode, + VERSION version = Math::DefaultMathVersion(), + bool merge = true, + const std::vector * prevCubes = NULL ); //------------------------------------------------------------------------------ @@ -358,14 +361,17 @@ private: Destruction of the results is a responsibility of the function caller. \~ \param[in] version - \ru Версия построения. Последняя версия Math::DefaultMathVersion(). \en The version of construction. The last version Math::DefaultMathVersion(). -\ingroup Mapping + \result \ru Возвращает результат операции. + \en Returns the result of the operation. \~ + + \ingroup Mapping */ // --- -MATH_FUNC (void) GetVestiges ( const SArray & settings, - const RPArray & lumps, - const MbProjectionsObjects & objects, - PArray & results, - VERSION version = Math::DefaultMathVersion() ); +MATH_FUNC (MbResultType) GetVestiges ( const SArray & settings, + const RPArray & lumps, + const MbProjectionsObjects & objects, + PArray & results, + VERSION version = Math::DefaultMathVersion() ); //------------------------------------------------------------------------------ /** \brief \ru Определение участков граничной кривой. diff --git a/C3d/Include/map_lump.h b/C3d/Include/map_lump.h index c295976..8290612 100644 --- a/C3d/Include/map_lump.h +++ b/C3d/Include/map_lump.h @@ -587,7 +587,7 @@ public: Free the unnecessary memory in arrays of points and names. \~ */ void AdjustMemory() { - #ifdef STANDARD_C11 + #ifdef C3D_STANDARD_CXX_11_PARTIAL points.shrink_to_fit(); names.shrink_to_fit(); #endif } @@ -877,7 +877,7 @@ public: Frees the unnecessary memory in arrays of points and names. \~ */ void AdjustMemory() { - #ifdef STANDARD_C11 + #ifdef C3D_STANDARD_CXX_11_PARTIAL curves.shrink_to_fit(); names.shrink_to_fit(); #endif } @@ -1140,9 +1140,9 @@ public: , willCut( false ) // конструктор по нескольким телам только в случае "не рассекать" { size_t count = _lumps.size(); - C3D_ASSERT( count > 0 ); + C3D_ASSERT( count > 0 && _lumps[0] != NULL ); - if ( count > 0 ) { + if ( count > 0 && _lumps[0] != NULL ) { from = _lumps[0]->GetMatrixFrom(); component = _lumps[0]->GetComponent(); identifier = _lumps[0]->GetIdentifier(); @@ -1154,7 +1154,8 @@ public: lumps = new c3d::LumpsSPtrVector(); for ( size_t i = 1; i < count; ++i ) { MbLump * lump = _lumps[i]; - lumps->push_back( c3d::LumpSPtr( lump ) ); + if ( lump != NULL ) + lumps->push_back( c3d::LumpSPtr(lump) ); } } } diff --git a/C3d/Include/map_vestige.h b/C3d/Include/map_vestige.h index 7e41dfe..bc4e949 100644 --- a/C3d/Include/map_vestige.h +++ b/C3d/Include/map_vestige.h @@ -609,14 +609,16 @@ protected: \en A name. \~ \param[in] isCenterLine - \ru Является ли кривая осевой линией или нет. \en Is curve is center line or not. \~ + \param[in] ownName - \ru Является ли otherName собственным именем объекта. + \en Is otherName own object name or not. \~ */ explicit - MbEdgeVestige( uint otherComp, size_t otherIdent, const MbName & otherName, bool isCenterLine ) + MbEdgeVestige( uint otherComp, size_t otherIdent, const MbName & otherName, bool isCenterLine, bool ownName = false ) : MbBaseVestige( otherComp, otherIdent, otherName ) , curveInfo ( ) , vesType ( (uint8)(isCenterLine ? vt_CenterLine : vt_SpaceCurve) ) , vesSubType ( vst_None ) - {} + { if ( ownName ) name.SetOwn( ownName ); } /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. MbEdgeVestige( const MbEdgeVestige & other, MbRegDuplicate * iReg ) : MbBaseVestige( other, iReg ) @@ -644,7 +646,7 @@ public: void SetSubType( SubType vt ) { vesSubType = (uint8)vt; } /// \ru Добавить MbEdgeVestige в массив. \en Add the MbEdgeVestige to an array. \~ - friend MbEdgeVestige * AddVestigeCurve( uint otherComp, size_t otherIdent, const MbName & otherName, RPArray & arr, bool isCenterLine ); + friend MbEdgeVestige * AddVestigeCurve( uint otherComp, size_t otherIdent, const MbName & otherName, RPArray & arr, bool isCenterLine, bool ownName ); private: bool operator == ( const MbEdgeVestige & ); // \ru Не реализован. \en Not implemented. @@ -670,6 +672,8 @@ IMPL_PERSISTENT_OPS( MbEdgeVestige ) \en Array of edges. \~ \param[in] isCenterLine - \ru Является ли ребро осевой линией или нет. \en Is curve is center line or not. \~ + \param[in] ownName - \ru Является ли otherName собственным именем объекта. + \en Is otherName own object name or not. \~ \ingroup Mapping */ // --- @@ -677,9 +681,10 @@ inline MbEdgeVestige * AddVestigeCurve( uint otherComp size_t otherIdent, const MbName & otherName, RPArray & arr, - bool isCenterLine ) + bool isCenterLine, + bool ownName ) { - MbEdgeVestige * vestige = new MbEdgeVestige( otherComp, otherIdent, otherName, isCenterLine ); + MbEdgeVestige * vestige = new MbEdgeVestige( otherComp, otherIdent, otherName, isCenterLine, ownName ); if ( vestige ) { arr.Add( vestige ); } @@ -977,6 +982,17 @@ public: pointVestiges.Flush(); curveVestiges.Flush(); } + /// \ru Очистить массивы следов. \en Clear arrays of vestiges. + void SetEmptyHard() + { + vertexVestiges.HardFlush(); + edgeVestiges.HardFlush(); + faceVestiges.HardFlush(); + annotateVestiges.HardFlush(); + symbolVestiges.HardFlush(); + pointVestiges.HardFlush(); + curveVestiges.HardFlush(); + } /// \ru Освободить неиспользуемую память. \en Adjust memory. void Adjust() { @@ -1184,7 +1200,7 @@ inline MbEdgeVestige * MbVEFVestiges::AddVestigeEdge( uint otherComp, size_t oth // // --- inline MbEdgeVestige * MbVEFVestiges::AddVestigeCurve( uint otherComp, size_t otherIdent, const MbName & otherName, bool isCenterLine ) { - return ::AddVestigeCurve( otherComp, otherIdent, otherName, curveVestiges, isCenterLine ); + return ::AddVestigeCurve( otherComp, otherIdent, otherName, curveVestiges, isCenterLine, false ); } //------------------------------------------------------------------------------ @@ -1195,7 +1211,7 @@ inline MbEdgeVestige * MbVEFVestiges::AddVestigeCurve( uint otherComp, size_t ot { MbEdgeVestige * ev = NULL; - ev = ::AddVestigeCurve( otherComp, otherIdent, otherName, curveVestiges, false ); + ev = ::AddVestigeCurve( otherComp, otherIdent, otherName, curveVestiges, false, false ); // BUG_93683 ev = ::AddVestigeEdge( otherComp, otherIdent, otherName, MbBaseVestige::vt_Edge, edgeVestiges ); if ( ev != NULL ) { diff --git a/C3d/Include/marker.h b/C3d/Include/marker.h index 24150b4..49b7d18 100644 --- a/C3d/Include/marker.h +++ b/C3d/Include/marker.h @@ -1,123 +1,123 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Маркер. - \en Marker. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __MARKER_H -#define __MARKER_H - - -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Маркер со свойствами геометрического объекта. - \en Marker with properties of a geometric object. \~ - \par \ru Определение - Маркером называется тройка объектов: точка и пара ортонормированных векторов. - Например, в системе КОМПАС-3D маркером задается "присоединительная точка", - которая применяется в качестве геометрического коннектора для сопряжения и - позиционирования тел в пространстве.\n - \en Definition - The marker is a triple of objects: a point and a pair of orthonormalized vectors. - For example, in the COMPAS-3D system marker sets the "connecting point", - which is used as geometric connector for conjugation and - positioning of solids in space.\n \~ - - \details \ru Термин "Маркер" позаимствован из книги Г.Крамера, Geometric constraint solving in kinematics.\n - С помощью маркера можно задавать вспомогательные построения, передавать - геометрию кинематических соединений или сопряжений. Маркер всегда принадлежит - какому-то подпространству, например, ЛСК твердого тела.\n - Для маркера всегда выполняется требование; его вектора (в отличие от локальной - системы координат) всегда ортонормированы. Ось Z всегда нормирована, ось X всегда - ортогональна Z (может быть, что X = 0). Маркер может задавать любые геометрические - объекты, которые удобно задать точкой или векторами; это унифицированная форма записи - таких объектов, как точка, прямая, плоскость, ортонормированная правая СК и т.д. - При дополнении маркера радиусом он используется, как сжатая форма записи цилиндра, - окружности, сферы, тора и т.д. \n - \en The term "Marker" is taken from the book "Geometric constraint solving in kinematics" (G.Kramera ).\n - With the help of marker it is possible to set auxiliary constructions and to transmit - geometry of kinematic compounds or conjugations. The marker always belongs - to some subspace, for example, to LSC of solid. \n - For the marker the following requirement is always satisfied: its vectors (as opposed to the local - coordinate system) are always orthonormalized. Z-axis is always normalized, X-axis is always - orthogonal to Z (it could be that X = 0). Marker can set any geometric - objects which are easy to set by point or vectors, this is a unified form of writing - of objects as point, line, plane, right orthonormal CS etc. - When adding a radius to a marker it is used as a compressed form of writing of cylinder, - circle, sphere, torus etc. \n \~ - \sa #MtMarker, #MtUnifiedGeom, #MtMatingGeometry, #MtMatingGeom - \ingroup Legend -*/ -// --- -class MATH_CLASS MbMarker: public MbLegend -{ - MbCartPoint3D origin; ///< \ru Точка маркера. \en Marker point. - MbVector3D axisZ; ///< \ru Нормированный вектор оси OZ. \en Normalized vector of OZ-axis. - MbVector3D axisX; ///< \ru Ортонормированный вектор оси OX. \en Orthonormalized vector of OX-axis. - -public: - /// \ru Конструктор копирования. \en Copy constructor. - MbMarker( const MbMarker & ); - /// \ru Конструктор по точке и вектору. \en Constructor by a point and a vector. - MbMarker( const MbCartPoint3D &, const MbVector3D & ); - /// \ru Конструктор по точке и векторам. \en Constructor by a point and vectors. - MbMarker( const MbCartPoint3D &, const MbVector3D &, const MbVector3D & ); - /// \ru Деструктор. \en Destructor. - virtual ~MbMarker(); - -public: - - // \ru Общие функции геометрического объекта \en Common functions of a geometric object - virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en Type of the object. - virtual MbeSpaceType Type() const; // \ru Тип объекта. \en Type of the object. - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Создать копию. \en Create a copy. - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. - virtual void Move( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвинуть вдоль вектора. \en Translate along a vector. - virtual void Rotate( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate around an axis. - virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Determine whether objects are equal. - virtual bool IsSimilar( const MbSpaceItem & init ) const; // \ru Являются ли объекты подобными? \en Determine whether objects are similar. - virtual bool SetEqual ( const MbSpaceItem & init ); // \ru Сделать объекты равным. \en Make objects equal. - virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. - virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Добавь свой габарит в куб. \en Add bounding box into a cube. - virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. - - // \ru Свойства \en Properties - virtual MbProperty & CreateProperty( MbePrompt n ) const; // \ru Создать собственное свойство. \en Create a custom property. - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & properties ); // \ru Установить свойства объекта. \en Set properties of the object. - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - /** \ru \name Функции маркера. - \en \name Functions of marker. - \{ */ - /// \ru Получить точку маркера. \en Get point of marker. - const MbCartPoint3D & GetOrigin() const { return origin; } - /// \ru Получить ось маркера. \en Get axis of marker. - const MbVector3D & GetAxisX() const { return axisX; } - /// \ru Получить вторую ось маркера. \en Get the second axis of marker. - const MbVector3D & GetAxisZ() const { return axisZ; } - /// \ru Задать нулевую ось X. \en Set the X-axis to null. - void SetNullX() { axisX.SetZero(); } - /// \ru Перевернуть ось Z. \en Invert the Z-axis. - MbMarker & InvertZ(); - - /** \} */ - -private: - MbMarker & operator = ( const MbMarker & ); - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbMarker ) -}; - - -IMPL_PERSISTENT_OPS( MbMarker ) - - -#endif // __MARKER_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Маркер. + \en Marker. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __MARKER_H +#define __MARKER_H + + +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Маркер со свойствами геометрического объекта. + \en Marker with properties of a geometric object. \~ + \par \ru Определение + Маркером называется тройка объектов: точка и пара ортонормированных векторов. + Например, в системе КОМПАС-3D маркером задается "присоединительная точка", + которая применяется в качестве геометрического коннектора для сопряжения и + позиционирования тел в пространстве.\n + \en Definition + The marker is a triple of objects: a point and a pair of orthonormalized vectors. + For example, in the COMPAS-3D system marker sets the "connecting point", + which is used as geometric connector for conjugation and + positioning of solids in space.\n \~ + + \details \ru Термин "Маркер" позаимствован из книги Г.Крамера, Geometric constraint solving in kinematics.\n + С помощью маркера можно задавать вспомогательные построения, передавать + геометрию кинематических соединений или сопряжений. Маркер всегда принадлежит + какому-то подпространству, например, ЛСК твердого тела.\n + Для маркера всегда выполняется требование; его вектора (в отличие от локальной + системы координат) всегда ортонормированы. Ось Z всегда нормирована, ось X всегда + ортогональна Z (может быть, что X = 0). Маркер может задавать любые геометрические + объекты, которые удобно задать точкой или векторами; это унифицированная форма записи + таких объектов, как точка, прямая, плоскость, ортонормированная правая СК и т.д. + При дополнении маркера радиусом он используется, как сжатая форма записи цилиндра, + окружности, сферы, тора и т.д. \n + \en The term "Marker" is taken from the book "Geometric constraint solving in kinematics" (G.Kramera ).\n + With the help of marker it is possible to set auxiliary constructions and to transmit + geometry of kinematic compounds or conjugations. The marker always belongs + to some subspace, for example, to LSC of solid. \n + For the marker the following requirement is always satisfied: its vectors (as opposed to the local + coordinate system) are always orthonormalized. Z-axis is always normalized, X-axis is always + orthogonal to Z (it could be that X = 0). Marker can set any geometric + objects which are easy to set by point or vectors, this is a unified form of writing + of objects as point, line, plane, right orthonormal CS etc. + When adding a radius to a marker it is used as a compressed form of writing of cylinder, + circle, sphere, torus etc. \n \~ + \sa #MtMarker, #MtUnifiedGeom, #MtMatingGeometry, #MtMatingGeom + \ingroup Legend +*/ +// --- +class MATH_CLASS MbMarker: public MbLegend +{ + MbCartPoint3D origin; ///< \ru Точка маркера. \en Marker point. + MbVector3D axisZ; ///< \ru Нормированный вектор оси OZ. \en Normalized vector of OZ-axis. + MbVector3D axisX; ///< \ru Ортонормированный вектор оси OX. \en Orthonormalized vector of OX-axis. + +public: + /// \ru Конструктор копирования. \en Copy constructor. + MbMarker( const MbMarker & ); + /// \ru Конструктор по точке и вектору. \en Constructor by a point and a vector. + MbMarker( const MbCartPoint3D &, const MbVector3D & ); + /// \ru Конструктор по точке и векторам. \en Constructor by a point and vectors. + MbMarker( const MbCartPoint3D &, const MbVector3D &, const MbVector3D & ); + /// \ru Деструктор. \en Destructor. + virtual ~MbMarker(); + +public: + + // \ru Общие функции геометрического объекта \en Common functions of a geometric object + virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en Type of the object. + virtual MbeSpaceType Type() const; // \ru Тип объекта. \en Type of the object. + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Создать копию. \en Create a copy. + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. + virtual void Move( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвинуть вдоль вектора. \en Translate along a vector. + virtual void Rotate( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate around an axis. + virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Determine whether objects are equal. + virtual bool IsSimilar( const MbSpaceItem & init ) const; // \ru Являются ли объекты подобными? \en Determine whether objects are similar. + virtual bool SetEqual ( const MbSpaceItem & init ); // \ru Сделать объекты равным. \en Make objects equal. + virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. + virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Добавь свой габарит в куб. \en Add bounding box into a cube. + virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. + + // \ru Свойства \en Properties + virtual MbProperty & CreateProperty( MbePrompt n ) const; // \ru Создать собственное свойство. \en Create a custom property. + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & properties ); // \ru Установить свойства объекта. \en Set properties of the object. + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + /** \ru \name Функции маркера. + \en \name Functions of marker. + \{ */ + /// \ru Получить точку маркера. \en Get point of marker. + const MbCartPoint3D & GetOrigin() const { return origin; } + /// \ru Получить ось маркера. \en Get axis of marker. + const MbVector3D & GetAxisX() const { return axisX; } + /// \ru Получить вторую ось маркера. \en Get the second axis of marker. + const MbVector3D & GetAxisZ() const { return axisZ; } + /// \ru Задать нулевую ось X. \en Set the X-axis to null. + void SetNullX() { axisX.SetZero(); } + /// \ru Перевернуть ось Z. \en Invert the Z-axis. + MbMarker & InvertZ(); + + /** \} */ + +private: + MbMarker & operator = ( const MbMarker & ); + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbMarker ) +}; + + +IMPL_PERSISTENT_OPS( MbMarker ) + + +#endif // __MARKER_H diff --git a/C3d/Include/math_cfg.h b/C3d/Include/math_cfg.h index 26c5a32..861f61d 100644 --- a/C3d/Include/math_cfg.h +++ b/C3d/Include/math_cfg.h @@ -61,5 +61,13 @@ #endif //C3D_WINDOWS +/* + \ru Включение/выключение контроля выделения памяти под отладкой. + \en Switching on/off memory allocation control during the debugging process. +*/ +//#define __DEBUG_MEMORY_ALLOCATE_FREE_ // Overload operators new/delete and check memory +//#define __MEMSET_USED_FREE_HEAP_HEAR__ // Mark used memory +//#define __REALLOC_ARRAYS_STATISTIC_ // Collect statistics on arrays size changes + #endif // __MATH_CFG_H diff --git a/C3d/Include/math_define.h b/C3d/Include/math_define.h index 7c63110..e76fd8b 100644 --- a/C3d/Include/math_define.h +++ b/C3d/Include/math_define.h @@ -32,7 +32,6 @@ #endif // PRECONDITION #endif // C3D_WINDOWS -#include // \ru Внимание! Читаем внимательно! Если вытереть отсюда, то подключить везде, где есть функции из этого файла! \en Attention! Read carefully! If remove this from here, then it should be included anywhere where the functions from this file exist! #include #include #include @@ -82,12 +81,15 @@ typedef UintSet::iterator UintSetIt; typedef UintSet::const_iterator UintSetConstIt; typedef std::pair UintSetRet; +typedef std::pair IndicesPairsPair; ///< \ru Пара индексных пар. \en Pair of indices' pair. + + //------------------------------------------------------------------------------ // // --- template bool IsNullPointer( const ItemPtr * itemPtr ) { - return ((NULL == itemPtr) ? true : false); + return ((C3D_NULL_PTR == itemPtr) ? true : false); } //------------------------------------------------------------------------------ @@ -194,7 +196,7 @@ size_t BinarySearch( Elements & items, const Element & item ) //------------------------------------------------------------------------------ // \ru Синтаксис дружественной шаблонной функции шаблона \en Syntax of friendly template function of a template -#if !(defined (_MSC_VER)) || __BORLANDC__ +#if !(defined (_MSC_VER)) #define TEMPLATE_FRIEND friend // \ru по стандарту C++98 \en by the C++98 standard #define TEMPLATE_SUFFIX @@ -266,7 +268,7 @@ private: \ #define __WARN__ __FILE__ "("__DEFTOSTR__(__LINE__)") : warning: " #else // _MSC_VER -// For linux and borland +// For linux #define __TODO__ #define __WARN__ @@ -295,15 +297,18 @@ private: \ #define MATH_CLASS __declspec( dllexport ) #define MATH_FUNC(retType) __declspec( dllexport ) retType CALL_DECLARATION #define MATH_FUNC_EX __declspec( dllexport ) // \ru для KNOWN_OBJECTS_RW_REF_OPERATORS_EX и KNOWN_OBJECTS_RW_PTR_OPERATORS_EX \en for KNOWN_OBJECTS_RW_REF_OPERATORS_EX and KNOWN_OBJECTS_RW_PTR_OPERATORS_EX + #define MATH_SYMBOL __declspec( dllexport ) #else #define MATH_CLASS __declspec( dllimport ) #define MATH_FUNC(retType) __declspec( dllimport ) retType CALL_DECLARATION #define MATH_FUNC_EX __declspec( dllimport ) + #define MATH_SYMBOL __declspec( dllimport ) #endif #else // C3D_WINDOWS #define MATH_CLASS #define MATH_FUNC(retType) retType #define MATH_FUNC_EX + #define MATH_SYMBOL #endif // \ru Модуль геометрических ограничений. \en Geometric constraints module. @@ -329,12 +334,12 @@ namespace c3d // namespace C3D //------------------------------------------------------------------------------ /// \ru Максимальное количество элементов матрицы MxN. \en Maximum number of MxN matrix elements. //--- -const size_t MATRIX_MAX_COUNT = 1000000000; // 1e9 +const_expr size_t MATRIX_MAX_COUNT = 1000000000; // 1e9 //------------------------------------------------------------------------------ /// \ru Максимальный размер массива. \en Maximum size of array. //--- -const size_t ARRAY_MAX_COUNT = 1000000; // 1e6 +const_expr size_t ARRAY_MAX_COUNT = 1000000; // 1e6 //------------------------------------------------------------------------------ /** @@ -559,6 +564,38 @@ inline void GetCosSin( const double & tt, double & cosT, double & sinT ) inline void DummyAssert( bool ) { } + namespace older_stl_support + { + //------------------------------------------------------------------------------ + /** \brief \ru Суррогат "iterator erase( iterator pos )" для старых версий std:map и std::set. + \en "iterator erase( iterator pos )" surrogate for older versions of std:map and std:: set.. \~ + \details \ru Для унификации кода при поддержке версий std:map и std::set, + в которых не реализована iterator erase( iterator pos ). \n + \en To unify the code with support for STD:map and std::set versions + that do not implement iterator erase( iterator pos). \n \~ + \param[in] obj - \ru Контейнер из которого удаляется элемент. + \en Container that the item is being removed from. \~ + \param[in] pos - \ru Итератор на удаляемый элемент. + \en Iterator to the element to remove. \~ + \return \ru Итератор на элемент, следующий за удаленным. + \en Iterator following the removed element. \~ + \ingroup Base_Tools + */ + // --- + template inline typename T::iterator erase( T& obj, typename T::iterator pos ) + { + #if defined(__GNUC__) && ( C3D_GCC_VERSION < 40805 ) + typename T::iterator temp = pos; + pos++; + obj.erase( temp ); + return pos; + #else + return obj.erase( pos ); + #endif + } + } + + } // namespace C3D diff --git a/C3d/Include/math_doxigen.h b/C3d/Include/math_doxigen.h index 2162966..1845d77 100644 --- a/C3d/Include/math_doxigen.h +++ b/C3d/Include/math_doxigen.h @@ -1,421 +1,421 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Группы для документирования с помощью Doxygen. - \en Groups for documenting by Doxygen. \~ -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __MATH_DOXIGEN_H -#define __MATH_DOXIGEN_H - - -//----------------------------------------------------------------------------- -// -// -// \ru Группы геометрического ядра \en Groups of the Geometric Kernel \~ -// -// -//----------------------------------------------------------------------------- - -/** - \ru \defgroup Geometric_Modelling C3D Modeler: Модуль геометрического моделирования - \en \defgroup Geometric_Modelling C3D Modeler: The Geometric Modelling Module - \~ \ingroup Geometric_Kernel -*/ - - -/** - \ru \defgroup Geometric_Constraints C3D Solver: Модуль геометрических ограничений - \en \defgroup Geometric_Constraints C3D Solver: The Geometric Constraints Module - \~ \ingroup Geometric_Kernel -*/ - - -/** - \ru \defgroup Data_Exchange C3D Converter: Модуль конвертеров - \en \defgroup Data_Exchange C3D Converter: The Converters Module - \~ \ingroup Geometric_Kernel -*/ - - -//----------------------------------------------------------------------------- -// -// \ru Подгруппа Geometric_Modelling - Модуль геометрического моделирования \en The Geometric Modelling Module \~ -// -//----------------------------------------------------------------------------- -/** - \ru \defgroup Geometric_Items Геометрические объекты - \en \defgroup Geometric_Items Geometric Objects - \~ \ingroup Geometric_Modelling -*/ -/** - \ru \defgroup Base_Items Объекты алгоритмов - \en \defgroup Base_Items Algorithm Objects - \~ \ingroup Geometric_Modelling -*/ -/** - \ru \defgroup Modelling_Functions Методы геометрических построений - \en \defgroup Modelling_Functions Geometric Construction Methods - \~ \ingroup Geometric_Modelling -*/ -/** - \ru \defgroup Geometric_Computation Методы геометрических вычислений - \en \defgroup Geometric_Computation Geometric Computations Methods - \~ \ingroup Geometric_Modelling -*/ -/** - \ru \defgroup Base_Tools Библиотека шаблонов и сериализации - \en \defgroup Base_Tools Templates and Serializations Library - \~ \ingroup Geometric_Modelling -*/ - - -//----------------------------------------------------------------------------------- -// \ru Подгруппа Geometric_Items - Геометрические объекты \en Geometric Objects \~ -//----------------------------------------------------------------------------------- -/** - \ru \defgroup Model_Items Объекты геометрической модели - \en \defgroup Model_Items Geometric Model Objects - \~ \ingroup Geometric_Items -*/ -/** - \ru \defgroup Topology_Items Топологические объекты - \en \defgroup Topology_Items Topological Objects - \~ \ingroup Geometric_Items -*/ -/** - \ru \defgroup Surfaces Поверхности - \en \defgroup Surfaces Surfaces - \~ \ingroup Geometric_Items -*/ -/** - \ru \defgroup Curves_3D Кривые - \en \defgroup Curves_3D Curves - \~ \ingroup Geometric_Items -*/ -/** - \ru \defgroup Point_3D Точка - \en \defgroup Point_3D Point - \~ \ingroup Geometric_Items -*/ -/** - \ru \defgroup Legend Вспомогательные объекты - \en \defgroup Legend Ancillary Items - \~ \ingroup Geometric_Items -*/ -/** - \ru \defgroup Curves_2D Двумерные кривые - \en \defgroup Curves_2D Two-Dimensional uv-Curves - \~ \ingroup Geometric_Items -*/ -/** - \ru \defgroup Region_2D Двумерные области - \en \defgroup Region_2D Two-Dimensional Regions - \~ \ingroup Geometric_Items -*/ - - -//----------------------------------------------------------------------------- -// \ru Подгруппа Base_Items - базовые объекты \en Base Objects \~ -//----------------------------------------------------------------------------- -/** - \ru \defgroup Mathematic_Base_3D Трёхмерные базовые объекты - \en \defgroup Mathematic_Base_3D Three-Dimensional Base Objects - \~ \ingroup Base_Items -*/ -/** - \ru \defgroup Mathematic_Base_2D Двумерные базовые объекты - \en \defgroup Mathematic_Base_2D Two-Dimensional Base Objects - \~ \ingroup Base_Items -*/ -/** - \ru \defgroup Model_Creators Строители - \en \defgroup Model_Creators Creators - \~ \ingroup Base_Items -*/ -/** - \ru \defgroup Model_Attributes Атрибуты - \en \defgroup Model_Attributes Attributes - \~ \ingroup Base_Items -*/ -/** - \ru \defgroup Functions Скалярные функции - \en \defgroup Functions Scalar Functions - \~ \ingroup Base_Items -*/ -/** - \ru \defgroup Build_Parameters Параметры операций - \en \defgroup Build_Parameters Operation Parameters - \~ \ingroup Base_Items -*/ -/** - \ru \defgroup Model_Properties Свойства - \en \defgroup Model_Properties Properties - \~ \ingroup Base_Items -*/ -/** - \ru \defgroup Data_Structures Структуры данных - \en \defgroup Data_Structures Data Structures - \~ \ingroup Base_Items -*/ -/** - \ru \defgroup Parser Разбор строки - \en \defgroup Parser Parser - \~ \ingroup Base_Items -*/ -/** - \ru \defgroup Names Имена - \en \defgroup Names Names - \~ \ingroup Base_Items -*/ -/** - \ru \defgroup Model Модель - \en \defgroup Model Model - \~ \ingroup Base_Items -*/ - - -//----------------------------------------------------------------------------------- -// \ru Подгруппа Modelling_Functions - Методы геометрических построений \en Methods of Geometric Constructions \~ -//----------------------------------------------------------------------------------- -/** - \ru \defgroup Solid_Modeling Построение тел - \en \defgroup Solid_Modeling Solid Modeling - \~ \ingroup Modelling_Functions -*/ -/** - \ru \defgroup Shell_Modeling Построение оболочек - \en \defgroup Shell_Modeling Shell Modeling - \~ \ingroup Modelling_Functions -*/ -/** - \ru \defgroup Sheet_Metal_Modeling Построения листовых тел - \en \defgroup Sheet_Metal_Modeling Sheet Metal Modeling - \~ \ingroup Modelling_Functions -*/ -/** - \ru \defgroup Direct_Modeling Прямое редактирование тел - \en \defgroup Direct_Modeling Direct Solid Modeling - \~ \ingroup Modelling_Functions -*/ -/** - \ru \defgroup Surface_Modeling Построение поверхностей - \en \defgroup Surface_Modeling Construction of Surfaces - \~ \ingroup Modelling_Functions -*/ -/** - \ru \defgroup Curve3D_Modeling Построение кривых в трёхмерном пространстве - \en \defgroup Curve3D_Modeling Construction of Curves - \~ \ingroup Modelling_Functions -*/ -/** - \ru \defgroup Curve_Modeling Построение кривых в двумерном пространстве - \en \defgroup Curve_Modeling Construction of uv-Curves in Two-Dimensional Space - \~ \ingroup Modelling_Functions -*/ -/** - \ru \defgroup Point_Modeling Операции с точками - \en \defgroup Point_Modeling Operations with Points - \~ \ingroup Modelling_Functions -*/ -/** - \ru \defgroup Base_Algorithms Базовые алгоритмы - \en \defgroup Base_Algorithms Base Algorithms - \~ \ingroup Modelling_Functions -*/ -/** - \ru \defgroup Algorithms_3D Алгоритмы в трёхмерном пространстве - \en \defgroup Algorithms_3D Algorithms in Three-Dimensional Space - \~ \ingroup Modelling_Functions -*/ -/** - \ru \defgroup Algorithms_2D Алгоритмы в двумерном пространстве - \en \defgroup Algorithms_2D Algorithms in Two-Dimensional Space - \~ \ingroup Modelling_Functions -*/ - - -//---------------------------------------------------------------------------- -// \ru Подгруппа Geometric_Computation - Методы геометрических расчетов \en Geometric Computations Methods \~ -//---------------------------------------------------------------------------- -/** - \ru \defgroup Polygonal_Objects Полигональные объекты - \en \defgroup Polygonal_Objects Polygonal Objects - \~ \ingroup Geometric_Computation -*/ -/** - \ru \defgroup Triangulation Триангуляция - \en \defgroup Triangulation Triangulation - \~ \ingroup Geometric_Computation -*/ -/** - \ru \defgroup Mapping Построение плоских проекций - \en \defgroup Mapping Construction of Plane Projections - \~ \ingroup Geometric_Computation -*/ -/** - \ru \defgroup Inertia_Computation Вычисление инерционных характеристик - \en \defgroup Inertia_Computation Mass-Inertial Properties - \~ \ingroup Geometric_Computation -*/ -/** - \ru \defgroup Collision_Detection Определение столкновений - \en \defgroup Collision_Detection Collision Detection - \~ \ingroup Geometric_Computation -*/ -/** - \ru \defgroup Drawing Визуализация объектов - \en \defgroup Drawing Objects Visualization - \~ \ingroup Geometric_Computation -*/ - - -//----------------------------------------------------------------------------- -// \ru Подгруппа Base_Tools - Библиотека шаблонов и сериализации \en Library of Templates and Serializations \~ -//----------------------------------------------------------------------------- -/** - \ru \defgroup Base_Tools_Containers Контейнеры - \en \defgroup Base_Tools_Containers Containers - \~ \ingroup Base_Tools -*/ -/** - \ru \defgroup Base_Tools_SmartPointers Автоматические указатели - \en \defgroup Base_Tools_SmartPointers Smart Pointers - \~ \ingroup Base_Tools -*/ -/** - \ru \defgroup Base_Tools_String Работа со строками - \en \defgroup Base_Tools_String Work with Strings - \~ \ingroup Base_Tools -*/ -/** - \ru \defgroup Base_Tools_IO Работа с потоками - \en \defgroup Base_Tools_IO Work with Streams - \~ \ingroup Base_Tools -*/ - - -//----------------------------------------------------------------------------- -// -// \ru Подгруппа Geometric_Constraints - Модуль геометрических ограничений \en The Geometric Constraints Module \~ -// -//----------------------------------------------------------------------------- -/** - \ru \defgroup Mating Геометрические ограничения трёхмерных объектов - \en \defgroup Mating Geometric Constraint Solver in Three-Dimensional Space - \~ \ingroup Geometric_Constraints -*/ -/** - \ru \defgroup MathGC Геометрические ограничения двумерных объектов - \en \defgroup MathGC Geometric Constraint Solver in Two-Dimensional Space - \~ \ingroup Geometric_Constraints -*/ - -//----------------------------------------------------------------------------- -// \ru Подгруппа трехмерного геометрического решателя \en The subgroup of three-dimensional geometric constraint manager (GCM) -//----------------------------------------------------------------------------- -/** - \ru \defgroup GCM_3D_API Базовые функции и типы данных - \en \defgroup GCM_3D_API Basic functions and data types - \~ \ingroup Mating -*/ -/** - \ru \defgroup GCM_3D_ObjectAPI Объектный интерфейс - \en \defgroup GCM_3D_ObjectAPI Object-oriented interface - \~ \ingroup Mating -*/ -/** - \ru \defgroup GCM_3D_Routines Вспомогательные процедуры - \en \defgroup GCM_3D_Routines Auxiliary routines - \~ \ingroup Mating -*/ - -//----------------------------------------------------------------------------- -// \ru Подгруппа двумерного геометрического решателя \en The subgroup of two-dimensional geometric constraint engine (GCE) -//----------------------------------------------------------------------------- -/** - \ru \defgroup Constraints2D_API Интерфейс - \en \defgroup Constraints2D_API Solver Interface - \~ \ingroup MathGC -*/ - -//----------------------------------------------------------------------------------- -// -// \ru Подгруппа Data_Exchange - Модуль конвертеров \en The Converters Module \~ -// -//----------------------------------------------------------------------------------- -/** - \ru \defgroup Exchange_Interface Интерфейс конвертеров - \en \defgroup Exchange_Interface Converters Interface - \~ \ingroup Data_Exchange -*/ -/** - \internal - \ru \defgroup Exchange_Base Базовые объекты конвертеров - \en \defgroup Exchange_Base Converters Basic Objects - \~ \ingroup Data_Exchange - \endinternal -*/ -/** - \internal - \ru \defgroup Exchange_Util Вспомогательные объекты конвертеров - \en \defgroup Exchange_Util Converters Ancillary Facilities - \~ \ingroup Data_Exchange - \endinternal -*/ -/** - \internal - \ru \defgroup Exchange_Algorithms Алгоритмы конвертеров - \en \defgroup Exchange_Algorithms Converters Algorithms - \~ \ingroup Data_Exchange - \endinternal -*/ -/** - \ru \defgroup Exchange_Formats Поддерживаемые форматы данных - \en \defgroup Exchange_Formats Supported Data Formats - \~ \ingroup Data_Exchange -*/ - - -//----------------------------------------------------------------------------------- -// \ru Подгруппа Exchange_Formats - Поддерживаемые конвертером форматы данных \en Converters Supported Data Formats \~ -//----------------------------------------------------------------------------------- -/** - \ru \defgroup Parasolid_Exchange Parasolid конвертер - \en \defgroup Parasolid_Exchange Parasolid Converter - \~ \ingroup Exchange_Formats -*/ -/** - \ru \defgroup ACIS_Exchange ACIS конвертер - \en \defgroup ACIS_Exchange ACIS Converter - \~ \ingroup Exchange_Formats -*/ -/** - \ru \defgroup IGES_Exchange IGES конвертер - \en \defgroup IGES_Exchange IGES Converter - \~ \ingroup Exchange_Formats -*/ -/** - \ru \defgroup STEP_Exchange STEP конвертер - \en \defgroup STEP_Exchange STEP Converter - \~ \ingroup Exchange_Formats -*/ -/** - \ru \defgroup STL_Exchange STL конвертер - \en \defgroup STL_Exchange STL Converter - \~ \ingroup Exchange_Formats -*/ -/** - \ru \defgroup VRML_Exchange VRML конвертер - \en \defgroup VRML_Exchange VRML Converter - \~ \ingroup Exchange_Formats -*/ -/** - \ru \defgroup DXF_Exchange DXF конвертер 3D - \en \defgroup DXF_Exchange DXF Converter for 3D - \~ \ingroup Exchange_Formats -*/ - - -#endif // __MATH_DOXIGEN_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Группы для документирования с помощью Doxygen. + \en Groups for documenting by Doxygen. \~ +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __MATH_DOXIGEN_H +#define __MATH_DOXIGEN_H + + +//----------------------------------------------------------------------------- +// +// +// \ru Группы геометрического ядра \en Groups of the Geometric Kernel \~ +// +// +//----------------------------------------------------------------------------- + +/** + \ru \defgroup Geometric_Modelling C3D Modeler: Модуль геометрического моделирования + \en \defgroup Geometric_Modelling C3D Modeler: The Geometric Modelling Module + \~ \ingroup Geometric_Kernel +*/ + + +/** + \ru \defgroup Geometric_Constraints C3D Solver: Модуль геометрических ограничений + \en \defgroup Geometric_Constraints C3D Solver: The Geometric Constraints Module + \~ \ingroup Geometric_Kernel +*/ + + +/** + \ru \defgroup Data_Exchange C3D Converter: Модуль конвертеров + \en \defgroup Data_Exchange C3D Converter: The Converters Module + \~ \ingroup Geometric_Kernel +*/ + + +//----------------------------------------------------------------------------- +// +// \ru Подгруппа Geometric_Modelling - Модуль геометрического моделирования \en The Geometric Modelling Module \~ +// +//----------------------------------------------------------------------------- +/** + \ru \defgroup Geometric_Items Геометрические объекты + \en \defgroup Geometric_Items Geometric Objects + \~ \ingroup Geometric_Modelling +*/ +/** + \ru \defgroup Base_Items Объекты алгоритмов + \en \defgroup Base_Items Algorithm Objects + \~ \ingroup Geometric_Modelling +*/ +/** + \ru \defgroup Modelling_Functions Методы геометрических построений + \en \defgroup Modelling_Functions Geometric Construction Methods + \~ \ingroup Geometric_Modelling +*/ +/** + \ru \defgroup Geometric_Computation Методы геометрических вычислений + \en \defgroup Geometric_Computation Geometric Computations Methods + \~ \ingroup Geometric_Modelling +*/ +/** + \ru \defgroup Base_Tools Библиотека шаблонов и сериализации + \en \defgroup Base_Tools Templates and Serializations Library + \~ \ingroup Geometric_Modelling +*/ + + +//----------------------------------------------------------------------------------- +// \ru Подгруппа Geometric_Items - Геометрические объекты \en Geometric Objects \~ +//----------------------------------------------------------------------------------- +/** + \ru \defgroup Model_Items Объекты геометрической модели + \en \defgroup Model_Items Geometric Model Objects + \~ \ingroup Geometric_Items +*/ +/** + \ru \defgroup Topology_Items Топологические объекты + \en \defgroup Topology_Items Topological Objects + \~ \ingroup Geometric_Items +*/ +/** + \ru \defgroup Surfaces Поверхности + \en \defgroup Surfaces Surfaces + \~ \ingroup Geometric_Items +*/ +/** + \ru \defgroup Curves_3D Кривые + \en \defgroup Curves_3D Curves + \~ \ingroup Geometric_Items +*/ +/** + \ru \defgroup Point_3D Точка + \en \defgroup Point_3D Point + \~ \ingroup Geometric_Items +*/ +/** + \ru \defgroup Legend Вспомогательные объекты + \en \defgroup Legend Ancillary Items + \~ \ingroup Geometric_Items +*/ +/** + \ru \defgroup Curves_2D Двумерные кривые + \en \defgroup Curves_2D Two-Dimensional uv-Curves + \~ \ingroup Geometric_Items +*/ +/** + \ru \defgroup Region_2D Двумерные области + \en \defgroup Region_2D Two-Dimensional Regions + \~ \ingroup Geometric_Items +*/ + + +//----------------------------------------------------------------------------- +// \ru Подгруппа Base_Items - базовые объекты \en Base Objects \~ +//----------------------------------------------------------------------------- +/** + \ru \defgroup Mathematic_Base_3D Трёхмерные базовые объекты + \en \defgroup Mathematic_Base_3D Three-Dimensional Base Objects + \~ \ingroup Base_Items +*/ +/** + \ru \defgroup Mathematic_Base_2D Двумерные базовые объекты + \en \defgroup Mathematic_Base_2D Two-Dimensional Base Objects + \~ \ingroup Base_Items +*/ +/** + \ru \defgroup Model_Creators Строители + \en \defgroup Model_Creators Creators + \~ \ingroup Base_Items +*/ +/** + \ru \defgroup Model_Attributes Атрибуты + \en \defgroup Model_Attributes Attributes + \~ \ingroup Base_Items +*/ +/** + \ru \defgroup Functions Скалярные функции + \en \defgroup Functions Scalar Functions + \~ \ingroup Base_Items +*/ +/** + \ru \defgroup Build_Parameters Параметры операций + \en \defgroup Build_Parameters Operation Parameters + \~ \ingroup Base_Items +*/ +/** + \ru \defgroup Model_Properties Свойства + \en \defgroup Model_Properties Properties + \~ \ingroup Base_Items +*/ +/** + \ru \defgroup Data_Structures Структуры данных + \en \defgroup Data_Structures Data Structures + \~ \ingroup Base_Items +*/ +/** + \ru \defgroup Parser Разбор строки + \en \defgroup Parser Parser + \~ \ingroup Base_Items +*/ +/** + \ru \defgroup Names Имена + \en \defgroup Names Names + \~ \ingroup Base_Items +*/ +/** + \ru \defgroup Model Модель + \en \defgroup Model Model + \~ \ingroup Base_Items +*/ + + +//----------------------------------------------------------------------------------- +// \ru Подгруппа Modelling_Functions - Методы геометрических построений \en Methods of Geometric Constructions \~ +//----------------------------------------------------------------------------------- +/** + \ru \defgroup Solid_Modeling Построение тел + \en \defgroup Solid_Modeling Solid Modeling + \~ \ingroup Modelling_Functions +*/ +/** + \ru \defgroup Shell_Modeling Построение оболочек + \en \defgroup Shell_Modeling Shell Modeling + \~ \ingroup Modelling_Functions +*/ +/** + \ru \defgroup Sheet_Metal_Modeling Построения листовых тел + \en \defgroup Sheet_Metal_Modeling Sheet Metal Modeling + \~ \ingroup Modelling_Functions +*/ +/** + \ru \defgroup Direct_Modeling Прямое редактирование тел + \en \defgroup Direct_Modeling Direct Solid Modeling + \~ \ingroup Modelling_Functions +*/ +/** + \ru \defgroup Surface_Modeling Построение поверхностей + \en \defgroup Surface_Modeling Construction of Surfaces + \~ \ingroup Modelling_Functions +*/ +/** + \ru \defgroup Curve3D_Modeling Построение кривых в трёхмерном пространстве + \en \defgroup Curve3D_Modeling Construction of Curves + \~ \ingroup Modelling_Functions +*/ +/** + \ru \defgroup Curve_Modeling Построение кривых в двумерном пространстве + \en \defgroup Curve_Modeling Construction of uv-Curves in Two-Dimensional Space + \~ \ingroup Modelling_Functions +*/ +/** + \ru \defgroup Point_Modeling Операции с точками + \en \defgroup Point_Modeling Operations with Points + \~ \ingroup Modelling_Functions +*/ +/** + \ru \defgroup Base_Algorithms Базовые алгоритмы + \en \defgroup Base_Algorithms Base Algorithms + \~ \ingroup Modelling_Functions +*/ +/** + \ru \defgroup Algorithms_3D Алгоритмы в трёхмерном пространстве + \en \defgroup Algorithms_3D Algorithms in Three-Dimensional Space + \~ \ingroup Modelling_Functions +*/ +/** + \ru \defgroup Algorithms_2D Алгоритмы в двумерном пространстве + \en \defgroup Algorithms_2D Algorithms in Two-Dimensional Space + \~ \ingroup Modelling_Functions +*/ + + +//---------------------------------------------------------------------------- +// \ru Подгруппа Geometric_Computation - Методы геометрических расчетов \en Geometric Computations Methods \~ +//---------------------------------------------------------------------------- +/** + \ru \defgroup Polygonal_Objects Полигональные объекты + \en \defgroup Polygonal_Objects Polygonal Objects + \~ \ingroup Geometric_Computation +*/ +/** + \ru \defgroup Triangulation Триангуляция + \en \defgroup Triangulation Triangulation + \~ \ingroup Geometric_Computation +*/ +/** + \ru \defgroup Mapping Построение плоских проекций + \en \defgroup Mapping Construction of Plane Projections + \~ \ingroup Geometric_Computation +*/ +/** + \ru \defgroup Inertia_Computation Вычисление инерционных характеристик + \en \defgroup Inertia_Computation Mass-Inertial Properties + \~ \ingroup Geometric_Computation +*/ +/** + \ru \defgroup Collision_Detection Определение столкновений + \en \defgroup Collision_Detection Collision Detection + \~ \ingroup Geometric_Computation +*/ +/** + \ru \defgroup Drawing Визуализация объектов + \en \defgroup Drawing Objects Visualization + \~ \ingroup Geometric_Computation +*/ + + +//----------------------------------------------------------------------------- +// \ru Подгруппа Base_Tools - Библиотека шаблонов и сериализации \en Library of Templates and Serializations \~ +//----------------------------------------------------------------------------- +/** + \ru \defgroup Base_Tools_Containers Контейнеры + \en \defgroup Base_Tools_Containers Containers + \~ \ingroup Base_Tools +*/ +/** + \ru \defgroup Base_Tools_SmartPointers Автоматические указатели + \en \defgroup Base_Tools_SmartPointers Smart Pointers + \~ \ingroup Base_Tools +*/ +/** + \ru \defgroup Base_Tools_String Работа со строками + \en \defgroup Base_Tools_String Work with Strings + \~ \ingroup Base_Tools +*/ +/** + \ru \defgroup Base_Tools_IO Работа с потоками + \en \defgroup Base_Tools_IO Work with Streams + \~ \ingroup Base_Tools +*/ + + +//----------------------------------------------------------------------------- +// +// \ru Подгруппа Geometric_Constraints - Модуль геометрических ограничений \en The Geometric Constraints Module \~ +// +//----------------------------------------------------------------------------- +/** + \ru \defgroup Mating Геометрические ограничения трёхмерных объектов + \en \defgroup Mating Geometric Constraint Solver in Three-Dimensional Space + \~ \ingroup Geometric_Constraints +*/ +/** + \ru \defgroup MathGC Геометрические ограничения двумерных объектов + \en \defgroup MathGC Geometric Constraint Solver in Two-Dimensional Space + \~ \ingroup Geometric_Constraints +*/ + +//----------------------------------------------------------------------------- +// \ru Подгруппа трехмерного геометрического решателя \en The subgroup of three-dimensional geometric constraint manager (GCM) +//----------------------------------------------------------------------------- +/** + \ru \defgroup GCM_3D_API Базовые функции и типы данных + \en \defgroup GCM_3D_API Basic functions and data types + \~ \ingroup Mating +*/ +/** + \ru \defgroup GCM_3D_ObjectAPI Объектный интерфейс + \en \defgroup GCM_3D_ObjectAPI Object-oriented interface + \~ \ingroup Mating +*/ +/** + \ru \defgroup GCM_3D_Routines Вспомогательные процедуры + \en \defgroup GCM_3D_Routines Auxiliary routines + \~ \ingroup Mating +*/ + +//----------------------------------------------------------------------------- +// \ru Подгруппа двумерного геометрического решателя \en The subgroup of two-dimensional geometric constraint engine (GCE) +//----------------------------------------------------------------------------- +/** + \ru \defgroup Constraints2D_API Интерфейс + \en \defgroup Constraints2D_API Solver Interface + \~ \ingroup MathGC +*/ + +//----------------------------------------------------------------------------------- +// +// \ru Подгруппа Data_Exchange - Модуль конвертеров \en The Converters Module \~ +// +//----------------------------------------------------------------------------------- +/** + \ru \defgroup Exchange_Interface Интерфейс конвертеров + \en \defgroup Exchange_Interface Converters Interface + \~ \ingroup Data_Exchange +*/ +/** + \internal + \ru \defgroup Exchange_Base Базовые объекты конвертеров + \en \defgroup Exchange_Base Converters Basic Objects + \~ \ingroup Data_Exchange + \endinternal +*/ +/** + \internal + \ru \defgroup Exchange_Util Вспомогательные объекты конвертеров + \en \defgroup Exchange_Util Converters Ancillary Facilities + \~ \ingroup Data_Exchange + \endinternal +*/ +/** + \internal + \ru \defgroup Exchange_Algorithms Алгоритмы конвертеров + \en \defgroup Exchange_Algorithms Converters Algorithms + \~ \ingroup Data_Exchange + \endinternal +*/ +/** + \ru \defgroup Exchange_Formats Поддерживаемые форматы данных + \en \defgroup Exchange_Formats Supported Data Formats + \~ \ingroup Data_Exchange +*/ + + +//----------------------------------------------------------------------------------- +// \ru Подгруппа Exchange_Formats - Поддерживаемые конвертером форматы данных \en Converters Supported Data Formats \~ +//----------------------------------------------------------------------------------- +/** + \ru \defgroup Parasolid_Exchange Parasolid конвертер + \en \defgroup Parasolid_Exchange Parasolid Converter + \~ \ingroup Exchange_Formats +*/ +/** + \ru \defgroup ACIS_Exchange ACIS конвертер + \en \defgroup ACIS_Exchange ACIS Converter + \~ \ingroup Exchange_Formats +*/ +/** + \ru \defgroup IGES_Exchange IGES конвертер + \en \defgroup IGES_Exchange IGES Converter + \~ \ingroup Exchange_Formats +*/ +/** + \ru \defgroup STEP_Exchange STEP конвертер + \en \defgroup STEP_Exchange STEP Converter + \~ \ingroup Exchange_Formats +*/ +/** + \ru \defgroup STL_Exchange STL конвертер + \en \defgroup STL_Exchange STL Converter + \~ \ingroup Exchange_Formats +*/ +/** + \ru \defgroup VRML_Exchange VRML конвертер + \en \defgroup VRML_Exchange VRML Converter + \~ \ingroup Exchange_Formats +*/ +/** + \ru \defgroup DXF_Exchange DXF конвертер 3D + \en \defgroup DXF_Exchange DXF Converter for 3D + \~ \ingroup Exchange_Formats +*/ + + +#endif // __MATH_DOXIGEN_H diff --git a/C3d/Include/math_version.h b/C3d/Include/math_version.h index 9acb56a..b2d3c52 100644 --- a/C3d/Include/math_version.h +++ b/C3d/Include/math_version.h @@ -83,6 +83,8 @@ #define MATH_19_START_VERSION 0x13000000L ///< \ru Версия файла - 19.0 (начало версии). \en The file version - 19.0 (start of version). \~ \ingroup Base_Tools #define MATH_18_SP1_VERSION 0x13000005L ///< \ru Версия файла - 18.1. \en The file version - 18.1. \~ \ingroup Base_Tools #define C3D_2019_VERSION 0x1300000FL ///< \ru Версия файла - C3D 2019. \en The file version - C3D 2019. \~ \ingroup Base_Tools +#define MATH_19_VERSION 0x13000101L ///< \ru Версия файла - 19.0. \en The file version - 19.0. \~ \ingroup Base_Tools +#define C3D_2020_VERSION 0x13001004L ///< \ru Версия файла - C3D 2020. \en The file version - C3D 2020. \~ \ingroup Base_Tools //------------------------------------------------------------------------------ @@ -128,6 +130,7 @@ enum MbeWritableReleaseVersion wrv_MATH_18 = MATH_18_VERSION, ///< \ru Версия файла - 18.0. \en The file version - 18.0. wrv_MATH_18_SP1 = MATH_18_SP1_VERSION, ///< \ru Версия файла - 18.1. \en The file version - 18.1. wrv_C3D_2019 = C3D_2019_VERSION, ///< \ru Версия файла - C3D 2019. \en The file version - C3D 2019. + wrv_MATH_19 = MATH_19_VERSION, ///< \ru Версия файла - 19.0. \en The file version - 19.0. wrv_PrevRelease = wrv_MATH_18_SP1, ///< \ru Версия потока предпоследнего релиза. \en The previous release version. wrv_LastRelease = wrv_C3D_2019, ///< \ru Версия потока последнего релиза. \en The last release version. diff --git a/C3d/Include/mb_cart_point.h b/C3d/Include/mb_cart_point.h index ef9d07a..752ced9 100644 --- a/C3d/Include/mb_cart_point.h +++ b/C3d/Include/mb_cart_point.h @@ -15,6 +15,25 @@ class MATH_CLASS MbFloatPoint; +class MATH_CLASS MbCartPoint; + + +namespace c3d // namespace C3D +{ +typedef std::vector ParamPointsVector; ///< \ru Вектор точек. \en Points vector. +typedef std::vector ParamVectorsVector; ///< \ru Вектор векторов. \en Vectors vector. + +typedef std::pair ParamPointIndex; ///< \ru Пара точка-индекс. \en Pair point-index. +typedef std::pair Point2DIndex; ///< \ru Пара точка-индекс. \en Pair point-index. + +typedef std::pair IndexParamPoint; ///< \ru Пара индекс-точка. \en Pair index-point. +typedef std::pair IndexPoint2D; ///< \ru Пара индекс-точка. \en Pair index-point. + +typedef std::pair ParamPointsPair; ///< \ru Пара точка-точка. \en Pair point-point. + +typedef std::pair ParamPointVector; ///< \ru Пара точка-вектор. \en Pair point-vector. +typedef std::pair PointVector2D; ///< \ru Пара точка-вектор. \en Pair point-vector. +} //------------------------------------------------------------------------------ @@ -255,7 +274,7 @@ public : MbCartPoint operator - ( const MbDirection & d ) const; /// \ru Унарный минус. \en The unary minus. - MbCartPoint operator - (); + MbCartPoint operator - () const; /// \ru Умножить точку на число. \en Multiply a point by a number. MbCartPoint operator * ( double factor ) const; /// \ru Разделить точку на число. \en Divide a point by a number. @@ -299,7 +318,7 @@ public : \return \ru Искомое расстояние. \en The required distance. \~ */ - double DistanceToLineSeg( const MbCartPoint & p1, const MbCartPoint &p2 ) const; + double DistanceToLineSeg( const MbCartPoint & p1, const MbCartPoint & p2 ) const; /// \ru Длина вектора ( 0, p(x,y) ). \en The vector length ( 0, p(x,y) ). double Length() const; @@ -574,7 +593,7 @@ inline void MbCartPoint::operator /= ( double factor ) { //------------------------------------------------------------------------------ // \ru Унарный минус. \en The unary minus. // --- -inline MbCartPoint MbCartPoint::operator - () { +inline MbCartPoint MbCartPoint::operator - () const { return MbCartPoint ( - x, - y ); } diff --git a/C3d/Include/mb_cart_point3d.h b/C3d/Include/mb_cart_point3d.h index 680cbbb..b71dd12 100644 --- a/C3d/Include/mb_cart_point3d.h +++ b/C3d/Include/mb_cart_point3d.h @@ -11,14 +11,32 @@ #ifndef __MB_CART_POINT3D_H #define __MB_CART_POINT3D_H - #include class MATH_CLASS MbCartPoint; +class MATH_CLASS MbCartPoint3D; class MATH_CLASS MbFloatPoint3D; +namespace c3d // namespace C3D +{ +typedef std::vector SpacePointsVector; ///< \ru Вектор точек. \en Points vector. +typedef std::vector SpaceVectorsVector; ///< \ru Вектор векторов. \en Vectors vector. + +typedef std::pair SpacePointIndex; ///< \ru Пара точка-индекс. \en Pair point-index. +typedef std::pair Point3DIndex; ///< \ru Пара точка-индекс. \en Pair point-index. + +typedef std::pair IndexSpacePoint; ///< \ru Пара индекс-точка. \en Pair index-point. +typedef std::pair IndexPoint3D; ///< \ru Пара индекс-точка. \en Pair index-point. + +typedef std::pair SpacePointsPair; ///< \ru Пара точка-точка. \en Pair point-point. + +typedef std::pair SpacePointVector; ///< \ru Пара точка-вектор. \en Pair point-vector. +typedef std::pair PointVector3D; ///< \ru Пара точка-вектор. \en Pair point-vector. +} + + //------------------------------------------------------------------------------ /** \brief \ru Трехмерная точка. \en The three-dimensional point. \~ @@ -179,7 +197,7 @@ public : /// \ru Вычесть из точки точку. \en Subtract a point from the point. MbVector3D operator - ( const MbCartPoint3D & ) const; /// \ru Унарный минус. \en The unary minus. - MbCartPoint3D operator - (); + MbCartPoint3D operator - () const; /// \ru Вычислить точку как копию данной точки, преобразованную матрицей. \en Calculate the point as this copy transformed by the matrix. MbCartPoint3D operator * ( const MbMatrix3D & ) const; @@ -546,7 +564,7 @@ inline void MbCartPoint3D::Minimum( const MbCartPoint3D & p ) { //------------------------------------------------------------------------------ // \ru Унарный минус \en The unary minus // --- -inline MbCartPoint3D MbCartPoint3D::operator - () { +inline MbCartPoint3D MbCartPoint3D::operator - () const { return MbCartPoint3D( -x, -y, -z ); } diff --git a/C3d/Include/mb_class_traits.h b/C3d/Include/mb_class_traits.h index a3494dd..f2572c6 100644 --- a/C3d/Include/mb_class_traits.h +++ b/C3d/Include/mb_class_traits.h @@ -1,145 +1,223 @@ -////////////////////////////////////////////////////////////////////////////////////////// -/** - \file \brief \ru Характеристики типов для математических классов ядра "Mb..." - \en Type traits of the math basic classes of the kernel "Mb..." \~ -*/ -////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef __MB_CLASS_TRAITS_H -#define __MB_CLASS_TRAITS_H - -#include -#include -#include - - -class MATH_CLASS MbLineSegment; -class MATH_CLASS MbArc; -class MATH_CLASS MbNurbs; -class MATH_CLASS MbLine; -class MATH_CLASS MbPlaneInstance; -class MATH_CLASS MbSpaceInstance; -class MATH_CLASS MbAssembly; -class MATH_CLASS MbInstance; -class MbSolid; -class MATH_CLASS MbWireFrame; -class MATH_CLASS MbMesh; -class MATH_CLASS MbLineSegment3D; -class MATH_CLASS MbArc3D; -class MATH_CLASS MbFace; -class MATH_CLASS MbEdge; -class MATH_CLASS MbVertex; - - -//---------------------------------------------------------------------------------------- -/** \brief \ru Характеристики классов геометрического ядра C3D. - \en Class traits of C3D geometric kernel. - \attention \ru Экспериментальный класс. Пока приведены не все типы классов. - \en Experimental class. While not all listed types of classes. \~ -*/ -//--- -template -struct MbClassTraits -{ -private: - // \ru Идентификатор класса математического ядра. \en Identifier of class of the geometric kernel. - static const MbeSpaceType typeId = st_Undefined; -}; - -/* - 2D-curve sub-classes. -*/ -template<> -struct MbClassTraits { static const MbePlaneType typeId = pt_LineSegment; }; -template<> -struct MbClassTraits { static const MbePlaneType typeId = pt_Arc; }; -template<> -struct MbClassTraits { static const MbePlaneType typeId = pt_Nurbs; }; -template<> -struct MbClassTraits { static const MbePlaneType typeId = pt_Line; }; -/* - C3D model sub-classes. Inherited from MbItem. -*/ -template<> -struct MbClassTraits { static const MbeSpaceType typeId = st_PlaneInstance; }; -template<> -struct MbClassTraits { static const MbeSpaceType typeId = st_SpaceInstance; }; -template<> -struct MbClassTraits { static const MbeSpaceType typeId = st_Assembly; }; -template<> -struct MbClassTraits { static const MbeSpaceType typeId = st_Instance; }; -template<> -struct MbClassTraits { static const MbeSpaceType typeId = st_Solid; }; -template<> -struct MbClassTraits { static const MbeSpaceType typeId = st_WireFrame; }; -template<> -struct MbClassTraits { static const MbeSpaceType typeId = st_Mesh; }; -/* - 3D-curve sub-classes. -*/ -template<> -struct MbClassTraits { static const MbeSpaceType typeId = st_LineSegment3D; }; -template<> -struct MbClassTraits { static const MbeSpaceType typeId = st_Arc3D; }; - -/* - Topology sub-classes. -*/ -template<> -struct MbClassTraits { static const MbeTopologyType typeId = tt_Face; }; -template<> -struct MbClassTraits { static const MbeTopologyType typeId = tt_Edge; }; -template<> -struct MbClassTraits { static const MbeTopologyType typeId = tt_Vertex; }; - -//---------------------------------------------------------------------------------------- -// \ru Статическое приведение из типа к (разадресация типа) \en Static cast from type to -//--- -template struct Deref { private: typedef _Type Type; }; -template struct Deref { typedef _Type Type; }; -template struct Deref<_Type*> { typedef _Type Type; }; -template struct Deref<_Type&> { typedef _Type Type; }; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Динамическое приведение типа, основанное на функции Derived::IsA(). - \en Dynamic type cast based on the function Derived::IsA(). -*/ -//--- -template< class DerivedPtr, class ParentType > -DerivedPtr isa_cast ( ParentType * obj ) -{ - if ( (obj != NULL) && obj->IsA() == MbClassTraits::Type>::typeId ) - { - return static_cast( obj ); - } - return static_cast( NULL ); -} - -//---------------------------------------------------------------------------------------- -// -//--- -template< class DerivedPtr, class ParentPtr > -DerivedPtr _IsaCast( ParentPtr * obj, const MbTopItem * tItem ) -{ - if ( (obj != tItem) && obj->RefType() == rt_TopItem ) - { - tItem = static_cast( obj ); - } - return isa_cast( tItem ); -} - -//---------------------------------------------------------------------------------------- -/** \brief \ru Динамическое приведение типа, основанное на функции Derived::IsA(). - \en Dynamic type cast based on the function Derived::IsA(). -*/ -//--- -template< class DerivedPtr > -DerivedPtr isa_cast ( const MbRefItem * obj ) -{ - DerivedPtr resPtr = NULL; - return _IsaCast( obj, resPtr ); -} - -#endif // __MB_CLASS_TRAITS_H - +////////////////////////////////////////////////////////////////////////////////////////// +/** + \file \brief \ru Характеристики типов для математических классов ядра "Mb..." + \en Type traits of the math basic classes of the kernel "Mb..." \~ +*/ +////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef __MB_CLASS_TRAITS_H +#define __MB_CLASS_TRAITS_H + +#include +#include +#include +#include +#include + +class MbLineSegment; +class MbArc; +class MbNurbs; +class MbLine; +class MbItem; +class MbPlaneInstance; +class MbSpaceInstance; +class MbAssembly; +class MbInstance; +class MbSolid; +class MbWireFrame; +class MbMesh; +class MbLineSegment3D; +class MbArc3D; +class MbFace; +class MbEdge; +class MbCurveEdge; +class MbVertex; + + +//---------------------------------------------------------------------------------------- +/** \brief \ru Характеристики классов геометрического ядра C3D. + \en Class traits of C3D geometric kernel. + \attention \ru Экспериментальный класс. Пока приведены не все типы классов. + \en Experimental class. While not all listed types of classes. \~ +*/ +//--- +template +struct MbClassTraits +{ +private: + // \ru Идентификатор класса математического ядра. \en Identifier of class of the geometric kernel. + static const MbeSpaceType typeId = st_Undefined; +}; + +//---------------------------------------------------------------------------------------- +// For instantiated math classes. +//--- +struct _IsInstant +{ + template + inline bool operator()( const ParentType * obj, const ClassEnum _typeId ) + { + return obj == NULL ? true : obj->IsA() == _typeId; + } +}; + +//---------------------------------------------------------------------------------------- +// For abstract family classes. +//--- +struct _IsFamily +{ + template + inline bool operator()( const ParentType * obj, const ClassEnum _typeId ) + { + return obj == NULL ? true : obj->Family() == _typeId; + } +}; + +/* + 2D-curve sub-classes. +*/ +template<> +struct MbClassTraits:_IsInstant { static const MbePlaneType typeId = pt_LineSegment; }; +template<> +struct MbClassTraits:_IsInstant { static const MbePlaneType typeId = pt_Arc; }; +template<> +struct MbClassTraits:_IsInstant { static const MbePlaneType typeId = pt_Nurbs; }; +template<> +struct MbClassTraits:_IsInstant { static const MbePlaneType typeId = pt_Line; }; + +/* + Declarations of abstract classes inherited from MbSpaceItem. +*/ +template<> +struct MbClassTraits:_IsFamily { static const MbeSpaceType typeId = st_Item; }; +template<> +struct MbClassTraits:_IsFamily { static const MbeSpaceType typeId = st_Curve3D; }; +template<> +struct MbClassTraits:_IsFamily { static const MbeSpaceType typeId = st_Surface; }; + +/* + C3D model sub-classes. Inherited from MbItem. +*/ +template<> +struct MbClassTraits:_IsInstant { static const MbeSpaceType typeId = st_PlaneInstance; }; +template<> +struct MbClassTraits:_IsInstant { static const MbeSpaceType typeId = st_SpaceInstance; }; +template<> +struct MbClassTraits:_IsInstant { static const MbeSpaceType typeId = st_Assembly; }; +template<> +struct MbClassTraits:_IsInstant { static const MbeSpaceType typeId = st_Instance; }; +template<> +struct MbClassTraits:_IsInstant { static const MbeSpaceType typeId = st_Solid; }; +template<> +struct MbClassTraits:_IsInstant { static const MbeSpaceType typeId = st_WireFrame; }; +template<> +struct MbClassTraits:_IsInstant { static const MbeSpaceType typeId = st_Mesh; }; + +/* + 3D-curve sub-classes. +*/ +template<> +struct MbClassTraits:_IsInstant { static const MbeSpaceType typeId = st_LineSegment3D; }; +template<> +struct MbClassTraits:_IsInstant { static const MbeSpaceType typeId = st_Arc3D; }; + +/* + Topology sub-classes. +*/ +template<> +struct MbClassTraits:_IsInstant { static const MbeTopologyType typeId = tt_Face; }; +template<> +struct MbClassTraits:_IsInstant { static const MbeTopologyType typeId = tt_Edge; }; +template<> +struct MbClassTraits:_IsInstant { static const MbeTopologyType typeId = tt_CurveEdge; }; +template<> +struct MbClassTraits:_IsInstant { static const MbeTopologyType typeId = tt_Vertex; }; + +//---------------------------------------------------------------------------------------- +// \ru Статическое приведение из типа к (разадресация типа) \en Static cast from type to +//--- +template struct Deref { private: typedef _Type Type; }; +template struct Deref { typedef _Type Type; }; +template struct Deref<_Type*> { typedef _Type Type; }; +template struct Deref<_Type&> { typedef _Type Type; }; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Динамическое приведение типа, основанное на функции Derived::IsA(). + \en Dynamic type cast based on the function Derived::IsA(). +*/ +//--- +template< class DerivedPtr, class ParentType > +inline DerivedPtr _IsaCast( ParentType * obj ) +{ + MbClassTraits::Type> isAClass; + if ( isAClass(obj, isAClass.typeId) ) + { + return static_cast( obj ); + } + return static_cast( NULL ); +} + +//---------------------------------------------------------------------------------------- +// +//--- +template< class DerivedPtr, class ParentPtr > +inline DerivedPtr _IsaCast( ParentPtr * obj, const MbSpaceItem * item ) +{ + if ( (obj != item) && obj->RefType() == rt_SpaceItem ) + { + item = static_cast( obj ); + } + return _IsaCast( item ); +} + +//---------------------------------------------------------------------------------------- +// +//--- +template< class DerivedPtr, class ParentPtr > +inline DerivedPtr _IsaCast( ParentPtr * obj, const MbTopItem * tItem ) +{ + if ( (obj != tItem) && obj->RefType() == rt_TopItem ) + { + tItem = static_cast( obj ); + } + return _IsaCast( tItem ); +} + +//---------------------------------------------------------------------------------------- +/** \brief \ru Динамическое приведение типа, основанное на функции Derived::IsA(). + \en Dynamic type cast based on the function Derived::IsA(). +*/ +//--- +template< class DerivedPtr, class ParentType > +DerivedPtr isa_cast( ParentType * obj ) +{ + return _IsaCast( obj ); +} + +//---------------------------------------------------------------------------------------- +/** \brief \ru Динамическое приведение типа, основанное на функции Derived::IsA(). + \en Dynamic type cast based on the function Derived::IsA(). +*/ +//--- +template< class DerivedPtr > +DerivedPtr isa_cast( const MbRefItem * obj ) +{ + DerivedPtr resPtr = NULL; + return _IsaCast( obj, resPtr ); +} + +//---------------------------------------------------------------------------------------- +/** \brief \ru Динамическое приведение типа, основанное на функции Derived::IsA(). + \en Dynamic type cast based on the function Derived::IsA(). +*/ +//--- +template< class DerivedPtr > +DerivedPtr isa_cast( MbRefItem * obj ) +{ + DerivedPtr resPtr = NULL; + return _IsaCast( obj, resPtr ); +} + +#endif // __MB_CLASS_TRAITS_H + // eof \ No newline at end of file diff --git a/C3d/Include/mb_cube.h b/C3d/Include/mb_cube.h index 8ea6017..401c367 100644 --- a/C3d/Include/mb_cube.h +++ b/C3d/Include/mb_cube.h @@ -16,10 +16,10 @@ #include -#define CUBE_CONTROL_POINTS_COUNT 26 ///< \ru Количество характерных точек куба. \en The number of control points of cube. -#define CUBE_VERTEX_COUNT 8 ///< \ru Количество вершин куба. \en The number of cube vertices. -#define CUBE_EDGES_COUNT 12 ///< \ru Количество рёбер куба. \en The number of cube edges. -#define CUBE_FACES_COUNT 6 ///< \ru Количество граней куба. \en The number of cube faces. +const_expr size_t CUBE_CONTROL_POINTS_COUNT = 26; ///< \ru Количество характерных точек куба. \en The number of control points of cube. +const_expr size_t CUBE_VERTEX_COUNT = 8; ///< \ru Количество вершин куба. \en The number of cube vertices. +const_expr size_t CUBE_EDGES_COUNT = 12; ///< \ru Количество рёбер куба. \en The number of cube edges. +const_expr size_t CUBE_FACES_COUNT = 6; ///< \ru Количество граней куба. \en The number of cube faces. class MATH_CLASS MbRect; @@ -425,6 +425,8 @@ public : double GetMin( size_t k ) const { return k ? (--k ? pmin.z : pmin.y) : pmin.x; } /// \ru Доступ к максимальной координате по индексу. \en Access to a coordinate by an index. double GetMax( size_t k ) const { return k ? (--k ? pmax.z : pmax.y) : pmax.x; } + /// \ru Дать длина по стороне. \en Get side length. + double GetSideLength( size_t k ) const { return k ? (--k ? ::fabs(GetLengthZ()) : ::fabs(GetLengthY())) : ::fabs(GetLengthX()); } /// \ru Дать себя. \en Give itself. const MbCube & GetCube() const { return *this; } diff --git a/C3d/Include/mb_data.h b/C3d/Include/mb_data.h index 0a98e72..0ca34ec 100644 --- a/C3d/Include/mb_data.h +++ b/C3d/Include/mb_data.h @@ -1,557 +1,597 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Данные. - \en Data. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __MB_DATA_H -#define __MB_DATA_H - - -#include -#include -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Данные для вычисления шага. - \en Data for step calculation. \~ - \details \ru Данные для вычисления шага при триангуляции поверхностей и граней. \n - \en Data for step calculation during face triangulation. \n \~ - \ingroup Data_Structures -*/ -// --- -class MATH_CLASS MbStepData { - -private: - /** \brief \ru Способ вычисления приращения параметра при движении по объекту. - \en The method of calculation of parameter increment by the object. \~ - \details \ru Способ вычисления приращения параметра при движении по кривой или поверхности. - Для визуализации геометрической формы используется способ ist_SpaceStep. \n - Для операций построения используется способ ist_DeviationStep. \n - Для 3D принтеров используется способ ist_MetricStep и могут быть добавлены первые два. \n - Для привязки объектов к параметрам поверхности следует добавить способ ist_ParamStep, - Для определения столкновений элементов модели используется способ ist_CollisionStep, - Для вычисления инерционных характеристик используется способ ist_MipStep. - \en Methods of calculation of parameter increment by the object. \n \~ - Step by sag ist_SpaceStep is used for visualizations. - Step by deviation angle ist_DeviationStep is used for calculation. - Step by length ist_MetricStep is used for 3D printer (plus by sag and by deviation angle). \n - Special step ist_ParamStep is added for binding with surface parameters. - Special step ist_CollisionStep is used for collision detection of model elements. - Special step ist_MipStep is used for calculation of inertial characteristics. \~ - */ - uint8 stepType; - double sag; ///< \ru Максимально допустимый прогиб кривой или поверхности в соседних точках на расстоянии шага. \en The maximum permissible sag of the curve or surface at adjacent points away step. \~ - double angle; ///< \ru Максимально допустимое угловое отклонение касательных кривой или нормалей поверхности в соседних точках на расстоянии шага. \en The maximum angular deviation of the curve or surface normal in the neighboring points on the distance of a step. \~ - double length; ///< \ru Максимально допустимое расстояние между соседними точками на расстоянии шага. \en The maximum distance between points a step away. \~ - size_t maxCount; ///< \ru Максимальное количество ячеек в строке и ряду триангуляционной сетки (если 0, то не задано). \en Maximum count of cell in row and column for triangulation grid (if 0, then unlimited). \~ - -public: - - /// \ru Конструктор с заданным типом шага. \en Constructor by step type. - MbStepData( MbeStepType t, double s ); - /// \ru Пустой конструктор. \en Empty constructor. - MbStepData() - : stepType( ist_SpaceStep ) - , sag ( Math::visualSag ) - , angle ( Math::deviateSag ) - , length ( MAXIMON ) - , maxCount( 0 ) - {} - /// \ru Конструктор копирования. \en Copy-constructor. - MbStepData( const MbStepData & other ) - : stepType( other.stepType ) - , sag ( other.sag ) - , angle ( other.angle ) - , length ( other.length ) - , maxCount( other.maxCount ) - {} - /// \ru Деструктор. \en Destructor. - ~MbStepData() {} - -public: - /// \ru Установить способ вычисления шага. \en Set the method of calculation of parameter increment by the object. \~ - void SetStepType( MbeStepType t, bool add = true ) { if ( add ) { stepType |= t; } else { stepType = (uint8)t; } } - /// \ru Установить максимально допустимый прогиб на расстоянии шага. \en Set the maximum permissible sag at adjacent points away step. \~ - void SetSag ( double s ) { sag = s; } - /// \ru Установить максимально допустимое угловое отклонение в соседних точках. \en Set the maximum angular deviation in the neighboring points on the distance of a step. \~ - void SetAngle ( double a ) { angle = a; } - /// \ru Установить максимально допустимое расстояние между соседними точками на расстоянии шага. \en Set the maximum distance between points a step away. \~ - void SetLength ( double l ) { length = l; } - /// \ru Установить максимально допустимое количество ячеек в строке или ряду триангуляционной сетки. \en Set the maximum count of cell in row and column for triangulation grid. \~ - void SetMaxCount( size_t c ) { maxCount = c; } - - /// \ru Дать максимально допустимый прогиб на расстоянии шага. \en Get the maximum permissible sag at adjacent points away step. \~ - double GetSag () const { return sag; } - /// \ru Дать максимально допустимое угловое отклонение в соседних точках. \en Get the maximum angular deviation in the neighboring points on the distance of a step. \~ - double GetAngle () const { return angle; } - /// \ru Дать максимально допустимое расстояние между соседними точками на расстоянии шага. \en Get the maximum distance between points a step away. \~ - double GetLength () const { return length; } - /// \ru Дать максимально допустимое количество ячеек в строке или ряду триангуляционной сетки. \en Get the maximum count of cell in row and column for triangulation grid. \~ - size_t GetMaxCount() const { return maxCount; } - - /// \ru Указанный шаг задан. \en This step is set. - bool StepIs( MbeStepType sType ) const { return !!(stepType & sType); } - - /// \ru Задан шаг по максимальному прогибу. \en Step by maximum deflection defined. \~ - bool SagIncluded() const { return //!!(stepType & ist_ParamStep) || - !!(stepType & ist_SpaceStep) || - !!(stepType & ist_CollisionStep); } - /// \ru Задан шаг по угловому отклонению. \en Step by angular deviation defined. \~ - bool AngleIncluded() const { return !!(stepType & ist_DeviationStep) || - !!(stepType & ist_MipStep); } - /// \ru Задан шаг по максимальному расстоянию. \en Step by maximum distance defined. \~ - bool LengthIncluded() const { return !!(stepType & ist_MetricStep); } - - /// \ru Установить данные для вычисления шага при триангуляции. \en Set data for step calculation during triangulation. - void Init( MbeStepType t, double s, double a, double l, size_t c = 0 ) - { - stepType = (uint8)t; - sag = s; - angle = a; - length = l; - maxCount = c; -} - - /// \ru Установить данные для вычисления шага при триангуляции. \en Set data for step calculation during triangulation. - void InitStepBySag( double s ) - { - stepType = (uint8)ist_SpaceStep; - sag = ::fabs(s); - angle = Math::deviateSag; - length = MAXIMON; - maxCount = 0; - } - - /// \ru Функция копирования данных. \en Copy function of data. - void Init( const MbStepData & other ) - { - stepType = other.stepType; - sag = other.sag; - angle = other.angle; - length = other.length; - maxCount = other.maxCount; - } - - /// \ru Оператор присваивания. \en Assignment operator. - MbStepData & operator = ( const MbStepData & other ) - { - stepType = other.stepType; - sag = other.sag; - angle = other.angle; - length = other.length; - maxCount = other.maxCount; - return *this; - } - - /// \ru Сбросить данные для вычисления шага. \en Reset data for step calculation. - void Reset() - { - stepType = (uint8)ist_SpaceStep; - sag = Math::visualSag; - angle = Math::deviateSag; - length = MAXIMON; - maxCount = 0; - } - - /// \ru Функция сравнения. \en Equal function. - bool IsEqual( const MbStepData & other, double epsilon ) const; - /// \ru Вырожденный ли объект? \en Is empty? - bool IsEmpty( double epsilon ) const; - - KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbStepData, MATH_FUNC_EX ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Данные для построения полигонального объекта. - \en Way for polygonal object constructing. \~ - \details \ru Дополнительные данные для построения полигонального объекта и триангуляции поверхностей и граней. \n - \en Way for polygonal object constructing or face triangulation. \n \~ - \ingroup Data_Structures -*/ -// --- -struct MATH_CLASS MbFormNote { - -private: - bool exact; ///< \ru Выполнить построение полигональных объектов на числах double (true) на числах float (false). \en Polygonal objects will created on double data (true) on float data (false). - bool wire; ///< \ru Строить изолинии поверхностей. \en Construct isolines of surfaces. \~ - bool grid; ///< \ru Строить триангуляцию поверхностей. \en Construct triangulations of surfaces. \~ - bool seam; ///< \ru Дублировать точки триангуляции на швах (true) замкнутых поверхностей, не дублировать точки триангуляции на швах (false). \en Flag for not ignore the seam edges. \~ - bool quad; ///< \ru Строить четырёхугольники (true) при триангуляции поверхностей (по возможности). \en Build quadrangles (true) in triangulations of surfaces (if possible). \~ - -public: - - /// \ru Пустой конструктор. \en Empty constructor. - MbFormNote() - : exact( false ) - , wire( false ) - , grid( true ) - , seam( true ) - , quad( false ) - {} - /// \ru Конструктор с заданным типом шага. \en Constructor by step type. - MbFormNote( bool w, bool g, bool s = true, bool e = false, bool q = false ) - : exact( e ) - , wire( w ) - , grid( g ) - , seam( s ) - , quad( q ) - {} - /// \ru Конструктор копирования. \en Copy-constructor. - MbFormNote( const MbFormNote & other ) - : exact( other.exact ) - , wire( other.wire ) - , grid( other.grid ) - , seam( other.seam ) - , quad( other.quad ) - {} - /// \ru Деструктор. \en Destructor. - ~MbFormNote() {} - -public: - /// \ru Выполнить построение полигональных объектов на числах double (true) на числах float (false). \en Polygonal objects will created on double data (true) on float data (false). - void SetExact( bool e ) { exact = e; } - /// \ru Установить флаг построения изолиний поверхностей. \en Set flag construction isolines of surfaces. \~ - void SetWire( bool w ) { wire = w; } - /// \ru Установить флаг cтроить триангуляцию поверхностей. \en Set flag constructing triangulations of surfaces. \~ - void SetGrid( bool g ) { grid = g; } - /// \ru Установить флаг шовных ребер. \en Set flag for seam edges. \~ - void SetSeam( bool s ) { seam = s; } - /// \ru Установить флаг cтроить четырёхугольники при триангуляции поверхностей (по возможности).. \en Set flag for build quadrangles in triangulations of surfaces (if possible). \~ - void SetQuad( bool q ) { quad = q; } - - /// \ru Выполнить построение полигональных объектов на числах double (true) на числах float (false). \en Polygonal objects will created on double data (true) on float data (false). - bool DoExact() const { return exact; } - /// \ru Дать флаг построения изолиний поверхностей. \en Whether to construct isolines of surfaces? \~ - bool Wire() const { return wire;} - /// \ru Cтроить триангуляцию поверхностей? \en Whether to construct triangulations of surfaces? \~ - bool Grid() const { return grid; } - /// \ru Дублировать точки триангуляции на швах? \en Get flag for seam edges. \~ - bool Seam() const { return seam; } - /// \ru Строить четырёхугольники при триангуляции поверхностей (по возможности).? \en Whether to build quadrangles in triangulations of surfaces (if possible)? \~ - bool Quad() const { return quad; } - - /// \ru Установить Данные для вычисления шага при триангуляции. \en Set data for step calculation during triangulation. - void Init( bool w, bool g, bool s, bool e = false, bool q = false ) { - exact = e; - wire = w; - grid = g; - seam = s; - quad = q; - } - - /// \ru Функция копирования данных. \en Copy function of data. - void Init( const MbFormNote & other ) { - exact = other.exact; - wire = other.wire; - grid = other.grid; - seam = other.seam; - quad = other.quad; - } - - /// \ru Оператор присваивания. \en Assignment operator. - MbFormNote & operator = ( const MbFormNote & other ) { - exact = other.exact; - wire = other.wire; - grid = other.grid; - seam = other.seam; - quad = other.quad; - return *this; - } - - /// \ru Функция сравнения. \en Equal function. - bool IsEqual( const MbFormNote & other ) const; - -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Данные для управления двумерными объектами. - \en The data for two-dimensional object control. \~ - \details \ru Данные содержат контрольные точки двумерных объектов. \n - \en The data consist of two-dimensional control points for object. \n \~ -\ingroup Data_Structures -*/ -// --- -struct MATH_CLASS MbControlData { - -private: - SArray total; ///< \ru Точки, перемещаемые вместе. \en Points conveyed along. \~ - SArray share; ///< \ru Точки, перемещаемые по отдельности. \en Points transported separately. \~ - mutable size_t totalIndex; ///< \ru Индекс текущей точки total. \en The index of current point total. \~ - mutable size_t shareIndex; ///< \ru Индекс текущей точки share. \en The index of current point share. \~ - -public: - /// \ru Пустой конструктор. \en Empty constructor. - MbControlData() : total( 0, 1 ), share(0, 1), totalIndex( 0 ), shareIndex(0) {} - /// \ru Конструктор копирования. \en Copy-constructor. - MbControlData( const MbControlData & other ) - : total( other.total ) - , share( other.share ) - , totalIndex( other.totalIndex ) - , shareIndex( other.shareIndex ) - {} - /// \ru Деструктор. \en Destructor. - ~MbControlData() {} - -public: - /// \ru Зарезервировать память. \en Size reserve. \~ - void ReserveTotal( size_t c ) { total.Reserve( c ); } - /// \ru Зарезервировать память. \en Size reserve. \~ - void ReserveShare( size_t c ) { share.Reserve( c ); } - - /// \ru Добавить точку. \en Add a point conveyed along. \~ - void AddTotal( const MbCartPoint & p ) { total.push_back(p); } - /// \ru Добавить точки. \en Add points. \~ - template - void AddTotals( const PointsVector & points ) - { - size_t addCnt = points.size(); - if ( addCnt > 0 ) { - total.reserve( total.size() + addCnt ); - for ( size_t k = 0; k < addCnt; ++k ) - total.push_back( points[k] ); - } - } - /// \ru Добавить точку. \en Add a point. \~ - void AddShare( const MbCartPoint & p ) { share.push_back(p); } - /// \ru Добавить точки. \en Add points. \~ - template - void AddShares( const PointsVector & points ) - { - size_t addCnt = points.size(); - if ( addCnt > 0 ) { - share.reserve( share.size() + addCnt ); - for ( size_t k = 0; k < addCnt; ++k ) - share.push_back( points[k] ); - } - } - - /// \ru Выдать количество точек. \en Get points count conveyed along. \~ - size_t TotalCount() const { return total.Count(); } - /// \ru Выдать количество точек. \en Get points count. \~ - size_t ShareCount() const { return share.Count(); } - - /// \ru Обнулить индексы. \en Reset index. - void ResetIndex() const { totalIndex = 0; shareIndex = 0; } - - /// \ru Выдать очередную точку. \en Get current point for totalIndex++. - bool GetTotal( MbCartPoint & p ) const; - /// \ru Выдать очередную точку. \en Get current point for shareIndex++. - bool GetShare( MbCartPoint & p ) const; - /// \ru Выдать точку по индексу. \en Get point by index conveyed along. - bool GetTotal( size_t i, MbCartPoint & p ) const; - /// \ru Выдать точку по индексу. \en Get point by index. - bool GetShare( size_t i, MbCartPoint & p ) const; - /// \ru Выдать общее точек. \en Get all points count. \~ - size_t Count() const { return total.Count() + share.Count(); } - /// \ru Выдать точку по индексу. \en Get point by index conveyed along. - bool GetPoint( size_t i, MbCartPoint & p ) const; - /// \ru Установить точку по индексу. \en Set point by index conveyed along. - bool SetPoint( size_t i, MbCartPoint & p ); - /// \ru Выдать все точки. \en Get points. - SArray & SetTotalPoints() { return total; } - /// \ru Выдать все точки. \en Get points. - SArray & SetSharePoints() { return share; } - /// \ru Освободить память. \en Free memory. - void HardFlush() { total.HardFlush(); share.HardFlush(); totalIndex = 0; shareIndex = 0; } - - /// \ru Преобразовать согласно матрице. \en Transform according to the matrix. - void Transform( const MbMatrix & matrix ); - /// \ru Сдвинуть вдоль вектора. \en Translate along a vector. - void Move( const MbVector & to ); - /// \ru Повернуть вокруг точки. \en Rotate around a point. - void Rotate( const MbCartPoint & point, double angle ); - - /// \ru Дать точку по индексу. \en Set point by index. - MbCartPoint & operator []( size_t i ) const; - /// \ru Оператор присваивания. \en Assignment operator. - MbControlData & operator = ( const MbControlData & other ) - { - total = other.total; - share = other.share; - totalIndex = other.totalIndex; - shareIndex = other.shareIndex; - return *this; - } - /// \ru Вырожденный ли объект? \en Is empty? - bool IsEmpty() const { return ( total.Count() == 0 && share.Count() == 0 ); } -}; // MbControlData - - -//------------------------------------------------------------------------------ -/** \brief \ru Данные для управления трехмерными объектами. - \en The data for three-dimensional object control. \~ - \details \ru Данные содержат контрольные точки трехмерных объектов. \n - \en The data consist of three-dimensional control points for object. \n \~ - \ingroup Data_Structures -*/ -// --- -struct MATH_CLASS MbControlData3D { - -private: - SArray total; ///< \ru Точки, перемещаемые вместе. \en Points conveyed along. \~ - SArray share; ///< \ru Точки, перемещаемые по отдельности. \en Points transported separately. \~ - mutable size_t totalIndex; ///< \ru Индекс текущей точки total. \en The index of current point total. \~ - mutable size_t shareIndex; ///< \ru Индекс текущей точки share. \en The index of current point share. \~ - -public: - /// \ru Пустой конструктор. \en Empty constructor. - MbControlData3D() : total( 0, 1 ), share(0, 1), totalIndex( 0 ), shareIndex(0) {} - /// \ru Конструктор копирования. \en Copy-constructor. - MbControlData3D( const MbControlData3D & other ) - : total( other.total ) - , share( other.share ) - , totalIndex( other.totalIndex ) - , shareIndex( other.shareIndex ) - {} - /// \ru Деструктор. \en Destructor. - ~MbControlData3D() {} - -public: - /// \ru Зарезервировать память. \en Size reserve. \~ - void ReserveTotal( size_t c ) { total.Reserve( c ); } - /// \ru Зарезервировать память. \en Size reserve. \~ - void ReserveShare( size_t c ) { share.Reserve( c ); } - - /// \ru Добавить точку. \en Add a point conveyed along. \~ - void AddTotal( const MbCartPoint3D & p ) { total.Add(p); } - /// \ru Добавить точки. \en Add points. \~ - template - void AddTotals( const PointsVector & points ) - { - size_t addCnt = points.size(); - if ( addCnt > 0 ) { - total.reserve( total.size() + addCnt ); - for ( size_t k = 0; k < addCnt; ++k ) - total.push_back( points[k] ); - } - } - /// \ru Добавить точку. \en Add a point. \~ - void AddShare( const MbCartPoint3D & p ) { share.Add(p); } - /// \ru Добавить точки. \en Add points. \~ - template - void AddShares( const PointsVector & points ) - { - size_t addCnt = points.size(); - if ( addCnt > 0 ) { - share.reserve( share.size() + addCnt ); - for ( size_t k = 0; k < addCnt; ++k ) - share.push_back( points[k] ); - } - } - /// \ru Выдать количество точек. \en Get points count conveyed along. \~ - size_t TotalCount() const { return total.Count(); } - /// \ru Выдать количество точек. \en Get points count. \~ - size_t ShareCount() const { return share.Count(); } - - /// \ru Обнулить индексы. \en Reset index. - void ResetIndex() const { totalIndex = 0; shareIndex = 0; } - /// \ru Выдать очередную точку. \en Get current point for totalIndex++. - bool GetTotal( MbCartPoint3D & p ) const; - /// \ru Выдать очередную точку. \en Get current point for shareIndex++. - bool GetShare( MbCartPoint3D & p ) const; - /// \ru Выдать точку по индексу. \en Get point by index conveyed along. - bool GetTotal( size_t i, MbCartPoint3D & p ) const; - /// \ru Выдать точку по индексу. \en Get point by index. - bool GetShare( size_t i, MbCartPoint3D & p ) const; - /// \ru Выдать общее точек. \en Get all points count. \~ - size_t Count() const { return total.Count() + share.Count(); } - /// \ru Выдать точку по индексу. \en Get point by index conveyed along. - bool GetPoint( size_t i, MbCartPoint3D & p ) const; - /// \ru Установить точку по индексу. \en Set point by index conveyed along. - bool SetPoint( size_t i, MbCartPoint3D & p ); - /// \ru Выдать все точки. \en Get points. - SArray & SetTotalPoints() { return total; } - /// \ru Выдать все точки. \en Get points. - SArray & SetSharePoints() { return share; } - /// \ru Освободить память. \en Free memory. - void HardFlush() { total.HardFlush(); share.HardFlush(); totalIndex = 0; shareIndex = 0; } - - /// \ru Преобразовать согласно матрице. \en Transform according to the matrix. - void Transform( const MbMatrix3D & matrix ); - /// \ru Сдвинуть вдоль вектора. \en Translate along a vector. - void Move( const MbVector3D & to ); - /// \ru Повернуть вокруг оси. \en Rotate around an axis. - void Rotate( const MbAxis3D & axis, double angle ); - - /// \ru Дать точку по индексу. \en Set point by index. - MbCartPoint3D & operator []( size_t i ) const; - /// \ru Оператор присваивания. \en Assignment operator. - MbControlData3D & operator = ( const MbControlData3D & other ) - { - total = other.total; - share = other.share; - totalIndex = other.totalIndex; - shareIndex = other.shareIndex; - return *this; - } - /// \ru Вырожденный ли объект? \en Is empty? - bool IsEmpty() const { return ( total.Count() == 0 && share.Count() == 0 ); } -}; // MbControlData3D - - -//------------------------------------------------------------------------------ -/** \brief \ru Данные управления построением гладких кривых на базе трехмерной ломаной. - \en The data for the construction of smooth curves based on a three-dimensional polyline. \~ - \details \ru Данные содержат параметры построения сплайнов с плавным изменением кривизны. \n - \en The data contains parameters for constructing splines with smooth curvature changes. \n \~ - \ingroup Data_Structures -*/ -// --- -struct MATH_CLASS MbFairCurveData { - -public: - int arrange; ///< \ru Перераспределение точек по контуру (0 - без перераспределения, 1 - с перераспределением). \en Redistribution of points (0 - without of distribution, 1 - with distribution) . \~ - int subdivision; ///< \ru Коэффициент уплотнения кривой (0 - без уплотнения, 1 - однократное уплотнение, 2 - двукратное уплотнение). \en Curve subdivision coefficient (0 - without subdivision, 1 - single subdivision, 2 - double subdivision). \~ - int accountCurvature; ///< \ru Учет кривизны в концевых точках (0 - не учитывать, 1 - в начальной точке, 2 - в конечной точке, 3 - учитывать оба конца). \en Accounting for curvature at end points (0 - do not take into account, 1 - at the starting point, 2 - at the ending point, 3 - take into account both ends). \~ - int accountInflexVector; ///< \ru Учет вектора в точке перегиба (0 - направление звена S-полигона, 1 - направление касательной). \en How to take into account the vector at the inflection point (0 - direction of segment of S-polygon, 1 - direction of tangent to curve). \~ - int approx; ///< \ru Метод аппроксимации (0 - B-сплайновая кривая по узловым точкам, 1 - изогеометрическая B-сплайновая кривая, 2 - изогеометрическая NURBzS кривая). \en Approx method (0 - B-spline curve on nodes, 1 - isogeometric B-spline curve, 2 - isogeometric NURBzS curve). \~ - int degreeBSpline; ///< \ru Степень B-сплайновой кривой m (3<=m<=10). \en The degree m (3<=m<=10) of B-Spline curve. \~ - int outFormat; ///< \ru Выходной формат сплайна (2 - S-полигон, 3 - GB-полигон). \en Output format of spline (2 - S-polygon, 3 - GB-polygon). \~ - int nSegments; ///< \ru Количество сегментов сплайна. \en Number of segments of spline. \~ - int numSegment; ///< \ru Номер сегмента. \en Number of segment. \~ - double tParam; ///< \ru Внутренний параметр точки сегмента сплайна. \en Point internal param on segment of spline. \~ - int warning; ///< \ru Предупреждение о работе. \en The operation warning. \~ - int error; ///< \ru Ошибка о работе. \en The operation error. \~ - double clothoidRMin; ///< \ru Радиус кривизны на конце начального участка клотоиды. \en Curvature radius on end of initial part of Clothoid. \~ - double clothoidLMax; ///< \ru Максимальная длина начального участка клотоиды. \en Max length of initial part of Clothoid. \~ - int clothoidSegms; ///< \ru Количество сегментов аппроксимирующей клотоиду кривой. \en Number of segments of curve approximated the Clothoid. \~ - - -public: - /// \ru Пустой конструктор. \en Empty constructor. - MbFairCurveData() {} - - ~MbFairCurveData() {} - - /// \ru Оператор присваивания. \en Assignment operator. - MbFairCurveData & operator = ( const MbFairCurveData & other ) - { - arrange = other.arrange; - subdivision = other.subdivision; - accountCurvature = other.accountCurvature; - accountInflexVector = other.accountInflexVector; - approx = other.approx; - degreeBSpline = other.degreeBSpline; - outFormat = other.outFormat; - nSegments = other.nSegments; - numSegment = other.numSegment; - tParam = other.tParam;; - warning = other.warning; - error = other.error; - clothoidRMin = other.clothoidRMin; - clothoidLMax = other.clothoidLMax; - clothoidSegms = other.clothoidSegms; - return *this; - } -}; // MbFairCurveData - - -#endif // __MB_DATA_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Данные. + \en Data. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __MB_DATA_H +#define __MB_DATA_H + + +#include +#include +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Данные для вычисления шага. + \en Data for step calculation. \~ + \details \ru Данные для вычисления шага при триангуляции поверхностей и граней. \n + \en Data for step calculation during face triangulation. \n \~ + \ingroup Data_Structures +*/ +// --- +class MATH_CLASS MbStepData { + +private: + /** \brief \ru Способ вычисления приращения параметра при движении по объекту. + \en The method of calculation of parameter increment by the object. \~ + \details \ru Способ вычисления приращения параметра при движении по кривой или поверхности. + Для визуализации геометрической формы используется способ ist_SpaceStep. \n + Для операций построения используется способ ist_DeviationStep. \n + Для 3D принтеров используется способ ist_MetricStep и могут быть добавлены первые два. \n + Для привязки объектов к параметрам поверхности следует добавить способ ist_ParamStep, + Для определения столкновений элементов модели используется способ ist_CollisionStep, + Для вычисления инерционных характеристик используется способ ist_MipStep. + \en Methods of calculation of parameter increment by the object. \n \~ + Step by sag ist_SpaceStep is used for visualizations. + Step by deviation angle ist_DeviationStep is used for calculation. + Step by length ist_MetricStep is used for 3D printer (plus by sag and by deviation angle). \n + Special step ist_ParamStep is added for binding with surface parameters. + Special step ist_CollisionStep is used for collision detection of model elements. + Special step ist_MipStep is used for calculation of inertial characteristics. \~ + */ + uint8 stepType; + double sag; ///< \ru Максимально допустимый прогиб кривой или поверхности в соседних точках на расстоянии шага. \en The maximum permissible sag of the curve or surface at adjacent points away step. \~ + double angle; ///< \ru Максимально допустимое угловое отклонение касательных кривой или нормалей поверхности в соседних точках на расстоянии шага. \en The maximum angular deviation of the curve or surface normal in the neighboring points on the distance of a step. \~ + double length; ///< \ru Максимально допустимое расстояние между соседними точками на расстоянии шага. \en The maximum distance between points a step away. \~ + size_t maxCount; ///< \ru Максимальное количество ячеек в строке и ряду триангуляционной сетки (если 0, то не задано). \en Maximum count of cell in row and column for triangulation grid (if 0, then unlimited). \~ + +public: + + /// \ru Конструктор с заданным типом шага. \en Constructor by step type. + MbStepData( MbeStepType t, double s ); + /// \ru Пустой конструктор. \en Empty constructor. + MbStepData() + : stepType( ist_SpaceStep ) + , sag ( Math::visualSag ) + , angle ( Math::deviateSag ) + , length ( MAXIMON ) + , maxCount( 0 ) + {} + /// \ru Конструктор копирования. \en Copy-constructor. + MbStepData( const MbStepData & other ) + : stepType( other.stepType ) + , sag ( other.sag ) + , angle ( other.angle ) + , length ( other.length ) + , maxCount( other.maxCount ) + {} + /// \ru Деструктор. \en Destructor. + ~MbStepData() {} + +public: + /// \ru Установить способ вычисления шага. \en Set the method of calculation of parameter increment by the object. \~ + void SetStepType( MbeStepType t, bool add = true ) { if ( add ) { stepType |= t; } else { stepType = (uint8)t; } } + /// \ru Установить максимально допустимый прогиб на расстоянии шага. \en Set the maximum permissible sag at adjacent points away step. \~ + void SetSag ( double s ) { sag = s; } + /// \ru Установить максимально допустимое угловое отклонение в соседних точках. \en Set the maximum angular deviation in the neighboring points on the distance of a step. \~ + void SetAngle ( double a ) { angle = a; } + /// \ru Установить максимально допустимое расстояние между соседними точками на расстоянии шага. \en Set the maximum distance between points a step away. \~ + void SetLength ( double l ) { length = l; } + /// \ru Установить максимально допустимое количество ячеек в строке или ряду триангуляционной сетки. \en Set the maximum count of cell in row and column for triangulation grid. \~ + void SetMaxCount( size_t c ) { maxCount = c; } + + /// \ru Дать максимально допустимый прогиб на расстоянии шага. \en Get the maximum permissible sag at adjacent points away step. \~ + double GetSag () const { return sag; } + /// \ru Дать максимально допустимое угловое отклонение в соседних точках. \en Get the maximum angular deviation in the neighboring points on the distance of a step. \~ + double GetAngle () const { return angle; } + /// \ru Дать максимально допустимое расстояние между соседними точками на расстоянии шага. \en Get the maximum distance between points a step away. \~ + double GetLength () const { return length; } + /// \ru Дать максимально допустимое количество ячеек в строке или ряду триангуляционной сетки. \en Get the maximum count of cell in row and column for triangulation grid. \~ + size_t GetMaxCount() const { return maxCount; } + + /// \ru Указанный шаг задан. \en This step is set. + bool StepIs( MbeStepType sType ) const { return !!(stepType & sType); } + + /// \ru Задан шаг по максимальному прогибу. \en Step by maximum deflection defined. \~ + bool SagIncluded() const { return //!!(stepType & ist_ParamStep) || + !!(stepType & ist_SpaceStep) || + !!(stepType & ist_CollisionStep); } + /// \ru Задан шаг по угловому отклонению. \en Step by angular deviation defined. \~ + bool AngleIncluded() const { return !!(stepType & ist_DeviationStep) || + !!(stepType & ist_MipStep); } + /// \ru Задан шаг по максимальному расстоянию. \en Step by maximum distance defined. \~ + bool LengthIncluded() const { return !!(stepType & ist_MetricStep); } + + /// \ru Установить данные для вычисления шага при триангуляции. \en Set data for step calculation during triangulation. + void Init( MbeStepType t, double s, double a, double l, size_t c = 0 ) + { + stepType = (uint8)t; + sag = s; + angle = a; + length = l; + maxCount = c; +} + + /// \ru Установить данные для вычисления шага при триангуляции. \en Set data for step calculation during triangulation. + void InitStepBySag( double s ) + { + stepType = (uint8)ist_SpaceStep; + sag = ::fabs(s); + angle = Math::deviateSag; + length = MAXIMON; + maxCount = 0; + } + + /// \ru Функция копирования данных. \en Copy function of data. + void Init( const MbStepData & other ) + { + stepType = other.stepType; + sag = other.sag; + angle = other.angle; + length = other.length; + maxCount = other.maxCount; + } + + /// \ru Оператор присваивания. \en Assignment operator. + MbStepData & operator = ( const MbStepData & other ) + { + stepType = other.stepType; + sag = other.sag; + angle = other.angle; + length = other.length; + maxCount = other.maxCount; + return *this; + } + + /// \ru Сбросить данные для вычисления шага. \en Reset data for step calculation. + void Reset() + { + stepType = (uint8)ist_SpaceStep; + sag = Math::visualSag; + angle = Math::deviateSag; + length = MAXIMON; + maxCount = 0; + } + + /// \ru Функция сравнения. \en Equal function. + bool IsEqual( const MbStepData & other, double epsilon ) const; + /// \ru Вырожденный ли объект? \en Is empty? + bool IsEmpty( double epsilon ) const; + + KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbStepData, MATH_FUNC_EX ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Данные для построения полигонального объекта. + \en Way for polygonal object constructing. \~ + \details \ru Дополнительные данные для построения полигонального объекта и триангуляции поверхностей и граней. \n + \en Way for polygonal object constructing or face triangulation. \n \~ + \ingroup Data_Structures +*/ +// --- +struct MATH_CLASS MbFormNote { + +private: + bool exact; ///< \ru Выполнить построение полигональных объектов на числах double (true) на числах float (false). \en Polygonal objects will created on double data (true) on float data (false). + bool wire; ///< \ru Строить изолинии поверхностей. \en Construct isolines of surfaces. \~ + bool grid; ///< \ru Строить триангуляцию поверхностей. \en Construct triangulations of surfaces. \~ + bool seam; ///< \ru Дублировать точки триангуляции на швах (true) замкнутых поверхностей, не дублировать точки триангуляции на швах (false). \en Flag for not ignore the seam edges. \~ + bool quad; ///< \ru Строить четырёхугольники (true) при триангуляции поверхностей (по возможности). \en Build quadrangles (true) in triangulations of surfaces (if possible). \~ + bool fair; ///< \ru Удалить вырожденные треугольники (true). \en Degenerate triangles removing (if surface has pole). \~ + +public: + + /// \ru Пустой конструктор. \en Empty constructor. + MbFormNote() + : exact( false ) + , wire( false ) + , grid( true ) + , seam( true ) + , quad( false ) + , fair( false ) + {} + /// \ru Конструктор с заданным типом шага. \en Constructor by step type. + MbFormNote( bool w, bool g, bool s = true, bool e = false, bool q = false, bool f = false ) + : exact( e ) + , wire( w ) + , grid( g ) + , seam( s ) + , quad( q ) + , fair( f ) + {} + /// \ru Конструктор копирования. \en Copy-constructor. + MbFormNote( const MbFormNote & other ) + : exact( other.exact ) + , wire( other.wire ) + , grid( other.grid ) + , seam( other.seam ) + , quad( other.quad ) + , fair( other.fair ) + {} + /// \ru Деструктор. \en Destructor. + ~MbFormNote() {} + +public: + /// \ru Выполнить построение полигональных объектов на числах double (true) на числах float (false). \en Polygonal objects will created on double data (true) on float data (false). + void SetExact( bool e ) { exact = e; } + /// \ru Установить флаг построения изолиний поверхностей. \en Set flag construction isolines of surfaces. \~ + void SetWire( bool w ) { wire = w; } + /// \ru Установить флаг cтроить триангуляцию поверхностей. \en Set flag constructing triangulations of surfaces. \~ + void SetGrid( bool g ) { grid = g; } + /// \ru Установить флаг шовных ребер. \en Set flag for seam edges. \~ + void SetSeam( bool s ) { seam = s; } + /// \ru Установить флаг cтроить четырёхугольники при триангуляции поверхностей (по возможности).. \en Set flag for build quadrangles in triangulations of surfaces (if possible). \~ + void SetQuad( bool q ) { quad = q; } + /// \ru Установить флаг удаления вырожденных треугольников. \en Set flag degenerate triangles removing. \~ + void SetFair( bool f ) { fair = f; } + + /// \ru Выполнить построение полигональных объектов на числах double (true) на числах float (false). \en Polygonal objects will created on double data (true) on float data (false). + bool DoExact() const { return exact; } + /// \ru Дать флаг построения изолиний поверхностей. \en Whether to construct isolines of surfaces? \~ + bool Wire() const { return wire;} + /// \ru Cтроить триангуляцию поверхностей? \en Whether to construct triangulations of surfaces? \~ + bool Grid() const { return grid; } + /// \ru Дублировать точки триангуляции на швах? \en Get flag for seam edges. \~ + bool Seam() const { return seam; } + /// \ru Строить четырёхугольники при триангуляции поверхностей (по возможности).? \en Whether to build quadrangles in triangulations of surfaces (if possible)? \~ + bool Quad() const { return quad; } + /// \ru Удалить вырожденные треугольники? \en Get flag for degenerate triangles removing. \~ + bool Fair() const { return fair; } + + /// \ru Установить Данные для вычисления шага при триангуляции. \en Set data for step calculation during triangulation. + void Init( bool w, bool g, bool s, bool e = false, bool q = false, bool f = false ) { + exact = e; + wire = w; + grid = g; + seam = s; + quad = q; + fair = f; + } + + /// \ru Функция копирования данных. \en Copy function of data. + void Init( const MbFormNote & other ) { + exact = other.exact; + wire = other.wire; + grid = other.grid; + seam = other.seam; + quad = other.quad; + fair = other.fair; + } + + /// \ru Оператор присваивания. \en Assignment operator. + MbFormNote & operator = ( const MbFormNote & other ) { + exact = other.exact; + wire = other.wire; + grid = other.grid; + seam = other.seam; + quad = other.quad; + fair = other.fair; + return *this; + } + + /// \ru Функция сравнения. \en Equal function. + bool IsEqual( const MbFormNote & other ) const; + +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Данные для управления двумерными объектами. + \en The data for two-dimensional object control. \~ + \details \ru Данные содержат контрольные точки двумерных объектов. \n + \en The data consist of two-dimensional control points for object. \n \~ +\ingroup Data_Structures +*/ +// --- +struct MATH_CLASS MbControlData { + +private: + SArray total; ///< \ru Точки, перемещаемые вместе. \en Points conveyed along. \~ + SArray share; ///< \ru Точки, перемещаемые по отдельности. \en Points transported separately. \~ + mutable size_t totalIndex; ///< \ru Индекс текущей точки total. \en The index of current point total. \~ + mutable size_t shareIndex; ///< \ru Индекс текущей точки share. \en The index of current point share. \~ + +public: + /// \ru Пустой конструктор. \en Empty constructor. + MbControlData() : total( 0, 1 ), share(0, 1), totalIndex( 0 ), shareIndex(0) {} + /// \ru Конструктор копирования. \en Copy-constructor. + MbControlData( const MbControlData & other ) + : total( other.total ) + , share( other.share ) + , totalIndex( other.totalIndex ) + , shareIndex( other.shareIndex ) + {} + /// \ru Деструктор. \en Destructor. + ~MbControlData() {} + +public: + /// \ru Зарезервировать память. \en Size reserve. \~ + void ReserveTotal( size_t c ) { total.Reserve( c ); } + /// \ru Зарезервировать память. \en Size reserve. \~ + void ReserveShare( size_t c ) { share.Reserve( c ); } + + /// \ru Добавить точку. \en Add a point conveyed along. \~ + void AddTotal( const MbCartPoint & p ) { total.push_back(p); } + /// \ru Добавить точки. \en Add points. \~ + template + void AddTotals( const PointsVector & points ) + { + size_t addCnt = points.size(); + if ( addCnt > 0 ) { + total.reserve( total.size() + addCnt ); + for ( size_t k = 0; k < addCnt; ++k ) + total.push_back( points[k] ); + } + } + /// \ru Добавить точку. \en Add a point. \~ + void AddShare( const MbCartPoint & p ) { share.push_back(p); } + /// \ru Добавить точки. \en Add points. \~ + template + void AddShares( const PointsVector & points ) + { + size_t addCnt = points.size(); + if ( addCnt > 0 ) { + share.reserve( share.size() + addCnt ); + for ( size_t k = 0; k < addCnt; ++k ) + share.push_back( points[k] ); + } + } + + /// \ru Выдать количество точек. \en Get points count conveyed along. \~ + size_t TotalCount() const { return total.Count(); } + /// \ru Выдать количество точек. \en Get points count. \~ + size_t ShareCount() const { return share.Count(); } + + /// \ru Обнулить индексы. \en Reset index. + void ResetIndex() const { totalIndex = 0; shareIndex = 0; } + + /// \ru Выдать очередную точку. \en Get current point for totalIndex++. + bool GetTotal( MbCartPoint & p ) const; + /// \ru Выдать очередную точку. \en Get current point for shareIndex++. + bool GetShare( MbCartPoint & p ) const; + /// \ru Выдать точку по индексу. \en Get point by index conveyed along. + bool GetTotal( size_t i, MbCartPoint & p ) const; + /// \ru Выдать точку по индексу. \en Get point by index. + bool GetShare( size_t i, MbCartPoint & p ) const; + /// \ru Выдать общее точек. \en Get all points count. \~ + size_t Count() const { return total.Count() + share.Count(); } + /// \ru Выдать точку по индексу. \en Get point by index conveyed along. + bool GetPoint( size_t i, MbCartPoint & p ) const; + /// \ru Установить точку по индексу. \en Set point by index conveyed along. + bool SetPoint( size_t i, MbCartPoint & p ); + /// \ru Выдать все точки. \en Get points. + SArray & SetTotalPoints() { return total; } + /// \ru Выдать все точки. \en Get points. + SArray & SetSharePoints() { return share; } + /// \ru Освободить память. \en Free memory. + void HardFlush() { total.HardFlush(); share.HardFlush(); totalIndex = 0; shareIndex = 0; } + + /// \ru Преобразовать согласно матрице. \en Transform according to the matrix. + void Transform( const MbMatrix & matrix ); + /// \ru Сдвинуть вдоль вектора. \en Translate along a vector. + void Move( const MbVector & to ); + /// \ru Повернуть вокруг точки. \en Rotate around a point. + void Rotate( const MbCartPoint & point, double angle ); + + /// \ru Дать точку по индексу. \en Set point by index. + MbCartPoint & operator []( size_t i ) const; + /// \ru Оператор присваивания. \en Assignment operator. + MbControlData & operator = ( const MbControlData & other ) + { + total = other.total; + share = other.share; + totalIndex = other.totalIndex; + shareIndex = other.shareIndex; + return *this; + } + /// \ru Вырожденный ли объект? \en Is empty? + bool IsEmpty() const { return ( total.Count() == 0 && share.Count() == 0 ); } +}; // MbControlData + + +//------------------------------------------------------------------------------ +/** \brief \ru Данные для управления трехмерными объектами. + \en The data for three-dimensional object control. \~ + \details \ru Данные содержат контрольные точки трехмерных объектов. \n + \en The data consist of three-dimensional control points for object. \n \~ + \ingroup Data_Structures +*/ +// --- +struct MATH_CLASS MbControlData3D { + +private: + SArray total; ///< \ru Точки, перемещаемые вместе. \en Points conveyed along. \~ + SArray share; ///< \ru Точки, перемещаемые по отдельности. \en Points transported separately. \~ + mutable size_t totalIndex; ///< \ru Индекс текущей точки total. \en The index of current point total. \~ + mutable size_t shareIndex; ///< \ru Индекс текущей точки share. \en The index of current point share. \~ + +public: + /// \ru Пустой конструктор. \en Empty constructor. + MbControlData3D() : total( 0, 1 ), share(0, 1), totalIndex( 0 ), shareIndex(0) {} + /// \ru Конструктор копирования. \en Copy-constructor. + MbControlData3D( const MbControlData3D & other ) + : total( other.total ) + , share( other.share ) + , totalIndex( other.totalIndex ) + , shareIndex( other.shareIndex ) + {} + /// \ru Деструктор. \en Destructor. + ~MbControlData3D() {} + +public: + /// \ru Зарезервировать память. \en Size reserve. \~ + void ReserveTotal( size_t c ) { total.Reserve( c ); } + /// \ru Зарезервировать память. \en Size reserve. \~ + void ReserveShare( size_t c ) { share.Reserve( c ); } + + /// \ru Добавить точку. \en Add a point conveyed along. \~ + void AddTotal( const MbCartPoint3D & p ) { total.Add(p); } + /// \ru Добавить точки. \en Add points. \~ + template + void AddTotals( const PointsVector & points ) + { + size_t addCnt = points.size(); + if ( addCnt > 0 ) { + total.reserve( total.size() + addCnt ); + for ( size_t k = 0; k < addCnt; ++k ) + total.push_back( points[k] ); + } + } + /// \ru Добавить точку. \en Add a point. \~ + void AddShare( const MbCartPoint3D & p ) { share.Add(p); } + /// \ru Добавить точки. \en Add points. \~ + template + void AddShares( const PointsVector & points ) + { + size_t addCnt = points.size(); + if ( addCnt > 0 ) { + share.reserve( share.size() + addCnt ); + for ( size_t k = 0; k < addCnt; ++k ) + share.push_back( points[k] ); + } + } + /// \ru Выдать количество точек. \en Get points count conveyed along. \~ + size_t TotalCount() const { return total.Count(); } + /// \ru Выдать количество точек. \en Get points count. \~ + size_t ShareCount() const { return share.Count(); } + + /// \ru Обнулить индексы. \en Reset index. + void ResetIndex() const { totalIndex = 0; shareIndex = 0; } + /// \ru Выдать очередную точку. \en Get current point for totalIndex++. + bool GetTotal( MbCartPoint3D & p ) const; + /// \ru Выдать очередную точку. \en Get current point for shareIndex++. + bool GetShare( MbCartPoint3D & p ) const; + /// \ru Выдать точку по индексу. \en Get point by index conveyed along. + bool GetTotal( size_t i, MbCartPoint3D & p ) const; + /// \ru Выдать точку по индексу. \en Get point by index. + bool GetShare( size_t i, MbCartPoint3D & p ) const; + /// \ru Выдать общее точек. \en Get all points count. \~ + size_t Count() const { return total.Count() + share.Count(); } + /// \ru Выдать точку по индексу. \en Get point by index conveyed along. + bool GetPoint( size_t i, MbCartPoint3D & p ) const; + /// \ru Установить точку по индексу. \en Set point by index conveyed along. + bool SetPoint( size_t i, MbCartPoint3D & p ); + /// \ru Выдать все точки. \en Get points. + SArray & SetTotalPoints() { return total; } + /// \ru Выдать все точки. \en Get points. + SArray & SetSharePoints() { return share; } + /// \ru Освободить память. \en Free memory. + void HardFlush() { total.HardFlush(); share.HardFlush(); totalIndex = 0; shareIndex = 0; } + + /// \ru Преобразовать согласно матрице. \en Transform according to the matrix. + void Transform( const MbMatrix3D & matrix ); + /// \ru Сдвинуть вдоль вектора. \en Translate along a vector. + void Move( const MbVector3D & to ); + /// \ru Повернуть вокруг оси. \en Rotate around an axis. + void Rotate( const MbAxis3D & axis, double angle ); + + /// \ru Дать точку по индексу. \en Set point by index. + MbCartPoint3D & operator []( size_t i ) const; + /// \ru Оператор присваивания. \en Assignment operator. + MbControlData3D & operator = ( const MbControlData3D & other ) + { + total = other.total; + share = other.share; + totalIndex = other.totalIndex; + shareIndex = other.shareIndex; + return *this; + } + /// \ru Вырожденный ли объект? \en Is empty? + bool IsEmpty() const { return ( total.Count() == 0 && share.Count() == 0 ); } +}; // MbControlData3D + + +//------------------------------------------------------------------------------ +/** \brief \ru Данные управления построением гладких кривых на базе трехмерной ломаной. + \en The data for the construction of smooth curves based on a three-dimensional polyline. \~ + \details \ru Данные содержат параметры построения сплайнов с плавным изменением кривизны. \n + \en The data contains parameters for constructing splines with smooth curvature changes. \n \~ + \ingroup Data_Structures +*/ +// --- +//#define C3D_DEBUG_FAIR_CURVES ///< \ru Для отладочной печати данных гладких кривых. \en For debugging printing of smooth curve data. +struct MATH_CLASS MbFairCurveData { + +public: + bool closed; ///< \ru Признак замкнутости кривой. \en Sign of closed curve \~ + bool fairing; ///< \ru Сглаживание (0 - без сглаживания, 1 - со сглаживанием). \en Smoothing of curve: 0 - disable, 1 - enable. \~ + bool arrange; ///< \ru Перераспределение точек по контуру (false - без перераспределения, true - с перераспределением). \en Redistribution of points (false - without of distribution, true - with distribution) . \~ + MbeFairSubdivision subdivision; ///< \ru Коэффициент уплотнения кривой. \en Curve subdivision coefficient . \~ + MbeFairCurvature accountCurvature; ///< \ru Учет кривизны в концевых точках. \en Accounting for curvature at end points. \~ + MbeFairVector accountInflexVector; ///< \ru Учет вектора в точке перегиба (0 - направление звена S-полигона, 1 - направление касательной). \en How to take into account the vector at the inflection point (0 - direction of segment of S-polygon, 1 - direction of tangent to curve). \~ + MbeFixPntTng fixPntTng; ///< \ru Фиксировать точки на касательных / касательные в точках. \en Fix the points on tangents / the tangents on points. + MbeFairApprox approx; ///< \ru Метод аппроксимации. \en Approx method. \~ + int create; ///< \ru Исходные ГО: 1 - опорная ломаная, 2 - касательная ломаная. \en Initial GD: 1 - base polyline, 2 - tangent polyline. \~ + size_t degreeBSpline; ///< \ru Степень B-сплайновой кривой m (3<=m<=10). \en The degree m (3<=m<=10) of B-Spline curve. \~ + MbeFairSplineFormat initFormat; ///< \ru Исходный формат сплайна (1 - открытый S-полигон, 2 - закрытый S-полигон, 3 - GB-полигон). \en Output format of spline (1 - foat S-polygon, 2 - clamped S-polygon, 3 - GB-polygon). \~ + MbeFairSplineFormat outFormat; ///< \ru Выходной формат сплайна (2 - S-полигон, 3 - GB-полигон). \en Output format of spline (2 - S-polygon, 3 - GB-polygon). \~ + size_t nSegments; ///< \ru Количество сегментов сплайна. \en Number of segments of spline. \~ + size_t numSegment; ///< \ru Номер сегмента. \en Number of segment. \~ + double tParam; ///< \ru Внутренний параметр точки сегмента сплайна. \en Point internal param on segment of spline. \~ + double clothoidRMin; ///< \ru Радиус кривизны на конце начального участка клотоиды. \en Curvature radius on end of initial part of Clothoid. \~ + double clothoidLMax; ///< \ru Максимальная длина начального участка клотоиды. \en Max length of initial part of Clothoid. \~ + size_t clothoidSegms; ///< \ru Количество сегментов аппроксимирующей клотоиду кривой. \en Number of segments of curve approximated the Clothoid. \~ + MbeFairWarning warning; ///< \ru Предупреждение о работе. \en The operation warning. \~ + MbResultType error; ///< \ru Ошибка о работе. \en The operation error. \~ + SArray arrayFixPntTngSign; ///< \ru Признаки учета касательных на точках / точек на касательных. \en Signs of points on tangents / tangents on points. + size_t numberOfIterationsBSpl; ///< \ru Количество итераций построения B-сплайна (заданное и фактическое). \en The number of iterations for building the B-spline (given and actual). + size_t numberOfIterationsVCurve; ///< \ru Количество итераций построения V-кривой (заданное и фактическое). \en The number of iterations for building the V-curve (given and actual). + double realAccuracyBSpl; ///< \ru Точность построения B-сплайна (заданная и фактическая). \en The accuracy of creating the B-spline (given and actual). + double realAccuracyVCurve; ///< \ru Точность построения V-кривой (заданная и фактическая). \en The accuracy of creating the V-curve (given and actual). +#ifdef C3D_DEBUG_FAIR_CURVES + /*DEBUG*/ FILE *prt; +#endif + +public: + /// \ru Пустой конструктор. \en Empty constructor. + MbFairCurveData() : + closed( false ), fairing( false ), arrange( false ), subdivision( fairSubdiv_Single ), + accountCurvature( fairCur_No ), accountInflexVector( fairVector_SegmentDir ), + fixPntTng( fixPntTng_NotFix ), + approx( fairApprox_KnotsSpline ), create(1), degreeBSpline( 8 ), + initFormat( fairFormat_Open ), outFormat( fairFormat_Close ), + nSegments( 4 ), numSegment( 0 ), tParam( 0.5 ), + warning( fwarn_Success ), error( rt_Success ), clothoidRMin( 50.0 ), + clothoidLMax( 200.0 ), clothoidSegms( 10 ), numberOfIterationsBSpl( 500 ), + numberOfIterationsVCurve( 192 ), +#ifdef C3D_DEBUG_FAIR_CURVES + prt(C3D_NULL_PTR), +#endif + realAccuracyBSpl( METRIC_ACCURACY ), realAccuracyVCurve( METRIC_EPSILON ) {} + + ~MbFairCurveData() {} + + /// \ru Оператор присваивания. \en Assignment operator. + MbFairCurveData & operator = ( const MbFairCurveData & other ) + { + closed = other.closed; + fairing = other.fairing; + arrange = other.arrange; + subdivision = other.subdivision; + accountCurvature = other.accountCurvature; + accountInflexVector = other.accountInflexVector; + approx = other.approx; + create = other.create; + degreeBSpline = other.degreeBSpline; + outFormat = other.outFormat; + nSegments = other.nSegments; + numSegment = other.numSegment; + tParam = other.tParam;; + warning = other.warning; + error = other.error; + clothoidRMin = other.clothoidRMin; + clothoidLMax = other.clothoidLMax; + clothoidSegms = other.clothoidSegms; + return *this; + } +}; // MbFairCurveData + + +#endif // __MB_DATA_H diff --git a/C3d/Include/mb_enum.h b/C3d/Include/mb_enum.h index eb3cc67..b754335 100644 --- a/C3d/Include/mb_enum.h +++ b/C3d/Include/mb_enum.h @@ -135,7 +135,7 @@ enum MbeSplineParamType { \ingroup Data_Structures */ // --- -enum MbeNurbsCurveForm { +enum MbeNurbsCurveForm { ncf_Unspecified = 0, ///< \ru Неопределенная форма. \en Undefined form ncf_PolylineForm, ///< \ru Ломаная. \en Polyline. ncf_CircularArc, ///< \ru Дуга окружности. \en Circle arc. @@ -145,6 +145,7 @@ enum MbeNurbsCurveForm { ncf_BezierForm, ///< \ru Сплайн Безье. \en Bezier spline. ncf_HermitForm, ///< \ru Сплайн Эрмита. \en Hermite spline. ncf_SurfacePoleForm, ///< \ru Сплайн в полюсе поверхности. \en Spline in the pole of surface. + ncf_FairCurveForm, ///< \ru Плавная кривая. \en Fair curve. }; @@ -167,6 +168,21 @@ enum MbeMatingType { }; +//------------------------------------------------------------------------------ +/** \brief \ru Тип сопряжения по кривой заплатки. + \en The conjugation type by patch curve. \~ + \details \ru Тип сопряжения по кривой заплатки. + \en The conjugation type by patch curve. \~ + \ingroup Data_Structures +*/ +//--- +enum MbePatchMatingType { + pmt_None = 0, ///< \ru Без сопряжений. \en Without conjugations. + pmt_Tangent = 1, ///< \ru Сопряжение по касательной. \en Tangential conjugation + pmt_SmoothG2 = 2, ///< \ru Гладкое сопряжение по первой производной касательной (по кривизне). \en The smooth conjugation by the first derivative of the tangent (the curvature). +}; + + //------------------------------------------------------------------------------ /** \brief \ru Тип сопряжения по ребрам. \en The type of conjugation by edges. \~ @@ -402,9 +418,9 @@ enum MbeConnectingType { //------------------------------------------------------------------------------ -/** \brief \ru Cпособы передачи данных при копировании оболочек. +/** \brief \ru Способы передачи данных при копировании оболочек. \en Methods of transferring data while copying shells. \~ - \details \ru Cпособы передачи данных при копировании оболочек в операциях над телами. \n + \details \ru Способы передачи данных при копировании оболочек в операциях над телами. \n Любая операция, и удачная, и ошибочная, безвозвратно модифицирует вершины, рёбра и грани оболочек операндов. \n Для сохранения неизменной исходной оболочки операнда применяется полное или частичное копирование данных. \n Используются четыре способа передачи данных в операцию. \n @@ -642,5 +658,129 @@ enum MbeProgBarId_PointsSurface pbarId_PointsSurface_End, }; +//------------------------------------------------------------------------------ +/** \brief \ru Управление построением гладких кривых на базе трехмерной ломаной. Коэффициент уплотнения кривой. + \en Construction of smooth curves based on a three-dimensional polyline. Curve subdivision coefficient. \~ + \ingroup Data_Structures +*/ +//--- +enum MbeFairSubdivision +{ + fairSubdiv_No = 0, ///< \ru Без уплотнения. \en Without subdivision. + fairSubdiv_Single = 1, ///< \ru Однократное уплотнение. \en Single subdivision. + fairSubdiv_Double = 2 ///< \ru Двукратное уплотнение. \en Double subdivision. +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Управление построением гладких кривых на базе трехмерной ломаной. Учет кривизны в концевых точках. + \en Construction of smooth curves based on a three-dimensional polyline. Accounting for curvature at end points. \~ + \ingroup Data_Structures +*/ +//--- +enum MbeFairCurvature +{ + fairCur_No = 0, ///< \ru Не учитывать. \en Do not take into accoun. + fairCur_Start = 1, ///< \ru В начальной точке. \en At the starting point. + fairCur_End = 2, ///< \ru В конечной точке. \en At the ending point. + fairCur_Both = 3, ///< \ru Учитывать оба конца. \en Take into account both ends. +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Управление построением гладких кривых на базе трехмерной ломаной. Метод аппроксимации. + \en Construction of smooth curves based on a three-dimensional polyline. Approx method. \~ + \ingroup Data_Structures +*/ +//--- +enum MbeFairApprox +{ + fairApprox_KnotsSpline = 0, ///< \ru B-сплайновая кривая по узловым точкам. \en B-spline curve on nodes. + fairApprox_IsoSpline = 1, ///< \ru Изогеометрическая B-сплайновая кривая. \en Isogeometric B-spline curve. + fairApprox_IsoNurbs = 2, ///< \ru Изогеометрическая NURBzS кривая. \en Isogeometric NURBzS curve. +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Управление построением гладких кривых на базе трехмерной ломаной. Учет вектора в точке перегиба. + \en Construction of smooth curves based on a three-dimensional polyline. Taking into account vector at inflection point. \~ +*/ +//--- +enum MbeFairVector +{ + fairVector_SegmentDir = 0, ///< \ru Направление звена S-полигона. \en Direction of segment of S-polygon. + fairVector_Tangent = 1, ///< \ru Направление касательной. \en Direction of tangent to curve. +}; +//------------------------------------------------------------------------------ +/** \brief \ru Управление построением гладких кривых на базе трехмерной ломаной. Учет касательной в заданной точке / точки на касательной. +\en Construction of smooth curves based on a three-dimensional polyline. Taking into account tangent on point / point on tangent. \~ +*/ +//--- +enum MbeFixPntTng +{ + fixPntTng_NotFix = 0, ///< \ru Не фиксировать точки на касательных / касательные в точках. \en Do not fix the points on tangents / the tangents on points. + fixPntTng_Fix = 1, ///< \ru Фиксировать точки на касательных / касательные в точках. \en Fix the points on tangents / the tangents on points. +}; +//------------------------------------------------------------------------------ +/** \brief \ru Управление построением гладких кривых на базе трехмерной ломаной. Формат сплайна. + \en Construction of smooth curves based on a three-dimensional polyline. Spline Format. \~ + \ingroup Data_Structures +*/ +// --- +enum MbeFairSplineFormat +{ + fairFormat_Open = 1, ///< \ru Открытый S-полигон. \en Open S-polygon. + fairFormat_Close = 2, ///< \ru Закрытый S-полигон. \en Close S-polygon. + fairFormat_GB = 3 ///< \ru GB-полигон. \en GB-polygon. +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Предупреждения построения плавной кривой. \en Warnings of fair curve creation. \~ + \ingroup Data_Structures +*/ +// --- +enum MbeFairWarning +{ + fwarn_Success = 0, ///< \ru Нормальная работа. \en Normal work. + fwarn_IncorrectFirstTang = 1, ///< \ru Некорректное направление первой касательной. Игнорируется. \en Incorrect direction of first tangent. Ignored. + fwarn_StraightFirstSite = 2, ///< \ru Прямолинейный первый участок. Первый касательный вектор игнорируется. \en Straighten start site. First tangent ignored. + fwarn_IncorrectPolylines = 3, ///< \ru Некорректные формы ломаных / направления касательных. \en Incorrect polylines / tangent directions. + fwarn_IncorrectFixPntTng = 4, ///< \ru Некорректное направление / положение фиксированной касательной / точки. Игнорируется. \en Incorrect direction / position of a fixed tangent / point. Ignored. + fwarn_BadAccuracyFixPntTng = 5, ///< \ru Погрешность направления / положения фиксированной касательной / точки превышает заданную точность. \en The error of the direction / position of the fixed tangent / point exceeds the specified accuracy. + fwarn_BadStructureFixPntTng = 6, ///< \ru Положения фиксированных касательных не согласованы со стркуктурой кривой. Игнорируются. \en The positions of the fixed tangents are not consistent with the structure of the curve. Are ignored. + fwarn_BadPositionFixPntTng = 7, ///< \ru Положения фиксированных касательных не согласованы по интервалу. Игнорируются. \en The positions of fixed tangents are not matched by interval. Are ignored. + fwarn_CriticalConfig = 8, ///< \ru Критическая конфигурация для B-сплайновой аппроксимации. \en Critical configuration for B-Spline approximation. + fwarn_CantSetCurvature = 9, ///< \ru Конфигурация ломаной не позволяет установить кривизну с заданным значением. \en Configuration of polyline does not allow setting curvature value. + fwarn_AccuracyCritical = 10, ///< \ru Точность на прямолинейных / критических участках. \en Accuracy of approximation rectilinear / critical sites. + fwarn_AccuracyStraight = 11, ///< \ru Точность на прямолинейных участках. \en Accuracy of approximation on straight sites. + fwarn_BadAccuracy = 13, ///< \ru Критический участок. Погрешность превышает заданную точность. \en The Critical site. The error is more than set accuracy. + fwarn_BadInflection = 15, ///< \ru Неуместный перегиб. \en Unexpected inflection. + fwarn_CurvatureOutOfRange = 16, ///< \ru Значение кривизны вне диапазона допустимых значений. \en Curvatute out of range. + fwarn_SawtoothPolyline = 17 ///< \ru Пилообразная ломаная. \en Sawtooth polyline. +}; + + +//------------------------------------------------------------------------------ +/// \ru Форма поверхности заметания переменного сечения. \en The swept surface cross-section shape. \~ +// --- +enum MbeSectionShape { + cs_Round = 0, ///< \ru Дуга окружности в сечении. \en The section is circle or arc. + cs_Linea = 1, ///< \ru Отрезок прямой в сечении. \en The section is line segment. + cs_Conic = 2, ///< \ru Кривая второго порядка в сечении. \en The section is conic curve. + cs_Cubic = 3, ///< \ru Кубическая кривая в сечении. \en The section is cubic curve. + cs_Shape = 4, ///< \ru Сплайн определяет форму сечения. \en The section is spline. +}; // MbeSectionShape + + +//------------------------------------------------------------------------------ +/** \brief \ru Поверхность кривой пересечения, отвечающая существующей грани. + \en Surface of the intersection curve corresponding to the existent face. \~ + \ingroup Data_Structures +*/ +// --- +enum MbeIntCurSurface +{ + ics_First = 0, ///<\ru Первая поверхность. \en First surface. \~ + ics_Second = 1, ///<\ru Вторая поверхность. \en Second surface. \~ + ics_Both = 2, ///<\ru Обе поверхности. \en Both surfaces. \~ +}; + #endif // __MB_ENUM_H diff --git a/C3d/Include/mb_homogeneous.h b/C3d/Include/mb_homogeneous.h index 54a14c9..a07fbc1 100644 --- a/C3d/Include/mb_homogeneous.h +++ b/C3d/Include/mb_homogeneous.h @@ -437,7 +437,7 @@ inline void MbHomogeneous::operator /= ( double factor ) { // \ru Умножение на число \en The multiplication by a number // --- inline MbHomogeneous MbHomogeneous::operator * ( double factor ) const { - return MbHomogeneous( x * factor, y * factor, w * factor ); + return MbHomogeneous( x * factor, y * factor, w * factor ); } diff --git a/C3d/Include/mb_matrix.h b/C3d/Include/mb_matrix.h index f84773d..6426e9b 100644 --- a/C3d/Include/mb_matrix.h +++ b/C3d/Include/mb_matrix.h @@ -15,7 +15,7 @@ #include -#define MATRIX_DIM_2D 3 +#define MATRIX_DIM_2D 3 class MATH_CLASS MbPlacement; diff --git a/C3d/Include/mb_matrix3d.h b/C3d/Include/mb_matrix3d.h index 7c0c6ab..b33ea87 100644 --- a/C3d/Include/mb_matrix3d.h +++ b/C3d/Include/mb_matrix3d.h @@ -16,10 +16,10 @@ #include -#define MATRIX_DIM_3D 4 // \ru Размер матрицы \en A matrix size -#define AXIS_0X 0 // \ru Ось 0X \en 0X-axis -#define AXIS_0Y 1 // \ru Ось 0Y \en 0Y-axis -#define AXIS_0Z 2 // \ru Ось 0Z \en 0Z-axis +const_expr size_t MATRIX_DIM_3D = 4; // \ru Размер матрицы \en A matrix size +const_expr size_t AXIS_0X = 0; // \ru Ось 0X \en 0X-axis +const_expr size_t AXIS_0Y = 1; // \ru Ось 0Y \en 0Y-axis +const_expr size_t AXIS_0Z = 2; // \ru Ось 0Z \en 0Z-axis class MATH_CLASS MbMatrix; @@ -524,13 +524,18 @@ public: \{ */ /// \ru Транспонировать матрицу. \en Transpose a matrix. - void Adj(); + void Adj(); /// \ru Вычислить алгебраическое дополнение. \en Calculate the algebraic adjunct. - double Delta( size_t line, size_t column, size_t dim ) const; + double Delta( size_t line, size_t column, size_t dim ) const; /// \ru Вычислить определитель матрицы. \en Calculate the determinant of a matrix. - double Det( size_t dim ) const; - /// \ru Вычислить обратную матрицу. \en Calculate inverse matrix. - void Div( MbMatrix3D & ) const; + double Det( size_t dim ) const; + /** + \brief \ru Вычислить обратную матрицу. + \en Calculate inverse matrix. + \return \ru Функция вернет аргумент, принятый по ссылке, с результатом вычисления обратной матрицы. + \en The function returns the argument passed by reference with the result of calculating the inverse matrix. \~ + */ + MbMatrix3D & Div( MbMatrix3D & ) const; /** \} */ @@ -598,7 +603,7 @@ public: private: /// \ru Удалить мусор в данных \en Remove trash from data. - void RemoveInaccuracies(); + //void RemoveInaccuracies(); /// \ru Выставить флаги. \en Set flags. uint8 ResetFlag() const; // Оценить флаги, если оценки не было diff --git a/C3d/Include/mb_matrixnn.h b/C3d/Include/mb_matrixnn.h index 3c89139..b88d6a3 100644 --- a/C3d/Include/mb_matrixnn.h +++ b/C3d/Include/mb_matrixnn.h @@ -158,7 +158,7 @@ MbeNewtonResult TypedGaussEquation ( MatrixNN & a, Type * b, double epsilon, Pro } if ( l != k ) { - // (!) Рекомендуется использовать MatrixNN::SwapLines(k,l) + // (!) Рекомендуется использовать MatrixNN::SwapLines(k,l) for ( j = k; j < count; j++ ) { tmp = a(k, j); @@ -269,14 +269,14 @@ MbeNewtonResult TypedTridiagonalSolve ( const size_t n, ArrayDouble & a, ArrayDo if ( ::fabs ( a[0] ) < epsZero ) return nr_Special; // \ru Прогонка не работает \en Sweep does not work // \ru Прямой ход прогонки \en Forward step of sweep - b[0] /= - a[0]; - r[0] /= a[0]; + b[0] /= - a[0]; + r[0] /= a[0]; for ( size_t i = 1; i < n; ++i ) { double dom = a[i] + b[i - 1] * c[i - 1]; if ( ::fabs ( dom ) < epsZero ) return nr_Special; // \ru Прогонка не работает \en Sweep does not work if ( i < n - 1 ) // \ru Размерность b равна n-1, поэтому не можем вычислять b[n-1], да и не нужно оно. \en B dimension is equal to n-1, therefore we can not calculate b [n-1], and it isn't necessary. - b[i] /= - dom; + b[i] /= - dom; r[i] = ( r[i] - r[i - 1] * c[i - 1] ) / dom; } // \ru Обратный ход \en Backward substitution diff --git a/C3d/Include/mb_nurbs_function.h b/C3d/Include/mb_nurbs_function.h index 082d9d3..5651beb 100644 --- a/C3d/Include/mb_nurbs_function.h +++ b/C3d/Include/mb_nurbs_function.h @@ -1,2316 +1,2344 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Модуль геометрических построений. - \en The module of geometric constructions. \~ - \details \ru Базовые алгоритмы Nurbs кривых и поверхностей. - \en The base algorithms for NURBS curves and surfaces. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __MB_NURBS_FUNCTION_H -#define __MB_NURBS_FUNCTION_H - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class MATH_CLASS MbContour; -class MATH_CLASS MbContour3D; -class MATH_CLASS MbNurbs; -class MATH_CLASS MbNurbs3D; - - -//------------------------------------------------------------------------------ -/** \brief \ru Флаг, определяющий построение сплайна, проходящего через точки. - \en Flag defining creation of spline passing through points. \~ - \details \ru Флаг, определяющий построение сплайна, проходящего через точки. Связан с версией. \n - \en Flag defining creation of spline passing through points. Related to the version. \n \~ - \ingroup Data_Structures -*/ -// --- -enum MbeSplineCreateType { - sct_Version0 = 0, ///< \ru Используется в версиях < V13 (центростремительная параметризация). \en Used in versions < V13 (centripetal parameterization). - sct_Version1 = 1, ///< \ru Используется в версии V13 (параметризация по длине хорды). \en Used in version V13 (parameterization by chord length). - sct_Version2 = 2, ///< \ru Используется в версии V13+. \en Used in version V13+. -}; - - -//------------------------------------------------------------------------------- -/** \brief \ru Дополнительная информация для преобразования кривой или поверхности в Nurbs. - \en Additional information for transformation of a curve or surface to NURBS. \~ - \details \ru Дополнительная информация для преобразования кривой или поверхности в Nurbs. \n - \en Additional information for transformation of a curve or surface to NURBS. \n \~ - \ingroup Data_Structures -*/ -// --- -class MbCurveIntoNurbsInfo { -private: - double tbeg; ///< \ru Параметр начала участка кривой. \en A parameter of the curve piece start. - double tend; ///< \ru Параметр конца участка кривой. \en A parameter of the curve piece end. - int sense; ///< \ru Направление сплайн-кривой. \en Direction of spline-curve. - bool matchParams; ///< \ru Сохранять ли при преобразовании однозначное соответствие параметрических областей. \en Whether to save correspondence of parametric regions while transforming or not. - bool extendRange; ///< \ru Строится ли преобразование на продолжении для незамкнутой подложки. \en Whether transformation is constructed on the extension for a non-closed substrate. - VERSION version; ///< \ru Версия исполнения. \en The version of execution. -private: - MbCurveIntoNurbsInfo(); -public: - /// \ru Конструктор. \en Constructor. - template - MbCurveIntoNurbsInfo( const Curve & c, bool match, bool ext, VERSION ver = Math::DefaultMathVersion() ) - : tbeg ( c.GetTMin() ) - , tend ( c.GetTMax() ) - , sense ( 1 ) - , matchParams( match ) - , extendRange( ext ) - , version ( ver ) - {} - /// \ru Конструктор. \en Constructor. - MbCurveIntoNurbsInfo( double t1, double t2, int s, bool match, bool ext, VERSION ver = Math::DefaultMathVersion() ) - : tbeg ( t1 ) - , tend ( t2 ) - , sense ( s ) - , matchParams( match ) - , extendRange( ext ) - , version ( ver ) - {} - /// \ru Конструктор. \en Constructor. - MbCurveIntoNurbsInfo( const MbCurveIntoNurbsInfo & other, double t1, double t2, int s ) - : tbeg ( t1 ) - , tend ( t2 ) - , sense ( s ) - , matchParams( other.matchParams ) - , extendRange( other.extendRange ) - , version ( other.version ) - {} - /// \ru Функция присвоения. \en Assignment function. - void Assign( const MbCurveIntoNurbsInfo & other ) - { - tbeg = other.tbeg; - tend = other.tend; - sense = other.sense; - matchParams = other.matchParams; - extendRange = other.extendRange; - version = other.version; - } - /// \ru Функция инициализации. \en The initialization function. - bool Init( double t1, double t2, int s ) - { - tbeg = t1; - tend = t2; - sense = s; - C3D_ASSERT( ((sense == 1) || (sense == -1)) ); - return ((sense == 1) || (sense == -1)); - } - /// \ru Функция инициализации. \en The initialization function. - bool Init( double t1, double t2, int s, bool match, bool ext ) { - matchParams = match; - extendRange = ext; - return Init( t1, t2, s ); - } - -public: - /// \ru Получить параметр начала участка кривой. \en Get the parameter of the start curve region. - double GetTBeg() const { return tbeg; } - /// \ru Получить параметр конца участка кривой. \en Get the parameter of the end curve region. - double GetTEnd() const { return tend; } - /// \ru Получить направление сплайн-кривой. \en Get the direction of spline-curve. - int GetSense() const { return sense; } - /// \ru Сохранять ли при преобразовании однозначное соответствие параметрических областей. \en Whether to save correspondence of parametric regions by transformation or not. - bool MatchParams() const { return matchParams; } - /// \ru Строится ли преобразование на продолжении для незамкнутой подложки. \en Whether transformation is constructed on the extension for a non-closed substrate or not. - bool ExtendRange() const { return extendRange; } - /// \ru Получить версию исполнения. \en Get the version of execution. - VERSION GetMathVersion() const { return version; } - -OBVIOUS_PRIVATE_COPY( MbCurveIntoNurbsInfo ) -}; - - -//------------------------------------------------------------------------------- -/** \brief \ru Параметры построения NURBS копии объекта. - \en Parameters for the construction of a NURBS copy of the object. \~ - \details \ru Параметры построения NURBS копии объекта. \n - \en Parameters for the construction of a NURBS copy of the object. \n \~ - \ingroup Data_Structures -*/ -// --- -struct MbNurbsParameters { -public: - size_t degree; ///< \ru Порядок NURBS копии. \en Order of NURBS copy. - size_t pointsCount; ///< \ru Количество контрольных точек (при 0 параметр игнорируется). \en The number of control points (if there is no control points, parameter is ignored). - MbRect1D region; ///< \ru Область объекта, подлежащая копированию: [0 1] соответствует [tMin tMax] объекта. \en Region of the object to be copied: [0, 1] corresponds to [tMin tMax] object. - SArray knots; ///< \ru Узловой вектор. \en Knot vector. - mutable bool useApprox; ///< \ru Не пытаться построить точную поверхность. \en Don't try to create the exact surface. - -public: - /// \ru Конструктор по умолчанию. \en Default constructor. - MbNurbsParameters() - : degree ( c3d::NURBS_DEGREE ) - , pointsCount( 0 ) - , region ( 0.0, 1.0 ) - , knots ( 0, 1 ) - , useApprox ( true ) - {} - /// \ru Конструктор по параметрам (без узлов) построения NURBS. \en The constructor of NURBS by parameters (without knots). - MbNurbsParameters( size_t d, size_t c, double zmin, double zmax, bool approx ) - : degree ( d ) - , pointsCount( c ) - , region ( zmin, zmax ) - , knots ( 0, 1 ) - , useApprox ( approx ) - { - C3D_ASSERT( d > 1 && d < SYS_MAX_UINT16 ); - degree = std_max( d, (size_t)2 ); - degree = std_min( d, (size_t)SYS_MAX_UINT16 ); - } - /// \ru Конструктор по полному набору параметров построения NURBS. \en The constructor of NURBS by a complete set of parameters. - MbNurbsParameters( size_t d, size_t c, double zmin, double zmax, bool approx, const SArray & aKnots ) - : degree ( d ) - , pointsCount( c ) - , region ( zmin, zmax ) - , knots ( aKnots ) - , useApprox ( approx ) - { - C3D_ASSERT( d > 1 && d < SYS_MAX_UINT16 ); - degree = std_max( d, (size_t)2 ); - degree = std_min( d, (size_t)SYS_MAX_UINT16 ); - } - /// \ru Конструктор копирования. \en The copy constructor. - MbNurbsParameters( const MbNurbsParameters & other ) - : degree ( other.degree ) - , pointsCount( other.pointsCount ) - , region ( other.region ) - , knots ( other.knots ) - , useApprox ( other.useApprox ) - {} - /// \ru Деструктор. \en Destructor. - ~MbNurbsParameters() {} - - /// \ru Инициализировать по другим параметрам построения NURBS копии объекта. \en Initialize by another parameters. - void Init( const MbNurbsParameters & other ) { - degree = other.degree; - pointsCount = other.pointsCount; - region = other.region; - knots = other.knots; - useApprox = other.useApprox; - } - /// \ru Являются ли объекты равными? \en Determine whether an object is equal? - bool IsSame( const MbNurbsParameters & other, double accuracy ) const; - /// \ru Оператор присваивания. \en The assignment operator. - MbNurbsParameters & operator = ( const MbNurbsParameters & other ) { - degree = other.degree; - pointsCount = other.pointsCount; - region = other.region; - knots = other.knots; - useApprox = other.useApprox; - return (*this); - } - -public: - size_t GetDegree() const { return degree; } - size_t GetPointsCount() const { return pointsCount; } - const MbRect1D & GetRegion() const { return region; } - const SArray & GetKnots() const { return knots; } - bool UseApprox() const { return useApprox; } - -KNOWN_OBJECTS_RW_REF_OPERATORS( MbNurbsParameters ) // \ru Для работы со ссылками и объектами класса \en For working with references and objects of the class -}; // MbNurbsParameters - - -//------------------------------------------------------------------------------- -/** \brief \ru Параметры узловой точки сплайновой копии объекта. - \en Parameters of knot point of the object spline copy. \~ - \details \ru Параметры узловой точки сплайновой копии объекта. \n - \en Parameters of knot point of the object spline copy. \n \~ - \ingroup Data_Structures -*/ -// --- -struct MbNurbsPointInfo { -public: - MbCartPoint3D point; ///< \ru Узловая точка сплайновой поверхности. \en A knot point of a spline surface. - bool visible; ///< \ru Флаг видимости точки. \en A point visibility flag. - int8 poleLocation; ///< \ru Расположение полюса в параметрической области. \en The location of a pole in the parametric region. -public: - /// \ru Конструктор по умолчанию. \en Default constructor. - MbNurbsPointInfo() - : point ( ) - , visible ( true ) - , poleLocation ( (uint8)pln_None ) - {} - /// \ru Конструктор по точке и флагу видимости. \en The constructor by a point and visibility flag. - MbNurbsPointInfo( MbCartPoint3D aPount, bool aVisible, MbePoleLocation aPoleLocation ) - : point ( aPount ) - , visible ( aVisible ) - , poleLocation ( (uint8)aPoleLocation ) - {} - /// \ru Конструктор копирования. \en The copy constructor. - MbNurbsPointInfo( const MbNurbsPointInfo & other ) - : point ( other.point ) - , visible ( other.visible ) - , poleLocation ( (uint8)other.poleLocation ) - {} - /// \ru Деструктор. \en Destructor. - ~MbNurbsPointInfo() {} - - /// \ru Инициализировать по точке и флагу видимости. \en Initialize by a point and visibility flag. - void Init( const MbNurbsPointInfo & other ) - { - point = other.point; - visible = other.visible; - poleLocation = other.poleLocation; - } - /// \ru Оператор присваивания. \en The assignment operator. - MbNurbsPointInfo & operator = ( const MbNurbsPointInfo & other ) - { - point = other.point; - visible = other.visible; - poleLocation = other.poleLocation; - return (*this); - } - /// \ru Определить, является ли точка полюсом. \en Define whether the point is a pole. - MbePoleLocation GetPoleLocation() { return (MbePoleLocation)poleLocation; } - - // \ru Закомментировала строчку ниже, поскольку объект служит только для передачи дополнительной информации об узлах сплайна \en Commented out the line below, because the object is used only to pass additional information about spline knots - // \ru В процессе прямого моделирования. Не пишется и не читается. \en In the direct modeling. Not written and not read. - // \ru KNOWN_OBJECTS_RW_REF_OPERATORS( MbNurbsPointInfo ) // для работы со ссылками и объектами класса \en KNOWN_OBJECTS_RW_REF_OPERATORS( MbNurbsPointInfo ) // for working with references and objects of the class -}; //MbNurbsPointInfo - - -//------------------------------------------------------------------------------ -/** \brief \ru Дать меру расстояния. - \en Get a measure of the distance. \~ - \details \ru Дать меру расстояния. - \en Get a measure of the distance. \~ - \param[in] p1, p2 - \ru Точки между которыми ищется расстояние. - \en Points between which the distance is computed. \~ - \param[in] spType - \ru Тип параметризации сплайновых объектов. - \en The parametrization type of spline objects. \~ - \return \ru Расстояние. - \en Distance. \~ - \ingroup Base_Algorithms -*/ -// --- -template -double GetParamDistance( const Type & p1, const Type & p2, MbeSplineParamType spType ) -{ - switch ( spType ) { - case spt_Unstated : - case spt_EquallySpaced : - return 1.0; - case spt_ChordLength : - return p1.DistanceToPoint( p2 ); - case spt_Centripetal: - return ::sqrt( p1.DistanceToPoint( p2 ) ); - } - - return 1.0; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить параметры инициализации nurbs-объекта. - \en Check initialization parameters of a nurbs-object. \~ - \details \ru Проверить параметры инициализации nurbs-объекта. - \en Check initialization parameters of a nurbs-object. \~ - \param[in] degree - \ru Порядок B-сплайна. - \en B-spline degree. \~ - \param[in] closed - \ru Признак замкнутости. - \en A closedness attribute. \~ - \param[in] pcnt - \ru Число точек. - \en Number of points. \~ - \return \ru true, если параметры согласованы. - \en Returns true if parameters are consistent. \~ - \ingroup Base_Algorithms -*/ -// --- -inline bool IsValidNurbsParams( ptrdiff_t degree, bool closed, size_t pcnt ) -{ - // \ru 1. Порядок B-сплайна должен быть не менее 2. \en 1. The order of B-spline must be at least 2. - // \ru 2а. Для незамкнутой кривой количество точек не меньше порядка сплайна. \en 2a. The number of open curve points isn't less than the order of spline. - // \ru 2б. Для замкнутой кривой должно быть как минимум 3 различных точки. \en 2b. Closed curve must have at least 3 different points. - - return ( (degree > 1) && (pcnt >= (closed ? 3 : (size_t)degree)) ); -} - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить параметры инициализации nurbs-объекта. - \en Check initialization parameters of a nurbs-object. \~ - \details \ru Проверить параметры инициализации nurbs-объекта. - \en Check initialization parameters of a nurbs-object. \~ - \param[in] degree - \ru Порядок B-сплайна. - \en B-spline degree. \~ - \param[in] closed - \ru Признак замкнутости. - \en A closedness attribute. \~ - \param[in] pcnt - \ru Число точек. - \en Number of points. \~ - \param[in] wcnt - \ru Число весов. - \en Number of weights. \~ - \return \ru true, если параметры согласованы. - \en Returns true if parameters are consistent. \~ - \ingroup Base_Algorithms -*/ -// --- -inline bool IsValidNurbsParams( ptrdiff_t degree, bool closed, size_t pcnt, size_t wcnt ) -{ - // \ru 1. Порядок B-сплайна должен быть не менее 2. \en 1. The order of B-spline must be at least 2. - // \ru 2а. Для незамкнутой кривой количество точек не меньше порядка сплайна. \en 2a. The number of open curve points isn't less than the order of spline. - // \ru 2б. Для замкнутой кривой должно быть как минимум 3 различных точки. \en 2b. Closed curve must have at least 3 different points. - // \ru 3. Количество точек и количество весов должны быть согласованы. \en 3. Number of points and number of weights must be equal. - - bool res = ::IsValidNurbsParams( degree, closed, pcnt ) && (wcnt == pcnt); - return res; -} - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить параметры инициализации nurbs-объекта. - \en Check initialization parameters of a nurbs-object. \~ - \details \ru Проверить параметры инициализации nurbs-объекта. - \en Check initialization parameters of a nurbs-object. \~ - \param[in] degree - \ru Порядок B-сплайна. - \en B-spline degree. \~ - \param[in] closed - \ru Признак замкнутости. - \en A closedness attribute. \~ - \param[in] pcnt - \ru Число точек. - \en Number of points. \~ - \param[in] wcnt - \ru Число весов. - \en Number of weights. \~ - \param[in] kcnt - \ru Число узлов. - \en Number of knots. \~ - \return \ru true, если параметры согласованы. - \en Returns true if parameters are consistent. \~ - \ingroup Base_Algorithms -*/ -// --- -inline bool IsValidNurbsParams( ptrdiff_t degree, bool closed, size_t pcnt, size_t wcnt, size_t kcnt ) -{ - // \ru 1. Порядок B-сплайна должен быть не менее 2. \en 1. The order of B-spline must be at least 2. - // \ru 2а. Для незамкнутой кривой количество точек не меньше порядка сплайна. \en 2a. The number of open curve points isn't less than the order of spline. - // \ru 2б. Для замкнутой кривой должно быть как минимум 3 различных точки. \en 2b. Closed curve must have at least 3 different points. - // \ru 3. Количество точек и количество весов должны быть согласованы. \en 3. Number of points and number of weights must be equal. - // \ru 4. Количество узлов должно быть согласовано по остальным параметрам. \en 4. Number of knots must be consistent with the other parameters. - - bool res = ::IsValidNurbsParams( degree, closed, pcnt, wcnt ) && - ( kcnt == ((size_t)degree + pcnt + (closed ? ((size_t)degree - 1) : 0)) ); - return res; -} - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить параметры инициализации nurbs-кривой. - \en Check initialization parameters of a nurbs-curve. \~ - \details \ru Проверить параметры инициализации nurbs-кривой. - \en Check initialization parameters of a nurbs-curve. \~ - \param[in] degree - \ru Порядок B-сплайна. - \en B-spline degree. \~ - \param[in] closed - \ru Признак замкнутости. - \en A closedness attribute. \~ - \param[in] pcnt - \ru Число точек. - \en Number of points. \~ - \param[in] knots - \ru Узловой вектор. - \en Knots vector. \~ - \return \ru true, если параметры согласованы. - \en Returns true if parameters are consistent. \~ - \ingroup Base_Algorithms -*/ -// --- -template -bool IsValidNurbsParamsExt( ptrdiff_t degree, bool closed, size_t pcnt, - const KnotsVector & knots ) -{ - // \ru 1. Порядок B-сплайна должен быть не менее 2. \en 1. The order of B-spline must be at least 2. - // \ru 2а. Для незамкнутой кривой количество точек не меньше порядка сплайна. \en 2a. The number of open curve points isn't less than the order of spline. - // \ru 2б. Для замкнутой кривой должно быть как минимум 3 различных точки. \en 2b. Closed curve must have at least 3 different points. - // \ru 3. Количество точек и количество весов должны быть согласованы. \en 3. Number of points and number of weights must be equal. - // \ru 4. Количество узлов должно быть согласовано по остальным параметрам. \en 4. Number of knots must be consistent with the other parameters. - - bool res = ::IsValidNurbsParams( degree, closed, pcnt ) && - ( knots.size() == ((size_t)degree + pcnt + (closed ? ((size_t)degree - 1) : 0)) ); - - if ( res ) { - if ( !c3d::IsMonotonic( knots, true, true ) ) - res = false; // SD#7118498 - } - - return res; -} - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить параметры инициализации nurbs-кривой. - \en Check initialization parameters of a nurbs-curve. \~ - \details \ru Проверить параметры инициализации nurbs-кривой. - \en Check initialization parameters of a nurbs-curve. \~ - \param[in] degree - \ru Порядок B-сплайна. - \en B-spline degree. \~ - \param[in] closed - \ru Признак замкнутости. - \en A closedness attribute. \~ - \param[in] pnts - \ru Точки. - \en Points vector. \~ - \param[in] wts - \ru Веса точек. - \en Weights vector. \~ - \param[in] knots - \ru Узловой вектор. - \en Knots vector. \~ - \return \ru true, если параметры согласованы. - \en Returns true if parameters are consistent. \~ - \ingroup Base_Algorithms -*/ -// --- -template -bool IsValidNurbsParamsExt( ptrdiff_t degree, bool closed, const PointVector & pnts, - const DoubleVector * wts, - const DoubleVector * knots = NULL ) -{ - // \ru 1. Порядок B-сплайна должен быть не менее 2. \en 1. The order of B-spline must be at least 2. - // \ru 2а. Для незамкнутой кривой количество точек не меньше порядка сплайна. \en 2a. The number of open curve points isn't less than the order of spline. - // \ru 2б. Для замкнутой кривой должно быть как минимум 3 различных точки. \en 2b. Closed curve must have at least 3 different points. - // \ru 3. Количество точек и количество весов должны быть согласованы. \en 3. Number of points and number of weights must be equal. - // \ru 4. Количество узлов должно быть согласовано по остальным параметрам. \en 4. Number of knots must be consistent with the other parameters. - - size_t pcnt = pnts.size(); - - bool res = ::IsValidNurbsParams( degree, closed, pcnt ) && - ( (wts == NULL) || (wts->size() == pcnt) ) && - ( (knots == NULL) || (knots->size() == ((size_t)degree + pcnt + (closed ? ((size_t)degree - 1) : 0))) ); - - if ( res && (knots != NULL) ) { - if ( !c3d::IsMonotonic( *knots, true, true ) ) - res = false; // SD#7118498 - } - - return res; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить узловой вектор nurbs-объекта. - \en Check knots vector of a nurbs-object. \~ - \details \ru Проверить узловой вектор nurbs-объекта. - \en Check knots vector of a nurbs-object. \~ - \param[in] knots - \ru Узловой вектор. - \en Knots vector. \~ - \return \ru true, если параметры согласованы. - \en Returns true if parameters are consistent. \~ - \ingroup Base_Algorithms -*/ -// --- -template -bool IsValidNurbsKnots( const KnotsVector & knots, double eps = EXTENT_EPSILON ) -{ - // \ru 1 . Количество узлов должно быть не менее 4 \en 1 . The number of knots must be at least 4 - // \ru 2 . Последовательность узлов должна быть неубывающей \en 2 . Sequence of nodes must be nondecreasing - // \ru 3 . Первый и последний узлы должны быть различны \en 3 . The first and the last nodes must be different - - size_t cnt = knots.size(); - if ( cnt < 4 ) - return false; - - eps = ::fabs(eps); - if ( ::fabs(knots[cnt-1] - knots[0]) < eps ) - return false; - - for ( size_t i = 0; i < (cnt - 1); i++ ) { - if ( knots[i+1] < knots[i] - eps ) - return false; - } - - return true; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Определение индекса узла left для первой ненулевой функции. - \en Definition of "left" knot index for the first non-zero function. \~ - \details \ru Определение индекса узла left для первой ненулевой функции (knots[mid] <= t < knots[mid + 1]). - \en Definition of "left" knot index for the first non-zero function (knots[mid] <= t < knots[mid + 1]). \~ - \param[in] degree - \ru Порядок B-сплайна. - \en B-spline degree. \~ - \param[in] knots - \ru Множество узлов. - \en Knots. \~ - \param[in, out] t - \ru Значение параметра. - \en A parameter value. \~ - \return \ru Индекс узла. - \en A knot index. \~ - \ingroup Base_Algorithms -*/ -// --- -template -ptrdiff_t KnotIndex( ptrdiff_t degree, const KnotsVector & knots, double & t ) -{ - ptrdiff_t low = ( (ptrdiff_t)degree - 1 ); - ptrdiff_t high = ( (ptrdiff_t)knots.size() - (ptrdiff_t)degree ); - ptrdiff_t mid = low; - - if ( t <= knots[low] ) { - t = knots[low]; - ptrdiff_t countKnt = knots.size(); - ptrdiff_t lowP = low; - lowP++; - // \ru Исправление ошибки BUG_21185 while ( (lowP < countKnt) && (t == knots[lowP]) ) { \en Bugfix BUG_21185 while ( (lowP < countKnt) && (t == knots[lowP]) ) { - while ( (lowP < countKnt - degree) && (t == knots[lowP]) ) { //-V550 - low = lowP; - lowP++; - } - mid = low; - } - else if ( t >= knots[high] ) { - t = knots[high]; - // BUG_82980 while ( (high > 0) && (knots[high] == t) ) { - while ( (high > degree - 1) && (knots[high] == t) ) { - high--; - } - mid = high; - } - else if ( c3d::ArFind( knots, t, mid ) ) { - while ( knots[mid] == t ) { - mid++; - } - mid--; - } - return mid; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Определить узловой вектор (равномерная параметризация). - \en Define knot vector (uniform parameterization). \~ - \details \ru Определить узловой вектор (равномерная параметризация). - \en Define knot vector (uniform parameterization). \~ - \param[in] degree - \ru Порядок B-сплайна. - \en B-spline degree. \~ - \param[in] closed - \ru Признак замкнутости. - \en A closedness attribute. \~ - \param[in] uppPointsIndex - \ru Индекс последней точки. - \en Last point index. \~ - \param[in] knots - \ru Узловой вектор. - \en Knots vector. \~ - \ingroup Base_Algorithms -*/ -// --- -template -ptrdiff_t DefineKnotsVector( ptrdiff_t degree, bool closed, ptrdiff_t uppPointsIndex, - KnotsVector & knots ) -{ - if ( (degree < 2) || (uppPointsIndex < 1) ) { - knots.clear(); - return -1; - } - - ptrdiff_t pointsCount = uppPointsIndex + 1; - ptrdiff_t power = degree - 1; - - ptrdiff_t knotsCount = degree + pointsCount + (closed ? power : 0); - knots.clear(); - knots.reserve( knotsCount ); - - ptrdiff_t i = 0; - - if ( closed ) { // замкнутый В-сплайн - double knot = 0.0; - for ( i = 0; i < knotsCount; i++ ) { - knot = (double)(i - power); - knots.push_back( knot ); - } - } - else { - double knot = 0.0; - for ( i = 0; i < degree; i++ ) - knots.push_back( knot ); - - ptrdiff_t cnt = pointsCount - degree; - for ( i = 0; i < cnt; i++ ) { - knot += 1.0; - knots.push_back( knot ); - } - - knot += 1.0; - for ( i = 0; i < degree; i++ ) - knots.push_back( knot ); - } - - return (knotsCount - 1); -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Определить узловой вектор. - \en Define knot vector. \~ - \details \ru Определить узловой вектор. - \en Define knot vector. \~ - \param[in] degree - \ru Порядок B-сплайна. - \en B-spline degree. \~ - \param[in] closed - \ru Признак замкнутости. - \en A closedness attribute. \~ - \param[in] count - \ru Количество точек. - \en Number of points. \~ - \param[in] params - \ru Параметры точек (для замкнутого count+1). - \en Points parameters (for closed spline - "count"+1). \~ - \param[out] knots - \ru Узловой вектор. - \en Knots vector. \~ - \return \ru true, если набор параметров сформирован. - \en Returns true if result is success. \~ - \ingroup Base_Algorithms -*/ -// --- -MATH_FUNC (bool) DefineKnotsVector( ptrdiff_t degree, bool closed, size_t count, // \ru Порядок, замкнутость, количество точек \en Order, closedness, number of points - const SArray * params, // \ru Параметры точек (для замкнутого count+1) \en Points parameters (for closed spline - "count"+1) - SArray & knots ); // \ru Формируемый узловой вектор \en Generated knot vector - - -//------------------------------------------------------------------------------ -/** \brief \ru Определить параметрическое распределение точек. - \en Define parametric distribution of points. \~ - \details \ru Определить параметрическое распределение точек. - \en Define parametric distribution of points. \~ - \param[in] points - \ru Массив точек. - \en Points vector. \~ - \param[in] spType - \ru Тип параметризации сплайновых объектов. - \en The parameterization type of spline objects. \~ - \param[in] closed - \ru Признак замкнутости. - \en A closedness attribute. \~ - \param[out] params - \ru Параметрическое распределение точек. - \en Parametric distribution of points. \~ - \return \ru true, если набор параметров сформирован. - \en Returns true if result is success. \~ - \ingroup Base_Algorithms -*/ -// --- -template -bool DefineThroughPointsParams( const Points & points, MbeSplineParamType spType, bool closed, - Params & params ) -{ - bool res = false; - - ptrdiff_t count = (ptrdiff_t)points.size(); - - if ( count > (closed ? 2 : 1) ) { - double param = 0.0; - ptrdiff_t extCount = closed ? (count + 1) : count; - params.clear(); - params.resize( extCount, param ); - - double paramSum = 0.0; - for ( ptrdiff_t j = 1; j < extCount; j++ ) { - ptrdiff_t jp = (j - 1 + count) % count; - ptrdiff_t jc = j % count; - param = ::GetParamDistance( points[jp], points[jc], spType ); - params[j] = ( params[j-1] + param ); - paramSum += param; - } - - if ( paramSum > METRIC_PRECISION ) { - for ( ptrdiff_t j = 0; j < extCount; j++ ) { - params[j] /= paramSum; - params[j] *= (double)(extCount-1); - } - res = true; - } - else if ( spType != spt_EquallySpaced ) { - C3D_ASSERT_UNCONDITIONAL( false ); - paramSum = 0.0; - for ( ptrdiff_t j = 1; j < extCount; j++ ) { - ptrdiff_t jp = (j - 1 + count) % count; - ptrdiff_t jc = j % count; - param = ::GetParamDistance( points[jp], points[jc], spt_EquallySpaced ); - params[j] = ( params[j-1] + param ); - paramSum += param; - } - if ( paramSum > METRIC_PRECISION ) { - for ( ptrdiff_t j = 0; j < extCount; j++ ) { - params[j] /= paramSum; - params[j] *= (double)(extCount-1); - } - res = true; - } - } - } - - return res; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Вычисление B - базиса (degree - порядок B-сплайна, p = (degree - 1) - степень полинома(B-сплайна)). - \en The calculation of B - basis ("degree" - order of B-spline, p = (degree - 1) - the degree of the polynomial (B-spline)). \~ - \details \ru Вычисление B - базиса (degree - порядок B-сплайна, p = (degree - 1) - степень полинома(B-сплайна)). - Для ускорения используется рабочий вектор lr = { left[p+1], right[p+1] } ). - \en The calculation of B - basis ("degree" - order of B-spline, p = (degree - 1) - the degree of the polynomial (B-spline)). - To speed up vector lr = { left[p+1], right[p+1] } is used. \~ - \param[in] i - \ru Индекс в массиве узлов, получаемый с помощью функции KnotIndex(). - \en Index of knots vector obtained by the function KnotIndex(). \~ - \param[in] t - \ru Параметр. - \en Parameter. \~ - \param[in] p - \ru p = degree - 1, где degree - порядок B-сплайна. - \en p = degree - 1, where degree is B-spline degree. \~ - \param[in] knots - \ru Узловой вектор. - \en Knots vector. \~ - \param[out] nsplines - \ru Массив размерности degree, заполняется значениями сплайна в поля 0..degree-2; nsplines[degree-1] = 0. - \en Array of B-spline values. \~ - \param[in,out] lrVect - \ru Массив размерности 2*(p+1) = 2*degree, содержимое игнорируется и будет перезаписано. Нужен для ускорения работы функции. В результате работы в нем останется мусор.. - \en Temporary working vector with dimension 2*(p+1) = 2*degree. \~ - \ingroup Base_Algorithms -*/ -// --- -template -MATH_FUNC (bool) BasisFuns( ptrdiff_t i, double t, ptrdiff_t p, const Knots & knots, DoubleVector & nsplines, - DoubleVector & lrVect ); - -//------------------------------------------------------------------------------ -/** \brief \ru Вычислить базисный сплайн по параметру t и узловому вектору. - \en Calculate basic spline by "t" parameter and knots vector. \~ - \details \ru Вычислить базисный сплайн по параметру t и узловому вектору. - \en Calculate basic spline by "t" parameter and knots vector. \~ - \ingroup Base_Algorithms -*/ -// --- -template -MATH_FUNC (bool) CalcBsplvb( const DoubleVector & knots, double t, ptrdiff_t left, ptrdiff_t degree, - DoubleVector & biatx, DoubleVector & lrVect ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Вычисление B - базиса ( с использованием рабочих указателей классов nurbs кривых и поверхностей ). - \en Calculation of B - basis (using working pointers to nurbs curves and surfaces). \~ - \details \ru Вычисление B - базиса ( с использованием рабочих указателей классов nurbs кривых и поверхностей ). - \en Calculation of B - basis (using working pointers to nurbs curves and surfaces). \~ - \ingroup Base_Algorithms -*/ -// --- -void AllBasisFuns( ptrdiff_t i, double t, ptrdiff_t p, const SArray & knots, double ** ndu, - double * left, double * right, bool newPatch = true ); - -//------------------------------------------------------------------------------ -/** \brief \ru Вычисление B - базиса ( с использованием рабочих указателей классов nurbs кривых и поверхностей ). - \en Calculation of B - basis (using working pointers to nurbs curves and surfaces). \~ - \details \ru Вычисление B - базиса ( с использованием рабочих указателей классов nurbs кривых и поверхностей ). - \en Calculation of B - basis (using working pointers to nurbs curves and surfaces). \~ - \ingroup Base_Algorithms -*/ -// --- -void AllBasisFuns( ptrdiff_t i, double t, ptrdiff_t p, const SArray & knots, double * ndu, size_t degree, - double * left, double * right, bool newPatch = true ); - -//------------------------------------------------------------------------------ -/// \ru Вычислить значения базисного сплайна и его производных. \en Calculate values and derivatives of basic spline. -/** - \param[in] t - \ru Параметр на кривой. - \en Curve parameter. \~ - \param[in] left - \ru Номер узла первого ненулевого сплайна. - \en Knot number of the first nonzero spline. \~ - \param[in] n - \ru Порядок вычисляемых производных. - \en Order of calculated derivatives. \~ - \param[out] values - \ru Двумерный массив значений. - \en 2D-Array filled by spline values. \~ - \ingroup Base_Algorithms -*/ -//--- -bool CalcDBsplvb( const SArray & knots, ptrdiff_t degree, double t, ptrdiff_t left, ptrdiff_t n, Array2 & values ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Вычислить все разностные формы кривой. - \en Calculate all difference forms of curve. \~ - \details \ru Вычислить все (или опционально некоторые) разностные формы кривой (характеристические производные). - \en Calculate all (or some, optionally) difference forms of curve (characteristic derivatives). \~ - \ingroup Base_Algorithms -*/ -// --- -template -void CurveDeriveCpts( ptrdiff_t p, const KnotsVector & U, const Point * P, const double * W, size_t pointCount, - const NurbsVector * PW, ptrdiff_t d, ptrdiff_t r1, ptrdiff_t r2, NurbsVector * PK ) -{ - C3D_ASSERT( (P != NULL && W != NULL) != (PW != NULL) ); - - ptrdiff_t r = ( r2 - r1 ); - ptrdiff_t degree = ( p + 1 ); - ptrdiff_t i, k, icount; - NurbsVector & PK0 = PK[0]; - - if ( PW != NULL ) { - for ( i = 0; i <= r; i++ ) - PK0.Set( i, *PW, (r1 + i) ); - } - else { - if ( !PK0.UseWeights() && ( r1 + r ) < pointCount ) { - for ( i = 0; i <= r; i++ ) { - PK0[i] = P[r1 + i]; - } - } - else { - for ( i = 0; i <= r; i++ ) { - k = ( ( r1 + i ) % pointCount ); - PK0.Init( i, P[k], W[k] ); - } - } - } - - for ( k = 1; k <= d; k++ ) { - NurbsVector & PKMin = PK[k - 1]; - NurbsVector & PKPls = PK[k]; - double tmp = (double)( degree - k ); - ptrdiff_t r1i = r1; - for ( i = 0, icount = (r - k); i <= icount; i++ ) { - r1i = ( r1 + i ); - C3D_ASSERT( ( ::fabs( U[r1i + degree] - U[r1i + k] ) > DOUBLE_EPSILON ) ); // \ru Проверка на сбой \en Check of failure - PKPls.Dec( i, PKMin, i, PKMin, (i + 1), (tmp / (U[r1i + degree] - U[r1i + k])) ); - } - } -} - -//------------------------------------------------------------------------------ -/** \brief \ru Вычислить все разностные формы кривой. - \en Calculate all difference forms of curve. \~ - \details \ru Вычислить все (или опционально некоторые) разностные формы кривой (характеристические производные). - \en Calculate all (or some, optionally) difference forms of curve (characteristic derivatives). \~ - \ingroup Base_Algorithms -*/ -// --- -template -void CurveDeriveCpts( ptrdiff_t p, const KnotsVector & U, const Point * P, const double w, size_t pointCount, - const NurbsVector * PW, ptrdiff_t d, ptrdiff_t r1, ptrdiff_t r2, NurbsVector * PK ) -{ - C3D_ASSERT( (P != NULL) != (PW != NULL) ); - - ptrdiff_t r = ( r2 - r1 ); - ptrdiff_t degree = ( p + 1 ); - ptrdiff_t i, k, icount; - NurbsVector & PK0 = PK[0]; - - if ( PW != NULL ) { - for ( i = 0; i <= r; i++ ) - PK0.Set( i, *PW, (r1 + i) ); - } - else { - if ( !PK0.UseWeights() && ( r1 + r ) < pointCount ) { - for ( i = 0; i <= r; i++ ) { - PK0[i] = P[r1 + i]; - } - } - else { - for ( i = 0; i <= r; i++ ) { - k = ( ( r1 + i ) % pointCount ); - PK0.Init( i, P[k], w ); - } - } - } - - for ( k = 1; k <= d; k++ ) { - NurbsVector & PKMin = PK[k - 1]; - NurbsVector & PKPls = PK[k]; - double tmp = (double)( degree - k ); - ptrdiff_t r1i = r1; - for ( i = 0, icount = (r - k); i <= icount; i++ ) { - r1i = ( r1 + i ); - C3D_ASSERT( ( ::fabs( U[r1i + degree] - U[r1i + k] ) > DOUBLE_EPSILON ) ); // \ru Проверка на сбой \en Check of failure - PKPls.Dec( i, PKMin, i, PKMin, (i + 1), (tmp / (U[r1i + degree] - U[r1i + k])) ); - } - } -} - -//------------------------------------------------------------------------------ - /** \brief \ru Вычислить все разностные формы кривой. - \en Calculate all difference forms of curve. \~ - \details \ru Вычислить все (или опционально некоторые) разностные формы кривой (характеристические производные). - \en Calculate all (or some, optionally) difference forms of curve (characteristic derivatives). \~ - \ingroup Base_Algorithms -*/ -// --- -template -void CurveDeriveCpts( ptrdiff_t p, const KnotsVector & U, const Point * P, const double * W, size_t pointCount, - const NurbsVector * PW, ptrdiff_t d, ptrdiff_t r1, ptrdiff_t r2, DoubleTriple ** DT, double ** WT ) -{ - C3D_ASSERT( ( P != NULL && W != NULL ) != ( PW != NULL ) ); - - ptrdiff_t r = ( r2 - r1 ); - ptrdiff_t degree = ( p + 1 ); - ptrdiff_t i, k, icount; - DoubleTriple * DT0 = DT[0]; - double * WT0 = WT[0]; - bool useWeight = WT0 != NULL; - - if ( PW != NULL ) { - if ( !useWeight ) { - for ( i = 0; i <= r; i++ ) - DT0[i].Init( (*PW)[r1 + i] ); - } - else { - for ( i = 0; i <= r; i++ ) { - DT0[i].Init( (*PW)[r1 + i] ); - WT0[i] = PW->w( r1 + i ); - } - } - } - else { - if ( !useWeight && ( r1 + r ) < pointCount ) { - for ( i = 0; i <= r; i++ ) { - DT0[i].Init( P[r1 + i].x, P[r1 + i].y, P[r1 + i].z ); - } - } - else { - for ( i = 0; i <= r; i++ ) { - k = ( ( r1 + i ) % pointCount ); - DT0[i].Init( P[k], W[k] ); - if ( useWeight ) - WT0[i] = W[k]; - } - } - } - - double * WTMin = NULL; - double * WTPls = NULL; - for ( k = 1; k <= d; k++ ) { - DoubleTriple * DTMin = DT[k - 1]; - DoubleTriple * DTPls = DT[k]; - if ( useWeight ) { - WTMin = WT[k - 1]; - WTPls = WT[k]; - } - - double tmp = (double)( degree - k ); - ptrdiff_t r1i = r1; - for ( i = 0, icount = ( r - k ); i <= icount; i++ ) { - r1i = ( r1 + i ); - double tmp2 = U[r1i + degree] - U[r1i + k]; - C3D_ASSERT( ( ::fabs( tmp2 ) > DOUBLE_EPSILON ) ); // \ru Проверка на сбой \en Check of failure - DTPls[i].Dec( DTMin[i], DTMin[i + 1], tmp / (tmp2) ); - if ( useWeight ) - WTPls[i] = ( WTMin[i + 1] - WTMin[i] ) * ( tmp / (tmp2) ); - } - } -} - -//------------------------------------------------------------------------------ - /** \brief \ru Вычислить все разностные формы кривой. - \en Calculate all difference forms of curve. \~ - \details \ru Вычислить все (или опционально некоторые) разностные формы кривой (характеристические производные). - \en Calculate all (or some, optionally) difference forms of curve (characteristic derivatives). \~ - \ingroup Base_Algorithms -*/ -// --- -template -void CurveDeriveCpts( ptrdiff_t p, const KnotsVector & U, const Point * P, const double w, size_t pointCount, - const NurbsVector * PW, ptrdiff_t d, ptrdiff_t r1, ptrdiff_t r2, DoubleTriple ** DT, double ** WT ) -{ - C3D_ASSERT( ( P != NULL ) != ( PW != NULL ) ); - - ptrdiff_t r = ( r2 - r1 ); - ptrdiff_t degree = ( p + 1 ); - ptrdiff_t i, k, icount; - DoubleTriple * DT0 = DT[0]; - double * WT0 = WT[0]; - bool useWeight = WT0 != NULL; - - if ( PW != NULL ) { - if ( !useWeight ) { - for ( i = 0; i <= r; i++ ) - DT0[i].Init( (*PW)[r1 + i] ); - } - else { - for ( i = 0; i <= r; i++ ) { - DT0[i].Init( (*PW)[r1 + i] ); - WT0[i] = PW->w( r1 + i ); - } - - } - } - else { - if ( !useWeight && ( r1 + r ) < pointCount ) { - for ( i = 0; i <= r; i++ ) { - DT0[i].Init( P[r1 + i].x, P[r1 + i].y, P[r1 + i].z ); - } - } - else { - for ( i = 0; i <= r; i++ ) { - k = ( ( r1 + i ) % pointCount ); - DT0[i].Init( P[k], w ); - if( useWeight ) - WT0[i] = w; - } - } - } - - double * WTMin = NULL; - double * WTPls = NULL; - for ( k = 1; k <= d; k++ ) { - DoubleTriple * DTMin = DT[k - 1]; - DoubleTriple * DTPls = DT[k]; - if ( useWeight ) { - WTMin = WT[k - 1]; - WTPls = WT[k]; - } - double tmp = (double)( degree - k ); - ptrdiff_t r1i = r1; - for ( i = 0, icount = ( r - k ); i <= icount; i++ ) { - r1i = ( r1 + i ); - double tmp2 = U[r1i + degree] - U[r1i + k]; - C3D_ASSERT( ( ::fabs( tmp2 ) > DOUBLE_EPSILON ) ); // \ru Проверка на сбой \en Check of failure - DTPls[i].Dec( DTMin[i], DTMin[i + 1], tmp / (tmp2) ); - if ( useWeight ) - WTPls[i] = ( WTMin[i + 1] - WTMin[i] ) * ( tmp / ( tmp2 ) ); - } - } -} - -//------------------------------------------------------------------------------ -/** \brief \ru Вычислить все разностные формы кривой. - \en Calculate all difference forms of curve. \~ - \details \ru Вычислить все (или опционально некоторые) разностные формы кривой (характеристические производные). - \en Calculate all (or some, optionally) difference forms of curve (characteristic derivatives). \~ - \ingroup Base_Algorithms -*/ -// --- -template -void CurveDeriveCpts( ptrdiff_t p, const double * U, const Point * P, const double * W, size_t pointCount, - ptrdiff_t r1, ptrdiff_t r2, - Homogeneous * H0, Homogeneous * H1, Homogeneous * H2, Homogeneous * H3 ) -{ - ptrdiff_t r = ( r2 - r1 ); - ptrdiff_t degree = ( p + 1 ); - ptrdiff_t i, k, icount; - - for ( i = 0; i <= r; i++ ) { - k = ( (r1 + i) % pointCount ); - if ( W != NULL ) - H0[i].Init( P[k], W[k] ); - else - H0[i].Init( P[k], 1.0 ); - } - k = 1; - { - Homogeneous * PKMin = H0; - Homogeneous * PKPls = H1; - double tmp = (double)( degree - k ); - ptrdiff_t r1i = r1; - for ( i = 0, icount = (r - k); i <= icount; i++ ) { - r1i = ( r1 + i ); - C3D_ASSERT( ( ::fabs( U[r1i + degree] - U[r1i + k] ) > DOUBLE_EPSILON ) ); // \ru Проверка на сбой \en Check of failure - PKPls[i].Dec( PKMin[i], PKMin[i + 1], (tmp / (U[r1i + degree] - U[r1i + k])) ); - } - } - k = 2; - { - Homogeneous * PKMin = H1; - Homogeneous * PKPls = H2; - double tmp = (double)( degree - k ); - ptrdiff_t r1i = r1; - for ( i = 0, icount = (r - k); i <= icount; i++ ) { - r1i = ( r1 + i ); - C3D_ASSERT( ( ::fabs( U[r1i + degree] - U[r1i + k] ) > DOUBLE_EPSILON ) ); // \ru Проверка на сбой \en Check of failure - PKPls[i].Dec( PKMin[i], PKMin[i + 1], (tmp / (U[r1i + degree] - U[r1i + k])) ); - } - } - k = 3; - { - Homogeneous * PKMin = H2; - Homogeneous * PKPls = H3; - double tmp = (double)( degree - k ); - ptrdiff_t r1i = r1; - for ( i = 0, icount = (r - k); i <= icount; i++ ) { - r1i = ( r1 + i ); - C3D_ASSERT( ( ::fabs( U[r1i + degree] - U[r1i + k] ) > DOUBLE_EPSILON ) ); // \ru Проверка на сбой \en Check of failure - PKPls[i].Dec( PKMin[i], PKMin[i + 1], (tmp / (U[r1i + degree] - U[r1i + k])) ); - } - } -} - - -//------------------------------------------------------------------------------ -// \ru Cвертка по Энш. \en Einstein's convolution product. -// --- -void EiSum( const double * pk, double ** nd, size_t jcount, double & sum ); - - -//------------------------------------------------------------------------------ -// \ru Cвертка по Энш. \en Einstein's convolution product. -// --- -void EiSum( const MbHomogeneous * pk, double * nd, size_t degree, size_t jcount, MbHomogeneous & sum ); - - -//------------------------------------------------------------------------------ -// \ru Cвертка по Энш. \en Einstein's convolution product. -// --- -void EiSum( const MbHomogeneous3D * pk, double * nd, size_t degree, size_t jcount, MbHomogeneous3D & sum ); - - -//------------------------------------------------------------------------------ -/// \ru Загнать параметр t в параметрическую область кривой. \en Reduce "t" to parameter in the parametric domain of the curve. -/** - \param[in] tMin, tMax - \ru Параметры, задающие параметрическую область кривой. - \en Parameters which define the parametric region of the curve. \~ - \param[in] closed - \ru Признак замкнутости кривой. - \en An attribute of curve closedness. \~ - \param[in, out] t - \ru Исходный параметр. - \en Initial parameter. \~ - \ingroup Base_Algorithms -*/ -// --- -inline void CheckParam( const double & tMin, const double & tMax, bool closed, double & t ) -{ - if ( (t < tMin) || (t > tMax) ) { - if ( closed ) { // \ru Сплайн кривая замкнута \en Spline curve is closed - double period = tMax - tMin; - t -= ::floor( (t - tMin) / period ) * period; - } - else if ( t < tMin ) - t = tMin; - else if ( t > tMax ) - t = tMax; - } -} - - -//------------------------------------------------------------------------------ -/// \ru Рассчитать ненулевые сплайны при данном параметре. \en Calculate non-zero splines with a given parameter. -/** - \param[in] degree - \ru Порядок B-сплайна. - \en B-spline degree. \~ - \param[in] knots - \ru Множество узлов. - \en Knots. \~ - \param[in] closed - \ru Признак замкнутости. - \en A closedness attribute. \~ - \param[in, out] t - \ru Параметр - \en A parameter \~ - \param[out] nsplines - \ru Множество размерности degree, заполняется значениями сплайна. - \en Array (dimension is "degree") is filled by spline values. \~ - \param[out] lrVect - \ru Вспомогательный массив (содержит мусор). - \en An assisting array (contains garbage). \~ - \return \ru Номер первого ненулевого B-сплайна. - \en The number of the first non-zero B-spline. \~ - \ingroup Base_Algorithms -*/ -// --- -template -ptrdiff_t CalculateSplines( ptrdiff_t degree, - const KnotsVector & knots, - bool closed, - double & t, - DoubleVector1 & nsplines, - DoubleVector2 & lrVect ) -{ - ptrdiff_t begInd = -1; // \ru Возвращаемое значение = номер первого ненулевого B-сплайна \en The return value = the number of the first non-zero B-spline. - - if ( (degree > 1) && ((ptrdiff_t)knots.size() > (ptrdiff_t)degree) ) { - ptrdiff_t power = degree-1; - ptrdiff_t lastKnotInd = (ptrdiff_t)knots.size() - 1; - - // \ru Загнать параметр t в параметрическую область кривой \en Reduce "t" to a parameter in the parametric domain of the curve - ::CheckParam( knots[power], knots[lastKnotInd - (ptrdiff_t)power], closed, t ); - - ptrdiff_t tspan = ::KnotIndex( degree, knots, t ); - begInd = tspan - (ptrdiff_t)power; - if ( begInd >= 0 && begInd <= lastKnotInd - (ptrdiff_t)power ) - ::BasisFuns( tspan, t, power, knots, nsplines, lrVect ); - else { - begInd = SYS_MAX_T; // \ru Ошибка \en Error - for ( size_t i = 0; i < (size_t)degree; i++ ) - nsplines[i] = 0.0; - } - - C3D_ASSERT( (size_t)begInd != SYS_MAX_T ); - } - - return begInd; -} - - -//------------------------------------------------------------------------------ -// \ru Вычисление характеристических точек pointList для прохождения NURBS-кривой через points[i] при params[i] \en Calculation of characteristic points "pointList" of NURBS-curve passing through points[i] with params[i] -// --- -template -MbeNewtonResult CalculatePointList( const DoubleVector & params, const PointVector & points, - ptrdiff_t degree, bool closed, const DoubleVector & knots, - PointVector & pointList ) -{ - MbeNewtonResult res = nr_Failure; - - ptrdiff_t pointsCount = (ptrdiff_t)points.size(); - C3D_ASSERT( points.size() > 1 ); - - if ( pointsCount > 1 && degree > 1 && knots.size() > 1 ) { - // \ru Инициализация опорных точек \en Initialization of support points - if ( &pointList != &points ) { - pointList.clear(); - pointList = points; - } - ptrdiff_t uppIndex = (ptrdiff_t)pointList.size() - 1; // \ru Количество точек \en The count of points - if ( closed && (uppIndex > 1) && - c3d::EqualPoints( pointList[0], pointList[uppIndex], METRIC_REGION ) ) { - pointList.erase( pointList.begin() + uppIndex ); - pointsCount = (ptrdiff_t)pointList.size(); - } - - DPtr matrixPtr( MatrixNN::Create( pointsCount ) ); // \ru Матрица системы уравнений для прохождения NURBS при params[i] через points[i] \en Matrix of equation system for constructing the NURBS-curve passing through the points[i] with params[i] - - if ( matrixPtr != NULL && ::IsValidNurbsParamsExt( degree, closed, pointList.size(), knots ) ) { - MatrixNN & matrix = *matrixPtr; - - std::vector bSplines; // \ru Ненулевые B-сплайны \en Non-zero B-splines - bSplines.resize( degree ); - - std::vector lrVect; - - for ( ptrdiff_t i = 0; i < pointsCount; i++ ) { // \ru Заполняем строки матрицы \en Fills matrix rows - double t = params[i]; - ptrdiff_t k = 0; - ptrdiff_t ind = ::CalculateSplines( degree, knots, closed, t, bSplines, lrVect ); - // \ru Заполняем i-ю строку \en Fill the i-th row - for ( k = 0; k < ind; k++ ) - matrix( i, k ) = 0.0; - for ( k = ind; k < ind + (ptrdiff_t)degree; k++ ) - matrix( i, k%pointsCount ) = bSplines[k - ind]; // \ru Ненулевые элементы строки \en Non-zero elements of row - for ( k = ind + (size_t)degree; k < pointsCount; k++ ) - matrix( i, k ) = 0.0; - } - - double epsilon = PARAM_EPSILON; - // \ru Решаем систему уравнений относительно характеристических точек pointList \en Solve the system of equations for the characteristic points "pointList" - // \ru Правая часть системы = адрес начала массива опорных точек pointList.begin(). \en The right system part = address of beginning of support point array pointList.begin(). - res = ::TypedGaussEquation( matrix, &pointList[0], epsilon ); - } - } - - return res; -} - - -//------------------------------------------------------------------------------ -// \ru Вычисление характеристических точек pointList для прохождения NURBS-кривой через points[i] при params[i] \en Calculation of characteristic points "pointList" of NURBS-curve passing through points[i] with params[i] -// --- -template -MATH_FUNC (MbeNewtonResult) CalculatePointListWithBandMatrix( const DoubleVector & params, const PointsVector & points, - ptrdiff_t degree, bool closed, const DoubleVector & knots, - PointsVector & pointList ); - - -//------------------------------------------------------------------------------ -// \ru Установить касательность сплайна к вектору \en Set tangency of spline to vector -//--- -template -bool AttachNurbsG1( TypedNurbs & nurbs, // \ru Модифицируемый сплайн \en Modifiable spline - const TypedVector & tang, // \ru Касательный вектор \en Tangent vector - bool begin, // \ru Сопряжение выставлено в начале \en Conjugation is defined for the start - bool modify, // \ru Можно ли менять существующие полюса \en Whether it is possible to modify the existing pole - bool isC1 ) // \ru Нужно сохранить длину касательного вектора \en Need to save the length of the tangent vector -{ - bool res = false; - - if ( !nurbs.IsClosed() && nurbs.GetPointListCount() > 2 && nurbs.GetDegree() > 2 ) { - bool needRebuild = false; - - TypedVector normTang( tang ); - double tangLen = tang.Length(); - if ( tangLen > LENGTH_EPSILON ) - normTang /= tangLen; - - SArray points ( 0, 1 ); - SArray weights( 0, 1 ); - SArray knots ( 0, 1 ); - nurbs.GetPointList( points ); - nurbs.GetWeights( weights ); - nurbs.GetKnots( knots ); - ptrdiff_t degree = nurbs.GetDegree(); - bool closed = false; - - // \ru Дополнительная точка, которая обеспечивает визуальную касательность \en Additional point which provides a visual tangency - TypedPoint point, add( points[begin ? 0 : points.MaxIndex()] ); - TypedVector curTang, curVect; - double dist = 0.0; - //double dKnots = 0.0; - - double part = modify ? 1.0 : 0.5; // \ru Параметрическая доля \en Parametric part - double pointKnot = 0.0; // \ru Узел точки, обеспечивающий сопряжение \en Point knot which provides a conjugation - double pointWeight = 1.0; // \ru Вес точки \en A point weight - - if ( begin ) { // \ru Стыковка производится в начале \en Connection is performed in the beginning - nurbs._Tangent( nurbs.GetTMin(), curTang ); - if ( !curTang.Colinear(tang) || - curTang * tang < -ANGLE_EPSILON ) // BUG_55564 - { // \ru Если еще не установлено сопряжение \en If conjugation is not set - curVect.Set( points[1], 1.0, points[0], -1.0 ); - - if ( isC1 || !modify || tang.Orthogonal( curTang, Math::metricNear ) ) { - // BUG_53978 if ( !modify || tang.Orthogonal( curTang, Math::metricNear ) ) { - pointKnot = modify ? knots[(size_t)degree] : - knots[(size_t)degree] * part + knots[0] * ( 1.0 - part ); - - pointWeight = modify ? weights[1] : weights[0]; - - dist = points[0].DistanceToPoint( points[1] ) * part; // \ru Длина производной \en Derivative length - - if ( isC1 ) - add.Add( tang, (pointKnot - knots[0]) / (double)(degree - 1) * weights[0] / pointWeight ); - else - add.Add( normTang, dist / (double)(degree - 1) * weights[0] / pointWeight ); - - if ( modify ) - points[1] = add; - else { - points.AddAt( add, 1 ); - weights.AddAt( pointWeight, 1 ); - knots.AddAt( pointKnot, (size_t)degree ); - } - } - else { - // \ru BUG_50080 dist = (normTang * curVect); // МСГ К12 решено не учитывать знак производной \en BUG_50080 dist = (normTang * curVect); // МСГ К12 ignore the sign of the derivative - dist = ::fabs(normTang * curVect); - points[1] = points[0] + normTang * dist; - } - needRebuild = true; - } - - res = true; - } - else { // \ru Стыковка производится в конце \en Connection is performed at the end - nurbs._Tangent( nurbs.GetTMax(), curTang ); - if ( !curTang.Colinear(tang) || - curTang * tang < -ANGLE_EPSILON ) // BUG_55564) - { // \ru Если еще не установлено сопряжение \en If conjugation is not set - ptrdiff_t shear = points.MaxIndex(); - curVect.Set( points[shear], 1.0, points[shear - 1], -1.0 ); - - if ( isC1 || !modify || tang.Orthogonal( curTang, Math::metricNear ) ) { - // BUG_53978 if ( !modify || tang.Orthogonal( curTang, Math::metricNear ) ) { - pointKnot = modify ? knots[shear] : - knots[shear] * ( 1.0 - part ) + knots[shear + (size_t)degree] * part; - - pointWeight = modify ? weights[shear - 1] : weights[shear]; - - dist = points[shear].DistanceToPoint( points[shear - 1] ) * part; // \ru Длина производной \en Derivative length - - if ( isC1 ) - add.Add( tang, -(knots[shear + degree] - pointKnot) / (double)(degree - 1) * weights[shear] / pointWeight ); - else { - // BUG_49940 add.Add( tang, -dist / (degree - 1) * weights[shear] / pointWeight ); - add.Add( normTang, -dist / (double)(degree - 1) * weights[shear] / pointWeight ); - } - - if ( modify ) - points[shear - 1] = add; - else { - points.AddAt( add, shear ); - weights.AddAt( pointWeight, shear ); - knots.AddAt( pointKnot, shear + 1 ); - } - } - else { - // \ru BUG_50080 dist = (normTang * curVect); // МСГ К12 решено не учитывать знак производной \en BUG_50080 dist = (normTang * curVect); // МСГ К12 ignore the sign of the derivative - dist = ::fabs(normTang * curVect); - points[shear - 1] = points[shear] - normTang * dist; - } - needRebuild = true; - } - - res = true; - } - - if ( res && needRebuild ) - nurbs.Init( degree, closed, points, weights, knots, ncf_Unspecified ); -/*#if defined(C3D_DEBUG) - if ( res ){ // \ru Отладка \en Debugging - TypedVector vect; - if ( begin ) nurbs._FirstDer( nurbs.GetTMin(), vect ); - else nurbs._FirstDer( nurbs.GetTMax(), vect ); - double vectLen = vect.Length(); - C3D_ASSERT( tang.Colinear( vect ) && tang * vect > ANGLE_EPSILON ); - C3D_ASSERT( !isC1 || ::fabs(tangLen - vectLen) < METRIC_EPSILON ); - } -#endif -*/ - } - - return res; -} - - -//------------------------------------------------------------------------------ -// \ru Установить касательные на краях \en Set tangents at the ends -// --- -template -bool SetLimitFirstDerivatives( const Curve & curve, bool setBeg, bool setEnd, Nurbs & nurbs, bool setLen ) -{ // BUG_59596 - bool changed = false; - - if ( setBeg ) { - Vector fd; - curve._FirstDer( curve.GetTMin(), fd ); - if ( ::AttachNurbsG1( nurbs, fd, true, false, setLen ) ) - changed = true; - } - if ( setEnd ) { - Vector fd; - curve._FirstDer( curve.GetTMax(), fd ); - if ( ::AttachNurbsG1( nurbs, fd, false, false, setLen ) ) - changed = true; - } - - return changed; -} - - -//------------------------------------------------------------------------------ -// \ru Установить точки так, чтобы совпала касательная и главная нормаль \en Set points such that tangent and principal normal are coincident -//--- -template -bool AttachNurbsG2( TypedNurbs & nurbs, // \ru Модифицируемый сплайн \en Modifiable spline - const TypedVector & tang, // \ru Касательный вектор \en Tangent vector - const TypedVector & tangDiff, // \ru Производная касательного вектора \en The derivative of a tangent vector - bool begin, // \ru Сопряжение выставлено в начале \en Conjugation is defined for the start - bool modify, // \ru Можно ли менять существующие полюса \en Whether it is possible to modify the existing poles - double * wDiff1, - double * wDiff2 ) -{ - bool res = false; - - if ( !nurbs.IsClosed() && nurbs.GetPointListCount() > 3 && nurbs.GetDegree() > 3 && - ::AttachNurbsG1( nurbs, tang, begin, modify, false ) ) // \ru Стыкуем сначала по касательной \en Join by a tangent at first - { - bool needRebuild = false; // \ru Нужно ли перестраивать кривую \en Whether to rebuild the curve - - TypedVector normTang( tang ); - double tangLen = tang.Length(); - if ( tangLen > LENGTH_EPSILON ) - normTang /= tangLen; - - SArray points ( 0, 1 ); - SArray weights( 0, 1 ); - SArray knots ( 0, 1 ); - nurbs.GetPointList( points ); - nurbs.GetWeights( weights ); - nurbs.GetKnots( knots ); - ptrdiff_t degree = nurbs.GetDegree(); - bool closed = nurbs.IsClosed(); - - double eps = Math::metricEpsilon; - - //double dKnots = 0.0; - ptrdiff_t degm = degree - 1; - double curCurv = 0.0; - double curvature = tangDiff.Length(); - - TypedVector curNormal; // \ru Текущая нормаль в стыке \en Current normal at the joint - TypedVector firstDer; // \ru Первая производная сплайна в точке \en The first spline derivative at the point - TypedVector secndDer; // \ru Старая производная сплайна в точке \en The old spline derivative at the point - TypedVector secDer( tangDiff ); // \ru Вторая производная, с которой фактически устанавливается равенство \en The second derivative which the equality is set with - - TypedPoint add; // \ru Точка, обеспечивающая равенство вторых производных \en Point which provides the equality of second derivatives - TypedPoint wp0, wp1; // \ru Взвешенные точки \en Weighted points - - double part = modify ? 1.0 : 0.5; // \ru Параметрическая доля \en Parametric part - double pointKnot = 0.0; // \ru Узел точки, обеспечивающий сопряжение \en Point knot which provides a conjugation - double pointWeight = 1.0; // \ru Вес точки \en A point weight - - double weightDiff1 = 0.0, weightDiff2 = 0.0; // \ru Первая и вторая производная весов \en The first and the second derivative of weights - if ( begin ) { // \ru Стыковка производится в начале \en Connection is performed at the start - nurbs._Normal( nurbs.GetTMin(), curNormal ); - curCurv = nurbs.Curvature( nurbs.GetTMin() ); - - pointKnot = modify ? knots[(size_t)degree + 1] : - knots[(size_t)degree + 1] * part + knots[degree] *( 1.0 - part ); - pointWeight = modify ? weights[2] : weights[0] ; - weightDiff1 = (double)degm * ( weights[1] - weights[0] ) / ( knots[degree] - knots[1] ); - weightDiff2 = ( (double)degm - 1 ) * (double)degm / ( knots[degree] - knots[2] ) * - ( (pointWeight - weights[1]) / (pointKnot - knots[2]) - - (weights[1] - weights[0]) / (knots[degree] - knots[1]) ); - - if ( !(curNormal.Colinear(tangDiff) && - ::fabs(curCurv - curvature) < eps) ) // \ru Еще нет необходимой гладкости стыка \en The required smoothness of a joint is not provided yet - { - nurbs._FirstDer( nurbs.GetTMin(), firstDer ); - secDer *= firstDer * firstDer; - nurbs._SecondDer( nurbs.GetTMin(), secndDer ); - secDer += normTang * ( part * normTang * secndDer ); // \ru Сохранение старой проекции \en Saving of the old projection - - wp0 += points[0] * weights[0]; - wp1 += points[1] * weights[1]; - - // \ru Обрабатываем вторую производную \en Process the second derivative - secDer *= weights[0]; - secDer.Add( points[0], weightDiff2, firstDer, 2.0 * weightDiff1 ); - - double dK = pointKnot - knots[2]; - double dK1 = 1.0 / dK + 1.0 / ( knots[(size_t)degree] - knots[1] ); - - add.Set( wp0, 1.0, wp1 - wp0, dK1 * dK ); - add.Add( secDer, (knots[(size_t)degree] - knots[2]) * dK / ((double)degm * (double)(degm - 1)) ); - add /= pointWeight; - - if ( modify ) - points[2] = add; - else { - knots.AddAt( pointKnot, (size_t)degree + 1 ); - points.AddAt( add, 2 ); - weights.AddAt( pointWeight, 2 ); - } - needRebuild = true; - } - - res = true; - } - else { // \ru Стыковка производится в конце \en Connection is performed at the end - nurbs._Normal( nurbs.GetTMax(), curNormal ); - curCurv = nurbs.Curvature( nurbs.GetTMax() ); - - ptrdiff_t shear = points.MaxIndex(); - - pointKnot = modify ? knots[shear - 1] : - knots[shear] * ( 1.0 - part ) + knots[shear - 1] * part ; - - pointWeight = modify ? weights[shear - 2] : weights[shear]; - - weightDiff1 = (double)degm * ( weights[shear] - weights[shear - 1] ) / - ( knots[shear + (size_t)degree] - knots[shear] ); - weightDiff2 = (double)(degm - 1) * (double)degm / ( knots[shear + (size_t)degm] - knots[shear] ) * - ( (weights[shear] - weights[shear - 1]) / (knots[shear + (size_t)degree] - knots[shear]) - - (weights[shear - 1] - pointWeight ) / (knots[shear + (size_t)degree - 1] - pointKnot ) ); - - if ( !(curNormal.Colinear(tangDiff) && - ::fabs(curCurv - curvature) < eps) ) // \ru Еще нет необходимой гладкости стыка \en The required smoothness of a joint is not provided yet - { - nurbs. _FirstDer( nurbs.GetTMax(), firstDer ); - secDer *= firstDer * firstDer; - nurbs._SecondDer( nurbs.GetTMax(), secndDer ); - secDer += normTang * ( part * normTang * secndDer ); // \ru Сохранение старой проекции \en Saving of the old projection - - wp0 += points[shear] * weights[shear]; - wp1 += points[shear - 1] * weights[shear - 1]; - - // \ru Обрабатываем вторую производную \en Process the second derivative - secDer *= weights[shear]; - secDer.Add( points[shear], weightDiff2, firstDer, 2.0 * weightDiff1 ); - - double dK = knots[shear + (size_t)degm] - pointKnot; - double dK1 = 1.0 / ( knots[shear + (size_t)degm] - knots[shear] ) + - 1.0 / ( knots[shear + (size_t)degm] - pointKnot ); - - add.Set( wp0, 1.0, wp1 - wp0, dK1 * dK ); - add.Add( secDer, (knots[shear + (size_t)degm] - knots[shear]) * dK / ((double)degm * (double)(degm - 1)) ); - add /= pointWeight; - - if ( modify ) - points[shear - 2] = add; - else { - knots.AddAt( pointKnot, shear ); - points.AddAt( add, shear - 1 ); - weights.AddAt( pointWeight, shear - 1 ); - } - needRebuild = true; - } - - res = true; - } - - // \ru Сохраняем вычисленные производные \en Save the calculated derivatives - if ( res ) { - if ( wDiff1 != NULL ) - *wDiff1 = weightDiff1; - if ( wDiff2 != NULL && res ) - *wDiff2 = weightDiff2; - } - - if ( res && needRebuild ) - nurbs.Init( degree, closed, points, weights, knots, ncf_Unspecified ); - } - -/*#if defined(C3D_DEBUG) - if ( res ) { // \ru Отладка \en Debugging - double t = begin ? nurbs.GetTMin() : nurbs.GetTMax(); - - MbVector3D vect; - nurbs._Tangent( t, vect ); - C3D_ASSERT( vect.Colinear( tang ) ); - - MbVector3D normal; - nurbs._Normal( t, normal ); - C3D_ASSERT( normal.Colinear(tangDiff) ); - - double curvature = tangDiff.Length(); - double curCurvature = nurbs.Curvature( t ); - C3D_ASSERT( ::fabs(curCurvature - curvature) < Math::metricAccuracy ); - } -#endif -*/ - - return res; -} - - -//------------------------------------------------------------------------------ -// \ru Создать замкнутый NURBS, проходящий через точки с заданными параметрами. \en Calculate closed NURBS by points which it passes through and points parameters. -//--- -template -bool CreateClosedNURBS4( Nurbs & nurbs, const SArray & initPoints, const SArray & initParams ) -{ - bool bRes = ( (initParams.Count() > 3) && (initParams.Count() == initPoints.Count() + 1) ); - - if ( bRes ) { - nurbs.Refresh(); // должен стоять первым, т.к. освобождается выделенная память - - const ptrdiff_t degree = 4; // степень В-сплайна //-V112 - const bool closed = true; // признак замкнутости - const MbeNurbsCurveForm form = ncf_Unspecified; // форма B - сплайна - - // скопировать массив характеристических точек - SArray points( initPoints ); - - // установить единичные веса - SArray weights( points.Count(), 1 ); - { - const double weight0 = 1.0; - weights.Fill( points.Count(), weight0 ); - } - - const ptrdiff_t degm = degree - 1; - - // заполнить массив параметров (на концах используем условие "отсутствия узла"), обеспечивающий замыкание - SArray knots ( (initParams.MaxIndex() + 2 * degree - 1), 1 ); - { - ptrdiff_t uppKnotsIndex = (ptrdiff_t)initParams.Count() - 1; - ptrdiff_t i = 0; // индекс - - // вставляем начальные узлы 0.. degree - 2 - for ( i = 0; i < degm; i++ ) { - ptrdiff_t startIndex = uppKnotsIndex - degm; - knots.Add( initParams[0] - initParams[uppKnotsIndex] + initParams[startIndex + i] ); - } - - knots += initParams; - - // вставляем конечные узлы - const double tmax = initParams[initParams.MaxIndex()]; - ptrdiff_t i0 = degm; - for ( i = i0; i < i0 + degm; i++ ) - knots.Add( tmax + knots[i + 1] - knots[i0] ); - - uppKnotsIndex = knots.MaxIndex(); - } - - points.Adjust(); - weights.Adjust(); - knots.Adjust(); - - const ptrdiff_t pointsCnt = initPoints.Count(); - const ptrdiff_t uppIndex = pointsCnt - 1; - // для приведения матрицы к матрице с диагональным преобладанием необходимо - // последнее уравнение исключать первым - // далее решаем систему уравнений методом Гаусса без выбора ведущего элемента - SArray biatx( degree, 1 ); // не нулевые B - сплайны - SArray d ( pointsCnt ); // массив диагоналей - SArray ar ( pointsCnt ); // массив правых частей - SArray n ( pointsCnt ); // последний столбец в результирующей матрице - - SArray lrVect; - lrVect.resize( 2*degree ); - - ar.Add( initPoints[initPoints.MaxIndex()] ); - ar += initPoints; - ar.RemoveInd( ar.MaxIndex() ); - - ptrdiff_t lastIndex = initParams.MaxIndex() - 1; - ptrdiff_t lastKnot = knots.Count() - degree - 1; - - // первое уравнение - ::CalcBsplvb( knots, initParams[lastIndex], lastKnot, degree, biatx, lrVect ); - - double norm = 1 / biatx[1]; - double normLast = 1.0; - - ar[0] *= norm; - - d.Add( biatx[2] * norm ); - n.Add( biatx[0] * norm ); - - // последнее уравнение - ::CalcBsplvb( knots, initParams[lastIndex - 1], lastKnot - 1, degree, biatx, lrVect ); - norm = 1.0 / biatx[2]; - // в последнем уравнении, соответственно, внедиагональный и диагональный элемент - double a = biatx[0] * norm; - double b = biatx[1] * norm; - - ar[lastIndex] *= norm; - Point & sn = ar[lastIndex]; - - // прямой ход - ptrdiff_t crLeft = degm; - ptrdiff_t i = 1, im = 0, ip = 1; - for ( ; i < lastIndex - 1; i++, im++, crLeft++ ) { - ::CalcBsplvb( knots, initParams[i - 1], crLeft, degree, biatx, lrVect ); - - norm = 1 / ( biatx[1] - d[im] * biatx[0] ); - - ar[i] = ( ar[i] - ar[im] * biatx[0] ) * norm; - d [i] = biatx[2] * norm; - // исключаем из последнего уравнения ведущую 1 и меняем последний столбец - normLast = -1.0 / d[im]; - - a *= normLast; - b = ( b - n[im] ) * normLast; - - sn = ( sn - ar[im] ) * normLast; - - n.Add( -norm * biatx[0] * n[im] ); - } - - // исключаем из 2-х последних уравнений 3-е с конца - ::CalcBsplvb( knots, initParams[i - 1], crLeft, degree, biatx, lrVect ); - - norm = 1.0 / ( biatx[1] - d[im] * biatx[0] ); - d [lastIndex - 1] = ( biatx[2] - n[im] * biatx[0] ) * norm; - ar[lastIndex - 1] = ( ar[lastIndex - 1] - ar[im] * biatx[0] ) * norm; - - normLast = 1.0 / ( a - d[im] ); - b = ( b - n[im] ) * normLast; - a = 1.0; - sn = ( sn - ar[im] ) * normLast; - - // исключаем из последнего предпоследнее - im++; - points[uppIndex] = ( sn - ar[im] ) / ( b - d[im] ); - - ptrdiff_t prevLast = uppIndex - 1; - points[prevLast] = ar[lastIndex - 1] - points[uppIndex] * d[prevLast]; - - // обратный ход - for ( i = prevLast - 1, ip = prevLast; i >= 0; i--, ip-- ) { - points[i] = ar[i] - points[ip] * d[i] - - points[uppIndex] * n[i]; - } - - bRes = nurbs.Init( degree, closed, points, weights, knots, form ); - C3D_ASSERT( bRes ); - } - - return bRes; -} - - -//------------------------------------------------------------------------------ -// \ru Получить массив параметров по точкам \en Get an array of parameters given the points -// --- -template -bool CreateSplineParameters( const PointsVector & points, MbeSplineParamType spType, bool cls, - DoubleVector & params ) -{ - bool isDone = false; - params.clear(); - isDone = ::DefineThroughPointsParams( points, spType, cls, params ); - - // \ru Нормализация \en Normalization - if ( isDone ) - c3d::SetLimitParam( params, 0.0, (double)((ptrdiff_t)points.size() - 1 + (cls ? 1 : 0)) ); - - return isDone; - -} - - -//------------------------------------------------------------------------------ -// \ru Выбрать точки на кривой для аппроксимации замкнутой nurbs \en Select points on the curve for approximation of closed nurbs -// --- -template -size_t DefineApproxPointsClosed( const Curve & curve, size_t pCount, double pmin, double pmax, ptrdiff_t degree, SArray & points, const KnotsVector & aKnots, const SArray & pCounts ) -{ - size_t pCountActual = pCount; - if ( pCount < 1 ) - return 0; - - points.clear(); - size_t stepCount = 0; - double factor = 1.0 / ( (double)pCount ); - const double epsilon = curve.GetTRegion( METRIC_REGION ); - const double angle = Math::deviateSag; - - size_t segmCount = curve.GetSegmentsCount(); - - SArray segmPointsCnt( segmCount, 2 ); - SArray tList, restList; - - double tmin, tmax; - ptrdiff_t first_Segm = curve.FindSegment( pmin, tmin ); - ptrdiff_t last_Segm = curve.FindSegment( pmax, tmax ); - ptrdiff_t i = 0; - - size_t freePntsCount = pCount; - - // \ru Найдем количество точек для каждого сегмента. \en Find the number of points for each segment. - size_t totalCount = 0; - for ( i = 0; i < pCounts.size(); ++i ) - totalCount += pCounts[i]; - if ( totalCount < 1 ) - totalCount = 1; - - for ( i = 0; i < pCounts.size(); i++ ){ - double part = ((double)pCounts[i]) / ((double)totalCount); - size_t segmPCnt = (size_t)(pCount * part); //-V113 - if ( i == last_Segm ) - segmPCnt = freePntsCount; - else { - segmPCnt = std_max( segmPCnt, (size_t)1); - segmPCnt = std_min( segmPCnt, freePntsCount ); - } - segmPointsCnt.push_back( segmPCnt ); - freePntsCount -= segmPCnt; - } - - Point p; - double plusT = 0.0; - for ( i = first_Segm; i <= last_Segm; i++ ) { - tList.clear(); - double smin, smax; - smin = curve.GetSegment(i)->GetTMin(); - smax = curve.GetSegment(i)->GetTMax(); - - if ( first_Segm == last_Segm ) { - plusT = pmin + smin - tmin; - smin = tmin; - smax = tmax; - } - else if ( i == first_Segm ){ - plusT = pmin + smin - tmin; - smin = tmin; - } - else if ( i == last_Segm ) { - smax = tmax; - } - double t = smin; - size_t tempCount = segmPointsCnt[i - first_Segm]; - factor = 1.0 / ( (double)tempCount ); - stepCount = 0; - while ( t < (smax - epsilon) ) { - double step = curve.GetSegment(i)->DeviationStep( t, angle ); - if ( (t + step) >= (smax - epsilon) ) - step = smax - t; - - for ( size_t k = 0; k < tempCount; k++ ) - tList.push_back( plusT + t + (step * factor * (double)k) ); - t += step; - stepCount++; - } - tList.push_back(plusT + smax); - plusT += smax; - - for ( size_t j = 0; j < tempCount; j++ ) { - t = tList[j * stepCount]; - curve._PointOn( t, p ); - points.push_back( p ); - restList.push_back( t ); - } - } - - restList.push_back( pmax ); - - // \ru Если задан узловой вектор, надо проверить, что между любыми 2 узлами есть хотя бы одна точка. \en If the knot vector is given, then it is necessary to check that there is at least one point between any two knots. - // \ru Если нет, то добавим точек, сохранив все уже набранные \en If not, then add the points and save old points - if ( aKnots.size() > 0 ) { - SArray pParams( 0, 1 ); - ::CreateSplineParameters( points, spt_ChordLength, true, pParams ); - c3d::SetLimitParam( pParams, aKnots[degree - 1], aKnots[aKnots.size() - degree] ); - double t1, t2; - bool bRes = false; - while ( !bRes ) { - bRes = true; - for ( i = degree - 1; i < aKnots.MaxIndex() - degree + 1 && bRes; i++ ) { - t1 = aKnots[i]; - t2 = aKnots[i + 1]; - if ( ::fabs(t2 - t1) > NULL_EPSILON ) { - size_t il = 0, ir = pParams.MaxIndex(); - size_t itemp = 0; - size_t ires1 = 0; - size_t ires2 = 0; - // \ru Левая граница \en The left boundary - bool goOn = true; - while ( goOn ) { - if ( ::fabs(pParams[il] - t1) < NULL_EPSILON ){ - ires1 = il; - goOn = false; - break; - } - else if ( ::fabs(pParams[ir] - t1) < NULL_EPSILON ) { - ires1 = ir; - goOn = false; - break; - } - else { - itemp = ( il + ir ) / 2; - if ( ::fabs(pParams[itemp] - t1) < NULL_EPSILON ) { - ires1 = itemp; - goOn = false; - break; - } - if ( pParams[itemp] < t1 ) - il = itemp; - else if ( pParams[itemp] > t1 ) - ir = itemp; - if ( ir - il < 2) { - ires1 = il; - goOn = false; - break; - } - } - } - // \ru Правая граница \en The right boundary - il = ires1; - ir = pParams.MaxIndex(); - goOn = true; - while ( goOn ) { - if ( ::fabs(pParams[il] - t2) < NULL_EPSILON ){ - ires2 = il; - goOn = false; - break; - } - else if ( ::fabs(pParams[ir] - t2) < NULL_EPSILON ) { - ires2 = ir; - goOn = false; - break; - } - else { - itemp = ( il + ir ) / 2; - if ( ::fabs(pParams[itemp] - t2) < NULL_EPSILON ) { - ires2 = itemp; - goOn = false; - break; - } - if ( pParams[itemp] < t2 ) - il = itemp; - else if ( pParams[itemp] > t2 ) - ir = itemp; - if ( ir - il < 2) { - ires2 = ir; - goOn = false; - break; - } - } - } - if ( ires2 - ires1 < 2 ) { - bRes = false; - // \ru Вставим среднюю точку между ires1 и ires2 и пересчитаем параметры \en Insert mid-point between ires1 and ires2 and recalculate parameters - double t = 0.5 * (restList[ ires1 ] + restList[ ires2 ]); - restList.AddAt( t, ires1 + 1 ); - - curve._PointOn( t, p ); - points.AddAt( p, ires1 + 1); - - pParams.clear(); - ::CreateSplineParameters( points, spt_ChordLength, false, pParams ); - c3d::SetLimitParam( pParams, aKnots[degree - 1], aKnots[aKnots.size() - degree] ); - pCountActual++; - } - } - } - } - } - - return pCountActual; -} - - -//------------------------------------------------------------------------------ -// \ru Выбрать точки на кривой для аппроксимации незамкнутой nurbs \en Select points on the curve for approximation of non-closed nurbs -// --- -template -size_t DefineApproxPointsOpen( const Curve & curve, size_t pCount, double pmin, double pmax, SArray & points, const KnotsVector & aKnots, const SArray & pCounts ) -{ - size_t pCountActual = pCount; - if ( pCount < 1 ) - return 0; - - points.clear(); - size_t stepCount = 0; - double factor = 1.0 / ( (double)(pCount - 1) ); - const double epsilon = curve.GetTRegion( METRIC_REGION ); - const double angle = Math::deviateSag; - - size_t segmCount = curve.GetSegmentsCount(); - if ( pCounts.size() != segmCount ) - return 0; - - SArray tList, restList; - SArray segmPointsCnt( segmCount, 2 ); - - double tpmin = pmin; - double tpmax = pmax; - - double tmin, tmax; - ptrdiff_t first_Segm = curve.FindSegment( tpmin, tmin ); - ptrdiff_t last_Segm = curve.FindSegment( tpmax, tmax ); - ptrdiff_t i; - - size_t freePntsCount = pCount - 1; - - // \ru Найдем количество точек для каждого сегмента. \en Find the number of points for each segment. - size_t totalCount = 0; - for ( i = 0; i < pCounts.size(); ++i ) - totalCount += pCounts[i]; - if ( totalCount == 0 ) - totalCount = 1; - - for ( i = 0; i < pCounts.size(); i++ ){ - double part = ((double)pCounts[i]) / ((double)totalCount); - size_t segmPCnt = (size_t)(pCount * part); //-V113 - if ( i == last_Segm ) - segmPCnt = freePntsCount; - else { - segmPCnt = std_max( segmPCnt, (size_t)1); - segmPCnt = std_min( segmPCnt, freePntsCount ); - } - segmPointsCnt.push_back( segmPCnt ); - freePntsCount -= segmPCnt; - } - - Point p; - double plusT = 0.0; - for ( i = first_Segm; i <= last_Segm; i++ ) { - tList.clear(); - - double smin = curve.GetSegment(i)->GetTMin(); - double smax = curve.GetSegment(i)->GetTMax(); - - if ( first_Segm == last_Segm ) { - plusT = tpmin + smin - tmin; - smin = tmin; - if (pmin < tpmin - PARAM_EPSILON ) - smin += pmin - tpmin; - smax = tmax; - if (pmax > tpmax + PARAM_EPSILON ) - smax += pmax - tpmax; - } - else if ( i == first_Segm ){ - plusT = tpmin + smin - tmin; - smin = tmin; - if (pmin < tpmin - PARAM_EPSILON ) - smin += pmin - tpmin; - } - else if ( i == last_Segm ) { - smax = tmax; - if (pmax > tpmax + PARAM_EPSILON ) - smax += pmax - tpmax; - } - double t = smin; - size_t tempCount = segmPointsCnt[i - first_Segm]; - factor = 1.0 / ( (double)tempCount ); - stepCount = 0; - while ( t < (smax - epsilon) ) { - double step = curve.GetSegment(i)->DeviationStep( t, angle ); - if ( (t + step) >= (smax - epsilon) ) - step = smax - t; - - for ( size_t j = 0; j < tempCount; j++ ) - tList.push_back( plusT + t + (step * factor * (double)j) ); - t += step; - stepCount++; - } - tList.push_back(plusT + smax); - plusT += smax; - - for ( size_t k = 0; k < tempCount; k++ ) { - t = tList[k * stepCount]; - curve._PointOn ( t, p ); - points.push_back( p ); - restList.push_back( t ); - } - } - - double t = pmax; - curve._PointOn ( t, p ); - points.push_back( p ); - restList.push_back( t ); - - // \ru Если задан узловой вектор, надо проверить, что между любыми 2 узлами есть хотя бы одна точка. \en If the knot vector is given, then it is necessary to check that there is at least one point between any two knots. - // \ru Если нет, то добавим точек, сохранив все уже набранные \en If not, then add the points and save old points - if ( aKnots.size() > 0 ) { - SArray pParams( 0, 1 ); - ::CreateSplineParameters( points, spt_ChordLength, false, pParams ); - c3d::SetLimitParam( pParams, aKnots.front(), aKnots.back() ); - - double t1, t2; - bool bRes = false; - while ( !bRes ) { - bRes = true; - for ( i = 0; i < aKnots.MaxIndex() && bRes; i++ ) { - t1 = aKnots[i]; - t2 = aKnots[i + 1]; - if ( ::fabs(t2 - t1) > NULL_EPSILON ) { - size_t il = 0, ir = pParams.MaxIndex(); - size_t itemp = 0; - size_t ires1 = 0; - size_t ires2 = 0; - // \ru Левая граница \en The left boundary - bool goOn = true; - while ( goOn ) { - if ( ::fabs(pParams[il] - t1) < NULL_EPSILON ){ - ires1 = il; - goOn = false; - break; - } - else if ( ::fabs(pParams[ir] - t1) < NULL_EPSILON ) { - ires1 = ir; - goOn = false; - break; - } - else { - itemp = ( il + ir ) / 2; - if ( ::fabs(pParams[itemp] - t1) < NULL_EPSILON ) { - ires1 = itemp; - goOn = false; - break; - } - if ( pParams[itemp] < t1 ) - il = itemp; - else if ( pParams[itemp] > t1 ) - ir = itemp; - if ( ir - il < 2) { - ires1 = il; - goOn = false; - break; - } - } - } - // \ru Правая граница \en The right boundary - il = ires1; - ir = pParams.MaxIndex(); - goOn = true; - while ( goOn ) { - if ( ::fabs(pParams[il] - t2) < NULL_EPSILON ){ - ires2 = il; - goOn = false; - break; - } - else if ( ::fabs(pParams[ir] - t2) < NULL_EPSILON ) { - ires2 = ir; - goOn = false; - break; - } - else { - itemp = ( il + ir ) / 2; - if ( ::fabs(pParams[itemp] - t2) < NULL_EPSILON ) { - ires2 = itemp; - goOn = false; - break; - } - if ( pParams[itemp] < t2 ) - il = itemp; - else if ( pParams[itemp] > t2 ) - ir = itemp; - if ( ir - il < 2) { - ires2 = ir; - goOn = false; - break; - } - } - } - if ( ires2 - ires1 < 2 ) { - bRes = false; - // \ru Вставим среднюю точку между ires1 и ires2 и пересчитаем параметры \en Insert mid-point between ires1 and ires2 and recalculate parameters - t = 0.5 * (restList[ ires1 ] + restList[ ires2 ]); - restList.AddAt( t, ires1 + 1 ); - - curve._PointOn( t, p ); - points.AddAt( p, ires1 + 1 ); - - pParams.clear(); - ::CreateSplineParameters( points, spt_ChordLength, false, pParams ); - c3d::SetLimitParam( pParams, aKnots.front(), aKnots.back() ); - pCountActual++; - } - } - } - } - } - return pCountActual; -} - - -//------------------------------------------------------------------------------ -// \ru Построение незамкнутого сплайна, аппроксимирующего набор точек, с помощью метода наименьших квадратов \en Construction of non-closed spline which approximates a set of points by the method of least squares -/*\ru aDegree - порядок сплайна, - pCount - количество узлов, - aPoints - набор точек для аппроксимации, - aKnots - предустановленный узловой вектор - \en ADegree - spline order, - pCount - count of knots, - aPoints - point set for approximation, - aKnots - predefined knots vector \~ -*/ -//--- -template -MATH_FUNC (bool) CreateNurbsLSMClosed( SPtr & nurbs, // \ru Модифицируемый сплайн \en Modifiable spline - const ptrdiff_t degree, - const ptrdiff_t pCount, - const PointsVector & aPoints, - const DoubleVector & aKnots, - const DoubleVector * aParams = NULL ); - -//------------------------------------------------------------------------------ -// \ru Построение незамкнутого сплайна, аппроксимирующего набор точек, с помощью метода наименьших квадратов \en Construction of non-closed spline which approximates a set of points by the method of least squares -// \ru Для построения замкнутого сплайна, надо затем использовать Unclamped( true ) \en Use Unclamped (true) for the construction of a closed spline -/*\ru aDegree - порядок сплайна, - pCount - количество узлов, - aPoints - набор точек для аппроксимации, - aKnots - предустановленный узловой вектор - \en ADegree - spline degree, - pCount - count of knots, - aPoints - point set for approximation, - aKnots - predefined knot vector \~ -*/ -//--- -template -MATH_FUNC (bool) CreateNurbsLSM( SPtr & nurbs, // \ru Модифицируемый сплайн \en Modifiable spline - const ptrdiff_t degree, - const ptrdiff_t pCount, - const PointsVector & aPoints, - const DoubleVector & aKnots, - const DoubleVector * aParams = NULL ); - -//------------------------------------------------------------------------------- -// -// --- -template -Nurbs * CreateLineOutRgn( const Curve & curve, double tn1, double tn2, double t1, double t2, - const MbCurveIntoNurbsInfo & nci ) -{ - Nurbs * nurbs = NULL; - - if ( !curve.IsClosed() && nci.ExtendRange() && ((tn2 - tn1) > Math::paramEpsilon) ) { - SArray points ( 2, 1 ); - SArray weights( 2, 1 ); - SArray knots ( 4, 1 ); //-V112 - - curve._PointOn( t1, *(points.Add()) ); - curve._PointOn( t2, *(points.Add()) ); - - weights.Add( 1.0 ); - weights.Add( 1.0 ); - - knots.Add( tn1 ); - knots.Add( tn1 ); - knots.Add( tn2 ); - knots.Add( tn2 ); - - nurbs = Nurbs::Create( 2, false, points, weights, knots, ncf_Unspecified ); - } - - return nurbs; -} - - -//------------------------------------------------------------------------------ -// \ru Преобразовать контур в нурбс \en Transform contour to NURBS -// --- -MbNurbs * ContourToNurbs( const MbContour & cntr, double t1, double t2, int sense, const MbCurveIntoNurbsInfo & nci, bool reparamByLength ); - -//------------------------------------------------------------------------------ -// \ru Преобразовать контур в нурбс \en Transform contour to NURBS -// --- -MbNurbs3D * ContourToNurbs( const MbContour3D & cntr, double t1, double t2, int sense, const MbCurveIntoNurbsInfo & nci, bool reparamByLength ); - - -#endif // __MB_NURBS_FUNCTION_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Модуль геометрических построений. + \en The module of geometric constructions. \~ + \details \ru Базовые алгоритмы Nurbs кривых и поверхностей. + \en The base algorithms for NURBS curves and surfaces. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __MB_NURBS_FUNCTION_H +#define __MB_NURBS_FUNCTION_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class MATH_CLASS MbContour; +class MATH_CLASS MbContour3D; +class MATH_CLASS MbNurbs; +class MATH_CLASS MbNurbs3D; + + +//------------------------------------------------------------------------------ +/** \brief \ru Флаг, определяющий построение сплайна, проходящего через точки. + \en Flag defining creation of spline passing through points. \~ + \details \ru Флаг, определяющий построение сплайна, проходящего через точки. Связан с версией. \n + \en Flag defining creation of spline passing through points. Related to the version. \n \~ + \ingroup Data_Structures +*/ +// --- +enum MbeSplineCreateType { + sct_Version0 = 0, ///< \ru Используется в версиях < V13 (центростремительная параметризация). \en Used in versions < V13 (centripetal parameterization). + sct_Version1 = 1, ///< \ru Используется в версии V13 (параметризация по длине хорды). \en Used in version V13 (parameterization by chord length). + sct_Version2 = 2, ///< \ru Используется в версии V13+. \en Used in version V13+. +}; + + +//------------------------------------------------------------------------------- +/** \brief \ru Дополнительная информация для преобразования кривой или поверхности в Nurbs. + \en Additional information for transformation of a curve or surface to NURBS. \~ + \details \ru Дополнительная информация для преобразования кривой или поверхности в Nurbs. \n + \en Additional information for transformation of a curve or surface to NURBS. \n \~ + \ingroup Data_Structures +*/ +// --- +class MbCurveIntoNurbsInfo { +private: + double tbeg; ///< \ru Параметр начала участка кривой. \en A parameter of the curve piece start. + double tend; ///< \ru Параметр конца участка кривой. \en A parameter of the curve piece end. + int sense; ///< \ru Направление сплайн-кривой. \en Direction of spline-curve. + bool matchParams; ///< \ru Сохранять ли при преобразовании однозначное соответствие параметрических областей. \en Whether to save correspondence of parametric regions while transforming or not. + bool extendRange; ///< \ru Строится ли преобразование на продолжении для незамкнутой подложки. \en Whether transformation is constructed on the extension for a non-closed substrate. + VERSION version; ///< \ru Версия исполнения. \en The version of execution. +private: + MbCurveIntoNurbsInfo(); +public: + /// \ru Конструктор. \en Constructor. + template + MbCurveIntoNurbsInfo( const Curve & c, bool match, bool ext, VERSION ver = Math::DefaultMathVersion() ) + : tbeg ( c.GetTMin() ) + , tend ( c.GetTMax() ) + , sense ( 1 ) + , matchParams( match ) + , extendRange( ext ) + , version ( ver ) + {} + /// \ru Конструктор. \en Constructor. + MbCurveIntoNurbsInfo( double t1, double t2, int s, bool match, bool ext, VERSION ver = Math::DefaultMathVersion() ) + : tbeg ( t1 ) + , tend ( t2 ) + , sense ( s ) + , matchParams( match ) + , extendRange( ext ) + , version ( ver ) + {} + /// \ru Конструктор. \en Constructor. + MbCurveIntoNurbsInfo( const MbCurveIntoNurbsInfo & other, double t1, double t2, int s ) + : tbeg ( t1 ) + , tend ( t2 ) + , sense ( s ) + , matchParams( other.matchParams ) + , extendRange( other.extendRange ) + , version ( other.version ) + {} + /// \ru Функция присвоения. \en Assignment function. + void Assign( const MbCurveIntoNurbsInfo & other ) + { + tbeg = other.tbeg; + tend = other.tend; + sense = other.sense; + matchParams = other.matchParams; + extendRange = other.extendRange; + version = other.version; + } + /// \ru Функция инициализации. \en The initialization function. + bool Init( double t1, double t2, int s ) + { + tbeg = t1; + tend = t2; + sense = s; + C3D_ASSERT( ((sense == 1) || (sense == -1)) ); + return ((sense == 1) || (sense == -1)); + } + /// \ru Функция инициализации. \en The initialization function. + bool Init( double t1, double t2, int s, bool match, bool ext ) { + matchParams = match; + extendRange = ext; + return Init( t1, t2, s ); + } + +public: + /// \ru Получить параметр начала участка кривой. \en Get the parameter of the start curve region. + double GetTBeg() const { return tbeg; } + /// \ru Получить параметр конца участка кривой. \en Get the parameter of the end curve region. + double GetTEnd() const { return tend; } + /// \ru Получить направление сплайн-кривой. \en Get the direction of spline-curve. + int GetSense() const { return sense; } + /// \ru Сохранять ли при преобразовании однозначное соответствие параметрических областей. \en Whether to save correspondence of parametric regions by transformation or not. + bool MatchParams() const { return matchParams; } + /// \ru Строится ли преобразование на продолжении для незамкнутой подложки. \en Whether transformation is constructed on the extension for a non-closed substrate or not. + bool ExtendRange() const { return extendRange; } + /// \ru Получить версию исполнения. \en Get the version of execution. + VERSION GetMathVersion() const { return version; } + +OBVIOUS_PRIVATE_COPY( MbCurveIntoNurbsInfo ) +}; + + +//------------------------------------------------------------------------------- +/** \brief \ru Параметры построения NURBS копии объекта. + \en Parameters for the construction of a NURBS copy of the object. \~ + \details \ru Параметры построения NURBS копии объекта. \n + \en Parameters for the construction of a NURBS copy of the object. \n \~ + \ingroup Data_Structures +*/ +// --- +struct MbNurbsParameters { +public: + size_t degree; ///< \ru Порядок NURBS копии. \en Order of NURBS copy. + size_t pointsCount; ///< \ru Количество контрольных точек (при 0 параметр игнорируется). \en The number of control points (if there is no control points, parameter is ignored). + MbRect1D region; ///< \ru Область объекта, подлежащая копированию: [0 1] соответствует [tMin tMax] объекта. \en Region of the object to be copied: [0, 1] corresponds to [tMin tMax] object. + SArray knots; ///< \ru Узловой вектор. \en Knot vector. + mutable bool useApprox; ///< \ru Не пытаться построить точную поверхность. \en Don't try to create the exact surface. + +public: + /// \ru Конструктор по умолчанию. \en Default constructor. + MbNurbsParameters() + : degree ( c3d::NURBS_DEGREE ) + , pointsCount( 0 ) + , region ( 0.0, 1.0 ) + , knots ( 0, 1 ) + , useApprox ( true ) + {} + /// \ru Конструктор по параметрам (без узлов) построения NURBS. \en The constructor of NURBS by parameters (without knots). + MbNurbsParameters( size_t d, size_t c, double zmin, double zmax, bool approx ) + : degree ( d ) + , pointsCount( c ) + , region ( zmin, zmax ) + , knots ( 0, 1 ) + , useApprox ( approx ) + { + C3D_ASSERT( d > 1 && d < SYS_MAX_UINT16 ); + degree = std_max( d, (size_t)2 ); + degree = std_min( d, (size_t)SYS_MAX_UINT16 ); + } + /// \ru Конструктор по полному набору параметров построения NURBS. \en The constructor of NURBS by a complete set of parameters. + MbNurbsParameters( size_t d, size_t c, double zmin, double zmax, bool approx, const SArray & aKnots ) + : degree ( d ) + , pointsCount( c ) + , region ( zmin, zmax ) + , knots ( aKnots ) + , useApprox ( approx ) + { + C3D_ASSERT( d > 1 && d < SYS_MAX_UINT16 ); + degree = std_max( d, (size_t)2 ); + degree = std_min( d, (size_t)SYS_MAX_UINT16 ); + } + /// \ru Конструктор копирования. \en The copy constructor. + MbNurbsParameters( const MbNurbsParameters & other ) + : degree ( other.degree ) + , pointsCount( other.pointsCount ) + , region ( other.region ) + , knots ( other.knots ) + , useApprox ( other.useApprox ) + {} + /// \ru Деструктор. \en Destructor. + ~MbNurbsParameters() {} + + /// \ru Инициализировать по другим параметрам построения NURBS копии объекта. \en Initialize by another parameters. + void Init( const MbNurbsParameters & other ) { + degree = other.degree; + pointsCount = other.pointsCount; + region = other.region; + knots = other.knots; + useApprox = other.useApprox; + } + /// \ru Являются ли объекты равными? \en Determine whether an object is equal? + bool IsSame( const MbNurbsParameters & other, double accuracy ) const; + /// \ru Оператор присваивания. \en The assignment operator. + MbNurbsParameters & operator = ( const MbNurbsParameters & other ) { + degree = other.degree; + pointsCount = other.pointsCount; + region = other.region; + knots = other.knots; + useApprox = other.useApprox; + return (*this); + } + +public: + size_t GetDegree() const { return degree; } + size_t GetPointsCount() const { return pointsCount; } + const MbRect1D & GetRegion() const { return region; } + const SArray & GetKnots() const { return knots; } + bool UseApprox() const { return useApprox; } + + void SetDegree( size_t c ) { if ( c > 1 ) degree = c; } + void SetPointsCount( size_t c ) { if ( c > 1 ) pointsCount = c; } + SArray & SetKnots() { return knots; } + void SetKnots( SArray & c ) { knots = c; } + void SetKnots( std::vector & c ) { knots = c; } + void SetApprox( bool c ) { useApprox = c; } + +KNOWN_OBJECTS_RW_REF_OPERATORS( MbNurbsParameters ) // \ru Для работы со ссылками и объектами класса \en For working with references and objects of the class +}; // MbNurbsParameters + + +//------------------------------------------------------------------------------- +/** \brief \ru Параметры узловой точки сплайновой копии объекта. + \en Parameters of knot point of the object spline copy. \~ + \details \ru Параметры узловой точки сплайновой копии объекта. \n + \en Parameters of knot point of the object spline copy. \n \~ + \ingroup Data_Structures +*/ +// --- +struct MbNurbsPointInfo { +public: + MbCartPoint3D point; ///< \ru Узловая точка сплайновой поверхности. \en A knot point of a spline surface. + bool visible; ///< \ru Флаг видимости точки. \en A point visibility flag. + int8 poleLocation; ///< \ru Расположение полюса в параметрической области. \en The location of a pole in the parametric region. +public: + /// \ru Конструктор по умолчанию. \en Default constructor. + MbNurbsPointInfo() + : point ( ) + , visible ( true ) + , poleLocation ( (uint8)pln_None ) + {} + /// \ru Конструктор по точке и флагу видимости. \en The constructor by a point and visibility flag. + MbNurbsPointInfo( MbCartPoint3D aPount, bool aVisible, MbePoleLocation aPoleLocation ) + : point ( aPount ) + , visible ( aVisible ) + , poleLocation ( (uint8)aPoleLocation ) + {} + /// \ru Конструктор копирования. \en The copy constructor. + MbNurbsPointInfo( const MbNurbsPointInfo & other ) + : point ( other.point ) + , visible ( other.visible ) + , poleLocation ( (uint8)other.poleLocation ) + {} + /// \ru Деструктор. \en Destructor. + ~MbNurbsPointInfo() {} + + /// \ru Инициализировать по точке и флагу видимости. \en Initialize by a point and visibility flag. + void Init( const MbNurbsPointInfo & other ) + { + point = other.point; + visible = other.visible; + poleLocation = other.poleLocation; + } + /// \ru Оператор присваивания. \en The assignment operator. + MbNurbsPointInfo & operator = ( const MbNurbsPointInfo & other ) + { + point = other.point; + visible = other.visible; + poleLocation = other.poleLocation; + return (*this); + } + /// \ru Определить, является ли точка полюсом. \en Define whether the point is a pole. + MbePoleLocation GetPoleLocation() { return (MbePoleLocation)poleLocation; } + + // \ru Закомментировала строчку ниже, поскольку объект служит только для передачи дополнительной информации об узлах сплайна \en Commented out the line below, because the object is used only to pass additional information about spline knots + // \ru В процессе прямого моделирования. Не пишется и не читается. \en In the direct modeling. Not written and not read. + // \ru KNOWN_OBJECTS_RW_REF_OPERATORS( MbNurbsPointInfo ) // для работы со ссылками и объектами класса \en KNOWN_OBJECTS_RW_REF_OPERATORS( MbNurbsPointInfo ) // for working with references and objects of the class +}; //MbNurbsPointInfo + + +//------------------------------------------------------------------------------ +/** \brief \ru Дать меру расстояния. + \en Get a measure of the distance. \~ + \details \ru Дать меру расстояния. + \en Get a measure of the distance. \~ + \param[in] p1, p2 - \ru Точки между которыми ищется расстояние. + \en Points between which the distance is computed. \~ + \param[in] spType - \ru Тип параметризации сплайновых объектов. + \en The parametrization type of spline objects. \~ + \return \ru Расстояние. + \en Distance. \~ + \ingroup Base_Algorithms +*/ +// --- +template +double GetParamDistance( const Type & p1, const Type & p2, MbeSplineParamType spType ) +{ + switch ( spType ) { + case spt_Unstated : + case spt_EquallySpaced : + return 1.0; + case spt_ChordLength : + return p1.DistanceToPoint( p2 ); + case spt_Centripetal: + return ::sqrt( p1.DistanceToPoint( p2 ) ); + } + + return 1.0; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить параметры инициализации nurbs-объекта. + \en Check initialization parameters of a nurbs-object. \~ + \details \ru Проверить параметры инициализации nurbs-объекта. + \en Check initialization parameters of a nurbs-object. \~ + \param[in] degree - \ru Порядок B-сплайна. + \en B-spline degree. \~ + \param[in] closed - \ru Признак замкнутости. + \en A closedness attribute. \~ + \param[in] pcnt - \ru Число точек. + \en Number of points. \~ + \return \ru true, если параметры согласованы. + \en Returns true if parameters are consistent. \~ + \ingroup Base_Algorithms +*/ +// --- +inline bool IsValidNurbsParams( size_t degree, bool closed, size_t pcnt ) +{ + // \ru 1. Порядок B-сплайна должен быть не менее 2. \en 1. The order of B-spline must be at least 2. + // \ru 2а. Для незамкнутой кривой количество точек не меньше порядка сплайна. \en 2a. The number of open curve points isn't less than the order of spline. + // \ru 2б. Для замкнутой кривой должно быть как минимум 3 различных точки. \en 2b. Closed curve must have at least 3 different points. + + return ( (degree > 1) && (pcnt >= (closed ? 3 : degree)) ); +} + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить параметры инициализации nurbs-объекта. + \en Check initialization parameters of a nurbs-object. \~ + \details \ru Проверить параметры инициализации nurbs-объекта. + \en Check initialization parameters of a nurbs-object. \~ + \param[in] degree - \ru Порядок B-сплайна. + \en B-spline degree. \~ + \param[in] closed - \ru Признак замкнутости. + \en A closedness attribute. \~ + \param[in] pcnt - \ru Число точек. + \en Number of points. \~ + \param[in] wcnt - \ru Число весов. + \en Number of weights. \~ + \return \ru true, если параметры согласованы. + \en Returns true if parameters are consistent. \~ + \ingroup Base_Algorithms +*/ +// --- +inline bool IsValidNurbsParams( size_t degree, bool closed, size_t pcnt, size_t wcnt ) +{ + // \ru 1. Порядок B-сплайна должен быть не менее 2. \en 1. The order of B-spline must be at least 2. + // \ru 2а. Для незамкнутой кривой количество точек не меньше порядка сплайна. \en 2a. The number of open curve points isn't less than the order of spline. + // \ru 2б. Для замкнутой кривой должно быть как минимум 3 различных точки. \en 2b. Closed curve must have at least 3 different points. + // \ru 3. Количество точек и количество весов должны быть согласованы. \en 3. Number of points and number of weights must be equal. + + bool res = ::IsValidNurbsParams( degree, closed, pcnt ) && (wcnt == pcnt); + return res; +} + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить параметры инициализации nurbs-объекта. + \en Check initialization parameters of a nurbs-object. \~ + \details \ru Проверить параметры инициализации nurbs-объекта. + \en Check initialization parameters of a nurbs-object. \~ + \param[in] degree - \ru Порядок B-сплайна. + \en B-spline degree. \~ + \param[in] closed - \ru Признак замкнутости. + \en A closedness attribute. \~ + \param[in] pcnt - \ru Число точек. + \en Number of points. \~ + \param[in] wcnt - \ru Число весов. + \en Number of weights. \~ + \param[in] kcnt - \ru Число узлов. + \en Number of knots. \~ + \return \ru true, если параметры согласованы. + \en Returns true if parameters are consistent. \~ + \ingroup Base_Algorithms +*/ +// --- +inline bool IsValidNurbsParams( size_t degree, bool closed, size_t pcnt, size_t wcnt, size_t kcnt ) +{ + // \ru 1. Порядок B-сплайна должен быть не менее 2. \en 1. The order of B-spline must be at least 2. + // \ru 2а. Для незамкнутой кривой количество точек не меньше порядка сплайна. \en 2a. The number of open curve points isn't less than the order of spline. + // \ru 2б. Для замкнутой кривой должно быть как минимум 3 различных точки. \en 2b. Closed curve must have at least 3 different points. + // \ru 3. Количество точек и количество весов должны быть согласованы. \en 3. Number of points and number of weights must be equal. + // \ru 4. Количество узлов должно быть согласовано по остальным параметрам. \en 4. Number of knots must be consistent with the other parameters. + + bool res = ::IsValidNurbsParams( degree, closed, pcnt, wcnt ) && + ( kcnt == (degree + pcnt + (closed ? (degree - 1) : 0)) ); + return res; +} + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить параметры инициализации nurbs-кривой. + \en Check initialization parameters of a nurbs-curve. \~ + \details \ru Проверить параметры инициализации nurbs-кривой. + \en Check initialization parameters of a nurbs-curve. \~ + \param[in] degree - \ru Порядок B-сплайна. + \en B-spline degree. \~ + \param[in] closed - \ru Признак замкнутости. + \en A closedness attribute. \~ + \param[in] pcnt - \ru Число точек. + \en Number of points. \~ + \param[in] knots - \ru Узловой вектор. + \en Knots vector. \~ + \return \ru true, если параметры согласованы. + \en Returns true if parameters are consistent. \~ + \ingroup Base_Algorithms +*/ +// --- +template +bool IsValidNurbsParamsExt( size_t degree, bool closed, size_t pcnt, + const KnotsVector & knots ) +{ + // \ru 1. Порядок B-сплайна должен быть не менее 2. \en 1. The order of B-spline must be at least 2. + // \ru 2а. Для незамкнутой кривой количество точек не меньше порядка сплайна. \en 2a. The number of open curve points isn't less than the order of spline. + // \ru 2б. Для замкнутой кривой должно быть как минимум 3 различных точки. \en 2b. Closed curve must have at least 3 different points. + // \ru 3. Количество точек и количество весов должны быть согласованы. \en 3. Number of points and number of weights must be equal. + // \ru 4. Количество узлов должно быть согласовано по остальным параметрам. \en 4. Number of knots must be consistent with the other parameters. + + bool res = ::IsValidNurbsParams( degree, closed, pcnt ) && + ( knots.size() == (degree + pcnt + (closed ? (degree - 1) : 0)) ); + + if ( res ) { + if ( !c3d::IsMonotonic(knots, true, true) ) + res = false; // SD#7118498 + } + + return res; +} + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить параметры инициализации nurbs-кривой. + \en Check initialization parameters of a nurbs-curve. \~ + \details \ru Проверить параметры инициализации nurbs-кривой. + \en Check initialization parameters of a nurbs-curve. \~ + \param[in] degree - \ru Порядок B-сплайна. + \en B-spline degree. \~ + \param[in] closed - \ru Признак замкнутости. + \en A closedness attribute. \~ + \param[in] pnts - \ru Точки. + \en Points vector. \~ + \param[in] wts - \ru Веса точек. + \en Weights vector. \~ + \param[in] knots - \ru Узловой вектор. + \en Knots vector. \~ + \return \ru true, если параметры согласованы. + \en Returns true if parameters are consistent. \~ + \ingroup Base_Algorithms +*/ +// --- +template +bool IsValidNurbsParamsExt( size_t degree, bool closed, const PointVector & pnts, + const DoubleVector * wts, + const DoubleVector * knots = NULL ) +{ + // \ru 1. Порядок B-сплайна должен быть не менее 2. \en 1. The order of B-spline must be at least 2. + // \ru 2а. Для незамкнутой кривой количество точек не меньше порядка сплайна. \en 2a. The number of open curve points isn't less than the order of spline. + // \ru 2б. Для замкнутой кривой должно быть как минимум 3 различных точки. \en 2b. Closed curve must have at least 3 different points. + // \ru 3. Количество точек и количество весов должны быть согласованы. \en 3. Number of points and number of weights must be equal. + // \ru 4. Количество узлов должно быть согласовано по остальным параметрам. \en 4. Number of knots must be consistent with the other parameters. + + size_t pcnt = pnts.size(); + + bool res = ::IsValidNurbsParams( degree, closed, pcnt ) && + ( (wts == NULL) || (wts->size() == pcnt) ) && + ( (knots == NULL) || (knots->size() == (degree + pcnt + (closed ? (degree - 1) : 0))) ); + + if ( res && (knots != NULL) ) { + if ( !c3d::IsMonotonic(*knots, true, true) ) + res = false; // SD#7118498 + } + + return res; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить узловой вектор nurbs-объекта. + \en Check knots vector of a nurbs-object. \~ + \details \ru Проверить узловой вектор nurbs-объекта. + \en Check knots vector of a nurbs-object. \~ + \param[in] knots - \ru Узловой вектор. + \en Knots vector. \~ + \return \ru true, если параметры согласованы. + \en Returns true if parameters are consistent. \~ + \ingroup Base_Algorithms +*/ +// --- +template +bool IsValidNurbsKnots( const KnotsVector & knots, double eps = EXTENT_EPSILON ) +{ + // \ru 1 . Количество узлов должно быть не менее 4 \en 1 . The number of knots must be at least 4 + // \ru 2 . Последовательность узлов должна быть неубывающей \en 2 . Sequence of nodes must be nondecreasing + // \ru 3 . Первый и последний узлы должны быть различны \en 3 . The first and the last nodes must be different + + size_t cnt = knots.size(); + if ( cnt < 4 ) + return false; + + eps = ::fabs(eps); + if ( ::fabs(knots[cnt-1] - knots[0]) < eps ) + return false; + + for ( size_t i = 0; i < (cnt - 1); i++ ) { + if ( knots[i+1] < knots[i] - eps ) + return false; + } + + return true; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Определение индекса узла left для первой ненулевой функции. + \en Definition of "left" knot index for the first non-zero function. \~ + \details \ru Определение индекса узла left для первой ненулевой функции (knots[mid] <= t < knots[mid + 1]). + \en Definition of "left" knot index for the first non-zero function (knots[mid] <= t < knots[mid + 1]). \~ + \param[in] degree - \ru Порядок B-сплайна. + \en B-spline degree. \~ + \param[in] knots - \ru Множество узлов. + \en Knots. \~ + \param[in, out] t - \ru Значение параметра. + \en A parameter value. \~ + \return \ru Индекс узла. + \en A knot index. \~ + \ingroup Base_Algorithms +*/ +// --- +template +ptrdiff_t KnotIndex( size_t degree, const KnotsVector & knots, double & t ) +{ + size_t low = degree - 1; + size_t high = knots.size() - degree; + ptrdiff_t mid = low; + + if ( t <= knots[low] ) { + t = knots[low]; + size_t countKnt = knots.size(); + size_t lowP = low; + lowP++; + // \ru Исправление ошибки BUG_21185 while ( (lowP < countKnt) && (t == knots[lowP]) ) { \en Bugfix BUG_21185 while ( (lowP < countKnt) && (t == knots[lowP]) ) { + while ( (lowP + degree < countKnt) && (t == knots[lowP]) ) { //-V550 + low = lowP; + lowP++; + } + mid = low; + } + else if ( t >= knots[high] ) { + t = knots[high]; + // BUG_82980 while ( (high > 0) && (knots[high] == t) ) { + while ( (high + 1 > degree) && (knots[high] == t) ) { + high--; + } + mid = high; + } + else if ( c3d::ArFind(knots, t, mid) ) { + while ( knots[mid] == t ) { + mid++; + } + mid--; + } + return mid; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Определить узловой вектор (равномерная параметризация). + \en Define knot vector (uniform parameterization). \~ + \details \ru Определить узловой вектор (равномерная параметризация). + \en Define knot vector (uniform parameterization). \~ + \param[in] degree - \ru Порядок B-сплайна. + \en B-spline degree. \~ + \param[in] closed - \ru Признак замкнутости. + \en A closedness attribute. \~ + \param[in] uppPointsIndex - \ru Индекс последней точки. + \en Last point index. \~ + \param[in] knots - \ru Узловой вектор. + \en Knots vector. \~ + \ingroup Base_Algorithms +*/ +// --- +template +ptrdiff_t DefineKnotsVector( ptrdiff_t degree, bool closed, ptrdiff_t uppPointsIndex, + KnotsVector & knots ) +{ + if ( (degree < 2) || (uppPointsIndex < 1) ) { + knots.clear(); + return -1; + } + + ptrdiff_t pointsCount = uppPointsIndex + 1; + ptrdiff_t power = degree - 1; + + ptrdiff_t knotsCount = degree + pointsCount + (closed ? power : 0); + knots.clear(); + knots.reserve( knotsCount ); + + ptrdiff_t i = 0; + + if ( closed ) { // замкнутый В-сплайн + double knot = 0.0; + for ( i = 0; i < knotsCount; i++ ) { + knot = (double)(i - power); + knots.push_back( knot ); + } + } + else { + double knot = 0.0; + for ( i = 0; i < degree; i++ ) + knots.push_back( knot ); + + ptrdiff_t cnt = pointsCount - degree; + for ( i = 0; i < cnt; i++ ) { + knot += 1.0; + knots.push_back( knot ); + } + + knot += 1.0; + for ( i = 0; i < degree; i++ ) + knots.push_back( knot ); + } + + return (knotsCount - 1); +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Определить узловой вектор. + \en Define knot vector. \~ + \details \ru Определить узловой вектор. + \en Define knot vector. \~ + \param[in] degree - \ru Порядок B-сплайна. + \en B-spline degree. \~ + \param[in] closed - \ru Признак замкнутости. + \en A closedness attribute. \~ + \param[in] count - \ru Количество точек. + \en Number of points. \~ + \param[in] params - \ru Параметры точек (для замкнутого count+1). + \en Points parameters (for closed spline - "count"+1). \~ + \param[out] knots - \ru Узловой вектор. + \en Knots vector. \~ + \return \ru true, если набор параметров сформирован. + \en Returns true if result is success. \~ + \ingroup Base_Algorithms +*/ +// --- +MATH_FUNC (bool) DefineKnotsVector( size_t degree, bool closed, size_t count, // \ru Порядок, замкнутость, количество точек \en Order, closedness, number of points + const SArray * params, // \ru Параметры точек (для замкнутого count+1) \en Points parameters (for closed spline - "count"+1) + SArray & knots ); // \ru Формируемый узловой вектор \en Generated knot vector + + +//------------------------------------------------------------------------------ +/** \brief \ru Определить параметрическое распределение точек. + \en Define parametric distribution of points. \~ + \details \ru Определить параметрическое распределение точек. + \en Define parametric distribution of points. \~ + \param[in] points - \ru Массив точек. + \en Points vector. \~ + \param[in] spType - \ru Тип параметризации сплайновых объектов. + \en The parameterization type of spline objects. \~ + \param[in] closed - \ru Признак замкнутости. + \en A closedness attribute. \~ + \param[out] params - \ru Параметрическое распределение точек. + \en Parametric distribution of points. \~ + \return \ru true, если набор параметров сформирован. + \en Returns true if result is success. \~ + \ingroup Base_Algorithms +*/ +// --- +template +bool DefineThroughPointsParams( const PointsVector & points, MbeSplineParamType spType, bool closed, + ParamsVector & params ) +{ + bool res = false; + + ptrdiff_t count = (ptrdiff_t)points.size(); + + if ( count > (closed ? 2 : 1) ) { + double param = 0.0; + ptrdiff_t extCount = closed ? (count + 1) : count; + params.clear(); + params.resize( extCount, param ); + + double paramSum = 0.0; + for ( ptrdiff_t j = 1; j < extCount; j++ ) { + ptrdiff_t jp = (j - 1 + count) % count; + ptrdiff_t jc = j % count; + param = ::GetParamDistance( points[jp], points[jc], spType ); + params[j] = ( params[j-1] + param ); + paramSum += param; + } + + if ( paramSum > METRIC_PRECISION ) { + for ( ptrdiff_t j = 0; j < extCount; j++ ) { + params[j] /= paramSum; + params[j] *= (double)(extCount-1); + } + res = true; + } + else if ( spType != spt_EquallySpaced ) { + C3D_ASSERT_UNCONDITIONAL( false ); + paramSum = 0.0; + for ( ptrdiff_t j = 1; j < extCount; j++ ) { + ptrdiff_t jp = (j - 1 + count) % count; + ptrdiff_t jc = j % count; + param = ::GetParamDistance( points[jp], points[jc], spt_EquallySpaced ); + params[j] = ( params[j-1] + param ); + paramSum += param; + } + if ( paramSum > METRIC_PRECISION ) { + for ( ptrdiff_t j = 0; j < extCount; j++ ) { + params[j] /= paramSum; + params[j] *= (double)(extCount-1); + } + res = true; + } + } + } + + return res; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Вычисление B - базиса (degree - порядок B-сплайна, p = (degree - 1) - степень полинома(B-сплайна)). + \en The calculation of B - basis ("degree" - order of B-spline, p = (degree - 1) - the degree of the polynomial (B-spline)). \~ + \details \ru Вычисление B - базиса (degree - порядок B-сплайна, p = (degree - 1) - степень полинома(B-сплайна)). + Для ускорения используется рабочий вектор lr = { left[p+1], right[p+1] } ). + \en The calculation of B - basis ("degree" - order of B-spline, p = (degree - 1) - the degree of the polynomial (B-spline)). + To speed up vector lr = { left[p+1], right[p+1] } is used. \~ + \param[in] i - \ru Индекс в массиве узлов, получаемый с помощью функции KnotIndex(). + \en Index of knots vector obtained by the function KnotIndex(). \~ + \param[in] t - \ru Параметр. + \en Parameter. \~ + \param[in] p - \ru p = degree - 1, где degree - порядок B-сплайна. + \en p = degree - 1, where degree is B-spline degree. \~ + \param[in] knots - \ru Узловой вектор. + \en Knots vector. \~ + \param[out] nsplines - \ru Массив размерности degree, заполняется значениями сплайна в поля 0..degree-2; nsplines[degree-1] = 0. + \en Array of B-spline values. \~ + \param[in,out] lrVect - \ru Массив размерности 2*(p+1) = 2*degree, содержимое игнорируется и будет перезаписано. Нужен для ускорения работы функции. В результате работы в нем останется мусор.. + \en Temporary working vector with dimension 2*(p+1) = 2*degree. \~ + \ingroup Base_Algorithms +*/ +// --- +template +MATH_FUNC (bool) BasisFuns( ptrdiff_t i, double t, ptrdiff_t p, const KnotsVector & knots, DoubleVector & nsplines, + DoubleVector & lrVect ); + +//------------------------------------------------------------------------------ +/** \brief \ru Вычислить базисный сплайн по параметру t и узловому вектору. + \en Calculate basic spline by "t" parameter and knots vector. \~ + \details \ru Вычислить базисный сплайн по параметру t и узловому вектору. + \en Calculate basic spline by "t" parameter and knots vector. \~ + \ingroup Base_Algorithms +*/ +// --- +template +MATH_FUNC (bool) CalcBsplvb( const DoubleVector & knots, double t, ptrdiff_t left, ptrdiff_t degree, + DoubleVector & biatx, DoubleVector & lrVect ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Вычисление B - базиса ( с использованием рабочих указателей классов nurbs кривых и поверхностей ). + \en Calculation of B - basis (using working pointers to nurbs curves and surfaces). \~ + \details \ru Вычисление B - базиса ( с использованием рабочих указателей классов nurbs кривых и поверхностей ). + \en Calculation of B - basis (using working pointers to nurbs curves and surfaces). \~ + \ingroup Base_Algorithms +*/ +// --- +void AllBasisFuns( ptrdiff_t i, double t, ptrdiff_t p, const SArray & knots, double ** ndu, + double * left, double * right, bool newPatch = true ); + +//------------------------------------------------------------------------------ +/** \brief \ru Вычисление B - базиса ( с использованием рабочих указателей классов nurbs кривых и поверхностей ). + \en Calculation of B - basis (using working pointers to nurbs curves and surfaces). \~ + \details \ru Вычисление B - базиса ( с использованием рабочих указателей классов nurbs кривых и поверхностей ). + \en Calculation of B - basis (using working pointers to nurbs curves and surfaces). \~ + \ingroup Base_Algorithms +*/ +// --- +void AllBasisFuns( ptrdiff_t i, double t, ptrdiff_t p, const SArray & knots, double * ndu, size_t degree, + double * left, double * right, bool newPatch = true ); + +//------------------------------------------------------------------------------ +/// \ru Вычислить значения базисного сплайна и его производных. \en Calculate values and derivatives of basic spline. +/** + \param[in] t - \ru Параметр на кривой. + \en Curve parameter. \~ + \param[in] left - \ru Номер узла первого ненулевого сплайна. + \en Knot number of the first nonzero spline. \~ + \param[in] n - \ru Порядок вычисляемых производных. + \en Order of calculated derivatives. \~ + \param[out] values - \ru Двумерный массив значений. + \en 2D-Array filled by spline values. \~ + \ingroup Base_Algorithms +*/ +//--- +bool CalcDBsplvb( const SArray & knots, size_t degree, double t, ptrdiff_t left, size_t n, Array2 & values ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Вычислить все разностные формы кривой. + \en Calculate all difference forms of curve. \~ + \details \ru Вычислить все (или опционально некоторые) разностные формы кривой (характеристические производные). + \en Calculate all (or some, optionally) difference forms of curve (characteristic derivatives). \~ + \ingroup Base_Algorithms +*/ +// --- +template +void CurveDeriveCpts( ptrdiff_t p, const KnotsVector & U, const Point * P, const double * W, size_t pointCount, + const NurbsVector * PW, ptrdiff_t d, ptrdiff_t r1, ptrdiff_t r2, NurbsVector * PK ) +{ + C3D_ASSERT( (P != NULL && W != NULL) != (PW != NULL) ); + + ptrdiff_t r = ( r2 - r1 ); + ptrdiff_t degree = ( p + 1 ); + ptrdiff_t i, k, icount; + NurbsVector & PK0 = PK[0]; + + if ( PW != NULL ) { + for ( i = 0; i <= r; i++ ) + PK0.Set( i, *PW, (r1 + i) ); + } + else { + if ( !PK0.UseWeights() && ( r1 + r ) < pointCount ) { + for ( i = 0; i <= r; i++ ) { + PK0[i] = P[r1 + i]; + } + } + else { + for ( i = 0; i <= r; i++ ) { + k = ( ( r1 + i ) % pointCount ); + PK0.Init( i, P[k], W[k] ); + } + } + } + + for ( k = 1; k <= d; k++ ) { + NurbsVector & PKMin = PK[k - 1]; + NurbsVector & PKPls = PK[k]; + double tmp = (double)( degree - k ); + ptrdiff_t r1i = r1; + for ( i = 0, icount = (r - k); i <= icount; i++ ) { + r1i = ( r1 + i ); + C3D_ASSERT( ( ::fabs( U[r1i + degree] - U[r1i + k] ) > DOUBLE_EPSILON ) ); // \ru Проверка на сбой \en Check of failure + PKPls.Dec( i, PKMin, i, PKMin, (i + 1), (tmp / (U[r1i + degree] - U[r1i + k])) ); + } + } +} + +//------------------------------------------------------------------------------ +/** \brief \ru Вычислить все разностные формы кривой. + \en Calculate all difference forms of curve. \~ + \details \ru Вычислить все (или опционально некоторые) разностные формы кривой (характеристические производные). + \en Calculate all (or some, optionally) difference forms of curve (characteristic derivatives). \~ + \ingroup Base_Algorithms +*/ +// --- +template +void CurveDeriveCpts( ptrdiff_t p, const KnotsVector & U, const Point * P, const double w, size_t pointCount, + const NurbsVector * PW, ptrdiff_t d, ptrdiff_t r1, ptrdiff_t r2, NurbsVector * PK ) +{ + C3D_ASSERT( (P != NULL) != (PW != NULL) ); + + ptrdiff_t r = ( r2 - r1 ); + ptrdiff_t degree = ( p + 1 ); + ptrdiff_t i, k, icount; + NurbsVector & PK0 = PK[0]; + + if ( PW != NULL ) { + for ( i = 0; i <= r; i++ ) + PK0.Set( i, *PW, (r1 + i) ); + } + else { + if ( !PK0.UseWeights() && ( r1 + r ) < pointCount ) { + for ( i = 0; i <= r; i++ ) { + PK0[i] = P[r1 + i]; + } + } + else { + for ( i = 0; i <= r; i++ ) { + k = ( ( r1 + i ) % pointCount ); + PK0.Init( i, P[k], w ); + } + } + } + + for ( k = 1; k <= d; k++ ) { + NurbsVector & PKMin = PK[k - 1]; + NurbsVector & PKPls = PK[k]; + double tmp = (double)( degree - k ); + ptrdiff_t r1i = r1; + for ( i = 0, icount = (r - k); i <= icount; i++ ) { + r1i = ( r1 + i ); + C3D_ASSERT( ( ::fabs( U[r1i + degree] - U[r1i + k] ) > DOUBLE_EPSILON ) ); // \ru Проверка на сбой \en Check of failure + PKPls.Dec( i, PKMin, i, PKMin, (i + 1), (tmp / (U[r1i + degree] - U[r1i + k])) ); + } + } +} + +//------------------------------------------------------------------------------ + /** \brief \ru Вычислить все разностные формы кривой. + \en Calculate all difference forms of curve. \~ + \details \ru Вычислить все (или опционально некоторые) разностные формы кривой (характеристические производные). + \en Calculate all (or some, optionally) difference forms of curve (characteristic derivatives). \~ + \ingroup Base_Algorithms +*/ +// --- +template +void CurveDeriveCpts( ptrdiff_t p, const KnotsVector & U, const Point * P, const double * W, size_t pointCount, + const NurbsVector * PW, ptrdiff_t d, ptrdiff_t r1, ptrdiff_t r2, DoubleTriple ** DT, double ** WT ) +{ + C3D_ASSERT( ( P != NULL && W != NULL ) != ( PW != NULL ) ); + + ptrdiff_t r = ( r2 - r1 ); + ptrdiff_t degree = ( p + 1 ); + ptrdiff_t i, k, icount; + DoubleTriple * DT0 = DT[0]; + double * WT0 = WT[0]; + bool useWeight = WT0 != NULL; + + if ( PW != NULL ) { + if ( !useWeight ) { + for ( i = 0; i <= r; i++ ) + DT0[i].Init( (*PW)[r1 + i] ); + } + else { + for ( i = 0; i <= r; i++ ) { + DT0[i].Init( (*PW)[r1 + i] ); + WT0[i] = PW->w( r1 + i ); + } + } + } + else { + if ( !useWeight && ( r1 + r ) < (ptrdiff_t)pointCount ) { + for ( i = 0; i <= r; i++ ) { + DT0[i].Init( P[r1 + i].x, P[r1 + i].y, P[r1 + i].z ); + } + } + else { + for ( i = 0; i <= r; i++ ) { + k = ( ( r1 + i ) % pointCount ); + DT0[i].Init( P[k], W[k] ); + if ( useWeight ) + WT0[i] = W[k]; + } + } + } + + double * WTMin = NULL; + double * WTPls = NULL; + for ( k = 1; k <= d; k++ ) { + DoubleTriple * DTMin = DT[k - 1]; + DoubleTriple * DTPls = DT[k]; + if ( useWeight ) { + WTMin = WT[k - 1]; + WTPls = WT[k]; + } + + double tmp = (double)( degree - k ); + ptrdiff_t r1i = r1; + for ( i = 0, icount = ( r - k ); i <= icount; i++ ) { + r1i = ( r1 + i ); + double tmp2 = U[r1i + degree] - U[r1i + k]; + C3D_ASSERT( ( ::fabs( tmp2 ) > DOUBLE_EPSILON ) ); // \ru Проверка на сбой \en Check of failure + DTPls[i].Dec( DTMin[i], DTMin[i + 1], tmp / (tmp2) ); + if ( useWeight ) + WTPls[i] = ( WTMin[i + 1] - WTMin[i] ) * ( tmp / (tmp2) ); + } + } +} + +//------------------------------------------------------------------------------ + /** \brief \ru Вычислить все разностные формы кривой. + \en Calculate all difference forms of curve. \~ + \details \ru Вычислить все (или опционально некоторые) разностные формы кривой (характеристические производные). + \en Calculate all (or some, optionally) difference forms of curve (characteristic derivatives). \~ + \ingroup Base_Algorithms +*/ +// --- +template +void CurveDeriveCpts( ptrdiff_t p, const KnotsVector & U, const Point * P, const double w, size_t pointCount, + const NurbsVector * PW, ptrdiff_t d, ptrdiff_t r1, ptrdiff_t r2, DoubleTriple ** DT, double ** WT ) +{ + C3D_ASSERT( ( P != NULL ) != ( PW != NULL ) ); + + ptrdiff_t r = ( r2 - r1 ); + ptrdiff_t degree = ( p + 1 ); + ptrdiff_t i, k, icount; + DoubleTriple * DT0 = DT[0]; + double * WT0 = WT[0]; + bool useWeight = WT0 != NULL; + + if ( PW != NULL ) { + if ( !useWeight ) { + for ( i = 0; i <= r; i++ ) + DT0[i].Init( (*PW)[r1 + i] ); + } + else { + for ( i = 0; i <= r; i++ ) { + DT0[i].Init( (*PW)[r1 + i] ); + WT0[i] = PW->w( r1 + i ); + } + + } + } + else { + if ( !useWeight && ( r1 + r ) < (ptrdiff_t)pointCount ) { + for ( i = 0; i <= r; i++ ) { + DT0[i].Init( P[r1 + i].x, P[r1 + i].y, P[r1 + i].z ); + } + } + else { + for ( i = 0; i <= r; i++ ) { + k = ( ( r1 + i ) % pointCount ); + DT0[i].Init( P[k], w ); + if( useWeight ) + WT0[i] = w; + } + } + } + + double * WTMin = NULL; + double * WTPls = NULL; + for ( k = 1; k <= d; k++ ) { + DoubleTriple * DTMin = DT[k - 1]; + DoubleTriple * DTPls = DT[k]; + if ( useWeight ) { + WTMin = WT[k - 1]; + WTPls = WT[k]; + } + double tmp = (double)( degree - k ); + ptrdiff_t r1i = r1; + for ( i = 0, icount = ( r - k ); i <= icount; i++ ) { + r1i = ( r1 + i ); + double tmp2 = U[r1i + degree] - U[r1i + k]; + C3D_ASSERT( ( ::fabs( tmp2 ) > DOUBLE_EPSILON ) ); // \ru Проверка на сбой \en Check of failure + DTPls[i].Dec( DTMin[i], DTMin[i + 1], tmp / (tmp2) ); + if ( useWeight ) + WTPls[i] = ( WTMin[i + 1] - WTMin[i] ) * ( tmp / ( tmp2 ) ); + } + } +} + +//------------------------------------------------------------------------------ +/** \brief \ru Вычислить все разностные формы кривой. + \en Calculate all difference forms of curve. \~ + \details \ru Вычислить все (или опционально некоторые) разностные формы кривой (характеристические производные). + \en Calculate all (or some, optionally) difference forms of curve (characteristic derivatives). \~ + \ingroup Base_Algorithms +*/ +// --- +template +void CurveDeriveCpts( ptrdiff_t p, const double * U, const Point * P, const double * W, size_t pointCount, + ptrdiff_t r1, ptrdiff_t r2, + Homogeneous * H0, Homogeneous * H1, Homogeneous * H2, Homogeneous * H3 ) +{ + ptrdiff_t r = ( r2 - r1 ); + ptrdiff_t degree = ( p + 1 ); + ptrdiff_t i, k, icount; + + for ( i = 0; i <= r; i++ ) { + k = ( (r1 + i) % pointCount ); + if ( W != NULL ) + H0[i].Init( P[k], W[k] ); + else + H0[i].Init( P[k], 1.0 ); + } + k = 1; + { + Homogeneous * PKMin = H0; + Homogeneous * PKPls = H1; + double tmp = (double)( degree - k ); + ptrdiff_t r1i = r1; + for ( i = 0, icount = (r - k); i <= icount; i++ ) { + r1i = ( r1 + i ); + C3D_ASSERT( ( ::fabs( U[r1i + degree] - U[r1i + k] ) > DOUBLE_EPSILON ) ); // \ru Проверка на сбой \en Check of failure + PKPls[i].Dec( PKMin[i], PKMin[i + 1], (tmp / (U[r1i + degree] - U[r1i + k])) ); + } + } + k = 2; + { + Homogeneous * PKMin = H1; + Homogeneous * PKPls = H2; + double tmp = (double)( degree - k ); + ptrdiff_t r1i = r1; + for ( i = 0, icount = (r - k); i <= icount; i++ ) { + r1i = ( r1 + i ); + C3D_ASSERT( ( ::fabs( U[r1i + degree] - U[r1i + k] ) > DOUBLE_EPSILON ) ); // \ru Проверка на сбой \en Check of failure + PKPls[i].Dec( PKMin[i], PKMin[i + 1], (tmp / (U[r1i + degree] - U[r1i + k])) ); + } + } + k = 3; + { + Homogeneous * PKMin = H2; + Homogeneous * PKPls = H3; + double tmp = (double)( degree - k ); + ptrdiff_t r1i = r1; + for ( i = 0, icount = (r - k); i <= icount; i++ ) { + r1i = ( r1 + i ); + C3D_ASSERT( ( ::fabs( U[r1i + degree] - U[r1i + k] ) > DOUBLE_EPSILON ) ); // \ru Проверка на сбой \en Check of failure + PKPls[i].Dec( PKMin[i], PKMin[i + 1], (tmp / (U[r1i + degree] - U[r1i + k])) ); + } + } +} + + +//------------------------------------------------------------------------------ +// \ru Cвертка по Энш. \en Einstein's convolution product. +// --- +void EiSum( const double * pk, double ** nd, size_t jcount, double & sum ); + + +//------------------------------------------------------------------------------ +// \ru Cвертка по Энш. \en Einstein's convolution product. +// --- +void EiSum( const MbHomogeneous * pk, double * nd, size_t degree, size_t jcount, MbHomogeneous & sum ); + + +//------------------------------------------------------------------------------ +// \ru Cвертка по Энш. \en Einstein's convolution product. +// --- +void EiSum( const MbHomogeneous3D * pk, double * nd, size_t degree, size_t jcount, MbHomogeneous3D & sum ); + + +//------------------------------------------------------------------------------ +/// \ru Загнать параметр t в параметрическую область кривой. \en Reduce "t" to parameter in the parametric domain of the curve. +/** + \param[in] tMin, tMax - \ru Параметры, задающие параметрическую область кривой. + \en Parameters which define the parametric region of the curve. \~ + \param[in] closed - \ru Признак замкнутости кривой. + \en An attribute of curve closedness. \~ + \param[in, out] t - \ru Исходный параметр. + \en Initial parameter. \~ + \ingroup Base_Algorithms +*/ +// --- +inline void CheckParam( const double & tMin, const double & tMax, bool closed, double & t ) +{ + if ( (t < tMin) || (t > tMax) ) { + if ( closed ) { // \ru Сплайн кривая замкнута \en Spline curve is closed + double period = tMax - tMin; + t -= ::floor( (t - tMin) / period ) * period; + } + else if ( t < tMin ) + t = tMin; + else if ( t > tMax ) + t = tMax; + } +} + + +//------------------------------------------------------------------------------ +/// \ru Рассчитать ненулевые сплайны при данном параметре. \en Calculate non-zero splines with a given parameter. +/** + \param[in] degree - \ru Порядок B-сплайна. + \en B-spline degree. \~ + \param[in] knots - \ru Множество узлов. + \en Knots. \~ + \param[in] closed - \ru Признак замкнутости. + \en A closedness attribute. \~ + \param[in, out] t - \ru Параметр + \en A parameter \~ + \param[out] nsplines - \ru Множество размерности degree, заполняется значениями сплайна. + \en Array (dimension is "degree") is filled by spline values. \~ + \param[out] lrVect - \ru Вспомогательный массив (содержит мусор). + \en An assisting array (contains garbage). \~ + \return \ru Номер первого ненулевого B-сплайна. + \en The number of the first non-zero B-spline. \~ + \ingroup Base_Algorithms +*/ +// --- +template +ptrdiff_t CalculateSplines( size_t degree, + const KnotsVector & knots, + bool closed, + double & t, + DoubleVector1 & nsplines, + DoubleVector2 & lrVect ) +{ + ptrdiff_t begInd = -1; // \ru Возвращаемое значение = номер первого ненулевого B-сплайна. \en The return value = the number of the first non-zero B-spline. + + if ( (degree > 1) && (knots.size() > degree) ) { + size_t power = degree - 1; + size_t lastKnotInd = knots.size() - 1; + + // \ru Загнать параметр t в параметрическую область кривой. \en Reduce "t" to a parameter in the parametric domain of the curve + ::CheckParam( knots[power], knots[lastKnotInd - power], closed, t ); + + ptrdiff_t tspan = ::KnotIndex( degree, knots, t ); + begInd = tspan - (ptrdiff_t)power; + if ( begInd >= 0 && begInd + power <= lastKnotInd ) + ::BasisFuns( tspan, t, power, knots, nsplines, lrVect ); + else { + begInd = SYS_MAX_T; // \ru Ошибка. \en Error + for ( size_t i = 0; i < degree; i++ ) + nsplines[i] = 0.0; + } + + C3D_ASSERT( (size_t)begInd != SYS_MAX_T ); + } + + return begInd; +} + + +//------------------------------------------------------------------------------ +// \ru Вычисление характеристических точек pointList для прохождения NURBS-кривой через points[i] при params[i] \en Calculation of characteristic points "pointList" of NURBS-curve passing through points[i] with params[i] +// --- +template +MbeNewtonResult CalculatePointList( const DoubleVector & params, const PointVector & points, + size_t degree, bool closed, const DoubleVector & knots, + PointVector & pointList ) +{ + MbeNewtonResult res = nr_Failure; + + size_t pointsCount = points.size(); + C3D_ASSERT( points.size() > 1 ); + + if ( pointsCount > 1 && degree > 1 && knots.size() > 1 ) { + // \ru Инициализация опорных точек. \en Initialization of support points. + if ( &pointList != &points ) { + pointList.clear(); + pointList = points; + } + ptrdiff_t uppIndex = (ptrdiff_t)pointList.size() - 1; // \ru Количество точек. \en The count of points + if ( closed && (uppIndex > 1) && + c3d::EqualPoints( pointList[0], pointList[uppIndex], METRIC_REGION ) ) { + pointList.erase( pointList.begin() + uppIndex ); + pointsCount = pointList.size(); + } + + DPtr matrixPtr( MatrixNN::Create(pointsCount) ); // \ru Матрица системы уравнений для прохождения NURBS при params[i] через points[i] \en Matrix of equation system for constructing the NURBS-curve passing through the points[i] with params[i] + + if ( matrixPtr != NULL && ::IsValidNurbsParamsExt(degree, closed, pointList.size(), knots) ) { + MatrixNN & matrix = *matrixPtr; + + std::vector bSplines; // \ru Ненулевые B-сплайны \en Non-zero B-splines + bSplines.resize( degree ); + + std::vector lrVect; + + for ( size_t i = 0; i < pointsCount; i++ ) { // \ru Заполняем строки матрицы \en Fills matrix rows + double t = params[i]; + ptrdiff_t k = 0; + ptrdiff_t ind = ::CalculateSplines( degree, knots, closed, t, bSplines, lrVect ); + // \ru Заполняем i-ю строку \en Fill the i-th row + for ( k = 0; k < ind; k++ ) + matrix( i, k ) = 0.0; + for ( k = ind; k < ind + (ptrdiff_t)degree; k++ ) + matrix( i, k%pointsCount ) = bSplines[k - ind]; // \ru Ненулевые элементы строки. \en Non-zero elements of row + for ( k = ind + degree; k < (ptrdiff_t)pointsCount; k++ ) + matrix( i, k ) = 0.0; + } + + double epsilon = PARAM_EPSILON; + // \ru Решаем систему уравнений относительно характеристических точек pointList \en Solve the system of equations for the characteristic points "pointList" + // \ru Правая часть системы = адрес начала массива опорных точек pointList.begin(). \en The right system part = address of beginning of support point array pointList.begin(). + res = ::TypedGaussEquation( matrix, &pointList[0], epsilon ); + } + } + + return res; +} + + +//------------------------------------------------------------------------------ +// \ru Вычисление характеристических точек pointList для прохождения NURBS-кривой через points[i] при params[i] \en Calculation of characteristic points "pointList" of NURBS-curve passing through points[i] with params[i] +// --- +template +MATH_FUNC (MbeNewtonResult) CalculatePointListWithBandMatrix( const DoubleVector & params, const PointsVector & points, + size_t degree, bool closed, const DoubleVector & knots, + PointsVector & pointList ); + + +//------------------------------------------------------------------------------ +// \ru Установить касательность сплайна к вектору \en Set tangency of spline to vector +//--- +template +bool AttachNurbsG1( TypedNurbs & nurbs, // \ru Модифицируемый сплайн \en Modifiable spline + const TypedVector & tang, // \ru Касательный вектор \en Tangent vector + bool begin, // \ru Сопряжение выставлено в начале \en Conjugation is defined for the start + bool modify, // \ru Можно ли менять существующие полюса \en Whether it is possible to modify the existing pole + bool isC1 ) // \ru Нужно сохранить длину касательного вектора \en Need to save the length of the tangent vector +{ + bool res = false; + + if ( !nurbs.IsClosed() && nurbs.GetPointListCount() > 2 && nurbs.GetDegree() > 2 ) { + bool needRebuild = false; + + TypedVector normTang( tang ); + double tangLen = tang.Length(); + if ( tangLen > LENGTH_EPSILON ) + normTang /= tangLen; + + SArray points ( 0, 1 ); + SArray weights( 0, 1 ); + SArray knots ( 0, 1 ); + nurbs.GetPointList( points ); + nurbs.GetWeights( weights ); + nurbs.GetKnots( knots ); + ptrdiff_t degree = nurbs.GetDegree(); + bool closed = false; + + // \ru Дополнительная точка, которая обеспечивает визуальную касательность \en Additional point which provides a visual tangency + TypedPoint point, add( points[begin ? 0 : points.MaxIndex()] ); + TypedVector curTang, curVect; + double dist = 0.0; + //double dKnots = 0.0; + + double part = modify ? 1.0 : 0.5; // \ru Параметрическая доля \en Parametric part + double pointKnot = 0.0; // \ru Узел точки, обеспечивающий сопряжение \en Point knot which provides a conjugation + double pointWeight = 1.0; // \ru Вес точки \en A point weight + + if ( begin ) { // \ru Стыковка производится в начале \en Connection is performed in the beginning + nurbs._Tangent( nurbs.GetTMin(), curTang ); + if ( !curTang.Colinear(tang) || + curTang * tang < -ANGLE_EPSILON ) // BUG_55564 + { // \ru Если еще не установлено сопряжение \en If conjugation is not set + curVect.Set( points[1], 1.0, points[0], -1.0 ); + + if ( isC1 || !modify || tang.Orthogonal( curTang, Math::metricNear ) ) { + // BUG_53978 if ( !modify || tang.Orthogonal( curTang, Math::metricNear ) ) { + pointKnot = modify ? knots[(size_t)degree] : + knots[(size_t)degree] * part + knots[0] * ( 1.0 - part ); + + pointWeight = modify ? weights[1] : weights[0]; + + dist = points[0].DistanceToPoint( points[1] ) * part; // \ru Длина производной \en Derivative length + + if ( isC1 ) + add.Add( tang, (pointKnot - knots[0]) / (double)(degree - 1) * weights[0] / pointWeight ); + else + add.Add( normTang, dist / (double)(degree - 1) * weights[0] / pointWeight ); + + if ( modify ) + points[1] = add; + else { + points.AddAt( add, 1 ); + weights.AddAt( pointWeight, 1 ); + knots.AddAt( pointKnot, (size_t)degree ); + } + } + else { + // \ru BUG_50080 dist = (normTang * curVect); // МСГ К12 решено не учитывать знак производной \en BUG_50080 dist = (normTang * curVect); // МСГ К12 ignore the sign of the derivative + dist = ::fabs(normTang * curVect); + points[1] = points[0] + normTang * dist; + } + needRebuild = true; + } + + res = true; + } + else { // \ru Стыковка производится в конце \en Connection is performed at the end + nurbs._Tangent( nurbs.GetTMax(), curTang ); + if ( !curTang.Colinear(tang) || + curTang * tang < -ANGLE_EPSILON ) // BUG_55564) + { // \ru Если еще не установлено сопряжение \en If conjugation is not set + ptrdiff_t shear = points.MaxIndex(); + curVect.Set( points[shear], 1.0, points[shear - 1], -1.0 ); + + if ( isC1 || !modify || tang.Orthogonal( curTang, Math::metricNear ) ) { + // BUG_53978 if ( !modify || tang.Orthogonal( curTang, Math::metricNear ) ) { + pointKnot = modify ? knots[shear] : + knots[shear] * ( 1.0 - part ) + knots[shear + (size_t)degree] * part; + + pointWeight = modify ? weights[shear - 1] : weights[shear]; + + dist = points[shear].DistanceToPoint( points[shear - 1] ) * part; // \ru Длина производной \en Derivative length + + if ( isC1 ) + add.Add( tang, -(knots[shear + degree] - pointKnot) / (double)(degree - 1) * weights[shear] / pointWeight ); + else { + // BUG_49940 add.Add( tang, -dist / (degree - 1) * weights[shear] / pointWeight ); + add.Add( normTang, -dist / (double)(degree - 1) * weights[shear] / pointWeight ); + } + + if ( modify ) + points[shear - 1] = add; + else { + points.AddAt( add, shear ); + weights.AddAt( pointWeight, shear ); + knots.AddAt( pointKnot, shear + 1 ); + } + } + else { + // \ru BUG_50080 dist = (normTang * curVect); // МСГ К12 решено не учитывать знак производной \en BUG_50080 dist = (normTang * curVect); // МСГ К12 ignore the sign of the derivative + dist = ::fabs(normTang * curVect); + points[shear - 1] = points[shear] - normTang * dist; + } + needRebuild = true; + } + + res = true; + } + + if ( res && needRebuild ) + nurbs.Init( degree, closed, points, weights, knots, ncf_Unspecified ); +/*#if defined(C3D_DEBUG) + if ( res ){ // \ru Отладка \en Debugging + TypedVector vect; + if ( begin ) nurbs._FirstDer( nurbs.GetTMin(), vect ); + else nurbs._FirstDer( nurbs.GetTMax(), vect ); + double vectLen = vect.Length(); + C3D_ASSERT( tang.Colinear( vect ) && tang * vect > ANGLE_EPSILON ); + C3D_ASSERT( !isC1 || ::fabs(tangLen - vectLen) < METRIC_EPSILON ); + } +#endif +*/ + } + + return res; +} + + +//------------------------------------------------------------------------------ +// \ru Установить касательные на краях \en Set tangents at the ends +// --- +template +bool SetLimitFirstDerivatives( const Curve & curve, bool setBeg, bool setEnd, Nurbs & nurbs, bool setLen ) +{ // BUG_59596 + bool changed = false; + + if ( setBeg ) { + Vector fd; + curve._FirstDer( curve.GetTMin(), fd ); + if ( ::AttachNurbsG1( nurbs, fd, true, false, setLen ) ) + changed = true; + } + if ( setEnd ) { + Vector fd; + curve._FirstDer( curve.GetTMax(), fd ); + if ( ::AttachNurbsG1( nurbs, fd, false, false, setLen ) ) + changed = true; + } + + return changed; +} + + +//------------------------------------------------------------------------------ +// \ru Установить точки так, чтобы совпала касательная и главная нормаль \en Set points such that tangent and principal normal are coincident +//--- +template +bool AttachNurbsG2( TypedNurbs & nurbs, // \ru Модифицируемый сплайн \en Modifiable spline + const TypedVector & tang, // \ru Касательный вектор \en Tangent vector + const TypedVector & tangDiff, // \ru Производная касательного вектора \en The derivative of a tangent vector + bool begin, // \ru Сопряжение выставлено в начале \en Conjugation is defined for the start + bool modify, // \ru Можно ли менять существующие полюса \en Whether it is possible to modify the existing poles + double * wDiff1, + double * wDiff2 ) +{ + bool res = false; + + if ( !nurbs.IsClosed() && nurbs.GetPointListCount() > 3 && nurbs.GetDegree() > 3 && + ::AttachNurbsG1( nurbs, tang, begin, modify, false ) ) // \ru Стыкуем сначала по касательной \en Join by a tangent at first + { + bool needRebuild = false; // \ru Нужно ли перестраивать кривую \en Whether to rebuild the curve + + TypedVector normTang( tang ); + double tangLen = tang.Length(); + if ( tangLen > LENGTH_EPSILON ) + normTang /= tangLen; + + SArray points ( 0, 1 ); + SArray weights( 0, 1 ); + SArray knots ( 0, 1 ); + nurbs.GetPointList( points ); + nurbs.GetWeights( weights ); + nurbs.GetKnots( knots ); + ptrdiff_t degree = nurbs.GetDegree(); + bool closed = nurbs.IsClosed(); + + double eps = Math::metricEpsilon; + + //double dKnots = 0.0; + ptrdiff_t degm = degree - 1; + double curCurv = 0.0; + double curvature = tangDiff.Length(); + + TypedVector curNormal; // \ru Текущая нормаль в стыке \en Current normal at the joint + TypedVector firstDer; // \ru Первая производная сплайна в точке \en The first spline derivative at the point + TypedVector secndDer; // \ru Старая производная сплайна в точке \en The old spline derivative at the point + TypedVector secDer( tangDiff ); // \ru Вторая производная, с которой фактически устанавливается равенство \en The second derivative which the equality is set with + + TypedPoint add; // \ru Точка, обеспечивающая равенство вторых производных \en Point which provides the equality of second derivatives + TypedPoint wp0, wp1; // \ru Взвешенные точки \en Weighted points + + double part = modify ? 1.0 : 0.5; // \ru Параметрическая доля \en Parametric part + double pointKnot = 0.0; // \ru Узел точки, обеспечивающий сопряжение \en Point knot which provides a conjugation + double pointWeight = 1.0; // \ru Вес точки \en A point weight + + double weightDiff1 = 0.0, weightDiff2 = 0.0; // \ru Первая и вторая производная весов \en The first and the second derivative of weights + if ( begin ) { // \ru Стыковка производится в начале \en Connection is performed at the start + nurbs._Normal( nurbs.GetTMin(), curNormal ); + curCurv = nurbs.Curvature( nurbs.GetTMin() ); + + pointKnot = modify ? knots[(size_t)degree + 1] : + knots[(size_t)degree + 1] * part + knots[degree] *( 1.0 - part ); + pointWeight = modify ? weights[2] : weights[0] ; + weightDiff1 = (double)degm * ( weights[1] - weights[0] ) / ( knots[degree] - knots[1] ); + weightDiff2 = ( (double)degm - 1 ) * (double)degm / ( knots[degree] - knots[2] ) * + ( (pointWeight - weights[1]) / (pointKnot - knots[2]) - + (weights[1] - weights[0]) / (knots[degree] - knots[1]) ); + + if ( !(curNormal.Colinear(tangDiff) && + ::fabs(curCurv - curvature) < eps) ) // \ru Еще нет необходимой гладкости стыка \en The required smoothness of a joint is not provided yet + { + nurbs._FirstDer( nurbs.GetTMin(), firstDer ); + secDer *= firstDer * firstDer; + nurbs._SecondDer( nurbs.GetTMin(), secndDer ); + secDer += normTang * ( part * normTang * secndDer ); // \ru Сохранение старой проекции \en Saving of the old projection + + wp0 += points[0] * weights[0]; + wp1 += points[1] * weights[1]; + + // \ru Обрабатываем вторую производную \en Process the second derivative + secDer *= weights[0]; + secDer.Add( points[0], weightDiff2, firstDer, 2.0 * weightDiff1 ); + + double dK = pointKnot - knots[2]; + double dK1 = 1.0 / dK + 1.0 / ( knots[(size_t)degree] - knots[1] ); + + add.Set( wp0, 1.0, wp1 - wp0, dK1 * dK ); + add.Add( secDer, (knots[(size_t)degree] - knots[2]) * dK / ((double)degm * (double)(degm - 1)) ); + add /= pointWeight; + + if ( modify ) + points[2] = add; + else { + knots.AddAt( pointKnot, (size_t)degree + 1 ); + points.AddAt( add, 2 ); + weights.AddAt( pointWeight, 2 ); + } + needRebuild = true; + } + + res = true; + } + else { // \ru Стыковка производится в конце \en Connection is performed at the end + nurbs._Normal( nurbs.GetTMax(), curNormal ); + curCurv = nurbs.Curvature( nurbs.GetTMax() ); + + ptrdiff_t shear = points.MaxIndex(); + + pointKnot = modify ? knots[shear - 1] : + knots[shear] * ( 1.0 - part ) + knots[shear - 1] * part ; + + pointWeight = modify ? weights[shear - 2] : weights[shear]; + + weightDiff1 = (double)degm * ( weights[shear] - weights[shear - 1] ) / + ( knots[shear + (size_t)degree] - knots[shear] ); + weightDiff2 = (double)(degm - 1) * (double)degm / ( knots[shear + (size_t)degm] - knots[shear] ) * + ( (weights[shear] - weights[shear - 1]) / (knots[shear + (size_t)degree] - knots[shear]) - + (weights[shear - 1] - pointWeight ) / (knots[shear + (size_t)degree - 1] - pointKnot ) ); + + if ( !(curNormal.Colinear(tangDiff) && + ::fabs(curCurv - curvature) < eps) ) // \ru Еще нет необходимой гладкости стыка \en The required smoothness of a joint is not provided yet + { + nurbs. _FirstDer( nurbs.GetTMax(), firstDer ); + secDer *= firstDer * firstDer; + nurbs._SecondDer( nurbs.GetTMax(), secndDer ); + secDer += normTang * ( part * normTang * secndDer ); // \ru Сохранение старой проекции \en Saving of the old projection + + wp0 += points[shear] * weights[shear]; + wp1 += points[shear - 1] * weights[shear - 1]; + + // \ru Обрабатываем вторую производную \en Process the second derivative + secDer *= weights[shear]; + secDer.Add( points[shear], weightDiff2, firstDer, 2.0 * weightDiff1 ); + + double dK = knots[shear + (size_t)degm] - pointKnot; + double dK1 = 1.0 / ( knots[shear + (size_t)degm] - knots[shear] ) + + 1.0 / ( knots[shear + (size_t)degm] - pointKnot ); + + add.Set( wp0, 1.0, wp1 - wp0, dK1 * dK ); + add.Add( secDer, (knots[shear + (size_t)degm] - knots[shear]) * dK / ((double)degm * (double)(degm - 1)) ); + add /= pointWeight; + + if ( modify ) + points[shear - 2] = add; + else { + knots.AddAt( pointKnot, shear ); + points.AddAt( add, shear - 1 ); + weights.AddAt( pointWeight, shear - 1 ); + } + needRebuild = true; + } + + res = true; + } + + // \ru Сохраняем вычисленные производные \en Save the calculated derivatives + if ( res ) { + if ( wDiff1 != NULL ) + *wDiff1 = weightDiff1; + if ( wDiff2 != NULL && res ) + *wDiff2 = weightDiff2; + } + + if ( res && needRebuild ) + nurbs.Init( degree, closed, points, weights, knots, ncf_Unspecified ); + } + +/*#if defined(C3D_DEBUG) + if ( res ) { // \ru Отладка \en Debugging + double t = begin ? nurbs.GetTMin() : nurbs.GetTMax(); + + MbVector3D vect; + nurbs._Tangent( t, vect ); + C3D_ASSERT( vect.Colinear( tang ) ); + + MbVector3D normal; + nurbs._Normal( t, normal ); + C3D_ASSERT( normal.Colinear(tangDiff) ); + + double curvature = tangDiff.Length(); + double curCurvature = nurbs.Curvature( t ); + C3D_ASSERT( ::fabs(curCurvature - curvature) < Math::metricAccuracy ); + } +#endif +*/ + + return res; +} + + +//------------------------------------------------------------------------------ +// \ru Создать замкнутый NURBS, проходящий через точки с заданными параметрами. \en Calculate closed NURBS by points which it passes through and points parameters. +//--- +template +bool CreateClosedNURBS4( Nurbs & nurbs, const SArray & initPoints, const SArray & initParams ) +{ + bool bRes = ( (initParams.Count() > 3) && (initParams.Count() == initPoints.Count() + 1) ); + + if ( bRes ) { + nurbs.Refresh(); // должен стоять первым, т.к. освобождается выделенная память + + const ptrdiff_t degree = 4; // степень В-сплайна //-V112 + const bool closed = true; // признак замкнутости + const MbeNurbsCurveForm form = ncf_Unspecified; // форма B - сплайна + + // скопировать массив характеристических точек + SArray points( initPoints ); + + // установить единичные веса + SArray weights( points.Count(), 1 ); + { + const double weight0 = 1.0; + weights.Fill( points.Count(), weight0 ); + } + + const ptrdiff_t degm = degree - 1; + + // заполнить массив параметров (на концах используем условие "отсутствия узла"), обеспечивающий замыкание + SArray knots ( (initParams.MaxIndex() + 2 * degree - 1), 1 ); + { + ptrdiff_t uppKnotsIndex = (ptrdiff_t)initParams.Count() - 1; + ptrdiff_t i = 0; // индекс + + // вставляем начальные узлы 0.. degree - 2 + for ( i = 0; i < degm; i++ ) { + ptrdiff_t startIndex = uppKnotsIndex - degm; + knots.Add( initParams[0] - initParams[uppKnotsIndex] + initParams[startIndex + i] ); + } + + knots += initParams; + + // вставляем конечные узлы + const double tmax = initParams[initParams.MaxIndex()]; + ptrdiff_t i0 = degm; + for ( i = i0; i < i0 + degm; i++ ) + knots.Add( tmax + knots[i + 1] - knots[i0] ); + + uppKnotsIndex = knots.MaxIndex(); + } + + points.Adjust(); + weights.Adjust(); + knots.Adjust(); + + const ptrdiff_t pointsCnt = initPoints.Count(); + const ptrdiff_t uppIndex = pointsCnt - 1; + // для приведения матрицы к матрице с диагональным преобладанием необходимо + // последнее уравнение исключать первым + // далее решаем систему уравнений методом Гаусса без выбора ведущего элемента + SArray biatx( degree, 1 ); // не нулевые B - сплайны + SArray d ( pointsCnt ); // массив диагоналей + SArray ar ( pointsCnt ); // массив правых частей + SArray n ( pointsCnt ); // последний столбец в результирующей матрице + + SArray lrVect; + lrVect.resize( 2*degree ); + + ar.Add( initPoints[initPoints.MaxIndex()] ); + ar += initPoints; + ar.RemoveInd( ar.MaxIndex() ); + + ptrdiff_t lastIndex = initParams.MaxIndex() - 1; + ptrdiff_t lastKnot = knots.Count() - degree - 1; + + // первое уравнение + ::CalcBsplvb( knots, initParams[lastIndex], lastKnot, degree, biatx, lrVect ); + + double norm = 1 / biatx[1]; + double normLast = 1.0; + + ar[0] *= norm; + + d.Add( biatx[2] * norm ); + n.Add( biatx[0] * norm ); + + // последнее уравнение + ::CalcBsplvb( knots, initParams[lastIndex - 1], lastKnot - 1, degree, biatx, lrVect ); + norm = 1.0 / biatx[2]; + // в последнем уравнении, соответственно, внедиагональный и диагональный элемент + double a = biatx[0] * norm; + double b = biatx[1] * norm; + + ar[lastIndex] *= norm; + Point & sn = ar[lastIndex]; + + // прямой ход + ptrdiff_t crLeft = degm; + ptrdiff_t i = 1, im = 0, ip = 1; + for ( ; i < lastIndex - 1; i++, im++, crLeft++ ) { + ::CalcBsplvb( knots, initParams[i - 1], crLeft, degree, biatx, lrVect ); + + norm = 1 / ( biatx[1] - d[im] * biatx[0] ); + + ar[i] = ( ar[i] - ar[im] * biatx[0] ) * norm; + d [i] = biatx[2] * norm; + // исключаем из последнего уравнения ведущую 1 и меняем последний столбец + normLast = -1.0 / d[im]; + + a *= normLast; + b = ( b - n[im] ) * normLast; + + sn = ( sn - ar[im] ) * normLast; + + n.Add( -norm * biatx[0] * n[im] ); + } + + // исключаем из 2-х последних уравнений 3-е с конца + ::CalcBsplvb( knots, initParams[i - 1], crLeft, degree, biatx, lrVect ); + + norm = 1.0 / ( biatx[1] - d[im] * biatx[0] ); + d [lastIndex - 1] = ( biatx[2] - n[im] * biatx[0] ) * norm; + ar[lastIndex - 1] = ( ar[lastIndex - 1] - ar[im] * biatx[0] ) * norm; + + normLast = 1.0 / ( a - d[im] ); + b = ( b - n[im] ) * normLast; + a = 1.0; + sn = ( sn - ar[im] ) * normLast; + + // исключаем из последнего предпоследнее + im++; + points[uppIndex] = ( sn - ar[im] ) / ( b - d[im] ); + + ptrdiff_t prevLast = uppIndex - 1; + points[prevLast] = ar[lastIndex - 1] - points[uppIndex] * d[prevLast]; + + // обратный ход + for ( i = prevLast - 1, ip = prevLast; i >= 0; i--, ip-- ) { + points[i] = ar[i] - points[ip] * d[i] + - points[uppIndex] * n[i]; + } + + bRes = nurbs.Init( degree, closed, points, weights, knots, form ); + C3D_ASSERT( bRes ); + } + + return bRes; +} + + +//------------------------------------------------------------------------------ +// \ru Получить массив параметров по точкам \en Get an array of parameters given the points +// --- +template +bool CreateSplineParameters( const PointsVector & points, MbeSplineParamType spType, bool cls, + DoubleVector & params ) +{ + bool isDone = false; + params.clear(); + isDone = ::DefineThroughPointsParams( points, spType, cls, params ); + + // \ru Нормализация \en Normalization + if ( isDone ) + c3d::SetLimitParam( params, 0.0, (double)((ptrdiff_t)points.size() - 1 + (cls ? 1 : 0)) ); + + return isDone; + +} + + +//------------------------------------------------------------------------------ +// \ru Выбрать точки на кривой для аппроксимации замкнутой nurbs \en Select points on the curve for approximation of closed nurbs +// --- +template +size_t DefineApproxPointsClosed( const Curve & curve, size_t pCount, double pmin, double pmax, ptrdiff_t degree, SArray & points, const KnotsVector & aKnots, const SArray & pCounts ) +{ + size_t pCountActual = pCount; + if ( pCount < 1 ) + return 0; + + points.clear(); + size_t stepCount = 0; + double factor = 1.0 / ( (double)pCount ); + const double epsilon = curve.GetTRegion( METRIC_REGION ); + const double angle = Math::deviateSag; + + size_t segmCount = curve.GetSegmentsCount(); + + SArray segmPointsCnt( segmCount, 2 ); + SArray tList, restList; + + double tmin, tmax; + ptrdiff_t first_Segm = curve.FindSegment( pmin, tmin ); + ptrdiff_t last_Segm = curve.FindSegment( pmax, tmax ); + ptrdiff_t i = 0; + + size_t freePntsCount = pCount, j = 0; + + // \ru Найдем количество точек для каждого сегмента. \en Find the number of points for each segment. + size_t totalCount = 0; + for ( j = 0; j < pCounts.size(); ++j ) + totalCount += pCounts[j]; + if ( totalCount < 1 ) + totalCount = 1; + + for ( j = 0; j < pCounts.size(); j++ ){ + double part = ((double)pCounts[j]) / ((double)totalCount); + size_t segmPCnt = (size_t)(pCount * part); //-V113 + if ( j == (size_t)last_Segm ) + segmPCnt = freePntsCount; + else { + segmPCnt = std_max( segmPCnt, (size_t)1); + segmPCnt = std_min( segmPCnt, freePntsCount ); + } + segmPointsCnt.push_back( segmPCnt ); + freePntsCount -= segmPCnt; + } + + Point p; + double plusT = 0.0; + for ( i = first_Segm; i <= last_Segm; i++ ) { + tList.clear(); + double smin, smax; + smin = curve.GetSegment(i)->GetTMin(); + smax = curve.GetSegment(i)->GetTMax(); + + if ( first_Segm == last_Segm ) { + plusT = pmin + smin - tmin; + smin = tmin; + smax = tmax; + } + else if ( i == first_Segm ){ + plusT = pmin + smin - tmin; + smin = tmin; + } + else if ( i == last_Segm ) { + smax = tmax; + } + double t = smin; + size_t tempCount = segmPointsCnt[i - first_Segm]; + factor = 1.0 / ( (double)tempCount ); + stepCount = 0; + while ( t < (smax - epsilon) ) { + double step = curve.GetSegment(i)->DeviationStep( t, angle ); + if ( (t + step) >= (smax - epsilon) ) + step = smax - t; + + for ( size_t k = 0; k < tempCount; k++ ) + tList.push_back( plusT + t + (step * factor * (double)k) ); + t += step; + stepCount++; + } + tList.push_back(plusT + smax); + plusT += smax; + + for ( j = 0; j < tempCount; j++ ) { + t = tList[j * stepCount]; + curve._PointOn( t, p ); + points.push_back( p ); + restList.push_back( t ); + } + } + + restList.push_back( pmax ); + + // \ru Если задан узловой вектор, надо проверить, что между любыми 2 узлами есть хотя бы одна точка. \en If the knot vector is given, then it is necessary to check that there is at least one point between any two knots. + // \ru Если нет, то добавим точек, сохранив все уже набранные \en If not, then add the points and save old points + if ( aKnots.size() > 0 ) { + SArray pParams( 0, 1 ); + ::CreateSplineParameters( points, spt_ChordLength, true, pParams ); + c3d::SetLimitParam( pParams, aKnots[degree - 1], aKnots[aKnots.size() - degree] ); + double t1, t2; + bool bRes = false; + while ( !bRes ) { + bRes = true; + for ( i = degree - 1; i < aKnots.MaxIndex() - degree + 1 && bRes; i++ ) { + t1 = aKnots[i]; + t2 = aKnots[i + 1]; + if ( ::fabs(t2 - t1) > NULL_EPSILON ) { + size_t il = 0, ir = pParams.MaxIndex(); + size_t itemp = 0; + size_t ires1 = 0; + size_t ires2 = 0; + // \ru Левая граница \en The left boundary + bool goOn = true; + while ( goOn ) { + if ( ::fabs(pParams[il] - t1) < NULL_EPSILON ){ + ires1 = il; + goOn = false; + break; + } + else if ( ::fabs(pParams[ir] - t1) < NULL_EPSILON ) { + ires1 = ir; + goOn = false; + break; + } + else { + itemp = ( il + ir ) / 2; + if ( ::fabs(pParams[itemp] - t1) < NULL_EPSILON ) { + ires1 = itemp; + goOn = false; + break; + } + if ( pParams[itemp] < t1 ) + il = itemp; + else if ( pParams[itemp] > t1 ) + ir = itemp; + if ( ir - il < 2) { + ires1 = il; + goOn = false; + break; + } + } + } + // \ru Правая граница \en The right boundary + il = ires1; + ir = pParams.MaxIndex(); + goOn = true; + while ( goOn ) { + if ( ::fabs(pParams[il] - t2) < NULL_EPSILON ){ + ires2 = il; + goOn = false; + break; + } + else if ( ::fabs(pParams[ir] - t2) < NULL_EPSILON ) { + ires2 = ir; + goOn = false; + break; + } + else { + itemp = ( il + ir ) / 2; + if ( ::fabs(pParams[itemp] - t2) < NULL_EPSILON ) { + ires2 = itemp; + goOn = false; + break; + } + if ( pParams[itemp] < t2 ) + il = itemp; + else if ( pParams[itemp] > t2 ) + ir = itemp; + if ( ir - il < 2) { + ires2 = ir; + goOn = false; + break; + } + } + } + if ( ires2 - ires1 < 2 ) { + bRes = false; + // \ru Вставим среднюю точку между ires1 и ires2 и пересчитаем параметры \en Insert mid-point between ires1 and ires2 and recalculate parameters + double t = 0.5 * (restList[ ires1 ] + restList[ ires2 ]); + restList.AddAt( t, ires1 + 1 ); + + curve._PointOn( t, p ); + points.AddAt( p, ires1 + 1); + + pParams.clear(); + ::CreateSplineParameters( points, spt_ChordLength, false, pParams ); + c3d::SetLimitParam( pParams, aKnots[degree - 1], aKnots[aKnots.size() - degree] ); + pCountActual++; + } + } + } + } + } + + return pCountActual; +} + + +//------------------------------------------------------------------------------ +// \ru Выбрать точки на кривой для аппроксимации незамкнутой nurbs \en Select points on the curve for approximation of non-closed nurbs +// --- +template +size_t DefineApproxPointsOpen( const Curve & curve, size_t pCount, double pmin, double pmax, SArray & points, const KnotsVector & aKnots, const SArray & pCounts ) +{ + size_t pCountActual = pCount; + if ( pCount < 1 ) + return 0; + + points.clear(); + size_t stepCount = 0; + double factor = 1.0 / ( (double)(pCount - 1) ); + const double epsilon = curve.GetTRegion( METRIC_REGION ); + const double angle = Math::deviateSag; + + size_t segmCount = curve.GetSegmentsCount(); + if ( pCounts.size() != segmCount ) + return 0; + + SArray tList, restList; + SArray segmPointsCnt( segmCount, 2 ); + + double tpmin = pmin; + double tpmax = pmax; + + double tmin, tmax; + ptrdiff_t first_Segm = curve.FindSegment( tpmin, tmin ); + ptrdiff_t last_Segm = curve.FindSegment( tpmax, tmax ); + ptrdiff_t i; + + size_t freePntsCount = pCount - 1, j = 0; + + // \ru Найдем количество точек для каждого сегмента. \en Find the number of points for each segment. + size_t totalCount = 0; + for ( j = 0; j < pCounts.size(); ++j ) + totalCount += pCounts[j]; + if ( totalCount == 0 ) + totalCount = 1; + + for ( j = 0; j < pCounts.size(); j++ ){ + double part = ((double)pCounts[j]) / ((double)totalCount); + size_t segmPCnt = (size_t)(pCount * part); //-V113 + if ( j == (size_t)last_Segm ) + segmPCnt = freePntsCount; + else { + segmPCnt = std_max( segmPCnt, (size_t)1); + segmPCnt = std_min( segmPCnt, freePntsCount ); + } + segmPointsCnt.push_back( segmPCnt ); + freePntsCount -= segmPCnt; + } + + Point p; + double plusT = 0.0; + for ( i = first_Segm; i <= last_Segm; i++ ) { + tList.clear(); + + double smin = curve.GetSegment(i)->GetTMin(); + double smax = curve.GetSegment(i)->GetTMax(); + + if ( first_Segm == last_Segm ) { + plusT = tpmin + smin - tmin; + smin = tmin; + if (pmin < tpmin - PARAM_EPSILON ) + smin += pmin - tpmin; + smax = tmax; + if (pmax > tpmax + PARAM_EPSILON ) + smax += pmax - tpmax; + } + else if ( i == first_Segm ){ + plusT = tpmin + smin - tmin; + smin = tmin; + if (pmin < tpmin - PARAM_EPSILON ) + smin += pmin - tpmin; + } + else if ( i == last_Segm ) { + smax = tmax; + if (pmax > tpmax + PARAM_EPSILON ) + smax += pmax - tpmax; + } + double t = smin; + size_t tempCount = segmPointsCnt[i - first_Segm]; + factor = 1.0 / ( (double)tempCount ); + stepCount = 0; + while ( t < (smax - epsilon) ) { + double step = curve.GetSegment(i)->DeviationStep( t, angle ); + if ( (t + step) >= (smax - epsilon) ) + step = smax - t; + + for ( j = 0; j < tempCount; j++ ) + tList.push_back( plusT + t + (step * factor * (double)j) ); + t += step; + stepCount++; + } + tList.push_back(plusT + smax); + plusT += smax; + + for ( size_t k = 0; k < tempCount; k++ ) { + t = tList[k * stepCount]; + curve._PointOn ( t, p ); + points.push_back( p ); + restList.push_back( t ); + } + } + + double t = pmax; + curve._PointOn ( t, p ); + points.push_back( p ); + restList.push_back( t ); + + // \ru Если задан узловой вектор, надо проверить, что между любыми 2 узлами есть хотя бы одна точка. \en If the knot vector is given, then it is necessary to check that there is at least one point between any two knots. + // \ru Если нет, то добавим точек, сохранив все уже набранные \en If not, then add the points and save old points + if ( aKnots.size() > 0 ) { + SArray pParams( 0, 1 ); + ::CreateSplineParameters( points, spt_ChordLength, false, pParams ); + c3d::SetLimitParam( pParams, aKnots.front(), aKnots.back() ); + + double t1, t2; + bool bRes = false; + while ( !bRes ) { + bRes = true; + for ( i = 0; i < aKnots.MaxIndex() && bRes; i++ ) { + t1 = aKnots[i]; + t2 = aKnots[i + 1]; + if ( ::fabs(t2 - t1) > NULL_EPSILON ) { + size_t il = 0, ir = pParams.MaxIndex(); + size_t itemp = 0; + size_t ires1 = 0; + size_t ires2 = 0; + // \ru Левая граница \en The left boundary + bool goOn = true; + while ( goOn ) { + if ( ::fabs(pParams[il] - t1) < NULL_EPSILON ){ + ires1 = il; + goOn = false; + break; + } + else if ( ::fabs(pParams[ir] - t1) < NULL_EPSILON ) { + ires1 = ir; + goOn = false; + break; + } + else { + itemp = ( il + ir ) / 2; + if ( ::fabs(pParams[itemp] - t1) < NULL_EPSILON ) { + ires1 = itemp; + goOn = false; + break; + } + if ( pParams[itemp] < t1 ) + il = itemp; + else if ( pParams[itemp] > t1 ) + ir = itemp; + if ( ir - il < 2) { + ires1 = il; + goOn = false; + break; + } + } + } + // \ru Правая граница \en The right boundary + il = ires1; + ir = pParams.MaxIndex(); + goOn = true; + while ( goOn ) { + if ( ::fabs(pParams[il] - t2) < NULL_EPSILON ){ + ires2 = il; + goOn = false; + break; + } + else if ( ::fabs(pParams[ir] - t2) < NULL_EPSILON ) { + ires2 = ir; + goOn = false; + break; + } + else { + itemp = ( il + ir ) / 2; + if ( ::fabs(pParams[itemp] - t2) < NULL_EPSILON ) { + ires2 = itemp; + goOn = false; + break; + } + if ( pParams[itemp] < t2 ) + il = itemp; + else if ( pParams[itemp] > t2 ) + ir = itemp; + if ( ir - il < 2) { + ires2 = ir; + goOn = false; + break; + } + } + } + if ( ires2 - ires1 < 2 ) { + bRes = false; + // \ru Вставим среднюю точку между ires1 и ires2 и пересчитаем параметры \en Insert mid-point between ires1 and ires2 and recalculate parameters + t = 0.5 * (restList[ ires1 ] + restList[ ires2 ]); + restList.AddAt( t, ires1 + 1 ); + + curve._PointOn( t, p ); + points.AddAt( p, ires1 + 1 ); + + pParams.clear(); + ::CreateSplineParameters( points, spt_ChordLength, false, pParams ); + c3d::SetLimitParam( pParams, aKnots.front(), aKnots.back() ); + pCountActual++; + } + } + } + } + } + return pCountActual; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Построение замкнутого сплайна. + \en Construction of closed spline. \~ + \details \ru Построение замкнутого сплайна, аппроксимирующего набор точек, с помощью метода наименьших квадратов. \n + \en Construction of closed spline which approximates a set of points by the method of least squares. \n \~ + \param[in/out] nurbs - \ru Модифицируемый сплайн. + \en Modifiable spline. \~ + \param[in] aDegree - \ru Порядок сплайна. + \en The spline order. \~ + \param[in] pCount - \ru Количество точек. + \en Count of points. \~ + \param[in] aPoints - \ru Набор точек для аппроксимации. + \en Point set for approximation. \~ + \param[in] aKnots - \ru Предустановленный узловой вектор. + \en The predefined knots vector. \~ + \param[in] aParams - \ru Параметры для набора точек. + \en The parameters for point set. \~ + \result \ru Возвращает true при успешном построении, в противном случае - false. + \en Returns true if success, false - on other case. \~ + \ingroup Curve_Modeling +*/ +// --- +template +MATH_FUNC (bool) CreateNurbsLSMClosed( SPtr & nurbs, // \ru Модифицируемый сплайн \en Modifiable spline + const ptrdiff_t degree, + const ptrdiff_t pCount, + const PointsVector & aPoints, + const DoubleVector & aKnots, + const DoubleVector * aParams = NULL ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Построение незамкнутого сплайна. + \en Construction of non-closed spline. \~ + \details \ru Построение незамкнутого сплайна, аппроксимирующего набор точек, с помощью метода наименьших квадратов. \n + \en Construction of non-closed spline which approximates a set of points by the method of least squares. \n \~ + \param[in/out] nurbs - \ru Модифицируемый сплайн. + \en Modifiable spline. \~ + \param[in] aDegree - \ru Порядок сплайна. + \en The spline order. \~ + \param[in] pCount - \ru Количество точек. + \en Count of points. \~ + \param[in] aPoints - \ru Набор точек для аппроксимации. + \en Point set for approximation. \~ + \param[in] aKnots - \ru Предустановленный узловой вектор. + \en The predefined knots vector. \~ + \param[in] aParams - \ru Параметры для набора точек. + \en The parameters for point set. \~ + \result \ru Возвращает true при успешном построении, в противном случае - false. + \en Returns true if success, false - on other case. \~ + \ingroup Curve_Modeling +*/ +// --- +template +MATH_FUNC (bool) CreateNurbsLSM( SPtr & nurbs, // \ru Модифицируемый сплайн \en Modifiable spline + const ptrdiff_t degree, + const ptrdiff_t pCount, + const PointsVector & aPoints, + const DoubleVector & aKnots, + const DoubleVector * aParams = NULL ); + + +//------------------------------------------------------------------------------- +/// \ru Построение приямолинейногло сплайна. \en Construction of line segment spline. \~ +// --- +template +Nurbs * CreateLineOutRgn( const Curve & curve, double tn1, double tn2, double t1, double t2, + const MbCurveIntoNurbsInfo & nci ) +{ + Nurbs * nurbs = NULL; + + if ( !curve.IsClosed() && nci.ExtendRange() && ((tn2 - tn1) > Math::paramEpsilon) ) { + SArray points ( 2, 1 ); + SArray weights( 2, 1 ); + SArray knots ( 4, 1 ); //-V112 + + curve._PointOn( t1, *(points.Add()) ); + curve._PointOn( t2, *(points.Add()) ); + + weights.Add( 1.0 ); + weights.Add( 1.0 ); + + knots.Add( tn1 ); + knots.Add( tn1 ); + knots.Add( tn2 ); + knots.Add( tn2 ); + + nurbs = Nurbs::Create( 2, false, points, weights, knots, ncf_Unspecified ); + } + + return nurbs; +} + + +//------------------------------------------------------------------------------ +// \ru Преобразовать контур в нурбс \en Transform contour to NURBS +// --- +MbNurbs * ContourToNurbs( const MbContour & cntr, double t1, double t2, int sense, const MbCurveIntoNurbsInfo & nci, bool reparamByLength ); + +//------------------------------------------------------------------------------ +// \ru Преобразовать контур в нурбс \en Transform contour to NURBS +// --- +MbNurbs3D * ContourToNurbs( const MbContour3D & cntr, double t1, double t2, int sense, const MbCurveIntoNurbsInfo & nci, bool reparamByLength ); + + +#endif // __MB_NURBS_FUNCTION_H diff --git a/C3d/Include/mb_operation_result.h b/C3d/Include/mb_operation_result.h index c7d946b..d9282b3 100644 --- a/C3d/Include/mb_operation_result.h +++ b/C3d/Include/mb_operation_result.h @@ -154,7 +154,7 @@ enum MbResultType { rt_ProcessIsStopped, ///< \ru Процесс остановлен. \en Process is stopped. rt_ContourSweptError, ///< \ru Контур невозможно использовать для заданного перемещения. \en Contour cannot be used for given movement. rt_SomeContourError, ///< \ru Один из контуров невозможно использовать для заданного построения. \en Contour cannot be used for given construction. - rt_SplitWireNotAllFaces, ///< \ru Линии разъема созданы не на всех выбранных гранях. \en Parting lines created not on all selected faces. + rt_SplitWireNotAllFaces, ///< \ru Линии разъема (ребра пересечения) созданы не на всех выбранных гранях или не доходят до границ граней. \en Parting lines (cross edges) created not on all selected faces or do not reach the boundaries of the faces. rt_GeneratrixColinearGuide, ///< \ru В некоторых точках образующая параллельна направляющей. \en Generatrix parallel to the giude at some points. rt_NotEnoughMemory, ///< \ru Недостаточно памяти. \en Not enough memory. rt_BorderColinearCurve, ///< \ru Направление боковой границы параллельно касательной на конце образующей кривой. \en Direction of lateral border is parallel to a tangent at the end of a guide curve. @@ -178,53 +178,63 @@ enum MbResultType { rt_FullFilletError, ///< \ru Ошибка при создании скругления грани. \en Error at creating full fillet. // \ru Ошибки построения плавных сплайнов. \en Build failure of fair splines. - rt_IncorrectSettings, ///< 0 \ru Некорректные объект / параметры. \en Incorrect object / parameters. rt_IncorrectData, ///< 1 \ru Некорректные данные. \en Incorrect data. rt_IncorrectPolylines, ///< 2 \ru Некорректные формы ломаных / направления касательных. \en Incorrect polylines / tangent directions. - rt_Incorrectstructure, ///< 3 \ru Некорректная структура ломаной с прямолинейными участками. \en Incorrect structure of 3d poly with straight sites. + rt_IncorrectStructure, ///< 3 \ru Некорректная структура ломаной с прямолинейными участками. \en Incorrect structure of 3d poly with straight sites. rt_TooFewPoints, ///< 4 \ru Слишком мало точек. \en Too few points. - rt_CoincidencePoints, ///< 5 \ru Совпадение точек. \en Coincidence of points. - rt_TooAcuteAngle, ///< 6 \ru Слишком острый угол между сегментами ломаной. \en Too acute angle between segments of polyline. - rt_ReturnMotion, ///< 7 \ru Обратный ход сегмента. \en Return motion of segment. + rt_CoincidentPoints, ///< 5 \ru Совпадение точек. \en Coincidence of points. + rt_TooSharpAngle, ///< 6 \ru Слишком острый угол между сегментами ломаной. \en Too acute angle between segments of polyline. + rt_ReverseMotion, ///< 7 \ru Обратный ход сегмента. \en Return motion of segment. rt_SharpTorsion, ///< 8 \ru Резкое кручение ломаной. \en Sharp torsion of spatial polyline. rt_IncorrectFirstDirection, ///< 9 \ru Некорректное направление касательной в начальной точке. \en Incorrect direction of first tangent vector. rt_IncorrectLastDirection, ///< 10 \ru Некорректное направление касательной в конечной точке. \en Incorrect direction of last tangent vector. rt_FirstTangentVector, ///< 11 \ru Первая касательная задана к прямолинейному участку. \en First tangent vector is set to a rectilinear site of a polyline. rt_LastTangentVector, ///< 12 \ru Последняя касательная задана к прямолинейному участку. \en Last tangent vector is set to a rectilinear site of a polyline. - rt_TangentVectorsSuitable, ///< 13 \ru Касательные векторы не корректны для локально-выпуклого участка. \en Tangent vectors are not suitable to convex shape of curve. - rt_IncorrectStructure, ///< 14 \ru Некорректная структура исходной кривой Безье. \en Incorrect structure of the initial Bezier curve. + rt_TangentVectorsUnsuitable, ///< 13 \ru Касательные векторы не корректны для локально-выпуклого участка. \en Tangent vectors are not suitable to convex shape of curve. + rt_IncorrectBezier, ///< 14 \ru Некорректная структура исходной кривой Безье. \en Incorrect structure of the initial Bezier curve. rt_NoIntersection, ///< 15 \ru Нет пересечения касательных. Некорректная структура ломаной. \en No intersection of tangents. Incorrect structure of polyline. rt_CriticalConfiguration, ///< 16 \ru Критическая конфигурация для B-сплайновой аппроксимации. \en Critical configuration for B-Spline approximation. rt_IncorrectInitialData, ///< 17 \ru Некорректные исходные данные. \en Incorrect initial data. - rt_CommandNotImplemented, ///< 18 \ru Данная команда не реализована. \en This command is not implemented. - rt_InsufficientMemory, ///< 19 \ru Недостаточно памятию \en Insufficient memory. + rt_NotImplemented, ///< 18 \ru Данная команда не реализована. \en This command is not implemented. + rt_InsufficientMemory, ///< 19 \ru Недостаточно памяти. \en Insufficient memory. rt_IncorrectFirstTangent, ///< 20 \ru Некорректное направление первой касательной. \en Incorrect direction of first tangent. rt_IncorrectLastTangent, ///< 21 \ru Некорректное направление последней касательной. \en Incorrect direction of last tangent. rt_StraightenLastSite, ///< 22 \ru Конечный прямолинейный участок. Касательный вектор не нужен. \en Straighten last site. Last tangent Ignored. rt_IncorrectPolylineStructure, ///< 23 \ru Некорректная структура ломаной с прямолинейными участками. \en Incorrect structure of polyline with straight sites. - rt_ncorrectDataStructure, ///< 24 \ru Некорректная структура данных в модели. \en Incorrect data structure in the model. + rt_IncorrectDataStructure, ///< 24 \ru Некорректная структура данных в модели. \en Incorrect data structure in the model. rt_ObjectIsNotPolyline, ///< 25 \ru Объект - не пространственная ломаная. \en Object is not a 3D polyline. rt_MissingKnots, ///< 26 \ru Отсутствует вектор узлов. \en Missing knots vector. rt_ClosedSpline, ///< 27 \ru Замкнутый сплайн во внешнем файле. \en Closed spline in an external file. rt_ObjectNotSelected, ///< 28 \ru Не выбран нужный объект. \en Object is not selected. - rt_InputFile, ///< 29 \ru Входной файл. \en Input file. - rt_ExternalMemory, ///< 30 \ru Внешняя память. \en External memory. + rt_Reserved1, ///< 29 \ru Зарезервировано. \en Reserved. + rt_Reserved2, ///< 30 \ru Зарезервировано. \en Reserved. rt_TooFewStartPoints, ///< 31 \ru Слишком мало точек на выпуклом участке начального участка. \en Too few points on convex start site of curve. rt_BanClosedConfiguration, ///< 32 \ru Запрет на замкнутую конфигурацию при количестве точек < 5. \en A ban on a closed configuration when the number of points < 5. rt_IncorrectDeterminant, ///< 33 \ru Некорректная структура исходного геометрического определителя. \en Incorrect structure of the initial geometric determinant. rt_DimensionsPointsArrays, ///< 34 \ru Размеры массивов точек. \en Dimensions of points arrays. rt_TangentsNotIntersect, ///< 35 \ru Касательные не пересекаются. \en Tangents do not intersect. - rt_PointsCoincide, ///< 36 \ru Точки совпадают. \en Points coincide. + rt_PointsCoincided, ///< 36 \ru Точки совпадают. \en Points coincide. rt_FewPointsForClosed, ///< 37 \ru Мало точек для замкнутой ломаной. \en Few points for a closed polyline. rt_TooFewPointsOnSite, ///< 38 \ru Слишком мало точек на выпуклом участке. \en Too few points on convex end site of curve. rt_PointsOnLine, ///< 39 \ru Точки на прямой. \en Points on the straight line. rt_TooManyApexes, ///< 40 \ru Слишком много точек. \en Too many points. rt_SiteNotConvex, ///< 41 \ru Трехсегментный участок не выпуклый. \en Three-linked site is not convex. - rt_CurvatuteRange, ///< 42 \ru Кривизна за пределами допустимого диапазона. \en Curvatute out of range. - rt_ObjectIsNotHermiteGD, ///< 43 \ru Объект не ГО Эрмита. \en The object is not Hermite GD. - rt_ObjectIsNotNURBS, ///< 44 \ru Объект не NURBzS. \en The object is not NURBzS. + rt_CurvatureRange, ///< 42 \ru Кривизна за пределами допустимого диапазона. \en Curvatute out of range. + rt_ObjectNotHermiteGD, ///< 43 \ru Объект не ГО Эрмита. \en The object is not Hermite GD. + rt_ObjectNotNURBS, ///< 44 \ru Объект не NURBzS. \en The object is not NURBzS. rt_DerivativeContinuityErr, ///< 45 \ru Ошибка обеспечения непрерывности производной. \en Error ensuring derivative continuity. + rt_NotEnoughBuildData, ///< 46 \ru Недостаточно данных для построения. \en Not enough data to build. + rt_InternalError, ///< 47 \ru Неизвестная ошибка. \en Unknown error. + rt_DerivativeGap = 198, ///< \ru Отсутствует непрерывность производной кривой по параметру. \en There is no continuity of the curve derivatives. + rt_DerivativeGapU, ///< \ru Отсутствует непрерывность производной поверхности по первому параметру. \en There is no continuity of the surface derivatives on first parameter. + rt_DerivativeGapV, ///< \ru Отсутствует непрерывность производной поверхности по второму параметру. \en There is no continuity of the surface derivatives on second parameter. + + rt_ContourGapError, ///< \ru В контуре есть разрывы между сегментами. \en There are gaps between the segments in the contour. + rt_ContourSegmentsOverlapError, ///< \ru В контуре есть наложения сегментов. \en There are segment overlays in the contour. + rt_ContourSegmentsNoTangentJoint,///< \en В контуре есть сегменты с не гладкой стыковкой. \ru In the contour there are segments with a non - smooth connection. + + rt_ExtensionOutOfRange, ///< \ru Удлинение не может быть ограничено заданной поверхностью. \en Extension cannot be limited given surface. // \ru !!! СТРОКИ ВСТАВЛЯТЬ СТРОГО ПЕРЕД ЭТОЙ СТРОКОЙ !!!! \en !!! INSERT LINES STRICTLY BEFORE THIS LINE !!!! rt_ErrorTotal // \ru НИЖЕ НЕ ДОБАВЛЯТЬ! \en DON'T ADD BELOW! }; diff --git a/C3d/Include/mb_placement.h b/C3d/Include/mb_placement.h index 8ae54a8..7e0858f 100644 --- a/C3d/Include/mb_placement.h +++ b/C3d/Include/mb_placement.h @@ -98,15 +98,15 @@ public : public: // \ru Функции инициализации \en Initialization functions. /// \ru Сделать совпадающей с мировой СК. \en Make coincident with global coordinate system. - void Init(); + void Init(); /// \ru Инициализировать по заданной ЛСК. \en Initialize by given local coordinate system. - void Init( const MbPlacement & init ); + void Init( const MbPlacement & init ); /// \ru Инициализировать по точке и вектору направления. \en Initialize by a point and direction vector. - void Init( const MbCartPoint & initOrigin, const MbDirection & initDir, bool l = false ); + void Init( const MbCartPoint & initOrigin, const MbDirection & initDir, bool l = false ); /// \ru Инициализировать по точке и углу. \en Initialize by a point and an angle. - void Init( const MbCartPoint & initOrigin, double angle, bool l = false ); + void Init( const MbCartPoint & initOrigin, double angle, bool l = false ); /// \ru Инициализировать по точке и двум векторам. \en Initialize by a point and two vectors. - void Init( const MbCartPoint &, const MbVector &, const MbVector & ); + void Init( const MbCartPoint &, const MbVector &, const MbVector & ); public: // \ru Функции доступа к данным. \en Getters and Setters. @@ -117,11 +117,11 @@ public: // \ru Функции доступа к данным. \en Getters and Se /// \ru Дать ось Y. \en Get the Y-axis. const MbVector & GetAxisY() const { return axisY; } /// \ru Дать начало ЛСК. \en Get the origin of local coordinate system. - void GetOrigin( MbCartPoint & pc ) const { pc = origin; } + void GetOrigin( MbCartPoint & pc ) const { pc = origin; } /// \ru Перевести точку и первые три производные из локальной в глобальную систему координат. \en Transform a point and the first three derivatives from a local coordinate system to the global coordinate system. - void GetPointAndDerivesFrom( MbCartPoint & point, MbVector & firstDer, - MbVector & secondDer, MbVector & thirdDer, - MbeLocalSystemType type = ls_CartesSystem ) const; + void GetPointAndDerivesFrom( MbCartPoint & point, MbVector & firstDer, + MbVector & secondDer, MbVector & thirdDer, + MbeLocalSystemType type = ls_CartesSystem ) const; // \ru Установить новое значение \en Set the new value /// \ru Дать начало ЛСК. \en Get the origin of local coordinate system. @@ -131,119 +131,119 @@ public: // \ru Функции доступа к данным. \en Getters and Se /// \ru Дать ось Y. \en Get the Y-axis. MbVector & SetAxisY() { flag = MB_UNSET; return axisY; } /// \ru Задать начало ЛСК. \en Set the origin of local coordinate system. - void SetOrigin( const MbCartPoint & p ); + void SetOrigin( const MbCartPoint & p ); /// \ru Задать ось X. \en Set the X-axis. - void SetAxisX ( const MbDirection & v ); + void SetAxisX ( const MbDirection & v ); /// \ru Задать ось Y. \en Set the Y-axis. - void SetAxisY ( const MbDirection & v ); + void SetAxisY ( const MbDirection & v ); /// \ru Задать ось X. \en Set the X-axis. - void SetAxisX ( const MbVector & v ); + void SetAxisX ( const MbVector & v ); /// \ru Задать ось Y. \en Set the Y-axis. - void SetAxisY ( const MbVector & v ); + void SetAxisY ( const MbVector & v ); /// \ru Дать матрицу преобразования в локальную СК: r=R*into (обратная матрица). \en Get the matrix of transformation to the local coordinate system: r=R*into (inverse matrix). - void GetMatrixInto( MbMatrix & m ) const; + void GetMatrixInto( MbMatrix & m ) const; /// \ru Дать матрицу преобразования из локальной СК: R=r*from (прямая матрица). \en Get the matrix of transformation from the local coordinate system: R=r*from (direct matrix). - void GetMatrixFrom( MbMatrix & m ) const; + void GetMatrixFrom( MbMatrix & m ) const; public: // \ru Функции доступа к данным. \en Getters and Setters. /// \ru Сдвинуть вдоль вектора. \en Translate along a vector. - void Move( const MbVector & ); + void Move( const MbVector & ); /// \ru Сдвинуть на заданные приращения. \en Translate by given increments. - void Move( double dx, double dy ); + void Move( double dx, double dy ); /// \ru Повернуть вокруг точки на угол. \en Rotate at angle around a point. - void Rotate( const MbCartPoint & pnt, double angle ); + void Rotate( const MbCartPoint & pnt, double angle ); /// \ru Повернуть вокруг точки на угол, заданный вектором направления. \en Rotate around a point at the angle given by direction vector. - void Rotate( const MbCartPoint & pnt, const MbDirection & angle ); + void Rotate( const MbCartPoint & pnt, const MbDirection & angle ); /// \ru Преобразовать согласно матрице. \en Transform according to the matrix. - void Transform( const MbMatrix & matr ); + void Transform( const MbMatrix & matr ); /// \ru Рассчитать СК по начальной и конечной точкам. \en Calculate the coordinate system by the start point and the end point. - void Calculate( const MbCartPoint & from, const MbCartPoint & to ); + void Calculate( const MbCartPoint & from, const MbCartPoint & to ); /// \ru Пересчитать СК по измененным внутренним данным. \en Recalculate the coordinate system by changed internal data. - void Reset(); + void Reset(); ///< \ru Масштабировать ЛСК. \en Scale the local coordinate system. - void Scale( double sx, double sy, double &lx, double &ly ); + void Scale( double sx, double sy, double &lx, double &ly ); /// \ru Являются ли объекты равными? \en Determine whether an object is equal? - bool IsSame( const MbPlacement & other, double accuracy ) const; + bool IsSame( const MbPlacement & other, double accuracy ) const; /// \ru Инвертировать ось 0X. \en Invert the 0X-axis. - void InvertAxisX(); + void InvertAxisX(); /// \ru Инвертировать ось 0Y. \en Invert the 0Y-axis. - void InvertAxisY(); + void InvertAxisY(); /// \ru Проверить на равенство. \en Check for equality. - bool operator == ( const MbPlacement & ) const; + bool operator == ( const MbPlacement & ) const; /// \ru Проверить на неравенство. \en Check for inequality. - bool operator != ( const MbPlacement & ) const; + bool operator != ( const MbPlacement & ) const; /// \ru Присвоить другую систему координат. \en Assign another coordinate system. - void operator = ( const MbPlacement & ); + void operator = ( const MbPlacement & ); /// \ru Умножить на локальную систему (как перемножение матриц): P = this * p. \en Multiply by a local coordinate system: P = this * p (as of matrix multiplication). MbPlacement operator * ( const MbPlacement & p ) const; /// \ru Перевести точку из глобальной в локальную систему координат. \en Transform point from the global coordinate system to a local coordinate system. - void TransformInto( MbCartPoint & ) const; + void TransformInto( MbCartPoint & ) const; /// \ru Перевести точку из локальной в глобальную систему координат. \en Transform point from a local coordinate system to the global coordinate system. - void TransformFrom( MbCartPoint & ) const; + void TransformFrom( MbCartPoint & ) const; /// \ru Перевести вектор из глобальной в локальную систему координат. \en Transform a vector from the global coordinate system to a local coordinate system. - void TransformInto( MbVector & ) const; + void TransformInto( MbVector & ) const; /// \ru Перевести вектор из локальной в глобальную систему координат. \en Transform a vector from a local coordinate system to the global coordinate system. - void TransformFrom( MbVector & ) const; + void TransformFrom( MbVector & ) const; /// \ru Нормализовать. \en Normalize. - void Normalize() { Reset(); } + void Normalize() { Reset(); } public: // \ru Функции получения свойств. \en Properties. /// \ru Свойство совпадения с мировой системой координат. \en Property of coincidence with global coordinate system. - bool IsSingle () const { return (MB_IDENTITY == CheckFlag()); } + bool IsSingle () const { return (MB_IDENTITY == CheckFlag()); } /// \ru Проверить, присутствует ли сдвиг системы координат относительно глобальной. \en Check if a coordinate system is translated relative to the global coordinate system. - bool IsTranslation() const { return !!(CheckFlag() & MB_TRANSLATION); } + bool IsTranslation() const { return !!(CheckFlag() & MB_TRANSLATION); } /// \ru Проверить, присутствует ли поворот системы координат относительно глобальной. \en Check if the coordinate system is rotated relative to the global coordinate system. - bool IsRotation () const { return !!(CheckFlag() & MB_ROTATION); } + bool IsRotation () const { return !!(CheckFlag() & MB_ROTATION); } /// \ru Выдать признак лево-ориентированного плейсмента. \en Get attribute of left placement. - bool IsLeft () const { return !!(CheckFlag() & MB_LEFT); } + bool IsLeft () const { return !!(CheckFlag() & MB_LEFT); } /// \ru Проверить, является ли СК ортогональной, но ненормированной. \en Check if a coordinate system is orthogonal, but not normalized. - bool IsOrt () const { return !!(CheckFlag() & MB_ORTOGONAL); } + bool IsOrt () const { return !!(CheckFlag() & MB_ORTOGONAL); } /// \ru Проверить признак ортогональности СК. \en Check orthogonality of a coordinate system. - bool IsOrthogonal () const { CheckFlag(); return ( !(flag & MB_AFFINE) || !!(flag & MB_ORTOGONAL) ); } + bool IsOrthogonal () const { CheckFlag(); return ( !(flag & MB_AFFINE) || !!(flag & MB_ORTOGONAL) ); } /// \ru Проверить, является ли СК аффинной (если нет - то она ортонормированная). \en Check if a coordinate system is affine (otherwise it is orthonormalized). - bool IsAffine () const { return !!(CheckFlag() & MB_AFFINE); } + bool IsAffine () const { return !!(CheckFlag() & MB_AFFINE); } /// \ru Проверить признак ортонормированности СК. \en Check if a coordinate system is orthonormalized. - bool IsNormal () const { return !IsAffine(); } + bool IsNormal () const { return !IsAffine(); } /// \ru Проверить, что битовые флаги не установлены. \en Check whether bit flags are not set. - bool IsUnSet () const { return !!( flag & MB_UNSET ); } + bool IsUnSet () const { return !!( flag & MB_UNSET ); } /// \ru Вычислить и вернуть признак лево-ориентированного плейсмента. \en Calculate and return attribute of left placement. - bool CheckLeft(); + bool CheckLeft(); /// \ru Проверить ортогональность. \en Check orthogonality. - bool IsCardinalPoint( double eps = Math::AngleEps ) const; + bool IsCardinalPoint( double eps = Math::AngleEps ) const; /// \ru Проверить параллелен оси 0X или 0Y. \en Check if parallel to 0X-axis or 0Y-axis - bool IsCardinalStrict( double eps ) const; + bool IsCardinalStrict( double eps ) const; /// \ru Являются ли оси СК совпадающими со стандартной (правой ортонормированной СК). \en Check if axes of coordinate system are coincident to a standard one (right orthonormalized coordinate system). - bool IsTranslationStandard() const; + bool IsTranslationStandard() const; /// \ru Проверить, является ли СК ортогональной с равными по длине осями X,Y (круг остается кругом). \en Check if a coordinate system is orthogonal with axes X and Y of equal length (circle moves to circle). - bool IsCircular() const; + bool IsCircular() const; /// \ru Проверить, является ли СК ортогональной с равными по длине осями X,Y (круг остается кругом). \en Check if a coordinate system is orthogonal with axes X and Y of equal length (circle moves to circle). - bool IsCircular( double & lxy ) const; + bool IsCircular( double & lxy ) const; /// \ru Проверить, является ли СК ортогональной с равными по длине осями X,Y (круг остается кругом). \en Check if a coordinate system is orthogonal with axes X and Y of equal length (circle moves to circle). - bool IsIsotropic() const { return IsCircular(); } + bool IsIsotropic() const { return IsCircular(); } /// \ru Проверить, является ли СК ортогональной с равными по длине осями X,Y (круг остается кругом). \en Check if a coordinate system is orthogonal with axes X and Y of equal length (circle moves to circle). - bool IsIsotropic( double & lxy ) const { return IsCircular( lxy ); } + bool IsIsotropic( double & lxy ) const { return IsCircular( lxy ); } /// \ru Выдать свойства объекта. \en Get properties of the object. - void GetProperties( MbProperties & properties ); + void GetProperties( MbProperties & properties ); /// \ru Записать свойства объекта. \en Set properties of the object. - void SetProperties( const MbProperties & properties ); + void SetProperties( const MbProperties & properties ); private: /// \ru Выставить флаги. \en Set flags. - uint8 ResetFlag() const; + uint8 ResetFlag() const; // Оценить флаги, если оценки не было - uint8 CheckFlag() const { return IsUnSet() ? ResetFlag() : flag; } + uint8 CheckFlag() const { return IsUnSet() ? ResetFlag() : flag; } // Проверить флаг смещения. - void CheckOrigin() const { ::CheckOrigin( *this, flag, true ); } + void CheckOrigin() const { ::CheckOrigin( *this, flag, true ); } // \ru Проверить флаг вращения. \en Check rotation flag. - void CheckRotation() const { ::CheckRotation( *this, flag, true ); } + void CheckRotation() const { ::CheckRotation( *this, flag, true ); } KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbPlacement, MATH_FUNC_EX ) DECLARE_NEW_DELETE_CLASS( MbPlacement ) diff --git a/C3d/Include/mb_placement3d.h b/C3d/Include/mb_placement3d.h index 9798737..83c40d0 100644 --- a/C3d/Include/mb_placement3d.h +++ b/C3d/Include/mb_placement3d.h @@ -395,9 +395,13 @@ public: /** \} void Invert( MbMatrix * = NULL ); /// \ru Найти ближайшую точку пересечения с линией. \en Find the nearest point of intersection with line. - bool LineIntersectionPoint( const MbCartPoint3D & pc, const MbVector3D & axis, MbCartPoint3D & p, double & d ) const; + bool LineIntersectionPoint( const MbCartPoint3D & pc, const MbVector3D & axis, MbCartPoint3D & p, double & d, double eps = ANGLE_EPSILON ) const; /// \ru Получить точку и направление линии пересечения плоскостей. \en Get the point and direction of planes intersection line. - bool PlanesIntersection ( const MbPlacement3D & place, MbCartPoint3D & p, MbVector3D & axis ) const; + bool PlanesIntersection ( const MbPlacement3D & place, MbCartPoint3D & p, MbVector3D & axis, double eps = ANGLE_EPSILON ) const; + /// \ru Нахождение точки пересечения плоскости XY и кривой. \en Intersection plane XY with a curve. + MbeNewtonResult CurveIntersectNewton( const MbCurve3D & curve, bool ext, double funcEpsilon, size_t iterLimit, + double & u, double & v, double & t ) const; + /// \ru Преобразовать 2D-точки в другую 2D-точку. \en Transform 2D-point to another 2D-point. void TransformPoint ( const MbMatrix3D &, MbCartPoint & ) const; diff --git a/C3d/Include/mb_point_mating.h b/C3d/Include/mb_point_mating.h index e5181cb..2790997 100644 --- a/C3d/Include/mb_point_mating.h +++ b/C3d/Include/mb_point_mating.h @@ -1,719 +1,713 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Модуль геометрических построений. Сопряжение в точке - \en Geometric constructions module. Conjugation at point. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - - -#ifndef __MB_POINT_MATING_H -#define __MB_POINT_MATING_H - - -#include -#include -#include -#include -#include - - -//------------------------------------------------------------------------------ -/// \ru Параметры сопряжения в точке \en Parameters of conjugation at point -//--- -template -class MbPntMatingData { -private: // \ru данные \en data -//OV_x64 The structure's size can be decreased via changing the fields' order. The size can be reduced from 56 to 40 bytes. -//OV_x64 MbeMatingType type; ///< \ru тип сопряжения \en conjugation type -//OV_x64 Vector * tangent; ///< \ru направляющий касательный вектор \en guide tangent vector -//OV_x64 Vector * tangentDer1; ///< \ru первая производная касательного вектора \en first derivative of tangent vector -//OV_x64 Vector * tangentDer2; ///< \ru вторая производная касательного вектора \en second derivative of tangent vector -//OV_x64 bool movePnts; ///< \ru двигать исходные точки или добавлять \en move or add source points -//OV_x64 SArray * changedPnts; ///< \ru индексы измененных точек \en indices of changed points -//OV_x64 bool attach; ///< \ru данные служат для стыковки сплайна с кривой \en data for spline and curve connection - - Vector * tangent; ///< \ru Направляющий касательный вектор. \en Guide tangent vector. - Vector * tangentDer1; ///< \ru Первая производная касательного вектора. \en First derivative of tangent vector. - Vector * tangentDer2; ///< \ru Вторая производная касательного вектора. \en Second derivative of tangent vector. - SArray * changedPnts; ///< \ru Индексы измененных точек. \en Indices of changed points. - MbeMatingType type; ///< \ru Тип сопряжения. \en Conjugation type. - bool movePnts; ///< \ru Двигать исходные точки или добавлять. \en Move or add source points. - bool attach; ///< \ru Данные служат для стыковки сплайна с кривой. \en Data for a spline and a curve connection. - -public: - /// \ru Конструктор по умолчанию. \en Default constructor. - MbPntMatingData(); - /// \ru Конструктор по всем параметрам сопряжения в точке. \en Constructor by all parameters of conjugation at point. - MbPntMatingData( const MbeMatingType type, const Vector * tang, - const Vector * tangDer1, const Vector * tangDer2, - SArray *& changedPnts, - bool movePnts, bool isAttach ); - /// \ru Конструктор копирования. \en Copy-constructor. - MbPntMatingData( const MbPntMatingData & ); - /// \ru Деструктор. \en Destructor. - ~MbPntMatingData(); - -public: - /// \ru Инициализировать по всем параметрам сопряжения в точке. \en Initialize by all parameters of conjugation at point. - void Init( const MbeMatingType type, const Vector * tang, - const Vector * tangDer1, const Vector * tangDer2, - SArray *& changedPnts, - bool movePnts, bool isAttach ); - - /// \ru Инициализировать по другому объекту параметров сопряжения в точке. \en Initialize by another parameters object of conjugation at point. - bool Init( const MbPntMatingData & ); - -public: - // \ru доступ к данным \en access to data - /// \ru Дать тип сопряжения. \en Get conjugation type. - MbeMatingType GetType() const { return type; } - /// \ru Дать направляющий касательный вектор. \en Get guide tangent vector. - const Vector * GetTangent() const { return tangent; } - /// \ru Дать первую производную касательного вектора. \en Get the first derivative of tangent vector. - const Vector * GetTangentDer1() const { return tangentDer1; } - /// \ru Дать вторую производную касательного вектора. \en Get the second derivative of tangent vector. - const Vector * GetTangentDer2() const { return tangentDer2; } - /// \ru Выдать признак совпадения направлений касательных в точке сопряжения. \en Get attribute of tangent directions coincidence at conjugation point. - bool IsAttach() const { return attach; } - /// \ru Выдать признак возможности передвижения исходных точек. \en Get attribute of source points movability. - bool CanMovePoints() const { return movePnts; } - /// \ru Вернуть массив изменных точек. \en Get array of changed points. - const SArray * GetChangedPoints() const { return changedPnts; } - /// \ru Вернуть массив изменных точек. \en Get array of changed points. - SArray *& SetChangedPoints() { return changedPnts; } - - /// \ru Являются ли объекты равными? \en Determine whether an object is equal? - bool IsSame( const MbPntMatingData &, double accuracy ) const; - /// \ru Сделать первичную проверку корректности параметров. \en Initial check of parameters correctness. - bool IsValid() const; - /// \ru Уcтановить параметры сопряжения. \en Set conjugation parameters. - void SetVector( ptrdiff_t i, const Vector & vect ); - /// \ru Нормализовать касательную в случае стыковки. \en Normalize tangent in case of connection. - void NormalizeAttachTangent(); - /// \ru Дать фактическую степень гладкости визуального перехода. \en Get the actual smoothness degree of visual transition. - ptrdiff_t GetSmoothDegree() const; - - /// \ru Выдать свойства объекта. \en Get properties of the object. - void GetProperties( MbProperties & ); - /// \ru Записать свойства объекта. \en Set properties of the object. - void SetProperties( const MbProperties & ); - -private: // \ru не реализовано \en not implemented - void operator = ( const MbPntMatingData & ); -}; - - -//------------------------------------------------------------------------------ -// \ru конструктор \en constructor -//--- -template -MbPntMatingData::MbPntMatingData() - : tangent ( NULL ) - , tangentDer1 ( NULL ) - , tangentDer2 ( NULL ) - , changedPnts ( NULL ) - , type ( trt_Position ) - , movePnts ( false ) - , attach ( false ) -{ -} - - -//------------------------------------------------------------------------------ -// \ru конструктор \en constructor -//--- -template -MbPntMatingData::MbPntMatingData( const MbeMatingType nType, - const Vector * nTang, - const Vector * nTangDer1, - const Vector * nTangDer2, - SArray *& nChangedPnts, - bool nMovePnts, - bool nAttach ) - : type ( nType ) - , tangent ( (nTang != NULL) ? new Vector( *nTang ) : NULL ) - , tangentDer1 ( (nTangDer1 != NULL) ? new Vector( *nTangDer1 ) : NULL ) - , tangentDer2 ( (nTangDer2 != NULL) ? new Vector( *nTangDer2 ) : NULL ) - , movePnts ( nMovePnts ) - , changedPnts ( nChangedPnts ) - , attach ( nAttach ) -{ - if ( type <= trt_Position ) { // BUG_52162 - ::DeleteMatItem( tangent ); - ::DeleteMatItem( tangentDer1 ); - ::DeleteMatItem( tangentDer2 ); - } -} - - -//------------------------------------------------------------------------------ -// \ru конструктор копирования \en copy constructor -//--- -template -MbPntMatingData::MbPntMatingData( const MbPntMatingData & d ) - : type ( d.type ) - , tangent ( (d.tangent != NULL) ? new Vector( *d.tangent ) : NULL ) - , tangentDer1 ( (d.tangentDer1 != NULL) ? new Vector( *d.tangentDer1 ) : NULL ) - , tangentDer2 ( (d.tangentDer2 != NULL) ? new Vector( *d.tangentDer2 ) : NULL ) - , movePnts ( d.movePnts ) - , changedPnts ( d.changedPnts ) - , attach ( d.attach ) -{ -} - - -//------------------------------------------------------------------------------ -// \ru деструктор \en destructor -//--- -template -MbPntMatingData ::~MbPntMatingData() -{ - ::DeleteMatItem( tangent ); - ::DeleteMatItem( tangentDer1 ); - ::DeleteMatItem( tangentDer2 ); -} - - -//------------------------------------------------------------------------------ -// \ru инициализация данных \en data initialization -//--- -template -void MbPntMatingData::Init( const MbeMatingType nType, - const Vector * nTang, - const Vector * nTangDer1, - const Vector * nTangDer2, - SArray *& nChangedPnts, - bool nMovePnts, - bool nAttach ) -{ - type = nType; - - if ( tangent != NULL && nTang != NULL ) // \ru касательный вектор \en tangent vector - tangent->Init( *nTang ); - else if ( nTang != NULL ) - tangent = new Vector( *nTang ); - else if ( tangent != NULL ) - ::DeleteMatItem( tangent ); - - if ( tangentDer1 != NULL && nTangDer1 != NULL ) // \ru первая производная касательного вектора \en first derivative of tangent vector - tangentDer1->Init( *nTangDer1 ); - else if ( nTangDer1 != NULL ) - tangentDer1 = new Vector( *nTangDer1 ); - else if ( tangentDer1 != NULL ) - ::DeleteMatItem( tangentDer1 ); - - if ( tangentDer2 != NULL && nTangDer2 != NULL ) // \ru вторая производная касательного вектора \en second derivative of tangent vector - tangentDer2->Init( *nTangDer2 ); - else if ( nTangDer2 != NULL ) - tangentDer2 = new Vector( *nTangDer2 ); - else if ( tangentDer2 != NULL ) - ::DeleteMatItem( tangentDer2 ); - - if ( type <= trt_Position ) { // BUG_52162 - ::DeleteMatItem( tangent ); - ::DeleteMatItem( tangentDer1 ); - ::DeleteMatItem( tangentDer2 ); - } - - if ( changedPnts != NULL ) - changedPnts->Flush(); - - movePnts = nMovePnts; - changedPnts = nChangedPnts; - attach = nAttach; -} - - -//------------------------------------------------------------------------------ -// \ru инициализация данных \en data initialization -//--- -template -bool MbPntMatingData::Init( const MbPntMatingData & d ) -{ - C3D_ASSERT( changedPnts == NULL ); - - if ( this != &d ) { - SArray * dummyInds = NULL; - Init( d.type, d.tangent, d.tangentDer1, d.tangentDer2, dummyInds, d.movePnts, d.attach ); - return true; - } - return false; -} - - -//------------------------------------------------------------------------------ -// \ru Являются ли объекты равными? \en Determine whether an object is equal? -//--- -template -bool MbPntMatingData::IsSame( const MbPntMatingData & other, double accuracy ) const -{ - bool isSame = false; - - if ( c3d::EqualVectors( *tangent, *other.tangent, accuracy ) && - c3d::EqualVectors( *tangentDer1, *other.tangentDer1, accuracy ) && - c3d::EqualVectors( *tangentDer2, *other.tangentDer2, accuracy ) && - *changedPnts == *other.changedPnts && - type == other.type && - movePnts == other.movePnts && - attach == other.attach ) - isSame = true; - - return isSame; -} - - -//------------------------------------------------------------------------------ -// \ru первичная проверка корректности параметров \en initial check of parameters correctness -//--- -template -bool MbPntMatingData::IsValid() const -{ - bool isValid = true; - - if ( type >= trt_Position ) { - double lenEps = LENGTH_EPSILON; - bool isTang = (tangent != NULL); - bool isTangDer1 = (tangentDer1 != NULL); - bool isTangDer2 = (tangentDer2 != NULL); - bool isTangLen = (isTang && tangent->Length() > lenEps); - - switch( type ) { - case trt_Tangent: - isValid = isTangLen; - break; - case trt_Normal: - // \ru под нормалью понимается как касательное сопряжение с тем, что названо нормалью, \en normal is considered to be either a tangent conjugation with what is called normal - // \ru так и управление только главной нормалью \en or only principal normal management - isValid = !attach && (isTangLen || (isTangDer1 && tangentDer1->Length() > lenEps)); - break; - case trt_SmoothG2 : - isValid = isTangLen && (isTangDer1 && tangent->Orthogonal( *tangentDer1 )); - break; - case trt_SmoothG3: - isValid = isTangLen && (isTangDer1 && tangent->Orthogonal( *tangentDer1 )) && isTangDer2; - break; - default: break; - } - } - - return isValid; -} - - -//------------------------------------------------------------------------------ -// \ru фактическая степень гладкости визуального перехода \en actual smoothness degree of visual transition -//--- -template -ptrdiff_t MbPntMatingData::GetSmoothDegree() const -{ - ptrdiff_t res = 0; - switch ( type ) { - case trt_Tangent : - res = 1; - break; - case trt_Normal : - if ( tangentDer1 != NULL ) res = 2; - else if ( tangent != NULL ) res = 1; - break; - case trt_SmoothG2: - res = 2; - break; - case trt_SmoothG3: - res = 3; - break; - default: break; - } - return res; -} - - -//------------------------------------------------------------------------------ -// \ru уcтановка параметров сопряжения \en set conjugation parameters -//--- -template -void MbPntMatingData::SetVector( ptrdiff_t i, const Vector & vect ) -{ - switch ( i ) { - case 0 : { - if ( tangent != NULL ) tangent->Init( vect ); - else tangent = new Vector( vect ); - break; - } - case 1 : { - if ( tangentDer1 != NULL ) tangentDer1->Init( vect ); - else tangentDer1 = new Vector( vect ); - break; - } - case 2 : { - if ( tangentDer2 != NULL ) tangentDer2->Init( vect ); - else tangentDer2 = new Vector( vect ); - break; - } - } -} - - -//------------------------------------------------------------------------------ -// \ru нормализовать касательную в случае стыковки \en normalize tangent in case of connection -//--- -template -void MbPntMatingData::NormalizeAttachTangent() -{ - if ( attach && tangent != NULL ) { - double tangLen = tangent->Length(); - if ( tangLen > LENGTH_EPSILON ) - (*tangent) /= tangLen; - } -} - - -//------------------------------------------------------------------------------ -// \ru выдать свойства объекта \en get properties of object -// --- -template -void MbPntMatingData::GetProperties( MbProperties & properties ) -{ -/* - properties.SetName( IDS_PROP_0900 ); - - TCHAR * typeName = new TCHAR[256]; - - switch ( type ) { - case trt_None : _sntprintf( typeName, IDS_PROP_0902 ); break; - case trt_Position : _sntprintf( typeName, IDS_PROP_0903 ); break; - case trt_Tangent : _sntprintf( typeName, IDS_PROP_0904 ); break; - case trt_Normal : _sntprintf( typeName, IDS_PROP_0905 ); break; - case trt_SmoothG2 : _sntprintf( typeName, IDS_PROP_0906 ); break; - case trt_SmoothG3 : _sntprintf( typeName, IDS_PROP_0907 ); break; - default : _sntprintf( typeName, IDS_ITEM_0902 ); break; - } - - properties.Add( new StringProperty( IDS_PROP_0901, typeName, false ) ); -*/ - if ( tangent != NULL ) - properties.Add( new MathItemProperty( IDS_PROP_0908, tangent, true ) ); - if ( tangentDer1 != NULL ) - properties.Add( new MathItemProperty( IDS_PROP_0909, tangentDer1, true ) ); - if ( tangentDer2 != NULL ) - properties.Add( new MathItemProperty( IDS_PROP_0910, tangentDer2, true ) ); - - properties.Add( new BoolProperty( IDS_PROP_0911, movePnts, false ) ); - properties.Add( new BoolProperty( IDS_PROP_0912, attach, false ) ); -} - - -//------------------------------------------------------------------------------ -// \ru выдать свойства объекта \en get properties of object -// --- -template -void MbPntMatingData::SetProperties( const MbProperties & /*properties*/ ) -{ -} - - -//------------------------------------------------------------------------------ -// \ru определено ли сопряжение \en whether conjugation is defined -//--- -template -bool IsMatingDefined( const MbPntMatingData * data ) -{ - bool isDefined = false; - - if ( data != NULL && data->IsValid() ) { - if ( data->GetType() > trt_Position ) // \ru по позиции и так выполнится, поэтому считаем не заданным \en would be held at position, so assumed as undefined - isDefined = true; - } - - return isDefined; -} - - -//------------------------------------------------------------------------------ -// \ru определены ли сопряжение \en whether conjugation is defined -//--- -template -bool IsAnyMatingDefined( const RPArray< MbPntMatingData > & data ) -{ - bool isDefined = false; - - if ( data.Count() > 0 ) { - for ( size_t k = 0, cnt = data.Count(); k < cnt; k++ ) { - if ( ::IsMatingDefined( data[k] ) ) { - isDefined = true; - break; - } - } - } - - return isDefined; -} - - -//------------------------------------------------------------------------------ -// \ru копировать сопряжения \en copy conjugations -//--- -template -bool CopyMating( const RPArray< MbPntMatingData > & src, RPArray< MbPntMatingData > & dst ) -{ - bool isDone = false; - - if ( src.Count() > 0 && dst.Count() < 1 ) { - isDone = true; - for ( size_t k = 0, cnt = src.Count(); k < cnt && isDone; k++ ) { - MbPntMatingData * copyItem = NULL; - if ( src[k] != NULL ) { - copyItem = new MbPntMatingData(); - isDone = copyItem->Init( *src[k] ); - } - dst.Add( copyItem ); - } - if ( !isDone ) - ::DeleteMatItems( dst ); - } - - return isDone; -} - - -//------------------------------------------------------------------------------ -// \ru Являются ли объекты равными? \en Determine whether an object is equal? -//--- -template -bool IsSame( const RPArray< MbPntMatingData > & data, const RPArray< MbPntMatingData > & other, double accuracy ) -{ - bool isSame = false; - - if ( data.Count() == other.Count() ) { - isSame = true; - for ( size_t k = 0, cnt = data.Count(); k < cnt; k++ ) - if ( !data[k]->IsSame( *other[k], accuracy ) ) { - isSame = false; - break; - } - } - - return isSame; -} - - -//------------------------------------------------------------------------------ -// \ru трансформировать сопряжения \en transform conjugations -//--- -template -void TransformMating( const RPArray< MbPntMatingData > & data, const Matrix & matr ) -{ - Vector vect; - for ( size_t k = 0, kcnt = data.Count(); k < kcnt; k++ ) { - MbPntMatingData * dataItem = data[k]; - - if ( dataItem != NULL ) { - if ( dataItem->GetTangent() != NULL ) { - vect = *dataItem->GetTangent(); - vect.Transform( matr ); - dataItem->SetVector( 0, vect ); - } - if ( dataItem->GetTangentDer1() != NULL ) { - vect = *dataItem->GetTangentDer1(); - vect.Transform( matr ); - dataItem->SetVector( 1, vect ); - } - if ( dataItem->GetTangentDer2() != NULL ) { - vect = *dataItem->GetTangentDer2(); - vect.Transform( matr ); - dataItem->SetVector( 2, vect ); - } - } - } -} - - -//------------------------------------------------------------------------------ -// \ru вращать сопряжения \en rotate conjugations -//--- -template -void RotateMating( const RPArray< MbPntMatingData > & data, const Axis & axis, double angle ) -{ - Vector vect; - for ( size_t i = 0; i < data.Count(); i++ ) { - MbPntMatingData * dataItem = data[i]; - - if ( dataItem->GetTangent() != NULL ){ - vect = *dataItem->GetTangent(); - vect.Rotate( axis, angle ); - dataItem->SetVector( 0, vect ); - } - if ( dataItem->GetTangentDer1() != NULL ){ - vect = *dataItem->GetTangentDer1(); - vect.Rotate( axis, angle ); - dataItem->SetVector( 1, vect ); - } - if ( dataItem->GetTangentDer2() != NULL ){ - vect = *dataItem->GetTangentDer2(); - vect.Rotate( axis, angle ); - dataItem->SetVector( 2, vect ); - } - } -} - - -//------------------------------------------------------------------------------ -// \ru запись массива данных сопряжений \en conjugation data array writing -//--- -template -void WriteMating( writer & out, const RPArray< MbPntMatingData > & data ) -{ - if ( out.good() ) { - size_t cnt = data.Count(); - WriteCOUNT( out, cnt ); - for ( size_t k = 0; k < cnt && out.good(); k++ ) { - const MbPntMatingData * item = data[k]; - // \ru наличие сопряжения \en presence of conjugation - bool isItem = (item != NULL); - out << isItem; - - if ( isItem && item->GetChangedPoints() != NULL ) { - C3D_ASSERT_UNCONDITIONAL( false ); // \ru KYA массив индексов должен быть пуст, т.к. он общий для всех сопряжений, им владеет заказчик операции \en KYA array of indices should be empy because it is shared between all of conjugations and owned by user of operation - out.setState( io::cantWriteObject ); - } - - if ( isItem && out.good() ) { - // \ru тип сопряжения \en conjugation type - uint32 type = (uint32)item->GetType(); - out << type; - // \ru касательный вектор \en tangent vector - isItem = (item->GetTangent() != NULL); - out << isItem; - if ( isItem ) - out << (*item->GetTangent()); - // \ru первая производная касательного вектора \en first derivative of tangent vector - isItem = (item->GetTangentDer1() != NULL); - out << isItem; - if ( isItem ) - out << (*item->GetTangentDer1()); - // \ru вторая производная касательного вектора \en second derivative of tangent vector - isItem = (item->GetTangentDer2() != NULL); - out << isItem; - if ( isItem ) - out << (*item->GetTangentDer2()); - - out << (bool)item->CanMovePoints(); - out << (bool)item->IsAttach(); - } - } - } -} - - -//------------------------------------------------------------------------------ -// \ru чтение массива данных сопряжений \en conjugation data array reading -//--- -template -void ReadMating( reader & in, RPArray< MbPntMatingData > & data ) -{ - if ( in.good() ) { - ::DeleteMatItems( data ); - size_t cnt = ReadCOUNT( in ); - - if ( cnt > 0 ) { - data.Reserve( cnt ); - SArray * dummyInds = NULL; - - for ( size_t k = 0; k < cnt && in.good(); k++ ) { - // \ru наличие сопряжения \en presence of conjugation - bool isItem = false; - in >> isItem; - - if ( isItem ) { - // \ru тип сопряжения \en conjugation type - uint32 type = trt_None; - in >> type; - - MbVector3D * v1 = NULL; - MbVector3D * v2 = NULL; - MbVector3D * v3 = NULL; - - // \ru касательный вектор \en tangent vector - in >> isItem; - if ( isItem ) { - v1 = new Vector; - in >> *v1; - } - // \ru первая производная касательного вектора \en first derivative of tangent vector - in >> isItem; - if ( isItem ) { - v2 = new Vector; - in >> *v2; - } - // \ru вторая производная касательного вектора \en second derivative of tangent vector - in >> isItem; - if ( isItem ) { - v3 = new Vector; - in >> *v3; - } - - bool movePnts = false; - in >> movePnts; - bool attach = false; - in >> attach; - - MbPntMatingData * item = new MbPntMatingData(); - item->Init( (MbeMatingType)type, v1, v2, v3, dummyInds, movePnts, attach ); - data.Add( item ); - - ::DeleteMatItem( v1 ); - ::DeleteMatItem( v2 ); - ::DeleteMatItem( v3 ); - } - else { - data.Add( NULL ); - } - } - } - } -} - - -//------------------------------------------------------------------------------ -// \ru Функция копирования. \en Copy function. -//--- -template -void CopyPntMatingData( const MbPntMatingData & srcData, MbPntMatingData & dstData ) -{ - MbeMatingType type = srcData.GetType(); - bool movePnts = srcData.CanMovePoints(); - bool attach = srcData.IsAttach(); - SArray * changedPnts = const_cast *>( srcData.GetChangedPoints() ); - - size_t dim = std_min( SrcVector::GetDimension(), DstVector::GetDimension() ); - - DstVector * tangent = NULL; - DstVector * tangentDer1 = NULL; - DstVector * tangentDer2 = NULL; - - if ( srcData.GetTangent() != NULL ) { - tangent = new DstVector; - for ( size_t k = 0; k < dim; k++ ) - (*tangent)[k] = (*srcData.GetTangent())[k]; - } - if ( srcData.GetTangentDer1() != NULL ) { - tangentDer1 = new DstVector; - for ( size_t k = 0; k < dim; k++ ) - (*tangentDer1)[k] = (*srcData.GetTangentDer1())[k]; - } - if ( srcData.GetTangentDer2() != NULL ) { - tangentDer2 = new DstVector; - for ( size_t k = 0; k < dim; k++ ) - (*tangentDer2)[k] = (*srcData.GetTangentDer2())[k]; - } - - dstData.Init( type, tangent, tangentDer1, tangentDer2, changedPnts, movePnts, attach ); - - ::DeleteMatItem( tangent ); - ::DeleteMatItem( tangentDer1 ); - ::DeleteMatItem( tangentDer2 ); -} - - -#endif +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Модуль геометрических построений. Сопряжение в точке + \en Geometric constructions module. Conjugation at point. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef __MB_POINT_MATING_H +#define __MB_POINT_MATING_H + + +#include +#include +#include +#include +#include + + +//------------------------------------------------------------------------------ +/// \ru Параметры сопряжения в точке \en Parameters of conjugation at point +//--- +template +class MbPntMatingData { +private: // \ru данные \en data + Vector * tangent; ///< \ru Направляющий касательный вектор. \en Guide tangent vector. + Vector * tangentDer1; ///< \ru Первая производная касательного вектора. \en First derivative of tangent vector. + Vector * tangentDer2; ///< \ru Вторая производная касательного вектора. \en Second derivative of tangent vector. + SArray * changedPnts; ///< \ru Индексы измененных точек. \en Indices of changed points. + MbeMatingType type; ///< \ru Тип сопряжения. \en Conjugation type. + bool movePnts; ///< \ru Двигать исходные точки или добавлять. \en Move or add source points. + bool attach; ///< \ru Данные служат для стыковки сплайна с кривой. \en Data for a spline and a curve connection. + +public: + /// \ru Конструктор по умолчанию. \en Default constructor. + MbPntMatingData(); + /// \ru Конструктор по всем параметрам сопряжения в точке. \en Constructor by all parameters of conjugation at point. + MbPntMatingData( const MbeMatingType type, const Vector * tang, + const Vector * tangDer1, const Vector * tangDer2, + SArray *& changedPnts, + bool movePnts, bool isAttach ); + /// \ru Конструктор копирования. \en Copy-constructor. + MbPntMatingData( const MbPntMatingData & ); + /// \ru Деструктор. \en Destructor. + ~MbPntMatingData(); + +public: + /// \ru Инициализировать по всем параметрам сопряжения в точке. \en Initialize by all parameters of conjugation at point. + void Init( const MbeMatingType type, const Vector * tang, + const Vector * tangDer1, const Vector * tangDer2, + SArray *& changedPnts, + bool movePnts, bool isAttach ); + + /// \ru Инициализировать по другому объекту параметров сопряжения в точке. \en Initialize by another parameters object of conjugation at point. + bool Init( const MbPntMatingData & ); + +public: + // \ru доступ к данным \en access to data + /// \ru Дать тип сопряжения. \en Get conjugation type. + MbeMatingType GetType() const { return type; } + /// \ru Дать направляющий касательный вектор. \en Get guide tangent vector. + const Vector * GetTangent() const { return tangent; } + /// \ru Дать первую производную касательного вектора. \en Get the first derivative of tangent vector. + const Vector * GetTangentDer1() const { return tangentDer1; } + /// \ru Дать вторую производную касательного вектора. \en Get the second derivative of tangent vector. + const Vector * GetTangentDer2() const { return tangentDer2; } + /// \ru Выдать признак совпадения направлений касательных в точке сопряжения. \en Get attribute of tangent directions coincidence at conjugation point. + bool IsAttach() const { return attach; } + /// \ru Выдать признак возможности передвижения исходных точек. \en Get attribute of source points movability. + bool CanMovePoints() const { return movePnts; } + /// \ru Вернуть массив изменных точек. \en Get array of changed points. + const SArray * GetChangedPoints() const { return changedPnts; } + /// \ru Вернуть массив изменных точек. \en Get array of changed points. + SArray *& SetChangedPoints() { return changedPnts; } + + /// \ru Являются ли объекты равными? \en Determine whether an object is equal? + bool IsSame( const MbPntMatingData &, double accuracy ) const; + /// \ru Сделать первичную проверку корректности параметров. \en Initial check of parameters correctness. + bool IsValid() const; + /// \ru Уcтановить параметры сопряжения. \en Set conjugation parameters. + void SetVector( ptrdiff_t i, const Vector & vect ); + /// \ru Нормализовать касательную в случае стыковки. \en Normalize tangent in case of connection. + void NormalizeAttachTangent(); + /// \ru Дать фактическую степень гладкости визуального перехода. \en Get the actual smoothness degree of visual transition. + size_t GetSmoothDegree() const; + + /// \ru Выдать свойства объекта. \en Get properties of the object. + void GetProperties( MbProperties & ); + /// \ru Записать свойства объекта. \en Set properties of the object. + void SetProperties( const MbProperties & ); + +private: // \ru не реализовано \en not implemented + void operator = ( const MbPntMatingData & ); +}; + + +//------------------------------------------------------------------------------ +// \ru конструктор \en constructor +//--- +template +MbPntMatingData::MbPntMatingData() + : tangent ( C3D_NULL_PTR ) + , tangentDer1 ( C3D_NULL_PTR ) + , tangentDer2 ( C3D_NULL_PTR ) + , changedPnts ( C3D_NULL_PTR ) + , type ( trt_Position ) + , movePnts ( false ) + , attach ( false ) +{ +} + + +//------------------------------------------------------------------------------ +// \ru конструктор \en constructor +//--- +template +MbPntMatingData::MbPntMatingData( const MbeMatingType nType, + const Vector * nTang, + const Vector * nTangDer1, + const Vector * nTangDer2, + SArray *& nChangedPnts, + bool nMovePnts, + bool nAttach ) + : type ( nType ) + , tangent ( (nTang != C3D_NULL_PTR) ? new Vector( *nTang ) : C3D_NULL_PTR ) + , tangentDer1 ( (nTangDer1 != C3D_NULL_PTR) ? new Vector( *nTangDer1 ) : C3D_NULL_PTR ) + , tangentDer2 ( (nTangDer2 != C3D_NULL_PTR) ? new Vector( *nTangDer2 ) : C3D_NULL_PTR ) + , movePnts ( nMovePnts ) + , changedPnts ( nChangedPnts ) + , attach ( nAttach ) +{ + if ( type <= trt_Position ) { // BUG_52162 + ::DeleteMatItem( tangent ); + ::DeleteMatItem( tangentDer1 ); + ::DeleteMatItem( tangentDer2 ); + } +} + + +//------------------------------------------------------------------------------ +// \ru конструктор копирования \en copy constructor +//--- +template +MbPntMatingData::MbPntMatingData( const MbPntMatingData & d ) + : type ( d.type ) + , tangent ( (d.tangent != C3D_NULL_PTR) ? new Vector( *d.tangent ) : C3D_NULL_PTR ) + , tangentDer1 ( (d.tangentDer1 != C3D_NULL_PTR) ? new Vector( *d.tangentDer1 ) : C3D_NULL_PTR ) + , tangentDer2 ( (d.tangentDer2 != C3D_NULL_PTR) ? new Vector( *d.tangentDer2 ) : C3D_NULL_PTR ) + , movePnts ( d.movePnts ) + , changedPnts ( d.changedPnts ) + , attach ( d.attach ) +{ +} + + +//------------------------------------------------------------------------------ +// \ru деструктор \en destructor +//--- +template +MbPntMatingData ::~MbPntMatingData() +{ + ::DeleteMatItem( tangent ); + ::DeleteMatItem( tangentDer1 ); + ::DeleteMatItem( tangentDer2 ); +} + + +//------------------------------------------------------------------------------ +// \ru инициализация данных \en data initialization +//--- +template +void MbPntMatingData::Init( const MbeMatingType nType, + const Vector * nTang, + const Vector * nTangDer1, + const Vector * nTangDer2, + SArray *& nChangedPnts, + bool nMovePnts, + bool nAttach ) +{ + type = nType; + + if ( tangent != C3D_NULL_PTR && nTang != C3D_NULL_PTR ) // \ru касательный вектор \en tangent vector + tangent->Init( *nTang ); + else if ( nTang != C3D_NULL_PTR ) + tangent = new Vector( *nTang ); + else if ( tangent != C3D_NULL_PTR ) + ::DeleteMatItem( tangent ); + + if ( tangentDer1 != C3D_NULL_PTR && nTangDer1 != C3D_NULL_PTR ) // \ru первая производная касательного вектора \en first derivative of tangent vector + tangentDer1->Init( *nTangDer1 ); + else if ( nTangDer1 != C3D_NULL_PTR ) + tangentDer1 = new Vector( *nTangDer1 ); + else if ( tangentDer1 != C3D_NULL_PTR ) + ::DeleteMatItem( tangentDer1 ); + + if ( tangentDer2 != C3D_NULL_PTR && nTangDer2 != C3D_NULL_PTR ) // \ru вторая производная касательного вектора \en second derivative of tangent vector + tangentDer2->Init( *nTangDer2 ); + else if ( nTangDer2 != C3D_NULL_PTR ) + tangentDer2 = new Vector( *nTangDer2 ); + else if ( tangentDer2 != C3D_NULL_PTR ) + ::DeleteMatItem( tangentDer2 ); + + if ( type <= trt_Position ) { // BUG_52162 + ::DeleteMatItem( tangent ); + ::DeleteMatItem( tangentDer1 ); + ::DeleteMatItem( tangentDer2 ); + } + + if ( changedPnts != C3D_NULL_PTR ) + changedPnts->clear(); + + movePnts = nMovePnts; + changedPnts = nChangedPnts; + attach = nAttach; +} + + +//------------------------------------------------------------------------------ +// \ru инициализация данных \en data initialization +//--- +template +bool MbPntMatingData::Init( const MbPntMatingData & d ) +{ + C3D_ASSERT( changedPnts == C3D_NULL_PTR ); + + if ( this != &d ) { + SArray * dummyInds = C3D_NULL_PTR; + Init( d.type, d.tangent, d.tangentDer1, d.tangentDer2, dummyInds, d.movePnts, d.attach ); + return true; + } + return false; +} + + +//------------------------------------------------------------------------------ +// \ru Являются ли объекты равными? \en Determine whether an object is equal? +//--- +template +bool MbPntMatingData::IsSame( const MbPntMatingData & other, double accuracy ) const +{ + bool isSame = false; + + if ( c3d::EqualVectors( *tangent, *other.tangent, accuracy ) && + c3d::EqualVectors( *tangentDer1, *other.tangentDer1, accuracy ) && + c3d::EqualVectors( *tangentDer2, *other.tangentDer2, accuracy ) && + *changedPnts == *other.changedPnts && + type == other.type && + movePnts == other.movePnts && + attach == other.attach ) + isSame = true; + + return isSame; +} + + +//------------------------------------------------------------------------------ +// \ru Первичная проверка корректности параметров. \en initial check of parameters correctness +//--- +template +bool MbPntMatingData::IsValid() const +{ + bool isValid = true; + + if ( type >= trt_Position ) { + double lenEps = LENGTH_EPSILON; + bool isTang = (tangent != C3D_NULL_PTR); + bool isTangDer1 = (tangentDer1 != C3D_NULL_PTR); + bool isTangDer2 = (tangentDer2 != C3D_NULL_PTR); + bool isTangLen = (isTang && tangent->Length() > lenEps); + + switch( type ) { + case trt_Position : + isValid = true; + break; + case trt_Tangent: + isValid = isTangLen; + break; + case trt_Normal: + // \ru под нормалью понимается как касательное сопряжение с тем, что названо нормалью, \en normal is considered to be either a tangent conjugation with what is called normal + // \ru так и управление только главной нормалью \en or only principal normal management + isValid = !attach && (isTangLen || (isTangDer1 && tangentDer1->Length() > lenEps)); + break; + case trt_SmoothG2 : + isValid = isTangLen && (isTangDer1 && tangent->Orthogonal( *tangentDer1 )); + break; + case trt_SmoothG3: + isValid = isTangLen && (isTangDer1 && tangent->Orthogonal( *tangentDer1 )) && isTangDer2; + break; + default: break; + } + } + + return isValid; +} + + +//------------------------------------------------------------------------------ +// \ru Фактическая степень гладкости визуального перехода. \en Actual smoothness degree of visual transition. +//--- +template +size_t MbPntMatingData::GetSmoothDegree() const +{ + size_t res = 0; + switch ( type ) { + case trt_Tangent : + res = 1; + break; + case trt_Normal : + if ( tangentDer1 != C3D_NULL_PTR ) res = 2; + else if ( tangent != C3D_NULL_PTR ) res = 1; + break; + case trt_SmoothG2: + res = 2; + break; + case trt_SmoothG3: + res = 3; + break; + default: break; + } + return res; +} + + +//------------------------------------------------------------------------------ +// \ru Уcтановка параметров сопряжения. \en set conjugation parameters +//--- +template +void MbPntMatingData::SetVector( ptrdiff_t i, const Vector & vect ) +{ + switch ( i ) { + case 0 : { + if ( tangent != C3D_NULL_PTR ) tangent->Init( vect ); + else tangent = new Vector( vect ); + break; + } + case 1 : { + if ( tangentDer1 != C3D_NULL_PTR ) tangentDer1->Init( vect ); + else tangentDer1 = new Vector( vect ); + break; + } + case 2 : { + if ( tangentDer2 != C3D_NULL_PTR ) tangentDer2->Init( vect ); + else tangentDer2 = new Vector( vect ); + break; + } + } +} + + +//------------------------------------------------------------------------------ +// \ru Нормализовать касательную в случае стыковки. \en normalize tangent in case of connection +//--- +template +void MbPntMatingData::NormalizeAttachTangent() +{ + if ( attach && tangent != C3D_NULL_PTR ) { + double tangLen = tangent->Length(); + if ( tangLen > LENGTH_EPSILON ) + (*tangent) /= tangLen; + } +} + + +//------------------------------------------------------------------------------ +// \ru выдать свойства объекта \en get properties of object +// --- +template +void MbPntMatingData::GetProperties( MbProperties & properties ) +{ +/* + properties.SetName( IDS_PROP_0900 ); + + TCHAR * typeName = new TCHAR[256]; + + switch ( type ) { + case trt_None : _sntprintf( typeName, IDS_PROP_0902 ); break; + case trt_Position : _sntprintf( typeName, IDS_PROP_0903 ); break; + case trt_Tangent : _sntprintf( typeName, IDS_PROP_0904 ); break; + case trt_Normal : _sntprintf( typeName, IDS_PROP_0905 ); break; + case trt_SmoothG2 : _sntprintf( typeName, IDS_PROP_0906 ); break; + case trt_SmoothG3 : _sntprintf( typeName, IDS_PROP_0907 ); break; + default : _sntprintf( typeName, IDS_ITEM_0902 ); break; + } + + properties.Add( new StringProperty( IDS_PROP_0901, typeName, false ) ); +*/ + if ( tangent != C3D_NULL_PTR ) + properties.Add( new MathItemProperty( IDS_PROP_0908, tangent, true ) ); + if ( tangentDer1 != C3D_NULL_PTR ) + properties.Add( new MathItemProperty( IDS_PROP_0909, tangentDer1, true ) ); + if ( tangentDer2 != C3D_NULL_PTR ) + properties.Add( new MathItemProperty( IDS_PROP_0910, tangentDer2, true ) ); + + properties.Add( new BoolProperty( IDS_PROP_0911, movePnts, false ) ); + properties.Add( new BoolProperty( IDS_PROP_0912, attach, false ) ); +} + + +//------------------------------------------------------------------------------ +// \ru выдать свойства объекта \en get properties of object +// --- +template +void MbPntMatingData::SetProperties( const MbProperties & /*properties*/ ) +{ +} + + +//------------------------------------------------------------------------------ +// \ru определено ли сопряжение \en whether conjugation is defined +//--- +template +bool IsMatingDefined( const MbPntMatingData * data ) +{ + bool isDefined = false; + + if ( data != C3D_NULL_PTR && data->IsValid() ) { + if ( data->GetType() > trt_Position ) // \ru по позиции и так выполнится, поэтому считаем не заданным \en would be held at position, so assumed as undefined + isDefined = true; + } + + return isDefined; +} + + +//------------------------------------------------------------------------------ +// \ru определены ли сопряжение \en whether conjugation is defined +//--- +template +bool IsAnyMatingDefined( const RPArray< MbPntMatingData > & data ) +{ + bool isDefined = false; + + if ( !data.empty() ) { + for ( size_t k = 0, cnt = data.size(); k < cnt; ++k ) { + if ( ::IsMatingDefined( data[k] ) ) { + isDefined = true; + break; + } + } + } + + return isDefined; +} + + +//------------------------------------------------------------------------------ +// \ru копировать сопряжения \en copy conjugations +//--- +template +bool CopyMating( const RPArray< MbPntMatingData > & src, RPArray< MbPntMatingData > & dst ) +{ + bool isDone = false; + + if ( src.size() > 0 && dst.size() < 1 ) { + isDone = true; + for ( size_t k = 0, cnt = src.size(); k < cnt && isDone; k++ ) { + MbPntMatingData * copyItem = C3D_NULL_PTR; + if ( src[k] != C3D_NULL_PTR ) { + copyItem = new MbPntMatingData(); + isDone = copyItem->Init( *src[k] ); + } + dst.Add( copyItem ); + } + if ( !isDone ) + ::DeleteMatItems( dst ); + } + + return isDone; +} + + +//------------------------------------------------------------------------------ +// \ru Являются ли объекты равными? \en Determine whether an object is equal? +//--- +template +bool IsSame( const RPArray< MbPntMatingData > & data, const RPArray< MbPntMatingData > & other, double accuracy ) +{ + bool isSame = false; + + if ( data.size() == other.size() ) { + isSame = true; + for ( size_t k = 0, cnt = data.size(); k < cnt; ++k ) + if ( !data[k]->IsSame( *other[k], accuracy ) ) { + isSame = false; + break; + } + } + + return isSame; +} + + +//------------------------------------------------------------------------------ +// \ru трансформировать сопряжения \en transform conjugations +//--- +template +void TransformMating( const RPArray< MbPntMatingData > & data, const Matrix & matr ) +{ + Vector vect; + for ( size_t k = 0, kcnt = data.size(); k < kcnt; ++k ) { + MbPntMatingData * dataItem = data[k]; + + if ( dataItem != C3D_NULL_PTR ) { + if ( dataItem->GetTangent() != C3D_NULL_PTR ) { + vect = *dataItem->GetTangent(); + vect.Transform( matr ); + dataItem->SetVector( 0, vect ); + } + if ( dataItem->GetTangentDer1() != C3D_NULL_PTR ) { + vect = *dataItem->GetTangentDer1(); + vect.Transform( matr ); + dataItem->SetVector( 1, vect ); + } + if ( dataItem->GetTangentDer2() != C3D_NULL_PTR ) { + vect = *dataItem->GetTangentDer2(); + vect.Transform( matr ); + dataItem->SetVector( 2, vect ); + } + } + } +} + + +//------------------------------------------------------------------------------ +// \ru вращать сопряжения \en rotate conjugations +//--- +template +void RotateMating( const RPArray< MbPntMatingData > & data, const Axis & axis, double angle ) +{ + Vector vect; + for ( size_t i = 0; i < data.size(); ++i ) { + MbPntMatingData * dataItem = data[i]; + + if ( dataItem->GetTangent() != C3D_NULL_PTR ) { + vect = *dataItem->GetTangent(); + vect.Rotate( axis, angle ); + dataItem->SetVector( 0, vect ); + } + if ( dataItem->GetTangentDer1() != C3D_NULL_PTR ) { + vect = *dataItem->GetTangentDer1(); + vect.Rotate( axis, angle ); + dataItem->SetVector( 1, vect ); + } + if ( dataItem->GetTangentDer2() != C3D_NULL_PTR ) { + vect = *dataItem->GetTangentDer2(); + vect.Rotate( axis, angle ); + dataItem->SetVector( 2, vect ); + } + } +} + + +//------------------------------------------------------------------------------ +// \ru запись массива данных сопряжений \en conjugation data array writing +//--- +template +void WriteMating( writer & out, const RPArray< MbPntMatingData > & data ) +{ + if ( out.good() ) { + size_t cnt = data.size(); + WriteCOUNT( out, cnt ); + for ( size_t k = 0; k < cnt && out.good(); k++ ) { + const MbPntMatingData * item = data[k]; + // \ru наличие сопряжения \en presence of conjugation + bool isItem = (item != NULL); + out << isItem; + + if ( isItem && item->GetChangedPoints() != C3D_NULL_PTR ) { + C3D_ASSERT_UNCONDITIONAL( false ); // \ru KYA массив индексов должен быть пуст, т.к. он общий для всех сопряжений, им владеет заказчик операции \en KYA array of indices should be empy because it is shared between all of conjugations and owned by user of operation + out.setState( io::cantWriteObject ); + } + + if ( isItem && out.good() ) { + // \ru тип сопряжения \en conjugation type + uint32 type = (uint32)item->GetType(); + out << type; + // \ru касательный вектор \en tangent vector + isItem = (item->GetTangent() != C3D_NULL_PTR); + out << isItem; + if ( isItem ) + out << (*item->GetTangent()); + // \ru первая производная касательного вектора \en first derivative of tangent vector + isItem = (item->GetTangentDer1() != C3D_NULL_PTR); + out << isItem; + if ( isItem ) + out << (*item->GetTangentDer1()); + // \ru вторая производная касательного вектора \en second derivative of tangent vector + isItem = (item->GetTangentDer2() != C3D_NULL_PTR); + out << isItem; + if ( isItem ) + out << (*item->GetTangentDer2()); + + out << (bool)item->CanMovePoints(); + out << (bool)item->IsAttach(); + } + } + } +} + + +//------------------------------------------------------------------------------ +// \ru чтение массива данных сопряжений \en conjugation data array reading +//--- +template +void ReadMating( reader & in, RPArray< MbPntMatingData > & data ) +{ + if ( in.good() ) { + ::DeleteMatItems( data ); + size_t cnt = ReadCOUNT( in ); + + if ( cnt > 0 ) { + data.Reserve( cnt ); + SArray * dummyInds = C3D_NULL_PTR; + + for ( size_t k = 0; k < cnt && in.good(); k++ ) { + // \ru наличие сопряжения \en presence of conjugation + bool isItem = false; + in >> isItem; + + if ( isItem ) { + // \ru тип сопряжения \en conjugation type + uint32 type = trt_None; + in >> type; + + MbVector3D * v1 = C3D_NULL_PTR; + MbVector3D * v2 = C3D_NULL_PTR; + MbVector3D * v3 = C3D_NULL_PTR; + + // \ru касательный вектор \en tangent vector + in >> isItem; + if ( isItem ) { + v1 = new Vector; + in >> *v1; + } + // \ru первая производная касательного вектора \en first derivative of tangent vector + in >> isItem; + if ( isItem ) { + v2 = new Vector; + in >> *v2; + } + // \ru вторая производная касательного вектора \en second derivative of tangent vector + in >> isItem; + if ( isItem ) { + v3 = new Vector; + in >> *v3; + } + + bool movePnts = false; + in >> movePnts; + bool attach = false; + in >> attach; + + MbPntMatingData * item = new MbPntMatingData(); + item->Init( (MbeMatingType)type, v1, v2, v3, dummyInds, movePnts, attach ); + data.Add( item ); + + ::DeleteMatItem( v1 ); + ::DeleteMatItem( v2 ); + ::DeleteMatItem( v3 ); + } + else { + data.Add( NULL ); + } + } + } + } +} + + +//------------------------------------------------------------------------------ +// \ru Функция копирования. \en Copy function. +//--- +template +void CopyPntMatingData( const MbPntMatingData & srcData, MbPntMatingData & dstData ) +{ + MbeMatingType type = srcData.GetType(); + bool movePnts = srcData.CanMovePoints(); + bool attach = srcData.IsAttach(); + SArray * changedPnts = const_cast *>( srcData.GetChangedPoints() ); + + size_t dim = std_min( SrcVector::GetDimension(), DstVector::GetDimension() ); + + DstVector * tangent = C3D_NULL_PTR; + DstVector * tangentDer1 = C3D_NULL_PTR; + DstVector * tangentDer2 = C3D_NULL_PTR; + + if ( srcData.GetTangent() != C3D_NULL_PTR ) { + tangent = new DstVector; + for ( size_t k = 0; k < dim; k++ ) + (*tangent)[k] = (*srcData.GetTangent())[k]; + } + if ( srcData.GetTangentDer1() != C3D_NULL_PTR ) { + tangentDer1 = new DstVector; + for ( size_t k = 0; k < dim; k++ ) + (*tangentDer1)[k] = (*srcData.GetTangentDer1())[k]; + } + if ( srcData.GetTangentDer2() != C3D_NULL_PTR ) { + tangentDer2 = new DstVector; + for ( size_t k = 0; k < dim; k++ ) + (*tangentDer2)[k] = (*srcData.GetTangentDer2())[k]; + } + + dstData.Init( type, tangent, tangentDer1, tangentDer2, changedPnts, movePnts, attach ); + + ::DeleteMatItem( tangent ); + ::DeleteMatItem( tangentDer1 ); + ::DeleteMatItem( tangentDer2 ); +} + + +#endif diff --git a/C3d/Include/mb_property.h b/C3d/Include/mb_property.h index 7889f83..4e226a4 100644 --- a/C3d/Include/mb_property.h +++ b/C3d/Include/mb_property.h @@ -1,881 +1,881 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Свойства математических объектов. - \en Properties of mathematical objects. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __MB_PROPERTY_H -#define __MB_PROPERTY_H - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#define GET_PROPERTY_VALUE( v ) _GetPropertyValue( &(v), sizeof(v) ) - - -class MbAttribute; -class MbCurve; -class MbCurve3D; -class MbMultiline; -class MbMatrix3D; -class MbFloatPoint; -class MbFloatPoint3D; -class MbFloatVector3D; -class MbTriangle; -class MbQuadrangle; -class MbElement; -class MbApex3D; -class MbPolygon3D; -class MbGrid; -class MbNamedAttributeContainer; -class MbPlacement3D; -class MbMarker; -class MbSurface; -class MbPoint3D; -class MbPointFrame; -class MbWireFrame; -class MbSolid; -class MbInstance; -class MbAssembly; -class MbConstraintSystem; -class MbMesh; -class MbItem; -class MbSpaceInstance; -class MbPlaneInstance; -class MbAssistingItem; -class MbCollection; -class MbModel; -class MbRegion; -class MbDirection; -class MbPlacement; -class MbMatrix; -class MbMultiline; -class MbRegion; -class MbSymbol; -class MbThread; -class MbFunction; -class MbVertex; -class MbEdge; -class MbCurveEdge; -class MbOrientedEdge; -class MbLoop; -class MbFace; -class MbFaceShell; -class MbName; -class MbCreator; -class MbAttributeContainer; -class MbAttributeAction; -class MbTransactions; -template -class MbPntMatingData; -class MbProperties; - - -//---------------------------------------------------------------------------------------- - /** \brief \ru Типы свойств. - \en Types of properties. \~ - \details \ru Типы свойств. \n - Свойства дают доступ к внутренним данным объектов. - \en Types of properties. \n - Properties give access to internal data of objects. \~ - \ingroup Geometric_Items - */ -// --- -enum PrePropType -{ - pt_UndefinedProp, ///< \ru Свойство неизвестного типа данных. \en Property of unknown datatype. \n - - // \ru Атомарные свойства. \en Atomic properties. - pt_BoolProp, ///< \ru Логическое значение. \en Logical value. - pt_IntProp, ///< \ru Целое значение. \en Integer value. - pt_UIntProp, ///< \ru Беззнаковое целое значение. \en Unsigned integer value. - pt_DoubleProp, ///< \ru Действительное значение. \en Real value. - pt_StringProp, ///< \ru Строковое значение. \en String value. - pt_CharProp, ///< \ru Строковое значение. \en String value. - pt_VersionProp, ///< \ru Свойство-версия. \en Version property. \n - - // \ru Комплексные свойства плоских объектов . \en Complex properties of planar objects. - pt_CartPointProp, ///< \ru Cвойство точки. \en Property of point. - pt_VectorProp, ///< \ru Cвойство вектора. \en Property of vector. - pt_DirectionProp, ///< \ru Cвойство вектора. \en Property of vector. - pt_PlacementProp, ///< \ru Cвойство системы координат. \en Property of coordinate system. - pt_MatrixProp, ///< \ru Cвойство матрицы. \en Property of matrix. - pt_CurveProp, ///< \ru Cвойство кривой. \en Property of curve. - pt_MultilineProp, ///< \ru Свойство мультилинии. \en Property of multiline. - pt_RegionProp, ///< \ru Свойство региона. \en Property of region. - pt_PntMatingProp, ///< \ru Свойство сопряжения в точке. \en Property of conjugation at a point. \n - - // \ru Комплексные свойства пространственных объектов. \en Complex properties of spatial objects. - pt_CartPoint3DProp, ///< \ru Cвойство точки. \en Property of point. - pt_Vector3DProp, ///< \ru Cвойство вектора. \en Property of vector. - pt_Placement3DProp, ///< \ru Cвойство системы. \en Property of coordinate system. - pt_Matrix3DProp, ///< \ru Cвойство матрицы. \en Property of matrix. - pt_FloatPointProp, ///< \ru Cвойство параметра. \en Property of parameter. - pt_FloatPoint3DProp, ///< \ru Cвойство точки. \en Property of point. - pt_FloatVector3DProp, ///< \ru Cвойство вектора. \en Property of vector. - pt_TriangleProp, ///< \ru Cвойство треугольника. \en Property of triangle. - pt_QuadrangleProp, ///< \ru Cвойство четырехугольника. \en Property of quadrangle. - pt_ElementProp, ///< \ru Cвойство элемента. \en Property of element. - pt_Apex3DProp, ///< \ru Cвойство аперса. \en Property of apex. - pt_Polygon3DProp, ///< \ru Cвойство полигона. \en Property of polygon. - pt_GridProp, ///< \ru Cвойство триангуляции. \en Property of triangulation. \n - - // \ru Комплексные свойства геометрических объектов. \en Complex properties of geometric objects. - pt_FunctionProp, ///< \ru Cвойство функции. \en Property of function. - pt_Curve3DProp, ///< \ru Cвойство кривой. \en Property of curve. - pt_SurfaceProp, ///< \ru Cвойство поверхности. \en Property of surface. - pt_Point3DProp, ///< \ru Cвойство точки. \en Property of point. - pt_MarkerProp, ///< \ru Cвойство маркера ("точка присоединения"). \en Property of marker ("point of joint"). - pt_SymbolProp, ///< \ru Cвойство условного обозначения. \en Property of conventional notation. - pt_ThreadProp, ///< \ru Cвойство резьбы. \en Property of thread. - pt_Pnt3DMatingProp, ///< \ru Cвойство сопряжения в точке. \en Property of conjugation at a point. \n - - // \ru Комплексные свойства тел и топологических объектов. \en Complex properties of solids and topological objects. - pt_CreatorProp, ///< \ru Cвойство строителя тела. \en Property of solid creator. - pt_VertexProp, ///< \ru Cвойство вершины. \en Property of vertex. - pt_EdgeProp, ///< \ru Cвойство ребра-кривой. \en Property of edge curve. - pt_CurveEdgeProp, ///< \ru Cвойство ребра грани. \en Property of face edge. - pt_OrientedEdgeProp, ///< \ru Cвойство ориентированного ребра. \en Property of oriented edge. - pt_LoopProp, ///< \ru Cвойство цикла. \en Property of loop. - pt_FaceProp, ///< \ru Cвойство грани. \en Property of face. - pt_FaceShellProp, ///< \ru Cвойство оболочки. \en Property of shell. - pt_NameProp, ///< \ru Cвойство имени. \en Property of name. \n - - // \ru Комплексные свойства объектов модели. \en Complex properties of model objects. - pt_AssistingItemProp, ///< \ru Cвойство вспомогательного объекта. \en Property of assisting item. - pt_CollectionProp, ///< \ru Cвойство коллекции 3D элементов. \en Property of the collection of 3D elements. \n - pt_PointFrameProp, ///< \ru Cвойство точечного каркаса. \en Property of point frame. - pt_WireFrameProp, ///< \ru Cвойство проволочного каркаса. \en Property of wire frame. - pt_SolidProp, ///< \ru Cвойство тела. \en Property of solid. - pt_InstanceProp, ///< \ru Cвойство вставки объекта. \en Property of object instance. - pt_AssemblyProp, ///< \ru Cвойство сборочной единицы. \en Property of assembly unit. - pt_ConstraintSystem, ///< \ru Cвойство системы ограничений. \en Property of constraint system. - pt_MeshProp, ///< \ru Cвойство сетки. \en Property of mesh. - pt_SpaceInstanceProp, ///< \ru Cвойство объекта. \en Property of object. - pt_PlaneInstanceProp, ///< \ru Cвойство плоского объекта. \en Property of flat object. - pt_ConstraintModelProp, ///< \ru Cвойство схемы сопряжений. \en Property of conjugation scheme. - pt_ItemProp, ///< \ru Cвойство объекта. \en Property of object. - pt_ModelProp, ///< \ru Cвойство объектной модели. \en Property of object model. - pt_TransactionsProp, ///< \ru Cвойство журнала построения. \en Property of build log. - pt_AttributeContainerProp, ///< \ru Cвойство контейнера атрибутов. \en Property of attribute container. - pt_AttributeProp, ///< \ru Cвойство атрибута. \en Property of attribute. - pt_NamedAttributeContainerProp, ///< \ru Cвойство именованного контейнера атрибутов. \en Property of named attribute container. - pt_AttributeActionProp, ///< \ru Cвойство атрибута. \en Property of attribute. \n - - pt_LastPropType, ///< \ru Последний тип свойства, все остальные добавлять перед ним. \en Last type of property, any other ones must be added before. -}; - - -//----------------------------------------------------------------------------- -/// \ru Структура соответствия типа объекта и типа свойства. \en Object type and property type correspondence structure. -//--- -template -struct PropType { static const PrePropType propId = pt_UndefinedProp; }; - - -//----------------------------------------------------------------------------- -// \ru Специализация шаблона PropType - организует таблицу соответствий \en PropType template specialization - organizes lookup table -//--- -template<> struct PropType { static const PrePropType propId = pt_CartPointProp; }; -template<> struct PropType { static const PrePropType propId = pt_VectorProp; }; -template<> struct PropType { static const PrePropType propId = pt_DirectionProp; }; -template<> struct PropType { static const PrePropType propId = pt_PlacementProp; }; -template<> struct PropType { static const PrePropType propId = pt_MatrixProp; }; -template<> struct PropType { static const PrePropType propId = pt_CurveProp; }; -template<> struct PropType { static const PrePropType propId = pt_MultilineProp; }; -template<> struct PropType { static const PrePropType propId = pt_RegionProp; }; -template<> struct PropType { static const PrePropType propId = pt_SymbolProp; }; -template<> struct PropType { static const PrePropType propId = pt_ThreadProp; }; -template<> struct PropType { static const PrePropType propId = pt_CartPoint3DProp; }; -template<> struct PropType { static const PrePropType propId = pt_Vector3DProp; }; -template<> struct PropType { static const PrePropType propId = pt_Placement3DProp; }; -template<> struct PropType { static const PrePropType propId = pt_Matrix3DProp; }; -template<> struct PropType { static const PrePropType propId = pt_FloatPointProp; }; -template<> struct PropType { static const PrePropType propId = pt_FloatPoint3DProp; }; -template<> struct PropType { static const PrePropType propId = pt_FloatVector3DProp; }; -template<> struct PropType { static const PrePropType propId = pt_TriangleProp; }; -template<> struct PropType { static const PrePropType propId = pt_QuadrangleProp; }; -template<> struct PropType { static const PrePropType propId = pt_ElementProp; }; -template<> struct PropType { static const PrePropType propId = pt_Apex3DProp; }; -template<> struct PropType { static const PrePropType propId = pt_Polygon3DProp; }; -template<> struct PropType { static const PrePropType propId = pt_GridProp; }; -template<> struct PropType { static const PrePropType propId = pt_MarkerProp; }; -template<> struct PropType { static const PrePropType propId = pt_Curve3DProp; }; -template<> struct PropType { static const PrePropType propId = pt_SurfaceProp; }; -template<> struct PropType { static const PrePropType propId = pt_Point3DProp; }; -template<> struct PropType { static const PrePropType propId = pt_PointFrameProp; }; -template<> struct PropType { static const PrePropType propId = pt_WireFrameProp; }; -template<> struct PropType { static const PrePropType propId = pt_SolidProp; }; -template<> struct PropType { static const PrePropType propId = pt_InstanceProp; }; -template<> struct PropType { static const PrePropType propId = pt_AssemblyProp; }; -template<> struct PropType { static const PrePropType propId = pt_ConstraintSystem; }; -template<> struct PropType { static const PrePropType propId = pt_MeshProp; }; -template<> struct PropType { static const PrePropType propId = pt_ItemProp; }; -template<> struct PropType { static const PrePropType propId = pt_SpaceInstanceProp; }; -template<> struct PropType { static const PrePropType propId = pt_PlaneInstanceProp; }; -template<> struct PropType { static const PrePropType propId = pt_AssistingItemProp; }; -template<> struct PropType { static const PrePropType propId = pt_CollectionProp; }; -template<> struct PropType { static const PrePropType propId = pt_ModelProp; }; -template<> struct PropType { static const PrePropType propId = pt_FunctionProp; }; -template<> struct PropType { static const PrePropType propId = pt_VertexProp; }; -template<> struct PropType { static const PrePropType propId = pt_EdgeProp; }; -template<> struct PropType { static const PrePropType propId = pt_CurveEdgeProp; }; -template<> struct PropType { static const PrePropType propId = pt_OrientedEdgeProp; }; -template<> struct PropType { static const PrePropType propId = pt_LoopProp; }; -template<> struct PropType { static const PrePropType propId = pt_FaceProp; }; -template<> struct PropType { static const PrePropType propId = pt_FaceShellProp; }; -template<> struct PropType { static const PrePropType propId = pt_NameProp; }; -template<> struct PropType { static const PrePropType propId = pt_CreatorProp; }; -template<> struct PropType { static const PrePropType propId = pt_AttributeContainerProp; }; -template<> struct PropType { static const PrePropType propId = pt_AttributeProp; }; -template<> struct PropType { static const PrePropType propId = pt_AttributeActionProp; }; -template<> struct PropType { static const PrePropType propId = pt_TransactionsProp; }; -template<> struct PropType { static const PrePropType propId = pt_NamedAttributeContainerProp; }; -template<> struct PropType > { static const PrePropType propId = pt_PntMatingProp; }; -template<> struct PropType > { static const PrePropType propId = pt_Pnt3DMatingProp; }; - - -//------------------------------------------------------------------------------ -/** \brief \ru Свойство. - \en Property. \~ - \details \ru Свойство является базовым классом для доступа к внутренним данным объектов. - Наследники свойства содержать внутренние данные объектов или их копии. - Свойства предназначены для просмотра и модификации внутренних данных объектов. - \en Property is the base class for access to internal data of objects. - Inheritors of property may contain internal data of objects or its copies. - Properties are intended for reading and changing internal data of objects. \~ - \ingroup Model_Properties -*/ -// --- -class MATH_CLASS MbProperty -{ -private: - MbePrompt prompt; ///< \ru Номер подсказки. \en Number of hint string. - bool changeable; ///< \ru Признак редактируемости. \en Attribute of editability. - -public: - /// \ru Конструктор. \en Constructor. - MbProperty( MbePrompt name, bool change = true ) : prompt( name ), changeable( change ) {} - /// \ru Деструктор. \en Destructor. - virtual ~MbProperty(); - - /// \ru Выдать тип свойства. \en Get type of property. - virtual PrePropType IsA() const = 0; - /// \ru Выдать строковое значение свойства. \en Get string value of the property. - virtual void GetCharValue( TCHAR * v ) const = 0; - /// \ru Выдать значение свойства. \en Get value of the property. - virtual void _GetPropertyValue( void * v, size_t size ) const = 0; - /// \ru Установить новое значение свойства. \en Set the new value of the property. - virtual void SetPropertyValue( TCHAR * v ) = 0; - /// \ru Выдать кортеж свойств составного свойства (не атомарный объект). \en Get tuple of the complex property (non-atomic object). - virtual void GetProperties( MbProperties & ) {} - /// \ru Задать кортеж свойств составного свойства (не атомарный объект). \en Set tuple of the complex property (non-atomic object). - virtual void SetProperties( const MbProperties & ) {} - /// \ru Выдать подсказку. \en Get a hint. - virtual size_t GetPrompt() const { return prompt; } - /// \ru Выдать подсказку. \en Get a hint. - MbePrompt & SetPrompt() { return prompt; } - /// \ru Можно ли изменять данные. \en Is it possible to change data. - bool IsChangeable() const { return changeable; } - -OBVIOUS_PRIVATE_COPY( MbProperty ) -}; // MbProperty - - -//------------------------------------------------------------------------------ -/** \brief \ru bool свойство. - \en Bool property. \~ - \details \ru bool свойство предназначено для просмотра и модификации данных типа bool.\n - \en Bool property is intended for reading and changing data of boolean type.\n \~ - \ingroup Model_Properties -*/ -// --- -class MATH_CLASS BoolProperty : public MbProperty { -public : - bool value; ///< \ru Значение. \en Value. - - /// \ru Конструктор. \en Constructor. - BoolProperty( MbePrompt name, bool initValue, bool change = true ) - : MbProperty( name, change ) - , value( initValue ) - {} - /// \ru Деструктор. \en Destructor. - virtual ~BoolProperty(); - - virtual PrePropType IsA() const; // \ru Выдать тип свойства. \en Get type of property. - virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get string value of the property. - virtual void _GetPropertyValue( void * v, size_t size ) const; // \ru Выдать значение свойства. \en Get value of the property. - virtual void SetPropertyValue( TCHAR * v ); // \ru Установить новое значение свойства. \en Set the new value of the property. - -OBVIOUS_PRIVATE_COPY( BoolProperty ) -}; // BoolProperty - - -//------------------------------------------------------------------------------ -/** \brief \ru int свойство. - \en Int property. \~ - \details \ru int свойство предназначено для просмотра и модификации данных типа int.\n - \en Int property is intended for reading and changing data of integer type.\n \~ - \ingroup Model_Properties -*/ -// --- -class MATH_CLASS IntProperty : public MbProperty { -public : - int64 value; ///< \ru Значение. \en Value. - - /// \ru Конструктор. \en Constructor. - IntProperty( MbePrompt name, int64 initValue, bool change = true ) - : MbProperty( name, change ) - , value( (int64)initValue ) - {} - - /// \ru Деструктор. \en Destructor. - virtual ~IntProperty(); - - virtual PrePropType IsA() const; // \ru Выдать тип свойства. \en Get type of property. - virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get string value of the property. - virtual void _GetPropertyValue( void * v, size_t size ) const; // \ru Выдать значение свойства. \en Get value of the property. - virtual void SetPropertyValue( TCHAR * v ); // \ru Установить новое значение свойства. \en Set the new value of the property. - -OBVIOUS_PRIVATE_COPY( IntProperty ) -}; // IntProperty - - -//------------------------------------------------------------------------------ -/** \brief \ru uint свойство. - \en Uint property. \~ - \details \ru uint свойство предназначено для просмотра и модификации данных типа uint64.\n - \en Uint property is intended for reading and changing data of uint64 type.\n \~ - \ingroup Model_Properties -*/ -// --- -class MATH_CLASS UIntProperty : public MbProperty { -public : - uint64 value; ///< \ru Значение. \en Value. - - /// \ru Конструктор. \en Constructor. - UIntProperty( MbePrompt name, size_t initValue, bool change = true ) - : MbProperty( name, change ) - , value( (uint64)initValue ) - {} - - /// \ru Деструктор. \en Destructor. - virtual ~UIntProperty(); - - virtual PrePropType IsA() const; // \ru Выдать тип свойства. \en Get type of property. - virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get string value of the property. - virtual void _GetPropertyValue( void * v, size_t size ) const; // \ru Выдать значение свойства. \en Get value of the property. - virtual void SetPropertyValue( TCHAR * v ); // \ru Установить новое значение свойства. \en Set the new value of the property. - -OBVIOUS_PRIVATE_COPY( UIntProperty ) -}; // UIntProperty - - -//------------------------------------------------------------------------------ -/** \brief \ru double свойство. - \en Double property. \~ - \details \ru double свойство предназначено для просмотра и модификации данных типа double.\n - \en Double property is intended for reading and changing data of double type.\n \~ - \ingroup Model_Properties -*/ -// --- -class MATH_CLASS DoubleProperty : public MbProperty { -public : - double value; ///< \ru Значение. \en Value. - - /// \ru Конструктор. \en Constructor. - DoubleProperty( MbePrompt name, double initValue, bool change = true ) - : MbProperty( name, change ) - , value( initValue ) - {} - /// \ru Деструктор. \en Destructor. - virtual ~DoubleProperty(); - - virtual PrePropType IsA() const; // \ru Выдать тип свойства. \en Get type of property. - virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get string value of the property. - virtual void _GetPropertyValue( void * v, size_t size ) const; // \ru Выдать значение свойства. \en Get value of the property. - virtual void SetPropertyValue( TCHAR * v ); // \ru Установить новое значение свойства. \en Set the new value of the property. - -OBVIOUS_PRIVATE_COPY( DoubleProperty ) -}; // DoubleProperty - - -//------------------------------------------------------------------------------ -/** \brief \ru double свойство с номером. - \en Double property with number. \~ - \details \ru double свойство с номером предназначено для просмотра и модификации данных типа double, имеющих порядковый номер.\n - \en Double property with number is intended for reading and changing data of double type which have number.\n \~ - \ingroup Model_Properties -*/ -// --- -class MATH_CLASS NDoubleProperty : public MbProperty { -public : - double value; ///< \ru Значение. \en Value. - uint32 number; ///< \ru Номер. \en Number. - - /// \ru Конструктор. \en Constructor. - NDoubleProperty( MbePrompt name, double initValue, bool change = true, uint32 n = 0 ) - : MbProperty( name, change ) - , value( initValue ) - , number( n ) - {} - /// \ru Деструктор. \en Destructor. - virtual ~NDoubleProperty(); - - virtual PrePropType IsA() const; // \ru Выдать тип свойства. \en Get type of property. - virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get string value of the property. - virtual void _GetPropertyValue( void * v, size_t size ) const; // \ru Выдать значение свойства. \en Get value of the property. - virtual void SetPropertyValue( TCHAR * v ); // \ru Установить новое значение свойства. \en Set the new value of the property. - -OBVIOUS_PRIVATE_COPY( NDoubleProperty ) -}; // NDoubleProperty - - -//---------------------------------------------------------------------------------------- -/** \brief \ru string свойство. - \en String property. \~ - \details \ru string свойство предназначено для просмотра и модификации данных типа TCHAR *.\n - \en String property is intended for reading and changing TCHAR * like data.\n \~ - \ingroup Model_Properties -*/ -// --- -class MATH_CLASS StringProperty : public MbProperty -{ - TCHAR * value; ///< \ru Значение. \en Value. - -public: - /// \ru Конструктор. \en Constructor. - StringProperty( MbePrompt name, const TCHAR * initValue, bool change = true ); - /// \ru Деструктор. \en Destructor. - virtual ~StringProperty(); - - virtual PrePropType IsA() const; // \ru Выдать тип свойства. \en Get type of property. - virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get string value of the property. - virtual void _GetPropertyValue( void * v, size_t size ) const; // \ru Выдать значение свойства. \en Get value of the property. - virtual void SetPropertyValue( TCHAR * v ); // \ru Установить новое значение свойства. \en Set the new value of the property. - const TCHAR * CharValue() const { return value; } - -OBVIOUS_PRIVATE_COPY(StringProperty) -}; // StringProperty - - -typedef StringProperty CharProperty; - - -//------------------------------------------------------------------------------ -/** \brief \ru Version свойство. - \en Version property. \~ - \details \ru Version свойство предназначено для просмотра и модификации данных типа VERSION.\n - \en Version property is intended for reading and changing VERSION like data.\n \~ - \ingroup Model_Properties -*/ -// --- -class MATH_CLASS VersionProperty : public MbProperty { -public : - VERSION value; ///< \ru Значение. \en Value. - - /// \ru Конструктор. \en Constructor. - VersionProperty( MbePrompt name, VERSION initValue, bool change = true ) - : MbProperty( name, change ) - , value( initValue ) - {} - /// \ru Деструктор. \en Destructor. - virtual ~VersionProperty() {} - - virtual PrePropType IsA() const; // \ru Выдать тип свойства. \en Get type of property. - virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get string value of the property. - virtual void _GetPropertyValue( void * v, size_t size ) const; // \ru Выдать значение свойства. \en Get value of the property. - virtual void SetPropertyValue( TCHAR * v ); // \ru Установить новое значение свойства. \en Set the new value of the property. - -OBVIOUS_PRIVATE_COPY( VersionProperty ) -}; // IntProperty - - -//------------------------------------------------------------------------------ -/** \brief \ru Выдать строковое значение данного свойства для данного его поля. - \en Get string value of given property for its given field. \~ - \details \ru Функция определена для случая "по умолчанию", для конкретных типов FieldType, - следует перегрузить для статического сопоставления типов компилятором.\n - \en In "default" case function is defined for explicit types FieldType, - it should be overloaded for static mapping of types by compiler.\n \~ - \ingroup Model_Properties -*/ -//--- -template -inline void GetCharValue( const PropType *, const FieldType *, uint32 n, TCHAR * v ) -{ - C3D_ASSERT( v != NULL ); - if ( v != NULL ) { - if ( n == 0 ) { - v[0] = _T(' '); - v[1] = _T('\0'); - } - else - _sntprintf( v, 64, _T("%d\t"), n ); - } -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Выдать свойства двумерной точки. - \en Get properties of two-dimensional point. \~ - \details \ru Выдать свойства двумерной точки MbCartPoint.\n - \en Get properties of two-dimensional point MbCartPoint.\n \~ - \ingroup Model_Properties -*/ -//--- -template -inline void GetCharValue( const PropType *, const MbCartPoint * value, uint32 n, TCHAR * v ) -{ - C3D_ASSERT( value != NULL && v != NULL ); - if ( value != NULL && v != NULL ) { - if ( n == 0 ) - _sntprintf( v, 64, _T("%.3f\t%.3f"), value->x, value->y ); - else - _sntprintf( v, 64, _T("%d %.3f\t%.3f"), n, value->x, value->y ); - } -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Выдать свойства двумерного вектора. - \en Get properties of two-dimensional vector. \~ - \details \ru Выдать свойства двумерного вектора MbVector.\n - \en Get properties of two-dimensional vector MbVector.\n \~ - \ingroup Model_Properties -*/ -// --- -template -inline void GetCharValue( const PropType *, const MbVector * value, uint32 n, TCHAR * v ) -{ - C3D_ASSERT( value != NULL && v != NULL ); - if ( value != NULL && v != NULL ) { - if ( n == 0 ) - _sntprintf( v, 64, _T("%.3f\t%.3f"), value->x, value->y ); - else - _sntprintf( v, 64, _T("%d %.3f\t%.3f"), n, value->x, value->y ); - } -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Выдать свойства двумерного нормированного вектора. - \en Get properties of two-dimensional normalized vector. \~ - \details \ru Выдать свойства двумерного нормированного вектора MbDirection.\n - \en Get properties of two-dimensional normalized vector MbDirection.\n \~ - \ingroup Model_Properties -*/ -// --- -template -inline void GetCharValue( const PropType *, const MbDirection * value, uint32 n, TCHAR * v ) -{ - C3D_ASSERT( value != NULL && v != NULL ); - if ( value != NULL && v != NULL ) { - double angle(0.0); - if ( value->ax==0 && value->ay==0 ) - angle = 0.0; - else - angle = 180 / M_PI * atan2( value->ay, value->ax ); - if ( n == 0 ) - _sntprintf( v, 64, _T("%.3f\t"), angle ); - else - _sntprintf( v, 64, _T("%d %.3f\t"), n, angle ); - } -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Выдать свойства трёхмерной точки. - \en Get properties of three-dimensional point. \~ - \details \ru Выдать свойства трёхмерной точки MbCartPoint3D.\n - \en Get properties of three-dimensional point MbCartPoint3D.\n \~ - \ingroup Model_Properties -*/ -// --- -template -inline void GetCharValue( const PropType *, const MbCartPoint3D * value, uint32 n, TCHAR * v ) -{ - C3D_ASSERT( value != NULL && v != NULL ); - if ( value != NULL && v != NULL ) { - if ( n == 0 ) - _sntprintf( v, 64, _T("%.3f\t%.3f\t%.3f"), value->x, value->y, value->z ); - else - _sntprintf( v, 64, _T("%d %.3f\t%.3f\t%.3f"), n, value->x, value->y, value->z ); - } -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Выдать свойства трёхмерного вектора. - \en Get properties of three-dimensional vector. \~ - \details \ru Выдать свойства трёхмерного вектора MbVector3D.\n - \en Get properties of three-dimensional vector MbVector3D.\n \~ - \ingroup Model_Properties -*/ -// --- -template -inline void GetCharValue( const PropType *, const MbVector3D * value, uint32 n, TCHAR * v ) -{ - C3D_ASSERT( value != NULL && v != NULL ); - if ( value != NULL && v != NULL ) { - if ( n == 0 ) - _sntprintf( v, 64, _T("%.3f\t%.3f\t%.3f"), value->x, value->y, value->z ); - else - _sntprintf( v, 64, _T("%d %.3f\t%.3f\t%.3f"), n, value->x, value->y, value->z ); - } -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Выдать свойства имени объекта. - \en Get properties of object name. \~ - \details \ru Выдать свойства имени объекта MbName.\n - \en Get properties of object name MbName.\n \~ - \ingroup Model_Properties -*/ -// --- -template -inline void GetCharValue( const PropType *, const MbName * value, uint32 n, TCHAR * v ) -{ - C3D_ASSERT( v != NULL ); - if ( v != NULL ) { - if ( value != NULL ) { - c3d::string_t str; - value->ToString( str ); - - if ( n !=0 ) { - _sntprintf( v, 64, _T("%d "), n ); - _tcscat( v, str.c_str() ); - } - else { - _tcscpy( v, str.c_str() ); - } - } - } -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Свойство объекта. - \en The property of the object. \~ - \details \ru Обертка, реализующая свойство объекта с настройкой владения ним.\n - \en Wrapper that implements property of an object with its ownership setting.\n \~ - \ingroup Model_Properties -*/ -// --- -template -class MathItemProperty : public MbProperty { -public : - Type * value; ///< \ru Объект. \en Object. - uint32 number; ///< \ru Номер. \en Number. - -public : - /// \ru Конструктор. \en Constructor. - MathItemProperty( MbePrompt name, Type * initValue, bool change, uint32 n = 0 ) - : MbProperty( name, change ) - , value( initValue ) - , number( n ) - {} - /// \ru Деструктор. \en Destructor. - virtual ~MathItemProperty() {} - -public : - // \ru Выдать тип свойства. \en Get type of property. - virtual PrePropType IsA() const { return PropType::propId; } - // \ru Выдать строковое значение свойства. \en Get string value of the property. - virtual void GetCharValue( TCHAR * v ) const { ::GetCharValue( this, value, number, v ); } - // \ru Выдать свойства неатомарного объекта. \en Get properties of the non-atomic object. - virtual void GetProperties( MbProperties & ); - // \ru Задать свойства неатомарного объекта объекта. \en Set properties of the non-atomic object. - virtual void SetProperties( const MbProperties & ); - // \ru Выдать значение свойства. \en Get value of the property. - virtual void _GetPropertyValue( void * v, size_t /*size*/ ) const { *(Type**)v = value; } - // \ru Установить новое значение свойства. \en Set the new value of the property. - virtual void SetPropertyValue( TCHAR * ) {} - -OBVIOUS_PRIVATE_COPY( MathItemProperty ) -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Свойство объекта. -\en The property of the object. \~ -\details \ru Обертка, реализующая свойство объекта с настройкой владения ним.\n -\en Wrapper that implements property of an object with its ownership setting.\n \~ -\ingroup Model_Properties -*/ -// --- -template -class MathItemCopyProperty : public MbProperty { -public: - Type value; ///< \ru Объект. \en Object. - uint32 number; ///< \ru Номер. \en Number. - -public: - /// \ru Конструктор. \en Constructor. - MathItemCopyProperty( MbePrompt name, const Type & initValue, bool change, uint32 n = 0 ) - : MbProperty( name, change ) - , value( initValue ) - , number( n ) - {} - /// \ru Деструктор. \en Destructor. - virtual ~MathItemCopyProperty() {} - -public: - // \ru Выдать тип свойства. \en Get type of property. - virtual PrePropType IsA() const { return PropType::propId; } - // \ru Выдать строковое значение свойства. \en Get string value of the property. - virtual void GetCharValue( TCHAR * v ) const { ::GetCharValue( this, &value, number, v ); } - // \ru Выдать свойства неатомарного объекта объекта. \en Get properties of the non-atomic object. - virtual void GetProperties( MbProperties & ); - // \ru Выдать значение свойства. \en Get value of the property. - virtual void _GetPropertyValue( void * v, size_t /*size*/ ) const { *(Type**)v = const_cast(&value); } - // \ru Установить новое значение свойства. \en Set the new value of the property. - virtual void SetPropertyValue( TCHAR * ) {} - -OBVIOUS_PRIVATE_COPY( MathItemCopyProperty ) -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Cвойство объекта. - \en The property of the object. \~ - \details \ru Обертка, реализующая свойство объекта со счетчиком ссылок.\n - \en Wrapper that implements property of an object with reference counter.\n \~ - \ingroup Model_Properties -*/ -// --- -template -class RefItemProperty : public MbProperty { -public : - SPtr value; ///< \ru Объект. \en Object. - uint32 number; ///< \ru Номер. \en Number. - -public : - /// \ru Конструктор. \en Constructor. - RefItemProperty( MbePrompt name, Type * initValue, bool change, uint32 n = 0 ) - : MbProperty( name, change ) - , value ( initValue ) - , number( n ) - {} - /// \ru Деструктор. \en Destructor. - virtual ~RefItemProperty() {} - - // \ru Выдать тип свойства. \en Get type of property. - virtual PrePropType IsA() const { return PropType::propId; } - // \ru Выдать строковое значение свойства. \en Get string value of the property. - virtual void GetCharValue( TCHAR * v ) const { ::GetCharValue( this, value.get(), number, v ); } - // \ru Выдать свойства неатомарного объекта объекта. \en Get properties of the non-atomic object. - virtual void GetProperties( MbProperties & ); - // \ru Задать свойства неатомарного объекта объекта. \en Set properties of the non-atomic object. - virtual void SetProperties( const MbProperties & ); - // \ru Выдать значение свойства. \en Get value of the property. - virtual void _GetPropertyValue( void * v, size_t /*size*/ ) const { *(Type**)v = value.get(); } - // \ru Установить новое значение свойства. \en Set the new value of the property. - virtual void SetPropertyValue( TCHAR * ) {} - -OBVIOUS_PRIVATE_COPY( RefItemProperty ) -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Множество свойств объекта. - \en Set of object properties. \~ - \details \ru Множество свойств объекта представляет собой контейнер, вызывающий деструктор своих элементов. \n - \en Set of object properties is container that calls destructor of its elements. \n \~ - \ingroup Model_Properties -*/ -// --- -class MATH_CLASS MbProperties : public PArray -{ - MbePrompt name; ///< \ru Имя объекта. \en A name of an object. - -public: - /// \ru Конструктор. \en Constructor. - MbProperties() - : PArray() - , name( IDS_ITEM_0000 ) {} - -public: - /// \ru Выдать имя объекта. \en Get name of object. - MbePrompt & SetName() { return name; } - /// \ru Выдать имя объекта. \en Get name of object. - size_t GetName() const { return (size_t)name; } - /// \ru Выдать имя объекта. \en Get name of object. - MbePrompt Name() const { return name; } - /// \ru Установить имя объекта. \en Set name of the object. - void SetName( MbePrompt s ) { name = s; } - /// \ru Установить имя объекта. \en Set name of the object. - void SetName( size_t s ) { name = (MbePrompt)s; } - /// \ru Найти свойство по имени и типу. \en Find property by name and type. - MbProperty * FindByPrompt( MbePrompt, uint type ) const; - /// \ru Найти индекс свойства в массиве по имени и типу. \en Find index of property in array by name and type. - size_t FindByPrompt( uint type, MbePrompt ) const; - -OBVIOUS_PRIVATE_COPY( MbProperties ) -}; // MbProperties - -//---------------------------------------------------------------------------------------- -// \ru Выдать свойства неатомарного объекта объекта. \en Get properties of the non-atomic object. -//--- -template -void MathItemProperty::GetProperties( MbProperties & props ) -{ - if ( value ) - value->GetProperties( props ); -} - -//---------------------------------------------------------------------------------------- -// \ru Задать свойства неатомарного объекта объекта. \en Set properties of the non-atomic object. -//--- -template -void MathItemProperty::SetProperties( const MbProperties & props ) -{ - if ( value ) - value->SetProperties( props ); -} - -//---------------------------------------------------------------------------------------- -// \ru Выдать свойства неатомарного объекта объекта. \en Get properties of the non-atomic object. -//--- -template -void MathItemCopyProperty::GetProperties( MbProperties & props ) -{ - value.GetProperties( props ); -} - -//---------------------------------------------------------------------------------------- -// \ru Выдать свойства неатомарного объекта объекта. \en Get properties of the non-atomic object. -//--- -template -void RefItemProperty::GetProperties( MbProperties & props ) -{ - if ( value ) - value->GetProperties( props ); -} - -//---------------------------------------------------------------------------------------- -// \ru Выдать свойства неатомарного объекта объекта. \en Get properties of the non-atomic object. -//--- -template -void RefItemProperty::SetProperties( const MbProperties & props ) -{ - if ( value ) - value->SetProperties( props ); -} - - -#endif // __PROPERTY_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Свойства математических объектов. + \en Properties of mathematical objects. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __MB_PROPERTY_H +#define __MB_PROPERTY_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define GET_PROPERTY_VALUE( v ) _GetPropertyValue( &(v), sizeof(v) ) + + +class MbAttribute; +class MbCurve; +class MbCurve3D; +class MbMultiline; +class MbMatrix3D; +class MbFloatPoint; +class MbFloatPoint3D; +class MbFloatVector3D; +class MbTriangle; +class MbQuadrangle; +class MbElement; +class MbApex3D; +class MbPolygon3D; +class MbGrid; +class MbNamedAttributeContainer; +class MbPlacement3D; +class MbMarker; +class MbSurface; +class MbPoint3D; +class MbPointFrame; +class MbWireFrame; +class MbSolid; +class MbInstance; +class MbAssembly; +class MbConstraintSystem; +class MbMesh; +class MbItem; +class MbSpaceInstance; +class MbPlaneInstance; +class MbAssistingItem; +class MbCollection; +class MbModel; +class MbRegion; +class MbDirection; +class MbPlacement; +class MbMatrix; +class MbMultiline; +class MbRegion; +class MbSymbol; +class MbThread; +class MbFunction; +class MbVertex; +class MbEdge; +class MbCurveEdge; +class MbOrientedEdge; +class MbLoop; +class MbFace; +class MbFaceShell; +class MbName; +class MbCreator; +class MbAttributeContainer; +class MbAttributeAction; +class MbTransactions; +template +class MbPntMatingData; +class MbProperties; + + +//---------------------------------------------------------------------------------------- + /** \brief \ru Типы свойств. + \en Types of properties. \~ + \details \ru Типы свойств. \n + Свойства дают доступ к внутренним данным объектов. + \en Types of properties. \n + Properties give access to internal data of objects. \~ + \ingroup Geometric_Items + */ +// --- +enum PrePropType +{ + pt_UndefinedProp, ///< \ru Свойство неизвестного типа данных. \en Property of unknown datatype. \n + + // \ru Атомарные свойства. \en Atomic properties. + pt_BoolProp, ///< \ru Логическое значение. \en Logical value. + pt_IntProp, ///< \ru Целое значение. \en Integer value. + pt_UIntProp, ///< \ru Беззнаковое целое значение. \en Unsigned integer value. + pt_DoubleProp, ///< \ru Действительное значение. \en Real value. + pt_StringProp, ///< \ru Строковое значение. \en String value. + pt_CharProp, ///< \ru Строковое значение. \en String value. + pt_VersionProp, ///< \ru Свойство-версия. \en Version property. \n + + // \ru Комплексные свойства плоских объектов . \en Complex properties of planar objects. + pt_CartPointProp, ///< \ru Cвойство точки. \en Property of point. + pt_VectorProp, ///< \ru Cвойство вектора. \en Property of vector. + pt_DirectionProp, ///< \ru Cвойство вектора. \en Property of vector. + pt_PlacementProp, ///< \ru Cвойство системы координат. \en Property of coordinate system. + pt_MatrixProp, ///< \ru Cвойство матрицы. \en Property of matrix. + pt_CurveProp, ///< \ru Cвойство кривой. \en Property of curve. + pt_MultilineProp, ///< \ru Свойство мультилинии. \en Property of multiline. + pt_RegionProp, ///< \ru Свойство региона. \en Property of region. + pt_PntMatingProp, ///< \ru Свойство сопряжения в точке. \en Property of conjugation at a point. \n + + // \ru Комплексные свойства пространственных объектов. \en Complex properties of spatial objects. + pt_CartPoint3DProp, ///< \ru Cвойство точки. \en Property of point. + pt_Vector3DProp, ///< \ru Cвойство вектора. \en Property of vector. + pt_Placement3DProp, ///< \ru Cвойство системы. \en Property of coordinate system. + pt_Matrix3DProp, ///< \ru Cвойство матрицы. \en Property of matrix. + pt_FloatPointProp, ///< \ru Cвойство параметра. \en Property of parameter. + pt_FloatPoint3DProp, ///< \ru Cвойство точки. \en Property of point. + pt_FloatVector3DProp, ///< \ru Cвойство вектора. \en Property of vector. + pt_TriangleProp, ///< \ru Cвойство треугольника. \en Property of triangle. + pt_QuadrangleProp, ///< \ru Cвойство четырехугольника. \en Property of quadrangle. + pt_ElementProp, ///< \ru Cвойство элемента. \en Property of element. + pt_Apex3DProp, ///< \ru Cвойство аперса. \en Property of apex. + pt_Polygon3DProp, ///< \ru Cвойство полигона. \en Property of polygon. + pt_GridProp, ///< \ru Cвойство триангуляции. \en Property of triangulation. \n + + // \ru Комплексные свойства геометрических объектов. \en Complex properties of geometric objects. + pt_FunctionProp, ///< \ru Cвойство функции. \en Property of function. + pt_Curve3DProp, ///< \ru Cвойство кривой. \en Property of curve. + pt_SurfaceProp, ///< \ru Cвойство поверхности. \en Property of surface. + pt_Point3DProp, ///< \ru Cвойство точки. \en Property of point. + pt_MarkerProp, ///< \ru Cвойство маркера ("точка присоединения"). \en Property of marker ("point of joint"). + pt_SymbolProp, ///< \ru Cвойство условного обозначения. \en Property of conventional notation. + pt_ThreadProp, ///< \ru Cвойство резьбы. \en Property of thread. + pt_Pnt3DMatingProp, ///< \ru Cвойство сопряжения в точке. \en Property of conjugation at a point. \n + + // \ru Комплексные свойства тел и топологических объектов. \en Complex properties of solids and topological objects. + pt_CreatorProp, ///< \ru Cвойство строителя тела. \en Property of solid creator. + pt_VertexProp, ///< \ru Cвойство вершины. \en Property of vertex. + pt_EdgeProp, ///< \ru Cвойство ребра-кривой. \en Property of edge curve. + pt_CurveEdgeProp, ///< \ru Cвойство ребра грани. \en Property of face edge. + pt_OrientedEdgeProp, ///< \ru Cвойство ориентированного ребра. \en Property of oriented edge. + pt_LoopProp, ///< \ru Cвойство цикла. \en Property of loop. + pt_FaceProp, ///< \ru Cвойство грани. \en Property of face. + pt_FaceShellProp, ///< \ru Cвойство оболочки. \en Property of shell. + pt_NameProp, ///< \ru Cвойство имени. \en Property of name. \n + + // \ru Комплексные свойства объектов модели. \en Complex properties of model objects. + pt_AssistingItemProp, ///< \ru Cвойство вспомогательного объекта. \en Property of assisting item. + pt_CollectionProp, ///< \ru Cвойство коллекции 3D элементов. \en Property of the collection of 3D elements. \n + pt_PointFrameProp, ///< \ru Cвойство точечного каркаса. \en Property of point frame. + pt_WireFrameProp, ///< \ru Cвойство проволочного каркаса. \en Property of wire frame. + pt_SolidProp, ///< \ru Cвойство тела. \en Property of solid. + pt_InstanceProp, ///< \ru Cвойство вставки объекта. \en Property of object instance. + pt_AssemblyProp, ///< \ru Cвойство сборочной единицы. \en Property of assembly unit. + pt_ConstraintSystem, ///< \ru Cвойство системы ограничений. \en Property of constraint system. + pt_MeshProp, ///< \ru Cвойство сетки. \en Property of mesh. + pt_SpaceInstanceProp, ///< \ru Cвойство объекта. \en Property of object. + pt_PlaneInstanceProp, ///< \ru Cвойство плоского объекта. \en Property of flat object. + pt_ConstraintModelProp, ///< \ru Cвойство схемы сопряжений. \en Property of conjugation scheme. + pt_ItemProp, ///< \ru Cвойство объекта. \en Property of object. + pt_ModelProp, ///< \ru Cвойство объектной модели. \en Property of object model. + pt_TransactionsProp, ///< \ru Cвойство журнала построения. \en Property of build log. + pt_AttributeContainerProp, ///< \ru Cвойство контейнера атрибутов. \en Property of attribute container. + pt_AttributeProp, ///< \ru Cвойство атрибута. \en Property of attribute. + pt_NamedAttributeContainerProp, ///< \ru Cвойство именованного контейнера атрибутов. \en Property of named attribute container. + pt_AttributeActionProp, ///< \ru Cвойство атрибута. \en Property of attribute. \n + + pt_LastPropType, ///< \ru Последний тип свойства, все остальные добавлять перед ним. \en Last type of property, any other ones must be added before. +}; + + +//----------------------------------------------------------------------------- +/// \ru Структура соответствия типа объекта и типа свойства. \en Object type and property type correspondence structure. +//--- +template +struct PropType { static const PrePropType propId = pt_UndefinedProp; }; + + +//----------------------------------------------------------------------------- +// \ru Специализация шаблона PropType - организует таблицу соответствий \en PropType template specialization - organizes lookup table +//--- +template<> struct PropType { static const PrePropType propId = pt_CartPointProp; }; +template<> struct PropType { static const PrePropType propId = pt_VectorProp; }; +template<> struct PropType { static const PrePropType propId = pt_DirectionProp; }; +template<> struct PropType { static const PrePropType propId = pt_PlacementProp; }; +template<> struct PropType { static const PrePropType propId = pt_MatrixProp; }; +template<> struct PropType { static const PrePropType propId = pt_CurveProp; }; +template<> struct PropType { static const PrePropType propId = pt_MultilineProp; }; +template<> struct PropType { static const PrePropType propId = pt_RegionProp; }; +template<> struct PropType { static const PrePropType propId = pt_SymbolProp; }; +template<> struct PropType { static const PrePropType propId = pt_ThreadProp; }; +template<> struct PropType { static const PrePropType propId = pt_CartPoint3DProp; }; +template<> struct PropType { static const PrePropType propId = pt_Vector3DProp; }; +template<> struct PropType { static const PrePropType propId = pt_Placement3DProp; }; +template<> struct PropType { static const PrePropType propId = pt_Matrix3DProp; }; +template<> struct PropType { static const PrePropType propId = pt_FloatPointProp; }; +template<> struct PropType { static const PrePropType propId = pt_FloatPoint3DProp; }; +template<> struct PropType { static const PrePropType propId = pt_FloatVector3DProp; }; +template<> struct PropType { static const PrePropType propId = pt_TriangleProp; }; +template<> struct PropType { static const PrePropType propId = pt_QuadrangleProp; }; +template<> struct PropType { static const PrePropType propId = pt_ElementProp; }; +template<> struct PropType { static const PrePropType propId = pt_Apex3DProp; }; +template<> struct PropType { static const PrePropType propId = pt_Polygon3DProp; }; +template<> struct PropType { static const PrePropType propId = pt_GridProp; }; +template<> struct PropType { static const PrePropType propId = pt_MarkerProp; }; +template<> struct PropType { static const PrePropType propId = pt_Curve3DProp; }; +template<> struct PropType { static const PrePropType propId = pt_SurfaceProp; }; +template<> struct PropType { static const PrePropType propId = pt_Point3DProp; }; +template<> struct PropType { static const PrePropType propId = pt_PointFrameProp; }; +template<> struct PropType { static const PrePropType propId = pt_WireFrameProp; }; +template<> struct PropType { static const PrePropType propId = pt_SolidProp; }; +template<> struct PropType { static const PrePropType propId = pt_InstanceProp; }; +template<> struct PropType { static const PrePropType propId = pt_AssemblyProp; }; +template<> struct PropType { static const PrePropType propId = pt_ConstraintSystem; }; +template<> struct PropType { static const PrePropType propId = pt_MeshProp; }; +template<> struct PropType { static const PrePropType propId = pt_ItemProp; }; +template<> struct PropType { static const PrePropType propId = pt_SpaceInstanceProp; }; +template<> struct PropType { static const PrePropType propId = pt_PlaneInstanceProp; }; +template<> struct PropType { static const PrePropType propId = pt_AssistingItemProp; }; +template<> struct PropType { static const PrePropType propId = pt_CollectionProp; }; +template<> struct PropType { static const PrePropType propId = pt_ModelProp; }; +template<> struct PropType { static const PrePropType propId = pt_FunctionProp; }; +template<> struct PropType { static const PrePropType propId = pt_VertexProp; }; +template<> struct PropType { static const PrePropType propId = pt_EdgeProp; }; +template<> struct PropType { static const PrePropType propId = pt_CurveEdgeProp; }; +template<> struct PropType { static const PrePropType propId = pt_OrientedEdgeProp; }; +template<> struct PropType { static const PrePropType propId = pt_LoopProp; }; +template<> struct PropType { static const PrePropType propId = pt_FaceProp; }; +template<> struct PropType { static const PrePropType propId = pt_FaceShellProp; }; +template<> struct PropType { static const PrePropType propId = pt_NameProp; }; +template<> struct PropType { static const PrePropType propId = pt_CreatorProp; }; +template<> struct PropType { static const PrePropType propId = pt_AttributeContainerProp; }; +template<> struct PropType { static const PrePropType propId = pt_AttributeProp; }; +template<> struct PropType { static const PrePropType propId = pt_AttributeActionProp; }; +template<> struct PropType { static const PrePropType propId = pt_TransactionsProp; }; +template<> struct PropType { static const PrePropType propId = pt_NamedAttributeContainerProp; }; +template<> struct PropType > { static const PrePropType propId = pt_PntMatingProp; }; +template<> struct PropType > { static const PrePropType propId = pt_Pnt3DMatingProp; }; + + +//------------------------------------------------------------------------------ +/** \brief \ru Свойство. + \en Property. \~ + \details \ru Свойство является базовым классом для доступа к внутренним данным объектов. + Наследники свойства содержать внутренние данные объектов или их копии. + Свойства предназначены для просмотра и модификации внутренних данных объектов. + \en Property is the base class for access to internal data of objects. + Inheritors of property may contain internal data of objects or its copies. + Properties are intended for reading and changing internal data of objects. \~ + \ingroup Model_Properties +*/ +// --- +class MATH_CLASS MbProperty +{ +private: + MbePrompt prompt; ///< \ru Номер подсказки. \en Number of hint string. + bool changeable; ///< \ru Признак редактируемости. \en Attribute of editability. + +public: + /// \ru Конструктор. \en Constructor. + MbProperty( MbePrompt name, bool change = true ) : prompt( name ), changeable( change ) {} + /// \ru Деструктор. \en Destructor. + virtual ~MbProperty(); + + /// \ru Выдать тип свойства. \en Get type of property. + virtual PrePropType IsA() const = 0; + /// \ru Выдать строковое значение свойства. \en Get string value of the property. + virtual void GetCharValue( TCHAR * v ) const = 0; + /// \ru Выдать значение свойства. \en Get value of the property. + virtual void _GetPropertyValue( void * v, size_t size ) const = 0; + /// \ru Установить новое значение свойства. \en Set the new value of the property. + virtual void SetPropertyValue( TCHAR * v ) = 0; + /// \ru Выдать кортеж свойств составного свойства (не атомарный объект). \en Get tuple of the complex property (non-atomic object). + virtual void GetProperties( MbProperties & ) {} + /// \ru Задать кортеж свойств составного свойства (не атомарный объект). \en Set tuple of the complex property (non-atomic object). + virtual void SetProperties( const MbProperties & ) {} + /// \ru Выдать подсказку. \en Get a hint. + virtual size_t GetPrompt() const { return prompt; } + /// \ru Выдать подсказку. \en Get a hint. + MbePrompt & SetPrompt() { return prompt; } + /// \ru Можно ли изменять данные. \en Is it possible to change data. + bool IsChangeable() const { return changeable; } + +OBVIOUS_PRIVATE_COPY( MbProperty ) +}; // MbProperty + + +//------------------------------------------------------------------------------ +/** \brief \ru bool свойство. + \en Bool property. \~ + \details \ru bool свойство предназначено для просмотра и модификации данных типа bool.\n + \en Bool property is intended for reading and changing data of boolean type.\n \~ + \ingroup Model_Properties +*/ +// --- +class MATH_CLASS BoolProperty : public MbProperty { +public : + bool value; ///< \ru Значение. \en Value. + + /// \ru Конструктор. \en Constructor. + BoolProperty( MbePrompt name, bool initValue, bool change = true ) + : MbProperty( name, change ) + , value( initValue ) + {} + /// \ru Деструктор. \en Destructor. + virtual ~BoolProperty(); + + virtual PrePropType IsA() const; // \ru Выдать тип свойства. \en Get type of property. + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get string value of the property. + virtual void _GetPropertyValue( void * v, size_t size ) const; // \ru Выдать значение свойства. \en Get value of the property. + virtual void SetPropertyValue( TCHAR * v ); // \ru Установить новое значение свойства. \en Set the new value of the property. + +OBVIOUS_PRIVATE_COPY( BoolProperty ) +}; // BoolProperty + + +//------------------------------------------------------------------------------ +/** \brief \ru int свойство. + \en Int property. \~ + \details \ru int свойство предназначено для просмотра и модификации данных типа int.\n + \en Int property is intended for reading and changing data of integer type.\n \~ + \ingroup Model_Properties +*/ +// --- +class MATH_CLASS IntProperty : public MbProperty { +public : + int64 value; ///< \ru Значение. \en Value. + + /// \ru Конструктор. \en Constructor. + IntProperty( MbePrompt name, int64 initValue, bool change = true ) + : MbProperty( name, change ) + , value( (int64)initValue ) + {} + + /// \ru Деструктор. \en Destructor. + virtual ~IntProperty(); + + virtual PrePropType IsA() const; // \ru Выдать тип свойства. \en Get type of property. + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get string value of the property. + virtual void _GetPropertyValue( void * v, size_t size ) const; // \ru Выдать значение свойства. \en Get value of the property. + virtual void SetPropertyValue( TCHAR * v ); // \ru Установить новое значение свойства. \en Set the new value of the property. + +OBVIOUS_PRIVATE_COPY( IntProperty ) +}; // IntProperty + + +//------------------------------------------------------------------------------ +/** \brief \ru uint свойство. + \en Uint property. \~ + \details \ru uint свойство предназначено для просмотра и модификации данных типа uint64.\n + \en Uint property is intended for reading and changing data of uint64 type.\n \~ + \ingroup Model_Properties +*/ +// --- +class MATH_CLASS UIntProperty : public MbProperty { +public : + uint64 value; ///< \ru Значение. \en Value. + + /// \ru Конструктор. \en Constructor. + UIntProperty( MbePrompt name, size_t initValue, bool change = true ) + : MbProperty( name, change ) + , value( (uint64)initValue ) + {} + + /// \ru Деструктор. \en Destructor. + virtual ~UIntProperty(); + + virtual PrePropType IsA() const; // \ru Выдать тип свойства. \en Get type of property. + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get string value of the property. + virtual void _GetPropertyValue( void * v, size_t size ) const; // \ru Выдать значение свойства. \en Get value of the property. + virtual void SetPropertyValue( TCHAR * v ); // \ru Установить новое значение свойства. \en Set the new value of the property. + +OBVIOUS_PRIVATE_COPY( UIntProperty ) +}; // UIntProperty + + +//------------------------------------------------------------------------------ +/** \brief \ru double свойство. + \en Double property. \~ + \details \ru double свойство предназначено для просмотра и модификации данных типа double.\n + \en Double property is intended for reading and changing data of double type.\n \~ + \ingroup Model_Properties +*/ +// --- +class MATH_CLASS DoubleProperty : public MbProperty { +public : + double value; ///< \ru Значение. \en Value. + + /// \ru Конструктор. \en Constructor. + DoubleProperty( MbePrompt name, double initValue, bool change = true ) + : MbProperty( name, change ) + , value( initValue ) + {} + /// \ru Деструктор. \en Destructor. + virtual ~DoubleProperty(); + + virtual PrePropType IsA() const; // \ru Выдать тип свойства. \en Get type of property. + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get string value of the property. + virtual void _GetPropertyValue( void * v, size_t size ) const; // \ru Выдать значение свойства. \en Get value of the property. + virtual void SetPropertyValue( TCHAR * v ); // \ru Установить новое значение свойства. \en Set the new value of the property. + +OBVIOUS_PRIVATE_COPY( DoubleProperty ) +}; // DoubleProperty + + +//------------------------------------------------------------------------------ +/** \brief \ru double свойство с номером. + \en Double property with number. \~ + \details \ru double свойство с номером предназначено для просмотра и модификации данных типа double, имеющих порядковый номер.\n + \en Double property with number is intended for reading and changing data of double type which have number.\n \~ + \ingroup Model_Properties +*/ +// --- +class MATH_CLASS NDoubleProperty : public MbProperty { +public : + double value; ///< \ru Значение. \en Value. + uint32 number; ///< \ru Номер. \en Number. + + /// \ru Конструктор. \en Constructor. + NDoubleProperty( MbePrompt name, double initValue, bool change = true, uint32 n = 0 ) + : MbProperty( name, change ) + , value( initValue ) + , number( n ) + {} + /// \ru Деструктор. \en Destructor. + virtual ~NDoubleProperty(); + + virtual PrePropType IsA() const; // \ru Выдать тип свойства. \en Get type of property. + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get string value of the property. + virtual void _GetPropertyValue( void * v, size_t size ) const; // \ru Выдать значение свойства. \en Get value of the property. + virtual void SetPropertyValue( TCHAR * v ); // \ru Установить новое значение свойства. \en Set the new value of the property. + +OBVIOUS_PRIVATE_COPY( NDoubleProperty ) +}; // NDoubleProperty + + +//---------------------------------------------------------------------------------------- +/** \brief \ru string свойство. + \en String property. \~ + \details \ru string свойство предназначено для просмотра и модификации данных типа TCHAR *.\n + \en String property is intended for reading and changing TCHAR * like data.\n \~ + \ingroup Model_Properties +*/ +// --- +class MATH_CLASS StringProperty : public MbProperty +{ + TCHAR * value; ///< \ru Значение. \en Value. + +public: + /// \ru Конструктор. \en Constructor. + StringProperty( MbePrompt name, const TCHAR * initValue, bool change = true ); + /// \ru Деструктор. \en Destructor. + virtual ~StringProperty(); + + virtual PrePropType IsA() const; // \ru Выдать тип свойства. \en Get type of property. + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get string value of the property. + virtual void _GetPropertyValue( void * v, size_t size ) const; // \ru Выдать значение свойства. \en Get value of the property. + virtual void SetPropertyValue( TCHAR * v ); // \ru Установить новое значение свойства. \en Set the new value of the property. + const TCHAR * CharValue() const { return value; } + +OBVIOUS_PRIVATE_COPY(StringProperty) +}; // StringProperty + + +typedef StringProperty CharProperty; + + +//------------------------------------------------------------------------------ +/** \brief \ru Version свойство. + \en Version property. \~ + \details \ru Version свойство предназначено для просмотра и модификации данных типа VERSION.\n + \en Version property is intended for reading and changing VERSION like data.\n \~ + \ingroup Model_Properties +*/ +// --- +class MATH_CLASS VersionProperty : public MbProperty { +public : + VERSION value; ///< \ru Значение. \en Value. + + /// \ru Конструктор. \en Constructor. + VersionProperty( MbePrompt name, VERSION initValue, bool change = true ) + : MbProperty( name, change ) + , value( initValue ) + {} + /// \ru Деструктор. \en Destructor. + virtual ~VersionProperty() {} + + virtual PrePropType IsA() const; // \ru Выдать тип свойства. \en Get type of property. + virtual void GetCharValue( TCHAR * v ) const; // \ru Выдать строковое значение свойства. \en Get string value of the property. + virtual void _GetPropertyValue( void * v, size_t size ) const; // \ru Выдать значение свойства. \en Get value of the property. + virtual void SetPropertyValue( TCHAR * v ); // \ru Установить новое значение свойства. \en Set the new value of the property. + +OBVIOUS_PRIVATE_COPY( VersionProperty ) +}; // IntProperty + + +//------------------------------------------------------------------------------ +/** \brief \ru Выдать строковое значение данного свойства для данного его поля. + \en Get string value of given property for its given field. \~ + \details \ru Функция определена для случая "по умолчанию", для конкретных типов FieldType, + следует перегрузить для статического сопоставления типов компилятором.\n + \en In "default" case function is defined for explicit types FieldType, + it should be overloaded for static mapping of types by compiler.\n \~ + \ingroup Model_Properties +*/ +//--- +template +inline void GetCharValue( const PropType *, const FieldType *, uint32 n, TCHAR * v ) +{ + C3D_ASSERT( v != NULL ); + if ( v != NULL ) { + if ( n == 0 ) { + v[0] = _T(' '); + v[1] = _T('\0'); + } + else + _sntprintf( v, 64, _T("%d\t"), n ); + } +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Выдать свойства двумерной точки. + \en Get properties of two-dimensional point. \~ + \details \ru Выдать свойства двумерной точки MbCartPoint.\n + \en Get properties of two-dimensional point MbCartPoint.\n \~ + \ingroup Model_Properties +*/ +//--- +template +inline void GetCharValue( const PropType *, const MbCartPoint * value, uint32 n, TCHAR * v ) +{ + C3D_ASSERT( value != NULL && v != NULL ); + if ( value != NULL && v != NULL ) { + if ( n == 0 ) + _sntprintf( v, 64, _T("%.3f\t%.3f"), value->x, value->y ); + else + _sntprintf( v, 64, _T("%d %.3f\t%.3f"), n, value->x, value->y ); + } +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Выдать свойства двумерного вектора. + \en Get properties of two-dimensional vector. \~ + \details \ru Выдать свойства двумерного вектора MbVector.\n + \en Get properties of two-dimensional vector MbVector.\n \~ + \ingroup Model_Properties +*/ +// --- +template +inline void GetCharValue( const PropType *, const MbVector * value, uint32 n, TCHAR * v ) +{ + C3D_ASSERT( value != NULL && v != NULL ); + if ( value != NULL && v != NULL ) { + if ( n == 0 ) + _sntprintf( v, 64, _T("%.3f\t%.3f"), value->x, value->y ); + else + _sntprintf( v, 64, _T("%d %.3f\t%.3f"), n, value->x, value->y ); + } +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Выдать свойства двумерного нормированного вектора. + \en Get properties of two-dimensional normalized vector. \~ + \details \ru Выдать свойства двумерного нормированного вектора MbDirection.\n + \en Get properties of two-dimensional normalized vector MbDirection.\n \~ + \ingroup Model_Properties +*/ +// --- +template +inline void GetCharValue( const PropType *, const MbDirection * value, uint32 n, TCHAR * v ) +{ + C3D_ASSERT( value != NULL && v != NULL ); + if ( value != NULL && v != NULL ) { + double angle(0.0); + if ( value->ax==0 && value->ay==0 ) + angle = 0.0; + else + angle = 180 / M_PI * atan2( value->ay, value->ax ); + if ( n == 0 ) + _sntprintf( v, 64, _T("%.3f\t"), angle ); + else + _sntprintf( v, 64, _T("%d %.3f\t"), n, angle ); + } +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Выдать свойства трёхмерной точки. + \en Get properties of three-dimensional point. \~ + \details \ru Выдать свойства трёхмерной точки MbCartPoint3D.\n + \en Get properties of three-dimensional point MbCartPoint3D.\n \~ + \ingroup Model_Properties +*/ +// --- +template +inline void GetCharValue( const PropType *, const MbCartPoint3D * value, uint32 n, TCHAR * v ) +{ + C3D_ASSERT( value != NULL && v != NULL ); + if ( value != NULL && v != NULL ) { + if ( n == 0 ) + _sntprintf( v, 64, _T("%.3f\t%.3f\t%.3f"), value->x, value->y, value->z ); + else + _sntprintf( v, 64, _T("%d %.3f\t%.3f\t%.3f"), n, value->x, value->y, value->z ); + } +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Выдать свойства трёхмерного вектора. + \en Get properties of three-dimensional vector. \~ + \details \ru Выдать свойства трёхмерного вектора MbVector3D.\n + \en Get properties of three-dimensional vector MbVector3D.\n \~ + \ingroup Model_Properties +*/ +// --- +template +inline void GetCharValue( const PropType *, const MbVector3D * value, uint32 n, TCHAR * v ) +{ + C3D_ASSERT( value != NULL && v != NULL ); + if ( value != NULL && v != NULL ) { + if ( n == 0 ) + _sntprintf( v, 64, _T("%.3f\t%.3f\t%.3f"), value->x, value->y, value->z ); + else + _sntprintf( v, 64, _T("%d %.3f\t%.3f\t%.3f"), n, value->x, value->y, value->z ); + } +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Выдать свойства имени объекта. + \en Get properties of object name. \~ + \details \ru Выдать свойства имени объекта MbName.\n + \en Get properties of object name MbName.\n \~ + \ingroup Model_Properties +*/ +// --- +template +inline void GetCharValue( const PropType *, const MbName * value, uint32 n, TCHAR * v ) +{ + C3D_ASSERT( v != NULL ); + if ( v != NULL ) { + if ( value != NULL ) { + c3d::string_t str; + value->ToString( str ); + + if ( n !=0 ) { + _sntprintf( v, 64, _T("%d "), n ); + _tcscat( v, str.c_str() ); + } + else { + _tcscpy( v, str.c_str() ); + } + } + } +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Свойство объекта. + \en The property of the object. \~ + \details \ru Обертка, реализующая свойство объекта с настройкой владения ним.\n + \en Wrapper that implements property of an object with its ownership setting.\n \~ + \ingroup Model_Properties +*/ +// --- +template +class MathItemProperty : public MbProperty { +public : + Type * value; ///< \ru Объект. \en Object. + uint32 number; ///< \ru Номер. \en Number. + +public : + /// \ru Конструктор. \en Constructor. + MathItemProperty( MbePrompt name, Type * initValue, bool change, uint32 n = 0 ) + : MbProperty( name, change ) + , value( initValue ) + , number( n ) + {} + /// \ru Деструктор. \en Destructor. + virtual ~MathItemProperty() {} + +public : + // \ru Выдать тип свойства. \en Get type of property. + virtual PrePropType IsA() const { return PropType::propId; } + // \ru Выдать строковое значение свойства. \en Get string value of the property. + virtual void GetCharValue( TCHAR * v ) const { ::GetCharValue( this, value, number, v ); } + // \ru Выдать свойства неатомарного объекта. \en Get properties of the non-atomic object. + virtual void GetProperties( MbProperties & ); + // \ru Задать свойства неатомарного объекта объекта. \en Set properties of the non-atomic object. + virtual void SetProperties( const MbProperties & ); + // \ru Выдать значение свойства. \en Get value of the property. + virtual void _GetPropertyValue( void * v, size_t /*size*/ ) const { *(Type**)v = value; } + // \ru Установить новое значение свойства. \en Set the new value of the property. + virtual void SetPropertyValue( TCHAR * ) {} + +OBVIOUS_PRIVATE_COPY( MathItemProperty ) +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Свойство объекта. +\en The property of the object. \~ +\details \ru Обертка, реализующая свойство объекта с настройкой владения ним.\n +\en Wrapper that implements property of an object with its ownership setting.\n \~ +\ingroup Model_Properties +*/ +// --- +template +class MathItemCopyProperty : public MbProperty { +public: + Type value; ///< \ru Объект. \en Object. + uint32 number; ///< \ru Номер. \en Number. + +public: + /// \ru Конструктор. \en Constructor. + MathItemCopyProperty( MbePrompt name, const Type & initValue, bool change, uint32 n = 0 ) + : MbProperty( name, change ) + , value( initValue ) + , number( n ) + {} + /// \ru Деструктор. \en Destructor. + virtual ~MathItemCopyProperty() {} + +public: + // \ru Выдать тип свойства. \en Get type of property. + virtual PrePropType IsA() const { return PropType::propId; } + // \ru Выдать строковое значение свойства. \en Get string value of the property. + virtual void GetCharValue( TCHAR * v ) const { ::GetCharValue( this, &value, number, v ); } + // \ru Выдать свойства неатомарного объекта объекта. \en Get properties of the non-atomic object. + virtual void GetProperties( MbProperties & ); + // \ru Выдать значение свойства. \en Get value of the property. + virtual void _GetPropertyValue( void * v, size_t /*size*/ ) const { *(Type**)v = const_cast(&value); } + // \ru Установить новое значение свойства. \en Set the new value of the property. + virtual void SetPropertyValue( TCHAR * ) {} + +OBVIOUS_PRIVATE_COPY( MathItemCopyProperty ) +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Cвойство объекта. + \en The property of the object. \~ + \details \ru Обертка, реализующая свойство объекта со счетчиком ссылок.\n + \en Wrapper that implements property of an object with reference counter.\n \~ + \ingroup Model_Properties +*/ +// --- +template +class RefItemProperty : public MbProperty { +public : + SPtr value; ///< \ru Объект. \en Object. + uint32 number; ///< \ru Номер. \en Number. + +public : + /// \ru Конструктор. \en Constructor. + RefItemProperty( MbePrompt name, Type * initValue, bool change, uint32 n = 0 ) + : MbProperty( name, change ) + , value ( initValue ) + , number( n ) + {} + /// \ru Деструктор. \en Destructor. + virtual ~RefItemProperty() {} + + // \ru Выдать тип свойства. \en Get type of property. + virtual PrePropType IsA() const { return PropType::propId; } + // \ru Выдать строковое значение свойства. \en Get string value of the property. + virtual void GetCharValue( TCHAR * v ) const { ::GetCharValue( this, value.get(), number, v ); } + // \ru Выдать свойства неатомарного объекта объекта. \en Get properties of the non-atomic object. + virtual void GetProperties( MbProperties & ); + // \ru Задать свойства неатомарного объекта объекта. \en Set properties of the non-atomic object. + virtual void SetProperties( const MbProperties & ); + // \ru Выдать значение свойства. \en Get value of the property. + virtual void _GetPropertyValue( void * v, size_t /*size*/ ) const { *(Type**)v = value.get(); } + // \ru Установить новое значение свойства. \en Set the new value of the property. + virtual void SetPropertyValue( TCHAR * ) {} + +OBVIOUS_PRIVATE_COPY( RefItemProperty ) +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Множество свойств объекта. + \en Set of object properties. \~ + \details \ru Множество свойств объекта представляет собой контейнер, вызывающий деструктор своих элементов. \n + \en Set of object properties is container that calls destructor of its elements. \n \~ + \ingroup Model_Properties +*/ +// --- +class MATH_CLASS MbProperties : public PArray +{ + MbePrompt name; ///< \ru Имя объекта. \en A name of an object. + +public: + /// \ru Конструктор. \en Constructor. + MbProperties() + : PArray() + , name( IDS_ITEM_0000 ) {} + +public: + /// \ru Выдать имя объекта. \en Get name of object. + MbePrompt & SetName() { return name; } + /// \ru Выдать имя объекта. \en Get name of object. + size_t GetName() const { return (size_t)name; } + /// \ru Выдать имя объекта. \en Get name of object. + MbePrompt Name() const { return name; } + /// \ru Установить имя объекта. \en Set name of the object. + void SetName( MbePrompt s ) { name = s; } + /// \ru Установить имя объекта. \en Set name of the object. + void SetName( size_t s ) { name = (MbePrompt)s; } + /// \ru Найти свойство по имени и типу. \en Find property by name and type. + MbProperty * FindByPrompt( MbePrompt, uint type ) const; + /// \ru Найти индекс свойства в массиве по имени и типу. \en Find index of property in array by name and type. + size_t FindByPrompt( uint type, MbePrompt ) const; + +OBVIOUS_PRIVATE_COPY( MbProperties ) +}; // MbProperties + +//---------------------------------------------------------------------------------------- +// \ru Выдать свойства неатомарного объекта объекта. \en Get properties of the non-atomic object. +//--- +template +void MathItemProperty::GetProperties( MbProperties & props ) +{ + if ( value ) + value->GetProperties( props ); +} + +//---------------------------------------------------------------------------------------- +// \ru Задать свойства неатомарного объекта объекта. \en Set properties of the non-atomic object. +//--- +template +void MathItemProperty::SetProperties( const MbProperties & props ) +{ + if ( value ) + value->SetProperties( props ); +} + +//---------------------------------------------------------------------------------------- +// \ru Выдать свойства неатомарного объекта объекта. \en Get properties of the non-atomic object. +//--- +template +void MathItemCopyProperty::GetProperties( MbProperties & props ) +{ + value.GetProperties( props ); +} + +//---------------------------------------------------------------------------------------- +// \ru Выдать свойства неатомарного объекта объекта. \en Get properties of the non-atomic object. +//--- +template +void RefItemProperty::GetProperties( MbProperties & props ) +{ + if ( value ) + value->GetProperties( props ); +} + +//---------------------------------------------------------------------------------------- +// \ru Выдать свойства неатомарного объекта объекта. \en Get properties of the non-atomic object. +//--- +template +void RefItemProperty::SetProperties( const MbProperties & props ) +{ + if ( value ) + value->SetProperties( props ); +} + + +#endif // __PROPERTY_H diff --git a/C3d/Include/mb_property_title.h b/C3d/Include/mb_property_title.h index 2973de4..deedaef 100644 --- a/C3d/Include/mb_property_title.h +++ b/C3d/Include/mb_property_title.h @@ -1,1173 +1,1216 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Свойства математических объектов. - \en Properties of mathematical objects. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __MB_PROPERTY_TITLE_H -#define __MB_PROPERTY_TITLE_H - - -//------------------------------------------------------------------------------ -/** \brief \ru Свойства математических объектов. - \en Properties of mathematical objects. \~ - \attention \ru Целочисленные значения данного перечислительного типа могут быть изменены! - \en Integer values of the enum can be changed! - \ingroup Base_Items -*/ -// --- -enum MbePrompt -{ - IDS_ITEM_0000 = 0, ///< \ru Неопределенный объект. - -// \ru Базовые объекты двумерной математики. \en Base 2D objects. - - IDS_ITEM_0001, ///< \ru Двумерная точка. \en A two-dimensional point. - IDS_ITEM_0002, ///< \ru Двумерный вектор. \en Two-dimensional vector. - IDS_ITEM_0003, ///< \ru Двумерная матрица преобразования. \en Two-dimensional matrix of transformation. - IDS_ITEM_0004, ///< \ru Двумерная локальная система. \en Two-dimensional local system. - IDS_ITEM_0005, ///< \ru Двумерный единичный вектор. \en Two-dimensional unit vector - -// \ru Типы двумерных кривых. \en Types of two-dimensional curves. - - IDS_ITEM_0011, ///< \ru Двумерная кривая. \en Two-dimensional curve - - IDS_ITEM_0013, ///< \ru Двумерная прямая. \en Two-dimensional line. - IDS_ITEM_0014, ///< \ru Двумерный отрезок. \en Two-dimensional segment. - IDS_ITEM_0015, ///< \ru Двумерный отрезок прямой. \en Two-dimensional line segment. - IDS_ITEM_0016, ///< \ru Двумерная дуга окружности. \en Two-dimensional circular arc. - IDS_ITEM_0017, ///< \ru Двумерная усеченная кривая. \en Two-dimensional truncated curve. - IDS_ITEM_0018, ///< \ru Двумерная эквидистантная кривая. \en Two-dimensional offset curve. - IDS_ITEM_0019, ///< \ru Двумерная эквидистанта. \en Two-dimensional equidistant. - IDS_ITEM_0020, ///< \ru Двумерная окружность. \en Two-dimensional circle. - IDS_ITEM_0021, ///< \ru Двумерный эллипс. \en Two-dimensional ellipse. - IDS_ITEM_0022, ///< \ru Двумерная парабола. \en Two-dimensional parabola. - IDS_ITEM_0023, ///< \ru Двумерная дуга эллипса. \en Two-dimensional elliptical arc. - IDS_ITEM_0024, ///< \ru Двумерная ломаная. \en Two-dimensional polyline. - IDS_ITEM_0025, ///< \ru Двумерная NURBS кривая. \en Two-dimensional NURBS curve. - IDS_ITEM_0026, ///< \ru Двумерный сплайн Эрмита. \en Two-dimensional Hermite spline. - IDS_ITEM_0027, ///< \ru Двумерный сплайн Безье. \en Two-dimensional Bezier spline. - IDS_ITEM_0028, ///< \ru Двумерный кубический сплайн. \en Two-dimensional cubic spline. - IDS_ITEM_0029, ///< \ru Двумерная репараметризованная кривая. \en Two-dimensional reparametrized curve. - IDS_ITEM_0030, ///< \ru Двумерный контур. \en Two-dimensional contour. - IDS_ITEM_0031, ///< \ru Двумерная косинусоида. \en Two-dimensional cosine curve. - IDS_ITEM_0032, ///< \ru Двумерная точечная кривая. \en Two-dimensional point curve. - IDS_ITEM_0040, ///< \ru Двумерная область. \en Two-dimensional region. - IDS_ITEM_0050, ///< \ru Двумерный объект. \en Two-dimensional object. - IDS_ITEM_0051, ///< \ru Двумерная мультилиния. \en Two-dimensional multiline. - IDS_ITEM_0052, ///< \ru Двумерная кривая на конусе, соответствующая кривой на коническом изгибе плоскости. \en Two-dimensional curve on cone corresponding to a curve on conic bend of a plane. - IDS_ITEM_0053, ///< \ru Двумерная кривая на плоскости, соответствующая кривой на изгибе конуса. \en Two-dimensional curve on plane corresponding to a curve on a bend of cone - IDS_ITEM_0054, ///< \ru Двумерная кривая, координатные функции которой заданы в символьном виде. \en Functionally defined two-dimensional curve. - IDS_ITEM_0055, ///< \ru Образ трехмерной кривой на поверхности при движении по направляющей. \en Image of a three-dimensional curve on surface while moving along the guide curve. - -// \ru Типы полигональных объектов. \en Types of simplified forms of an object. - - IDS_ITEM_0060, ///< \ru Параметр. \en Parameter. - IDS_ITEM_0061, ///< \ru Вершина. \en Vertex. - IDS_ITEM_0062, ///< \ru Нормаль. \en Normal. - IDS_ITEM_0063, ///< \ru Треугольник. \en Triangle. - IDS_ITEM_0064, ///< \ru Четырехугольник. \en Quadrangle. - IDS_ITEM_0071, ///< \ru Полигональный объект на числах double. \en Polygonal object on double data. - IDS_ITEM_0072, ///< \ru Апекс на числах double. \en Apex on double data. - IDS_ITEM_0073, ///< \ru Полигон на числах double. \en Polygon on double data. - IDS_ITEM_0074, ///< \ru Триангуляция на числах double. \en Triangulation on double data. - IDS_ITEM_0075, ///< \ru Полигональный объект на числах float. \en Polygonal object on float data. - IDS_ITEM_0076, ///< \ru Апекс на числах float. \en Apex on float data. - IDS_ITEM_0077, ///< \ru Полигон на числах float. \en Polygon on float data. - IDS_ITEM_0078, ///< \ru Триангуляция на числах float. \en Triangulation on float data. - -// \ru Базовые объекты трехмерной математики. \en Base objects of three-dimensional mathematics. - - IDS_ITEM_0101, ///< \ru Точка. \en Point. - IDS_ITEM_0102, ///< \ru Вектор. \en Vector. - IDS_ITEM_0103, ///< \ru Матрица преобразования. \en Transformation matrix. - IDS_ITEM_0104, ///< \ru Локальная система координат. \en Local coordinate system. - -// \ru Типы функций \en Types of functions - - IDS_ITEM_0111, ///< \ru Kонстантная функция. \en Constant function. - IDS_ITEM_0112, ///< \ru Линейная функция. \en Linear function. - IDS_ITEM_0113, ///< \ru Kубическая функция. \en Cubic function. - IDS_ITEM_0114, ///< \ru Kубическай сплайн-функция. \en Cubic spline function. - IDS_ITEM_0115, ///< \ru Символьная функция. \en Symbolic function. - IDS_ITEM_0116, ///< \ru Степенная функция. \en Power function. - IDS_ITEM_0117, ///< \ru Синус функция. \en Sinus function. - -// \ru Типы трехмерных кривы.х \en Types of three-dimensional curves. - - IDS_ITEM_0201, ///< \ru Кривая. \en Curve. - IDS_ITEM_0202, ///< \ru B-сплайн. \en B-spline. - IDS_ITEM_0213, ///< \ru Прямая линия. \en Straight line. - IDS_ITEM_0214, ///< \ru Отрезок. \en Segment. - IDS_ITEM_0215, ///< \ru Дуга эллипса. \en Elliptic arc. - IDS_ITEM_0216, ///< \ru Дуга окружности. \en Circular arc. - IDS_ITEM_0217, ///< \ru Усеченная кривая. \en Truncated curve. - IDS_ITEM_0218, ///< \ru Эквидистантная кривая. \en Offset curve. - IDS_ITEM_0219, ///< \ru Коническая спираль. \en Conical spiral. - IDS_ITEM_0220, ///< \ru Oкружность. \en Circle. - IDS_ITEM_0221, ///< \ru Эллипс. \en Ellipse. - IDS_ITEM_0222, ///< \ru Парабола. \en Parabola. - IDS_ITEM_0223, ///< \ru Гипербола. \en Hyperbola. - IDS_ITEM_0224, ///< \ru Ломаная линия. \en Polyline. - IDS_ITEM_0225, ///< \ru NURBS кривая. \en NURBS curve. - IDS_ITEM_0226, ///< \ru Сплайн Эрмита. \en Hermite spline. - IDS_ITEM_0227, ///< \ru Сплайн Безье. \en Bezier spline. - IDS_ITEM_0228, ///< \ru Кубический сплайн. \en Cubic spline. - IDS_ITEM_0229, ///< \ru Репараметризованная кривая. \en Reparametrized curve. - IDS_ITEM_0231, ///< \ru Плоская кривая. \en Plane curve. - IDS_ITEM_0232, ///< \ru Спираль с переменым радиусом. \en Spiral with variable radius. - IDS_ITEM_0233, ///< \ru Спираль с криволинейной осью. \en Spiral with a curved axis. - IDS_ITEM_0234, ///< \ru Кривая-мостик. \en Bridge curve. - IDS_ITEM_0235, ///< \ru Символьная кривая. \en Functionally defined curve. - IDS_ITEM_0236, ///< \ru Кривая на поверхности. \en Curve on a surface. - IDS_ITEM_0237, ///< \ru Линия пересечения поверхностей. \en Intersection curve of surfaces. - IDS_ITEM_0238, ///< \ru Контур на поверхности. \en Contour on a surface. - IDS_ITEM_0239, ///< \ru Контур на плоскости. \en Contour on a plane. - IDS_ITEM_0240, ///< \ru Контур. \en Contour. - IDS_ITEM_0241, ///< \ru Проекционная кривая. \en Projection curve. - IDS_ITEM_0242, ///< \ru Силуэтная кривая. \en Silhouette curve. - IDS_ITEM_0243, ///< \ru Кривая сопряжения кривых. \en Curve of curves conjugation. - IDS_ITEM_0244, ///< \ru Кривая производных поверхности Кунса. \en Curve of Coons surface derivetives. - IDS_ITEM_0249, ///< \ru Направляющая кривая. \en Guide curve. - IDS_ITEM_0250, ///< \ru Кривая пересечения. \en Intersection curve. - -// \ru Типы параметрических поверхностей. \en Types of parametric surfaces. - - IDS_ITEM_0301, ///< \ru Поверхность. \en Surface. - IDS_ITEM_0302, ///< \ru Поверхность заметания. \en Sweep surface. - IDS_ITEM_0303, ///< \ru Поверхность сдвига. \en Motion surface. - IDS_ITEM_0304, ///< \ru Поверхность выдавливания. \en Extrusion surface. - IDS_ITEM_0305, ///< \ru Поверхность вращения. \en Revolution surface. - IDS_ITEM_0306, ///< \ru Линейчатая поверхность. \en Ruled surface. - IDS_ITEM_0307, ///< \ru Поверхность по кривой и точке. \en Surface defined by a curve and a point. - IDS_ITEM_0308, ///< \ru Четырехугольная поверхность. \en Quadrangular surface. - IDS_ITEM_0309, ///< \ru Треугольная поверхность. \en Triangular surface. - IDS_ITEM_0310, ///< \ru Поверхность движения с доворотом. \en Sweep with guide curve surface with rotating ends. - IDS_ITEM_0311, ///< \ru Поверхность на семействе кривых и напрвляющей. \en Loft surface with guide curve. - IDS_ITEM_0312, ///< \ru Спиральная поверхность. \en Spiral surface. - IDS_ITEM_0313, ///< \ru Цилиндрически согнутая поверхность. \en Cylindrically bent surface. - IDS_ITEM_0314, ///< \ru Цилиндрически разогнутая поверхность. \en Cylindrically unbent surface. - IDS_ITEM_0315, ///< \ru Конически согнутая поверхность. \en Conically bent surface. - IDS_ITEM_0316, ///< \ru Конически разогнутая поверхность. \en Conically unbent surface. - IDS_ITEM_0317, ///< \ru Поверхность заметания с изменением образующей. \en Sweep surface with changin generatin. - IDS_ITEM_0319, ///< \ru Плоскость. \en Plane. - IDS_ITEM_0320, ///< \ru Сферическая поверхность. \en Spherical surface. - IDS_ITEM_0321, ///< \ru Тороидальная поверхность. \en Toroidal surface. - IDS_ITEM_0322, ///< \ru Цилиндрическая поверхность. \en Cylindrical surface. - IDS_ITEM_0323, ///< \ru Коническая поверхность. \en Conical surface. - IDS_ITEM_0325, ///< \ru NURBS поверхность. \en NURBS surface. - IDS_ITEM_0326, ///< \ru Треугольная NURBS поверхность. \en Triangular NURBS surface. - IDS_ITEM_0327, ///< \ru Поверхность Безье. \en Bezier surface. - IDS_ITEM_0328, ///< \ru Эквидистантная поверхность. \en Offset surface. - IDS_ITEM_0329, ///< \ru Деформированная поверхность. \en Deformed surface. - IDS_ITEM_0330, ///< \ru Поверхность Грегори. \en Gregory Surface. - IDS_ITEM_0331, ///< \ru Поверхность соединения. \en Joint surface. - IDS_ITEM_0332, ///< \ru Поверхность объединения. \en Join surface. - IDS_ITEM_0333, ///< \ru Поверхность на трех кривых. \en Surface based on three curves. - IDS_ITEM_0334, ///< \ru Поверхность на четырех кривых. \en Surface based on four curves. - IDS_ITEM_0335, ///< \ru Поверхность-фаска. \en Chamfer surface. - IDS_ITEM_0336, ///< \ru Поверхность скругления. \en Fillet surface. - IDS_ITEM_0337, ///< \ru Переменная поверхность скругления. \en Variable fillet surface. - IDS_ITEM_0338, ///< \ru Поверхность на семействе кривых. \en Lofted surface. - IDS_ITEM_0339, ///< \ru Поверхность на сетке кривых. \en Surface defined on a mesh of curves. - IDS_ITEM_0340, ///< \ru Поверхность скругления по кромке. \en Surface of fillet by border. - IDS_ITEM_0341, ///< \ru Поверхность по замкнутому контуру. \en Surface on closed contour. - IDS_ITEM_0342, ///< \ru Плазовая поверхность. \en Spiling surface. - IDS_ITEM_0343, ///< \ru Поверхность Кунса. \en Coons surface. - IDS_ITEM_0345, ///< \ru Поверхность на сетке точек. \en Surface based on a point grid. - IDS_ITEM_0346, ///< \ru Треугольная поверхность Безье. \en Triangular Bezier surface. - IDS_ITEM_0349, ///< \ru Усеченная контурами поверхность. \en Curve bounded surface. - IDS_ITEM_0350, ///< \ru Поверхность-копия. \en Copy surface. - IDS_ITEM_0351, ///< \ru Поверхность соединения. \en Joint surface. - IDS_ITEM_0352, ///< \ru Поверхность полного скругления. \en Full fillet surface. - IDS_ITEM_0353, ///< \ru Поверхность заметания с масштабированием. \en Swept surface with scaling. - -// \ru Типы тел \en Types of solids - - IDS_ITEM_0401, ///< \ru Тело. \en Solid. - IDS_ITEM_0402, ///< \ru Оболочка. \en Shell. - IDS_ITEM_0403, ///< \ru Проволочный каркас. \en Wireframe. - IDS_ITEM_0404, ///< \ru Точечный каркас. \en Point frame. - IDS_ITEM_0405, ///< \ru Коллекция элементов. \en Collection of elements. - -// \ru Типы строителей. \en Types of creators. - - IDS_ITEM_0501, ///< \ru Журнал построения. \en Build log. - IDS_ITEM_0502, ///< \ru Шар. \en Sphere. - IDS_ITEM_0503, ///< \ru Тор. \en Torus. - IDS_ITEM_0504, ///< \ru Цилиндр. \en Cylinder. - IDS_ITEM_0505, ///< \ru Конус. \en Cone. - IDS_ITEM_0506, ///< \ru Блок. \en Block. - IDS_ITEM_0507, ///< \ru Клин. \en Wedge. - IDS_ITEM_0508, ///< \ru Призма. \en Prism. - IDS_ITEM_0509, ///< \ru Пирамида. \en Pyramid. - IDS_ITEM_0510, ///< \ru Твёрдое тело. \en Solid. - - IDS_ITEM_0515, ///< \ru Объединение оболочек. \en Shells union. - IDS_ITEM_0516, ///< \ru Пересечение оболочек. \en Shells intersection. - IDS_ITEM_0517, ///< \ru Разность оболочек. \en Shells subtraction. - - IDS_ITEM_0520, ///< \ru Отверстие. \en Hole. - IDS_ITEM_0521, ///< \ru Карман/Бобышка. \en Pocket/Boss. - IDS_ITEM_0522, ///< \ru Паз. \en Groove. - IDS_ITEM_0523, ///< \ru Заплатка. \en Patch. - IDS_ITEM_0524, ///< \ru Тонкая оболочка. \en Thin shell. - - IDS_ITEM_0526, ///< \ru Оболочка на семействе кривых. \en Shell defined by a set of curves. - IDS_ITEM_0527, ///< \ru Продолженная оболочка. \en Extended shell. - IDS_ITEM_0528, ///< \ru Эквидистантная оболочка. \en Offset shell. - IDS_ITEM_0529, ///< \ru Срединная оболочка. \en Median shell. - - IDS_ITEM_0531, ///< \ru Булево объединение тел. \en Boolean union of solids. - IDS_ITEM_0532, ///< \ru Булево пересечение тел. \en Boolean intersection of solids. - IDS_ITEM_0533, ///< \ru Булевa разность тел. \en Boolean subtraction of solids. - IDS_ITEM_0534, ///< \ru Разрезанное тело. \en Cut solid. - IDS_ITEM_0535, ///< \ru Фаски ребер. \en Edges chamfers. - IDS_ITEM_0536, ///< \ru Скругление ребер. \en Edges fillets. - IDS_ITEM_0537, ///< \ru Симметричное тело. \en Symmetric solid. - IDS_ITEM_0538, ///< \ru Оболочечное тело. \en Thin shell solid. - IDS_ITEM_0539, ///< \ru Тело приданием толщины. \en Solid of thickening. - IDS_ITEM_0540, ///< \ru Оболочка с удалёнными гранями. \en Shell with removed faces. - IDS_ITEM_0541, ///< \ru Коробчатое тело. \en Box-like solid. - IDS_ITEM_0542, ///< \ru Кинематическое тело. \en Sweeping solid. - IDS_ITEM_0543, ///< \ru Тело заметания. \en Swept solid. - IDS_ITEM_0544, ///< \ru Тело выдавливания. \en Extrusion solid. - IDS_ITEM_0545, ///< \ru Тело вращения. \en Revolution solid. - IDS_ITEM_0546, ///< \ru Тело по сечениям. \en Loft solid. - IDS_ITEM_0547, ///< \ru Простое тело. \en Simple solid. - IDS_ITEM_0548, ///< \ru Ребро жесткости тела. \en Rib of a solid. - IDS_ITEM_0549, ///< \ru Набор тел. \en Set of solids. - IDS_ITEM_0550, ///< \ru Часть набора тел. \en Set of solids part. - IDS_ITEM_0551, ///< \ru Клон граней тела. \en Solid's faces drafting. - IDS_ITEM_0552, ///< \ru Разбивка граней тела. \en Splitting of solid's faces. - IDS_ITEM_0553, ///< \ru Сшитое из оболочек тело. \en Stitched solid. - IDS_ITEM_0554, ///< \ru Сшитая из оболочек оболочка. \en Shell stitched from shells. - IDS_ITEM_0555, ///< \ru Оболочка из NURBS-поверхностей. \en Shell from NURBS-surfaces. - IDS_ITEM_0556, ///< \ru Трансформированное тело. \en Transformed solid. - IDS_ITEM_0557, ///< \ru Модифицированное тело. \en Modified solid. - IDS_ITEM_0558, ///< \ru Оболочка из линейчатых поверхностей. \en Shell from ruled surfaces. - IDS_ITEM_0559, ///< \ru Усеченная оболочка. \en Truncated shell. - IDS_ITEM_0560, ///< \ru Оболочка соединения. \en Joint shell. - IDS_ITEM_0561, ///< \ru Тело с восстановленными боковыми рёбрами. \en Solid with restored lateral edges. - IDS_ITEM_0562, ///< \ru Объединение с кинематическим телом. \en Union with a sweeping solid. - IDS_ITEM_0563, ///< \ru Модифицированное тело NURBS-поверхностями. \en Solid modified with NURBS-surfaces. - IDS_ITEM_0564, ///< \ru Объединение с телом выдавливания. \en Union with an extrusion solid. - IDS_ITEM_0565, ///< \ru Объединение с телом вращения. \en Union with a revolution solid. - IDS_ITEM_0566, ///< \ru Объединение с телом по сечениям. \en Union with a lofted solid. - IDS_ITEM_0567, ///< \ru Объединение с простым телом. \en Union with an elementary solid. - IDS_ITEM_0568, ///< \ru Модифицированная NURBS-поверхность грани. \en Modified NURBS-surface of the face. - IDS_ITEM_0569, ///< \ru Объединение с набором тел. \en Union with a solid set. - IDS_ITEM_0570, ///< \ru Оболочка грани соединения. \en Joint face shell. - IDS_ITEM_0571, ///< \ru Скругление граней. \en Faces fillet. - IDS_ITEM_0572, ///< \ru Разность с кинематическим телом. \en Subtraction with a sweeping solid. - IDS_ITEM_0573, ///< \ru Удаление результата операции. \en Delete the result of the operation. - IDS_ITEM_0574, ///< \ru Разность с телом выдавливания. \en Subtraction with an extrusion solid. - IDS_ITEM_0575, ///< \ru Разность с телом вращения. \en Subtraction with a revolution solid. - IDS_ITEM_0576, ///< \ru Разность с телом по сечениям. \en Subtraction with a lofted solid. - IDS_ITEM_0577, ///< \ru Разность с простым телом. \en Subtraction with an elementary solid. - IDS_ITEM_0578, ///< \ru Упрощение развёртки. \en The flat pattern simplification. - IDS_ITEM_0579, ///< \ru Разность с набором тел. \en Subtraction with a set of solids. - IDS_ITEM_0780, ///< \ru Вывернутое тело. \en Reversed solid. - IDS_ITEM_0581, ///< \ru Сгиб нелистового тела. \en Bend of a non-sheet solid. - IDS_ITEM_0582, ///< \ru Пересечение с кинематическим телом. \en Intersection with a sweeping solid. - IDS_ITEM_0583, ///< \ru Сферическая штамповка. \en Spherical stamping. - IDS_ITEM_0584, ///< \ru Пересечение с телом выдавливания. \en Intersection with an extrusion solid. - IDS_ITEM_0585, ///< \ru Пересечение с телом вращения. \en Intersection with a revolution solid. - IDS_ITEM_0586, ///< \ru Пересечение с телом по сечениям. \en Intersection with a lofted solid. - IDS_ITEM_0587, ///< \ru Пересечение с простым телом. \en Intersection with an elementary solid. - IDS_ITEM_0588, ///< \ru Обечайка. \en Ruled shell. - IDS_ITEM_0589, ///< \ru Пересечение с набором тел. \en Intersection with a set of solids. - IDS_ITEM_0590, ///< \ru Оболочка по сети кривых. \en Shell defined by a mesh of curves. - IDS_ITEM_0591, ///< \ru Комбинированный сгиб. \en Combined bend. - IDS_ITEM_0592, ///< \ru Кинематическая оболочка. \en Sweep with guide curve shell. - IDS_ITEM_0593, ///< \ru Жалюзи. \en Jalousie. - IDS_ITEM_0594, ///< \ru Оболочка выдавливания. \en Extrusion shell. - IDS_ITEM_0595, ///< \ru Оболочка вращения. \en Revolution shell. - IDS_ITEM_0596, ///< \ru Оболочка по сечениям. \en Loft shell. - IDS_ITEM_0597, ///< \ru Тонкая оболочка. \en Thin shell. - IDS_ITEM_0598, ///< \ru Разрезанная оболочка. \en Cut shell. - IDS_ITEM_0599, ///< \ru Буртик. \en Bead. - IDS_ITEM_0600, ///< \ru Сгиб/разгиб листового тела. \en Bend/unbend of a sheet solid. - IDS_ITEM_0601, ///< \ru Сгиб листового тела по отрезку. \en Bend of a sheet solid by a segment. - IDS_ITEM_0602, ///< \ru Сгиб листового тела по рёбрам. \en Bend of a sheet solid along edges. - IDS_ITEM_0603, ///< \ru Замыкание угла листового тела. \en Closure of a sheet solid corner. - IDS_ITEM_0604, ///< \ru Листовое тело. \en Sheet solid. - IDS_ITEM_0605, ///< \ru Пластина листового тела. \en Sheet solid plate. - IDS_ITEM_0606, ///< \ru Вырез листового тела. \en Cut of a sheet solid. - IDS_ITEM_0607, ///< \ru Пересечение листового тела. \en Sheet solid intersection. - IDS_ITEM_0608, ///< \ru Подсечка листового тела. \en Jog of a sheet solid. - IDS_ITEM_0609, ///< \ru Штамповка. \en Stamping. - -// \ru Способы построения оболочек. \en Shells construction methods. - - IDS_ITEM_0610, ///< \ru Оболочка. \en Shell. - IDS_ITEM_0611, ///< \ru Оболочка на базе поверхности. \en Shell based on a surface. - - IDS_ITEM_0614, ///< \ru Оболочка тела. \en Shell. - IDS_ITEM_0615, ///< \ru Журнал построения. \en Build log. - IDS_ITEM_0616, ///< \ru Атрибуты объекта. \en Object Attributes. - - IDS_ITEM_0620, ///< \ru Оболочка тела. \en Shell. - IDS_ITEM_0621, ///< \ru Ориентированное ребро цикла. \en Oriented edge of a loop. - IDS_ITEM_0622, ///< \ru Цикл грани. \en Face loop. - IDS_ITEM_0623, ///< \ru Грань оболочки. \en Face of a shell. - IDS_ITEM_0624, ///< \ru Вершина. \en Vertex. - IDS_ITEM_0625, ///< \ru Ребро оболочки. \en Edge of a shell. - IDS_ITEM_0626, ///< \ru Ребро каркаса. \en Edge of a frame. - - IDS_ITEM_0627, ///< \ru Вершина. \en Vertex. - - IDS_ITEM_0628, ///< \ru Разделенная оболочка. \en Divided shell. - -// \ru Способы построения проекций тела\оболочки. \en Solid/shell projections creation methods. - - IDS_ITEM_0650, ///< \ru Проекция тела. \en Solid projection. - IDS_ITEM_0651, ///< \ru Разрез тела. \en Solid cutting. - IDS_ITEM_0652, ///< \ru Сечение тела. \en Solid section. - - IDS_ITEM_0653, ///< \ru Размножение тела. \en Duplication of solids. - -// \ru Вспомогательный объект. \en The helper object. - - IDS_ITEM_0669, ///< \ru Вспомогательный объект. \en The helper object. - -// \ru Резьба. \en A thread. - - IDS_ITEM_0670, ///< \ru Резьба. \en Thread. - -// \ru Обозначение \en Notation - - IDS_ITEM_0671, ///< \ru Условное обозначение. \en Symbolic notation. - -// \ru Объекты. \en Objects. - - IDS_ITEM_0700, ///< \ru Геометрический объект. \en Geometric object. - IDS_ITEM_0701, ///< \ru Переменная уравнения. \en Equation variable. - IDS_ITEM_0702, ///< \ru Объект на плоскости. \en Object on a plane. - IDS_ITEM_0703, ///< \ru Объект в пространстве. \en Object in space. - IDS_ITEM_0704, ///< \ru Объект модели. \en Model object. - IDS_ITEM_0705, ///< \ru Сборочная единица. \en Assembly unit. - IDS_ITEM_0706, ///< \ru Вспомогательный объект. \en Auxiliary object. - IDS_ITEM_0707, ///< \ru Вставка объекта. \en Object instance. - IDS_ITEM_0708, ///< \ru Количество элементов. \en Number of elements. - IDS_ITEM_0709, ///< \ru Геометрическая модель. \en Geometric model. - -// \ru Атрибуты \en Attributes - - IDS_ITEM_0729, ///< \ru Атрибуты модели. \en Model attributes. - IDS_ITEM_0730, ///< \ru Поставщик атрибутов. \en Attributes provider. - IDS_ITEM_0731, ///< \ru Атрибуты объекта. \en Object attributes. - IDS_ITEM_0732, ///< \ru Атрибут. \en Attribute. - IDS_ITEM_0733, ///< \ru Имя примитива. \en Primitive name. - IDS_ITEM_0734, ///< \ru Поведение атрибутов. \en Attributes behavior. - - IDS_ITEM_0751, ///< \ru Механические характеристики. \en Mechanical properties. - IDS_ITEM_0754, ///< \ru Деформации. \en Strains. - - IDS_ITEM_0761, ///< \ru Исполнение (вариант реализации модели). \en Embodiment (variant of model implementation). - IDS_ITEM_0762, ///< \ru Количество u-линий и v-линий отрисовочной сетки. \en The number of u-mesh and v-mesh lines. - IDS_ITEM_0763, ///< \ru Плотность. \en Density. - IDS_ITEM_0764, ///< \ru Цвет. \en Color. - IDS_ITEM_0765, ///< \ru Толщина. \en Thickness. - IDS_ITEM_0766, ///< \ru Стиль. \en Style. - IDS_ITEM_0767, ///< \ru Визуальные свойства. \en Visual properties. - IDS_ITEM_0768, ///< \ru Идентификатор. \en Identifier. - IDS_ITEM_0769, ///< \ru Селектированность. \en Selectivity. - IDS_ITEM_0770, ///< \ru Видимость. \en Visibility. - IDS_ITEM_0771, ///< \ru Измененность. \en Modification. - IDS_ITEM_0772, ///< \ru Топологическое имя. \en Topological name. - IDS_ITEM_0773, ///< \ru Якорь. \en Anchor. - IDS_ITEM_0774, ///< \ru Геометрический атрибут. \en Geometric attribute. - IDS_ITEM_0775, ///< \ru Метка времени обновления. \en Label of update time. - IDS_ITEM_0776, ///< \ru Уникальность ключей. \en Keys uniqueness. - IDS_ITEM_0777, ///< \ru Имя объекта в модели. \en Name of object in the model. - IDS_ITEM_0778, ///< \ru Данные об изделии. \en Product data. - IDS_ITEM_0779, ///< \ru Атрибут ребра жесткости листового тела. \en Attribute of stamp rib of sheet solid. - - IDS_ITEM_0782, ///< \ru Атрибут пользовательский. \en Custom attribute. - IDS_ITEM_0783, ///< \ru Атрибут обобщенный. \en Generalized attribute. - IDS_ITEM_0784, ///< \ru Атрибут булев. \en Boolean attribute. - IDS_ITEM_0785, ///< \ru Атрибут целочисленный (32-битный). \en (32 bit ) Integer attribute. - IDS_ITEM_0786, ///< \ru Атрибут действительный. \en Real attribute. - IDS_ITEM_0787, ///< \ru Атрибут строковый. \en String attribute. - IDS_ITEM_0788, ///< \ru Атрибут элементарный. \en Elementary attribute. - IDS_ITEM_0789, ///< \ru Пояснение. \en Prompt. - IDS_ITEM_0790, ///< \ru Атрибут int64. \en Int64 attribute. - IDS_ITEM_0791, ///< \ru Атрибут бинарный. \en Binary attribute. - -// \ru Сообщения. \en Messages. - - IDS_ITEM_0900, ///< \ru ! Ошибка !. \en ! Error ! - IDS_ITEM_0901, ///< \ru Остановлено. \en Stopped. - IDS_ITEM_0902, ///< \ru Пропущено. \en Missed. - -// \ru Состав объектов \en Structure of objects - - IDS_PROP_0000, ///< \ru Пусто. \en Empty. - - IDS_PROP_0001, ///< \ru Кривая на плоскости. \en Curve on a plane. - IDS_PROP_0002, ///< \ru Параметр кривой. \en Parameter of a curve. - IDS_PROP_0003, ///< \ru Кривая 1 на плоскости. \en Curve 1 on the plane. - IDS_PROP_0004, ///< \ru Кривая 2 на плоскости. \en Curve 2 on the plane. - IDS_PROP_0005, ///< \ru Параметр кривой 1. \en Parameter of curve 1. - IDS_PROP_0006, ///< \ru Параметр кривой 2. \en Parameter of curve 2. - IDS_PROP_0007, ///< \ru Начальная точка. \en Start point. - IDS_PROP_0008, ///< \ru Направление. \en Direction. - IDS_PROP_0009, ///< \ru Конечная точка. \en End point. - IDS_PROP_0014, ///< \ru Начальный параметр усечения. \en Start parameter of truncation. - IDS_PROP_0015, ///< \ru Конечный параметр усечения. \en End parameter of truncation. - IDS_PROP_0016, ///< \ru Совпадение направления. \en Coincidence of direction. - IDS_PROP_0017, ///< \ru Циклическая частота. \en Cyclic frequency. - IDS_PROP_0018, ///< \ru Начальная фаза (град). \en Initial phase (degrees). - IDS_PROP_0019, ///< \ru Амплитуда. \en Amplitude. - IDS_PROP_0020, ///< \ru Перевернуть направление. \en Reverse the direction. - IDS_PROP_0021, ///< \ru Неподвижная точка. \en Fixed point. - IDS_PROP_0022, ///< \ru Использовать точку. \en Use a point. - IDS_PROP_0023, ///< \ru Равномерное преобразование. \en Uniform transformation. - IDS_PROP_0024, ///< \ru Точность. \en Tolerance. - IDS_PROP_0025, ///< \ru Начальное значение. \en Start value. - IDS_PROP_0026, ///< \ru Коэффициент усиления. \en Scale gain. - IDS_PROP_0027, ///< \ru Амплитуда. \en Amplitude. - IDS_PROP_0028, ///< \ru Сдвиг параметра. \en Parameter shift. - IDS_PROP_0029, ///< \ru Степень возведения. \en Exponent parameter. - IDS_PROP_0030, ///< \ru Циклическая частота. \en Frequency. - IDS_PROP_0031, ///< \ru Эквидистантное смещение. \en The offset range. - IDS_PROP_0032, ///< \ru Тип кривой. \en Type of curve. - - IDS_PROP_0101, ///< \ru Координата X. \en Coordinate X. - IDS_PROP_0102, ///< \ru Координата Y. \en Coordinate Y. - IDS_PROP_0103, ///< \ru Координата Z. \en Coordinate Z. - IDS_PROP_0104, ///< \ru Матрица. \en Matrix. - IDS_PROP_0107, ///< \ru Угол с осью X. \en Angle with axis X. - IDS_PROP_0108, ///< \ru Угол с осью Y. \en Angle with axis Y. - IDS_PROP_0109, ///< \ru Угол с осью Z. \en Angle with axis Z. - IDS_PROP_0110, ///< \ru Точка. \en Point. - IDS_PROP_0111, ///< \ru Компонента 1.X. \en Component 1.X. - IDS_PROP_0112, ///< \ru Компонента 1.Y. \en Component 1.Y. - IDS_PROP_0113, ///< \ru Компонента 1.Z. \en Component 1.Z. - IDS_PROP_0114, ///< \ru Компонента 2.X. \en Component 2.X. - IDS_PROP_0115, ///< \ru Компонента 2.Y. \en Component 2.Y. - IDS_PROP_0116, ///< \ru Компонента 2.Z. \en Component 2.Z. - IDS_PROP_0117, ///< \ru Компонента 3.X. \en Component 3.X. - IDS_PROP_0118, ///< \ru Компонента 3.Y. \en Component 3.Y. - IDS_PROP_0119, ///< \ru Компонента 3.Z. \en Component 3.Z. - IDS_PROP_0120, ///< \ru Вектор. \en Vector. - IDS_PROP_0121, ///< \ru Сдвиг по X. \en Shift by X. - IDS_PROP_0122, ///< \ru Сдвиг по Y. \en Shift by Y. - IDS_PROP_0123, ///< \ru Сдвиг по Z. \en Shift by Z. - IDS_PROP_0124, ///< \ru Ось X. \en Axis X. - IDS_PROP_0125, ///< \ru Ось Y. \en Axis Y. - IDS_PROP_0126, ///< \ru Ось Z. \en Axis Z. - IDS_PROP_0127, ///< \ru Длина оси X. \en Length of axis X. - IDS_PROP_0128, ///< \ru Длина оси Y. \en Length of axis Y. - IDS_PROP_0129, ///< \ru Длина оси Z. \en Length of axis Z. - IDS_PROP_0130, ///< \ru Центр. \en Center. - IDS_PROP_0131, ///< \ru Левая система. \en Left system. - IDS_PROP_0132, ///< \ru Прямоугольная. \en Rectangular. - IDS_PROP_0133, ///< \ru Базовая точка. \en Base point. - IDS_PROP_0134, ///< \ru Строка. \en String. - IDS_PROP_0135, ///< \ru Шрифт. \en Font. - IDS_PROP_0136, ///< \ru Позиция начала. \en Start position. - IDS_PROP_0137, ///< \ru Высота. \en Height. - IDS_PROP_0138, ///< \ru Сужение. \en Taper. - IDS_PROP_0139, ///< \ru Угол наклона. \en Slope angle. - IDS_PROP_0140, ///< \ru Радиус. \en Radius. - IDS_PROP_0141, ///< \ru Масштаб по X. \en Scale by X. - IDS_PROP_0142, ///< \ru Масштаб по Y. \en Scale by Y. - IDS_PROP_0143, ///< \ru Масштаб по Z. \en Scale by Z. - IDS_PROP_0144, ///< \ru Функция масштабирования. \en The function of scaling. - IDS_PROP_0145, ///< \ru Функция вращения. \en The function of rotation. - - IDS_PROP_0150, ///< \ru Угол. \en Angle. - IDS_PROP_0151, ///< \ru Шаг. \en Step. - IDS_PROP_0152, ///< \ru Смещение зазора. \en Gap displacement. - IDS_PROP_0153, ///< \ru Перемещение. \en Translation. - IDS_PROP_0154, ///< \ru Вращение. \en Rotation. - IDS_PROP_0155, ///< \ru Общий масштаб. \en Common scale. - IDS_PROP_0156, ///< \ru Зеркальность. \en Specularity. - IDS_PROP_0157, ///< \ru Только ортогональность. \en Orthogonality only. - IDS_PROP_0158, ///< \ru Объект общего вида. \en General object. - IDS_PROP_0159, ///< \ru Перспектива. \en Perspective. - IDS_PROP_0160, ///< \ru Локальная система координат. \en Local coordinate system. - IDS_PROP_0161, ///< \ru Начальное значение. \en Start value. - IDS_PROP_0162, ///< \ru Конечное значение. \en End value. - IDS_PROP_0163, ///< \ru Значение. \en Value. - IDS_PROP_0164, ///< \ru Функция изменения радиусов. \en Radius function. - IDS_PROP_0165, ///< \ru Функция изменения веса. \en Weight function. - IDS_PROP_0166, ///< \ru Функция. \en Function. - IDS_PROP_0167, ///< \ru Минимум. \en Minimum. - IDS_PROP_0168, ///< \ru Максимум. \en Maximum - - IDS_PROP_0169, ///< \ru Продлевать вверх?. \en Extend upwards?. - IDS_PROP_0170, ///< \ru Поворот оси вокруг нормали. \en Rotation of axis about the normal. - IDS_PROP_0171, ///< \ru Угол между осью и нормалью. \en Angle between the axis and the normal. - IDS_PROP_0172, ///< \ru Диаметр головки. \en Cap diameter. - IDS_PROP_0173, ///< \ru Глубина под головку. \en Depth for a cap. - IDS_PROP_0174, ///< \ru Угол фаски под головку. \en Chamfer angle for a cap. - IDS_PROP_0175, ///< \ru Диаметр отверстия под резьбу. \en Diameter of hole for a cap. - IDS_PROP_0176, ///< \ru Глубина отверстия под резьбу. \en Depth of hole for a cap. - IDS_PROP_0177, ///< \ru Угол конусности отверстия. \en Taper angle of hole. - IDS_PROP_0178, ///< \ru Угол раствора конца отверстия. \en Apical angle of hole end. - IDS_PROP_0179, ///< \ru Тип отверстия. \en Hole type. - IDS_PROP_0180, ///< \ru Способ модификации. \en Method of modification. - - IDS_PROP_0181, ///< \ru Тип(true-бобышка, false- карман). \en Type(true-boss, false- pocket). - IDS_PROP_0182, ///< \ru Тип. \en Type. - IDS_PROP_0183, ///< \ru Ширина. \en Width. - IDS_PROP_0184, ///< \ru Ширина. \en Width. - IDS_PROP_0185, ///< \ru Способ. \en Method. - IDS_PROP_0186, ///< \ru Новая грань. \en New face. - - IDS_PROP_0188, ///< \ru Направление (вниз/вверх). \en Direction (down/up). - IDS_PROP_0189, ///< \ru Радиус дуги. \en Arc radius. - -// \ru Параметры. \en Parameters. - - IDS_PROP_0201, ///< \ru Объект на числах double. \en Object on double data. - IDS_PROP_0202, ///< \ru Объект на числах float. \en Object on float data. - IDS_PROP_0203, ///< \ru Кривая 1. \en Curve 1. - IDS_PROP_0204, ///< \ru Кривая 2. \en Curve 2. - IDS_PROP_0205, ///< \ru Кривая 3. \en Curve 3. - IDS_PROP_0206, ///< \ru Кривая 4. \en Curve 4 - IDS_PROP_0207, ///< \ru Начальная точка. \en Start point. - IDS_PROP_0208, ///< \ru Направляющий вектор. \en Direction vector. - IDS_PROP_0209, ///< \ru Конечная точка. \en End point. - IDS_PROP_0210, ///< \ru Нормаль к плоскости.\en Normal to surface. - IDS_PROP_0211, ///< \ru Первая полуось. \en First semiaxis. - IDS_PROP_0212, ///< \ru Вторая полуось. \en Second semiaxis. - IDS_PROP_0213, ///< \ru Фокусное расстояние. \en Focal distance. - IDS_PROP_0214, ///< \ru Параметр min. \en Parameter min. - IDS_PROP_0215, ///< \ru Параметр max. \en Parameter max. - IDS_PROP_0216, ///< \ru Действительная полуось. \en Real semiaxis. - IDS_PROP_0217, ///< \ru Мнимая полуось. \en Imaginary semiaxis. - IDS_PROP_0218, ///< \ru Приращение начального параметра. \en Increment of start parameter. - IDS_PROP_0219, ///< \ru Приращение конечного параметра. \en Increment of end parameter. - IDS_PROP_0220, ///< \ru Замкнутость. \en Closedness. - IDS_PROP_0221, ///< \ru Порядок. \en Order. - IDS_PROP_0222, ///< \ru Количество точек. \en Number of points. - IDS_PROP_0223, ///< \ru Точка. \en Point. - IDS_PROP_0224, ///< \ru Начальный параметр. \en Start parameter. - IDS_PROP_0225, ///< \ru Производная параметра. \en Derivative of parameter. - IDS_PROP_0226, ///< \ru Параметрическая длина. \en Parametric length. - IDS_PROP_0227, ///< \ru Положительное направление. \en Positive direction. - IDS_PROP_0228, ///< \ru Вес. \en Weight. - // IDS_PROP_0229, ///< \ru Касание. \en Tangency. (МА 2019 Лишнее?) - IDS_PROP_0230, ///< \ru Расстояние. \en Distance. - IDS_PROP_0232, ///< \ru Число кривых контура. \en Number of curves of contour. - IDS_PROP_0233, ///< \ru Кривая. \en Curve. - IDS_PROP_0234, ///< \ru Начальный параметр кривой. \en Start parameter of a curve. - IDS_PROP_0235, ///< \ru Конечный параметр кривой. \en End parameter of a curve. - IDS_PROP_0236, ///< \ru Число узлов. \en Number of knots. - IDS_PROP_0237, ///< \ru Значение узла. \en Knot value. - IDS_PROP_0238, ///< \ru Вторая производная в точке. \en Second derivative at a point. - IDS_PROP_0239, ///< \ru Производная. \en Derivative. - IDS_PROP_0240, ///< \ru Кривая. \en Curve. - IDS_PROP_0241, ///< \ru Зависимость по X. \en Dependency by X. - IDS_PROP_0242, ///< \ru Зависимость по Y. \en Dependency by Y. - IDS_PROP_0243, ///< \ru Зависимость по Z. \en Dependency by Z. - IDS_PROP_0244, ///< \ru Расширение minPar. \en Extension minPar. - IDS_PROP_0245, ///< \ru Расширение maxPar. \en Extension maxPar. - IDS_PROP_0246, ///< \ru Количество сплайнов. \en Number of splines. - IDS_PROP_0250, ///< \ru Базовая кривая. \en Base curve. - IDS_PROP_0260, ///< \ru Двумерная кривая. \en Two-dimensional curve. - IDS_PROP_0263, ///< \ru Угол между OX плоскости и прямой касания ее с конусом. \en Angle of OX axis of a plane and the line of its tangency with a cone. - IDS_PROP_0264, ///< \ru Расстояние от листовой грани до нейтрального слоя. \en Distance from a sheet face to the neutral layer. - IDS_PROP_0265, ///< \ru Угол образующей конуса, касательной к плоскости при разгибе. \en Angle of a cone generatrix tangent to the plane while unbending. - IDS_PROP_0266, ///< \ru Количество образующих кривых. \en Number of generating lines. - IDS_PROP_0267, ///< \ru Объект модифицирован. \en Object is modified. - IDS_PROP_0268, ///< \ru Количество нормалей. \en Number of normals. - IDS_PROP_0269, ///< \ru Нормаль. \en Normal. - IDS_PROP_0270, ///< \ru Аппроксимационная кривая. \en Approximation curve. - - IDS_PROP_0271, ///< \ru Удаление выбранных граней. \en Remove selected faces. - IDS_PROP_0272, ///< \ru Создание тела из выбранных граней. \en Solid creation by selected faces. - IDS_PROP_0273, ///< \ru Перемещение выбранных граней. \en Move selected faces. - IDS_PROP_0274, ///< \ru Смещение выбранных граней по нормали. \en Offset selected faces. - IDS_PROP_0275, ///< \ru Изменение радиусов выбранных скруглений. \en Change selected fillets. - IDS_PROP_0276, ///< \ru Замена выбранных граней деформируемыми. \en Replace selected faces by deformed. - IDS_PROP_0277, ///< \ru Удаление выбранных скруглений. \en Remove selected features. - IDS_PROP_0278, ///< \ru Слияние вершин выбранных ребер. \en Merging vertices of selected edges. - - IDS_PROP_0282, ///< \ru Вектор модификации. \en The vector of modification. - IDS_PROP_0283, ///< \ru Количество модифицированных граней. \en Number of modified faces. - IDS_PROP_0284, ///< \ru Положение срединной оболочки тела. \en Position of median shell. - IDS_PROP_0285, ///< \ru Минимальное расстояние между гранями. \en Minimal equidistation value. - IDS_PROP_0286, ///< \ru Максимальное расстояние между гранями. \en Maximal equidistant value. - - IDS_PROP_0301, ///< \ru Начальный параметр U. \en Start parameter U. - IDS_PROP_0302, ///< \ru Начальный параметр V. \en Start parameter V. - IDS_PROP_0303, ///< \ru Положительное направление U. \en Positive direction by U. - IDS_PROP_0304, ///< \ru Положительное направление V. \en Positive direction by V. - IDS_PROP_0305, ///< \ru Параметрическая длина U. \en Parametric length by U. - IDS_PROP_0306, ///< \ru Параметрическая длина V. \en Parametric length by V. - IDS_PROP_0307, ///< \ru Положительное направление 1. \en Positive direction 1. - IDS_PROP_0308, ///< \ru Положительное направление 2. \en Positive direction 2. - IDS_PROP_0309, ///< \ru Тип сопряжения. \en Conjugation type. - IDS_PROP_0310, ///< \ru Точка. \en Point. - IDS_PROP_0311, ///< \ru Замкнутость по U. \en Closedness by U. - IDS_PROP_0312, ///< \ru Замкнутость по V. \en Closedness by V. - IDS_PROP_0313, ///< \ru Число порций по U. \en Number of portions by U. - IDS_PROP_0314, ///< \ru Число порций по V. \en Number of portions by V. - IDS_PROP_0315, ///< \ru Порядок по U. \en Order by U. - IDS_PROP_0316, ///< \ru Порядок по V. \en Order by V. - IDS_PROP_0317, ///< \ru Число точек по U. \en Number of points by U. - IDS_PROP_0318, ///< \ru Число точек по V. \en Number of points by V. - IDS_PROP_0320, ///< \ru Локальная система. \en Local system. - IDS_PROP_0321, ///< \ru Радиус основания. \en Radius of base. - IDS_PROP_0322, ///< \ru Высота. \en Height. - IDS_PROP_0323, ///< \ru Половина угла. \en Half-angle. - IDS_PROP_0324, ///< \ru Большой радиус. \en Major radius. - IDS_PROP_0325, ///< \ru Малый радиус. \en Minor radius. - IDS_PROP_0326, ///< \ru Длина. \en Length. - IDS_PROP_0327, ///< \ru Смещение. \en Shift. - IDS_PROP_0328, ///< \ru Форма. \en Shape. - IDS_PROP_0329, ///< \ru Закрепление границы поверхности. \en Surface boundary fixation. - IDS_PROP_0330, ///< \ru Отличается от базовой поверхности. \en Differs from the base surface. - IDS_PROP_0331, ///< \ru Видимая длина Xmin. \en Visible length Xmin. - IDS_PROP_0332, ///< \ru Видимая длина Ymin. \en Visible length Ymin. - IDS_PROP_0333, ///< \ru Видимая длина Xmax. \en Visible length Xmax. - IDS_PROP_0334, ///< \ru Видимая длина Ymax. \en Visible length Ymax. - IDS_PROP_0336, ///< \ru Число узлов по U. \en Number of knots by U. - IDS_PROP_0337, ///< \ru Значение U узла. \en Value of U knot. - IDS_PROP_0338, ///< \ru Число узлов по V. \en Number of knots by V. - IDS_PROP_0339, ///< \ru Значение V узла. \en Value of V knot. - IDS_PROP_0340, ///< \ru Поверхность. \en Surface. - IDS_PROP_0341, ///< \ru Направляющая кривая. \en Guide curve . - IDS_PROP_0342, ///< \ru Образующая кривая. \en Generating curve. - IDS_PROP_0343, ///< \ru Вектор смещения. \en Translation vector. - IDS_PROP_0344, ///< \ru Вектор направления. \en Direction vector. - IDS_PROP_0345, ///< \ru Точка оси вращения. \en Point of the rotation axis. - IDS_PROP_0346, ///< \ru Направление оси. \en Axis direction. - IDS_PROP_0347, ///< \ru Угол вращения. \en Rotation angle. - IDS_PROP_0348, ///< \ru Вершина. \en Vertex. - IDS_PROP_0350, ///< \ru Базовая поверхность. \en Base surface. - IDS_PROP_0351, ///< \ru Поверхность 1. \en Surface 1. - IDS_PROP_0352, ///< \ru Поверхность 2. \en Surface 2. - IDS_PROP_0353, ///< \ru Контур. \en Contour. - IDS_PROP_0354, ///< \ru Число контуров. \en Number of contours. - IDS_PROP_0355, ///< \ru Двумерный контур. \en Two-dimensional contour. - IDS_PROP_0356, ///< \ru Двумерная кривая. \en Two-dimensional curve. - IDS_PROP_0357, ///< \ru Контур 1. \en Contour 1. - IDS_PROP_0358, ///< \ru Контур 2. \en Contour 2. - IDS_PROP_0360, ///< \ru Плоскость. \en Plane. - IDS_PROP_0361, ///< \ru Вес поверхности 1. \en Weight of surface 1. - IDS_PROP_0362, ///< \ru Вес поверхности 2. \en Weight of surface 2. - IDS_PROP_0363, ///< \ru Производная в начале. \en Derivative at the beginning. - IDS_PROP_0364, ///< \ru Производная в конце. \en Derivative at the end. - IDS_PROP_0370, ///< \ru Кривая на поверхности 0. \en Curve on surface 0. - IDS_PROP_0371, ///< \ru Кривая на поверхности 1. \en Curve on surface 1. - IDS_PROP_0372, ///< \ru Кривая на поверхности 2. \en Curve on surface 2. - IDS_PROP_0373, ///< \ru Кривая вершин. \en Curve of vertices. - IDS_PROP_0374, ///< \ru Параметр Umin. \en Parameter Umin. - IDS_PROP_0375, ///< \ru Параметр Umax. \en Parameter Umax. - IDS_PROP_0376, ///< \ru Параметр Vmin. \en Parameter Vmin. - IDS_PROP_0377, ///< \ru Параметр Vmax. \en Parameter Vmax. - IDS_PROP_0378, ///< \ru Производная dU. \en Derivative dU. - IDS_PROP_0379, ///< \ru Производная dV. \en Derivative dV. - IDS_PROP_0380, ///< \ru Кромка проходит по Vmin. \en Boundary passes through Vmin. - IDS_PROP_0384, ///< \ru Расширение minUPar. \en Extension minUPar. - IDS_PROP_0385, ///< \ru Расширение maxUPar. \en Extension maxUPar. - IDS_PROP_0386, ///< \ru Расширение minVPar. \en Extension minVPar. - IDS_PROP_0387, ///< \ru Расширение maxVPar. \en Extension maxVPar. - IDS_PROP_0390, ///< \ru Число кривых. \en Number of curves. - IDS_PROP_0391, ///< \ru Кривая по U. \en Curve by U - IDS_PROP_0392, ///< \ru Кривая по V. \en Curve by V. - IDS_PROP_0393, ///< \ru Число кривых по U. \en Number of curves by U. - IDS_PROP_0394, ///< \ru Число кривых по V. \en Number of curves by V. - IDS_PROP_0395, ///< \ru Тип поверхности. \en Type of surface. - IDS_PROP_0397, ///< \ru Нейтральная плоскость. \en Neutral plane. - IDS_PROP_0398, ///< \ru Плоскость контура. \en Plane of the contour. - IDS_PROP_0399, ///< \ru Через элементы. \en Through elements. - IDS_PROP_0400, ///< \ru Сопряжение на границе. \en Conjugation on the boundary. - IDS_PROP_0401, ///< \ru Натяжение на границе. \en Tension on the boundary. - IDS_PROP_0402, ///< \ru Параметр определения длины производных. \en Parameter of derivatives length definition. - IDS_PROP_0403, ///< \ru Не сохранять длину производной. \en Do not keep the derivative length. - IDS_PROP_0404, ///< \ru Тип сопряжения (0-4). \en Conjugation type (0-4). - IDS_PROP_0405, ///< \ru Использовать готовый узловой вектор. \en Use prepared knot vector. - IDS_PROP_0406, ///< \ru Количество узлов. \en Number of knots. - IDS_PROP_0407, ///< \ru Проверка самопересечений. \en Check for self-intersections. - IDS_PROP_0408, ///< \ru Используется общий вес точек. \en The common weight of points is used. - IDS_PROP_0409, ///< \ru Построена по пласту точек. \en Build from a cloud of points. - IDS_PROP_0410, ///< \ru Построена по сети точек. \en Build from a mesh of points. - IDS_PROP_0411, ///< \ru В виде набора треугольников. \en As a set of triangles. - IDS_PROP_0412, ///< \ru Использовать проекционную кривую. \en Use projection curve. - IDS_PROP_0413, ///< \ru Усекать границами. \en Truncate by bounds. - IDS_PROP_0414, ///< \ru Привязка к началу. \en Binding to the beginning. - IDS_PROP_0415, ///< \ru Соединять скруглениями.\en Join by fillets. - IDS_PROP_0416, ///< \ru Сохранять радиус. \en Keep the radius. - IDS_PROP_0417, ///< \ru Притуплять острый угол. \en Blunt a sharp angle. - IDS_PROP_0418, ///< \ru Проверка пересечений. \en Check for intersections. - IDS_PROP_0419, ///< \ru Слияние подобных граней. \en Merging of similar faces. - IDS_PROP_0420, ///< \ru Слияние подобных ребер. \en Merging of similar edges. - - IDS_PROP_0421, ///< \ru Номер соседнего объекта. \en The number of neighbour object. - - IDS_PROP_0450, ///< \ru Начальный радиус (поверхность). \en Start radius (surface). - IDS_PROP_0451, ///< \ru Конечный радиус (резьба). \en End radius (thread). - IDS_PROP_0452, ///< \ru Длина резьбы. \en Thread length. - IDS_PROP_0453, ///< \ru Угол коничности резьбы. \en Taper angle of the thread. - - IDS_PROP_0461, ///< \ru Триангуляция. \en Triangulation. - IDS_PROP_0462, ///< \ru Количество точек триангуляции. \en Number of points of triangulation. - IDS_PROP_0463, ///< \ru Количество двумерных точек. \en Number of two-dimension points. - IDS_PROP_0464, ///< \ru Количество точек полигонов. \en Number of points of polygons. - - IDS_PROP_0501, ///< \ru Число вершин. \en Number of vertices. - IDS_PROP_0502, ///< \ru Число ребер. \en Number of edges. - IDS_PROP_0503, ///< \ru Число граней. \en Number of faces. - IDS_PROP_0504, ///< \ru Ориентация вершины. \en Vertex orientation. - IDS_PROP_0505, ///< \ru Ориентация ребра. \en Edge orientation. - IDS_PROP_0506, ///< \ru Ориентация грани. \en Face orientation. - IDS_PROP_0508, ///< \ru Ребро. \en Edge. - IDS_PROP_0509, ///< \ru Грань. \en Face. - IDS_PROP_0510, ///< \ru Число циклов. \en Number of loops. - IDS_PROP_0511, ///< \ru Цикл. \en Loop. - IDS_PROP_0512, ///< \ru Автоопределение. \en Automatic identification. - IDS_PROP_0513, ///< \ru Автоматическое. \en Automatic. - - IDS_PROP_0514, ///< \ru Тип размножения. \en Type of duplication. - IDS_PROP_0515, ///< \ru Кол-во шагов. \en Number of steps. - IDS_PROP_0516, ///< \ru Шаг. \en Step. - IDS_PROP_0517, ///< \ru Количество угловых шагов. \en Number of angular step. - IDS_PROP_0518, ///< \ru Элемент. \en Element. - IDS_PROP_0519, ///< \ru Сегмент полигональной сетки. \en Segment of polygonal mesh. - - IDS_PROP_0521, ///< \ru Длина Lx. \en Length Lx. - IDS_PROP_0522, ///< \ru Ширина Ly. \en Width Ly. - IDS_PROP_0523, ///< \ru Высота Lz. \en Height Lz. - IDS_PROP_0524, ///< \ru Малая длина lx. \en Minor length lx. - IDS_PROP_0525, ///< \ru Толщина. \en Thickness. - IDS_PROP_0526, ///< \ru Толщина стенки. \en Wall thickness. - IDS_PROP_0527, ///< \ru Число вскрытых граней. \en Number of opened faces. - IDS_PROP_0528, ///< \ru Форма. \en Shape. - IDS_PROP_0529, ///< \ru Сохранять кромку\поверхность\автоопределение. \en Keep the boundary\surface\auto. - IDS_PROP_0530, ///< \ru Продолжить далее. \en Continue. - IDS_PROP_0531, ///< \ru Катет 1. \en Cathetus 1. - IDS_PROP_0532, ///< \ru Катет 2. \en Cathetus 2. - IDS_PROP_0533, ///< \ru Число фасок. \en Number of chamfers. - IDS_PROP_0534, ///< \ru Число скруглений. \en Number of fillets. - IDS_PROP_0535, ///< \ru Радиус скругления. \en Fillet radius. - IDS_PROP_0536, ///< \ru Номер грани. \en Face number. - IDS_PROP_0537, ///< \ru Параметр U. \en Parameter U. - IDS_PROP_0538, ///< \ru Параметр V. \en Parameter V. - IDS_PROP_0539, ///< \ru Число модифицированных граней. \en Number of modified faces. - IDS_PROP_0540, ///< \ru Тело. \en Solid. - IDS_PROP_0541, ///< \ru Строитель тела. \en Construct solid. - IDS_PROP_0542, ///< \ru Количество четырёхугольников. \en Number of quadrangles. - IDS_PROP_0543, ///< \ru Остановка от начала. \en Termination from the start. - IDS_PROP_0544, ///< \ru Остановка до конца. \en Termination to the end. - IDS_PROP_0545, ///< \ru Радиус скругления 1. \en Fillet radius 1. - IDS_PROP_0546, ///< \ru Радиус скругления 2. \en Fillet radius 2. - IDS_PROP_0547, ///< \ru Коэффициент полноты. \en Coefficient of completeness. - IDS_PROP_0548, ///< \ru Способ обработки углов стыковки рёбер. \en Method of processing corners of edges connection. - IDS_PROP_0549, ///< \ru Четырехугольник. \en Quadrangle. - - IDS_PROP_0550, ///< \ru Базовое тело. \en Base solid. - IDS_PROP_0551, ///< \ru Тело 1. \en Solid 1. - IDS_PROP_0552, ///< \ru Тело 2. \en Solid 2. - IDS_PROP_0553, ///< \ru Исходное тело. \en Initial solid. - IDS_PROP_0554, ///< \ru Режущая поверхность. \en Cutting surface. - IDS_PROP_0555, ///< \ru Оставляемая часть. \en A part to keep. - IDS_PROP_0556, ///< \ru Точка симметрии. \en Symmetry point. - IDS_PROP_0557, ///< \ru Ось X симметрии. \en Axis X of symmetry. - IDS_PROP_0558, ///< \ru Ось Y симметрии. \en Axis Y of symmetry. - IDS_PROP_0559, ///< \ru Поверхность. \en Surface. - IDS_PROP_0560, ///< \ru Внешняя оболочка. \en Outer shell. - IDS_PROP_0561, ///< \ru Пустотная оболочка. \en Void shell. - IDS_PROP_0562, ///< \ru Число пустот. \en Number of voids. - IDS_PROP_0563, ///< \ru Глубина 1. \en Depth 1. - IDS_PROP_0564, ///< \ru Глубина 2. \en Depth 2. - IDS_PROP_0565, ///< \ru Угол уклона 1. \en Slope angle 1. - IDS_PROP_0566, ///< \ru Угол уклона 2. \en Slope angle 2. - IDS_PROP_0567, ///< \ru Толщина стенки 1. \en Wall thickness 1. - IDS_PROP_0568, ///< \ru Толщина стенки 2. \en Wall thickness 2. - IDS_PROP_0569, ///< \ru Толщина. \en Thickness. - IDS_PROP_0570, ///< \ru Глубина. \en Depth. - - IDS_PROP_0571, ///< \ru Способ построения. \en Method of construction. - IDS_PROP_0572, ///< \ru Число оболочек. \en Number of shells. - IDS_PROP_0573, ///< \ru Количество треугольников. \en Number of triangles. - IDS_PROP_0575, ///< \ru Угол вращения 1. \en Rotation angle 1. - IDS_PROP_0576, ///< \ru Угол вращения 2. \en Rotation angle 2. - - IDS_PROP_0577, ///< \ru Первая вершина. \en The first vertex. - IDS_PROP_0578, ///< \ru Вторая вершина. \en The second vertex. - IDS_PROP_0579, ///< \ru Третья вершина. \en The third vertex. - IDS_PROP_0580, ///< \ru Четвертая вершина. \en The fourth vertex. - - IDS_PROP_0581, ///< \ru Способ построения 1. \en Method of construction 1. - IDS_PROP_0582, ///< \ru Способ построения 2. \en Method of construction 2. - IDS_PROP_0583, ///< \ru Расстояние 1. \en Distance 1. - IDS_PROP_0584, ///< \ru Расстояние 2. \en Distance 2. - IDS_PROP_0585, ///< \ru Треугольник. \en Triangle. - IDS_PROP_0586, ///< \ru Количество апексов. \en Number of apices. - IDS_PROP_0587, ///< \ru Количество полигонов. \en Number of polygons. - IDS_PROP_0588, ///< \ru Количество триангуляций. \en Number of triangulations. - IDS_PROP_0589, ///< \ru Контур. \en Contour. - IDS_PROP_0590, ///< \ru Число сечений. \en Number of sections. - IDS_PROP_0591, ///< \ru Сечение. \en Section. - IDS_PROP_0592, ///< \ru Параллельность. \en Parallelization. - IDS_PROP_0593, ///< \ru Ориентация образующей. \en Generatrix orientation. - IDS_PROP_0594, ///< \ru Положение образующей. \en Generatrix position. - IDS_PROP_0595, ///< \ru Сфероид (0) или тороид (1). \en Spheroid (0) or toroid (1). - IDS_PROP_0596, ///< \ru Полюс в начале. \en Pole at the beginning. - IDS_PROP_0597, ///< \ru Полюс в конце. \en Pole at the end. - IDS_PROP_0598, ///< \ru Продолжение. \en Extension. - IDS_PROP_0599, ///< \ru Смещение. \en Shift. - - IDS_PROP_0600, ///< \ru Оболочка тела. \en Shell. - IDS_PROP_0601, ///< \ru Ориентация ребра в цикле. \en Sense of edge in the loop. - IDS_PROP_0602, ///< \ru Ориентация кривой ребра. \en Edge curve orientation. - IDS_PROP_0603, ///< \ru Ориентация нормали оболочки. \en Orientation of a shell normal. - IDS_PROP_0604, ///< \ru Кривая ребра. \en Edge curve. - IDS_PROP_0605, ///< \ru Двумерная кривая ребра. \en Two-dimensional edge curve. - IDS_PROP_0606, ///< \ru Поверхность грани. \en Surface of a face. - IDS_PROP_0607, ///< \ru Вершина-начало. \en Start vertex. - IDS_PROP_0608, ///< \ru Вершина-конец. \en End vertex. - IDS_PROP_0609, ///< \ru Количество ссылок. \en References count. - IDS_PROP_0611, ///< \ru Грань плюс. \en Face plus. - IDS_PROP_0612, ///< \ru Грань минус. \en Face minus. - IDS_PROP_0613, ///< \ru Указатель на грань. \en Pointer to a face. - IDS_PROP_0614, ///< \ru Номер по порядку. \en Number by and index. - IDS_PROP_0615, ///< \ru Сортировка. \en Sorting. - IDS_PROP_0616, ///< \ru Пуансон или матрица. \en Punch or die. - IDS_PROP_0651, ///< \ru Разрезанное тело. \en Cutting solid. - IDS_PROP_0652, ///< \ru Плоскость раскроя. \en Cutting plane. - IDS_PROP_0654, ///< \ru Наличие штриховки. \en Whether there is hatching. - IDS_PROP_0655, ///< \ru Шаг штриховки. \en Hatching step. - IDS_PROP_0656, ///< \ru Угол штриховки. \en Hatching angle. - IDS_PROP_0657, ///< \ru Проекционная плоскость. \en Projection plane. - IDS_PROP_0658, ///< \ru Наличие невидимых линий. \en Whether there are invisible lines. - IDS_PROP_0659, ///< \ru Hash имени. \en Hash of name. - - IDS_PROP_0660, ///< \ru Коэффициент нейтрального слоя. \en Neutral layer coefficient. - IDS_PROP_0661, ///< \ru Радиус сгиба. \en Bend radius. - IDS_PROP_0662, ///< \ru Угол сгиба. \en Bend angle. - IDS_PROP_0663, ///< \ru Длина продолжения сгиба. \en Bend extension length. - IDS_PROP_0664, ///< \ru Смещение сгиба. \en Bend shift. - IDS_PROP_0665, ///< \ru Отступ от края сгиба 1. \en Distance from the bound of bend 1. - IDS_PROP_0666, ///< \ru Отступ от края сгиба 2. \en Distance from the bound of bend 2. - IDS_PROP_0667, ///< \ru Угол уклона края сгиба 1. \en Slope angle of the bound of bend 1. - IDS_PROP_0668, ///< \ru Угол уклона края сгиба 2. \en Slope angle of the bound of bend 2. - IDS_PROP_0669, ///< \ru Угол уклона продолжения сгиба 1. \en Slope angle of bend extension 1. - IDS_PROP_0670, ///< \ru Угол уклона продолжения сгиба 2. \en Slope angle of bend extension 2. - IDS_PROP_0671, ///< \ru Расширение продолжения сгиба 1. \en Expansion of extension of bend 1. - IDS_PROP_0672, ///< \ru Расширение продолжения сгиба 2. \en Expansion of extension of bend 2. - IDS_PROP_0673, ///< \ru Ширина разгрузки сгиба. \en Width of bend relief. - IDS_PROP_0674, ///< \ru Глубина разгрузки сгиба. \en Depth of bend relief. - IDS_PROP_0675, ///< \ru Радиус скругления разгрузки. \en Radius of relief rounding. - IDS_PROP_0676, ///< \ru Способ освобождения углов. \en Method of freeing the corners. - IDS_PROP_0677, ///< \ru Фиксированная часть грани слева. \en Fixed part of a face on the left. - IDS_PROP_0678, ///< \ru Строить разогнутым. \en Build in unbent state. - IDS_PROP_0679, ///< \ru Зазор. \en Gap. - IDS_PROP_0680, ///< \ru Перехлёстывающая сторона слева. \en Overlapping side on the left. - IDS_PROP_0681, ///< \ru С добавлением материала. \en With addition of material. - IDS_PROP_0682, ///< \ru Высота. \en Height. - IDS_PROP_0683, ///< \ru Коэффициент сгиба 1. \en Coefficient of bend 1. - IDS_PROP_0684, ///< \ru Радиус сгиба 1. \en Radius of bend 1. - IDS_PROP_0685, ///< \ru Коэффициент сгиба 2. \en Coefficient of bend 2. - IDS_PROP_0686, ///< \ru Радиус сгиба 2. \en Radius of bend 2. - IDS_PROP_0687, ///< \ru Радиус скругления эскиза. \en Radius of a sketch fillet. - IDS_PROP_0688, ///< \ru Радиус скругления основания. \en Radius of a base fillet. - IDS_PROP_0689, ///< \ru Радиус скругления дна. \en Radius of a bottom fillet. - IDS_PROP_0690, ///< \ru Открытая штамповка. \en Open stamping. - IDS_PROP_0691, ///< \ru Боковая стенка внутри. \en Side wall is inside. - IDS_PROP_0692, ///< \ru Ширина основания. \en Width of base. - IDS_PROP_0693, ///< \ru Ширина выпуклой части. \en Width of a salient part. - IDS_PROP_0694, ///< \ru Зазор рубленой законцовки. \en Gap of a cropped tip. - IDS_PROP_0695, ///< \ru Тип буртика. \en Bead type. - IDS_PROP_0696, ///< \ru Тип законцовки. \en Type of a tip. - IDS_PROP_0697, ///< \ru Вытяжка. \en Stretch. - IDS_PROP_0698, ///< \ru По нормали к толщине. \en By the normal to thickness. - IDS_PROP_0699, ///< \ru Способ замыкания цилиндрических частей. \en Method of cylindric parts closure. - IDS_PROP_0700, ///< \ru Разрешение на замыкание углов. \en Permission for corners closure. - - IDS_PROP_0701, ///< \ru Имя. \en Name. - IDS_PROP_0702, ///< \ru Значение. \en Value. - IDS_PROP_0703, ///< \ru Положение. \en Position. - IDS_PROP_0704, ///< \ru Число. \en Number. - IDS_PROP_0705, ///< \ru Ориентация. \en Orientation. - IDS_PROP_0706, ///< \ru Длина. \en Length. - IDS_PROP_0707, ///< \ru Толщина. \en Thickness. - IDS_PROP_0708, ///< \ru Угол. \en Angle. - IDS_PROP_0709, ///< \ru Параметр. \en Parameter. - IDS_PROP_0710, ///< \ru Геометрический объект. \en Geometric object. - IDS_PROP_0711, ///< \ru Точка. \en Point. - IDS_PROP_0712, ///< \ru Кривая. \en Curve. - IDS_PROP_0713, ///< \ru Поверхность. \en Surface. - IDS_PROP_0714, ///< \ru Вершина. \en Vertex. - IDS_PROP_0715, ///< \ru Ребро грани. \en Edge of a face. - IDS_PROP_0716, ///< \ru Цикл грани. \en Face loop. - IDS_PROP_0717, ///< \ru Грань. \en Face. - IDS_PROP_0718, ///< \ru Полюс при umin. \en Pole at umin. - IDS_PROP_0719, ///< \ru Полюс при umax. \en Pole at umax. - IDS_PROP_0720, ///< \ru Полюс при vmin. \en Pole at vmin. - IDS_PROP_0721, ///< \ru Полюс при vmax. \en Pole at vmax. - IDS_PROP_0724, ///< \ru Вершина. \en Vertex. - IDS_PROP_0725, ///< \ru Ребро. \en Edge. - IDS_PROP_0726, ///< \ru Цикл. \en Loop. - IDS_PROP_0727, ///< \ru Грань. \en Face. - IDS_PROP_0729, ///< \ru Количество граней. \en Number of faces. - IDS_PROP_0730, ///< \ru Количество операций. \en Number of operations. - IDS_PROP_0731, ///< \ru Количество объектов. \en Number of objects. - IDS_PROP_0732, ///< \ru Объединение граней. \en Faces unification. - IDS_PROP_0733, ///< \ru Обработка углов. \en Corners treatment. - IDS_PROP_0734, ///< \ru Операция объединения. \en Union operation. - IDS_PROP_0735, ///< \ru Операция пересечения. \en Intersection operation. - IDS_PROP_0736, ///< \ru Операция разности. \en Subtraction operation. - IDS_PROP_0737, ///< \ru Базовая операция. \en Base operations. - IDS_PROP_0738, ///< \ru Флаг состояния. \en Flag of state. - IDS_PROP_0739, ///< \ru Параметр полюса по U. \en Parameter of a pole by U. - IDS_PROP_0740, ///< \ru Номер грани. \en Number of a face. - IDS_PROP_0741, ///< \ru Номер ребра. \en Number of an edge. - IDS_PROP_0742, ///< \ru Номер грани плюс. \en Number of a face plus. - IDS_PROP_0743, ///< \ru Номер грани минус. \en Number of a face minus. - IDS_PROP_0744, ///< \ru Формировать твёрдое тело. \en Create a solid. - IDS_PROP_0745, ///< \ru Точность сшивки. \en Stitching tolerance. - IDS_PROP_0746, ///< \ru Через сгиб. \en Through a bend. - IDS_PROP_0747, ///< \ru Полюс. \en Pole. - IDS_PROP_0748, ///< \ru Край. \en Border. - IDS_PROP_0749, ///< \ru Шов. \en Seam. - IDS_PROP_0750, ///< \ru Линия перехода. \en Transition line. - IDS_PROP_0751, ///< \ru Адрес начальной вершины. \en Start vertex address. - IDS_PROP_0752, ///< \ru Адрес конечной вершины. \en End vertex address. - IDS_PROP_0753, ///< \ru Адрес грани слева. \en Address of a face on the left. - IDS_PROP_0754, ///< \ru Адрес грани справа. \en Address of a face on the right. - IDS_PROP_0755, ///< \ru Примитив разрезан. \en Primitive is cut. - IDS_PROP_0756, ///< \ru Листовой примитив. \en Sheet primitive. - IDS_PROP_0757, ///< \ru Внутренняя грань сгиба. \en Internal face of a bend. - IDS_PROP_0758, ///< \ru Внешняя грань сгиба. \en External face of a bend. - IDS_PROP_0759, ///< \ru Угол раствора конуса. \en Cone angle. - -// \ru Версии \en Versions - - IDS_PROP_0760, ///< \ru Версия. \en Version. - IDS_PROP_0761, ///< \ru Версия имени. \en Version of name. - IDS_PROP_0762, ///< \ru Версия операции. \en Version of operation. - IDS_PROP_0763, ///< \ru Версия объекта. \en Version of object. - -// \ru Информация от геометрической модели \en Information from geometric model - - IDS_PROP_0771, ///< \ru Количество вершин. \en Number of vertices. - IDS_PROP_0772, ///< \ru Количество кривых. \en Number of curves. - IDS_PROP_0773, ///< \ru Количество поверхностей. \en Number of surfaces. - IDS_PROP_0774, ///< \ru Количество тел. \en Number of solids. - IDS_PROP_0775, ///< \ru Количество полигональных объектов. \en Number of polygonal objects. - IDS_PROP_0776, ///< \ru Количество проволочных каркасов. \en Number of wireframes. - IDS_PROP_0777, ///< \ru Количество точечных каркасов. \en Number of point frames. - IDS_PROP_0778, ///< \ru Количество сборочных единиц. \en Number of assembly units. - IDS_PROP_0779, ///< \ru Количество вставок. \en Number of instances. - IDS_PROP_0780, ///< \ru Количество других объектов. \en Number of other objects. - IDS_PROP_0781, ///< \ru Количество регионов. \en Number of regions. - IDS_PROP_0782, ///< \ru Количество элементов. \en Number of elements. - IDS_PROP_0783, ///< \ru Количество сегментов. \en Number of segments. - IDS_PROP_0784, ///< \ru Количество всех граней. \en Number of all faces. - IDS_PROP_0785, ///< \ru Количество всех уникальных граней. \en Number of all unique faces. - - IDS_PROP_0791, ///< \ru Первая деформация. \en First strain. - IDS_PROP_0792, ///< \ru Вторая деформация. \en Second strain. - IDS_PROP_0793, ///< \ru Третья деформация. \en Third strain. - - IDS_PROP_0797, ///< \ru Модуль Юнга. \en Young's modulus. - IDS_PROP_0798, ///< \ru Коэффициент Пуассона. \en Poisson's ratio. - - IDS_PROP_0830, ///< \ru Число радиусов эквидистант. \en Number of offsets radii. - IDS_PROP_0831, ///< \ru Гладкий стык. \en Smooth joint. - IDS_PROP_0832, ///< \ru Тип обхода угла. \en Type of corner bypass. - IDS_PROP_0833, ///< \ru Радиус специального скругления. \en Radius of a special fillet. - IDS_PROP_0834, ///< \ru Тип законцовки. \en Type of a tip. - IDS_PROP_0835, ///< \ru Законцовка первого сегмента. \en Tip of the first segment. - IDS_PROP_0836, ///< \ru Тип законцовки в начале. \en Type of tip at the beginning. - IDS_PROP_0837, ///< \ru Тип законцовки в конце. \en Type of tip at the end. - IDS_PROP_0838, ///< \ru Параметр законцовки. \en Parameter of a tip. - - IDS_PROP_0839, ///< \ru Параметр построения заплатки. \en Parameter of a patch construction. - IDS_PROP_0840, ///< \ru Число кривых образующего контура. \en Number of curves of a generating contour. - IDS_PROP_0841, ///< \ru Даны образующие грани. \en The generating faces are given. - IDS_PROP_0842, ///< \ru Кривая образующего контура. \en Curve of a generaing contour. - IDS_PROP_0843, ///< \ru Ориентация кривой контура. \en Orientation of a curve of the contour. - IDS_PROP_0844, ///< \ru Сторона существующей грани оболочки. \en Side of an existent face of the shell. - IDS_PROP_0845, ///< \ru Да. \en Yes. - IDS_PROP_0846, ///< \ru Нет. \en No. - -// \ru Контейнер атрибутов \en Attribute container - - IDS_PROP_0847, ///< \ru Поставщик атрибутов. \en Attribute provider. - IDS_PROP_0848, ///< \ru Количество контейнеров. \en Number of containers. - IDS_PROP_0849, ///< \ru Контейнер. \en Container. - - IDS_PROP_0851, ///< \ru Количество атрибутов. \en Number of attributes. - - IDS_PROP_0853, ///< \ru При изменении. \en While changing. - IDS_PROP_0854, ///< \ru При конвертации. \en While convertation. - IDS_PROP_0855, ///< \ru При трансформировании. \en While transforming. - IDS_PROP_0856, ///< \ru При копировании. \en While copying. - IDS_PROP_0857, ///< \ru При объединении. \en While joining. - IDS_PROP_0858, ///< \ru При замене. \en While replacing. - IDS_PROP_0859, ///< \ru При разделении. \en While splitting. - IDS_PROP_0860, ///< \ru При удалении. \en While deleting. - IDS_PROP_0861, ///< \ru Объект свободен. \en The object is free. - IDS_PROP_0862, ///< \ru Копируемость. \en Whether it can be copied. - IDS_PROP_0863, ///< \ru Количество u-линий. \en The number of u-lines. - IDS_PROP_0864, ///< \ru Количество v-линий. \en The number of v-lines. - - IDS_PROP_0869, ///< \ru Имя исполнения. \en Embodiment name. - IDS_PROP_0870, ///< \ru Имя родительского исполнения. \en Parent embodiment name. - IDS_PROP_0871, ///< \ru Красный. \en Red. - IDS_PROP_0872, ///< \ru Зелёный. \en Green. - IDS_PROP_0873, ///< \ru Синий. \en Blue. - IDS_PROP_0874, ///< \ru Толщина. \en Thickness. - IDS_PROP_0875, ///< \ru Стиль. \en Slyle. - IDS_PROP_0876, ///< \ru Плотность. \en Density. - IDS_PROP_0877, ///< \ru Идентификатор. \en Identifier. - IDS_PROP_0878, ///< \ru Селектированность. \en Selectivity. - IDS_PROP_0879, ///< \ru Видимость. \en Visibility. - IDS_PROP_0880, ///< \ru Изменённость. \en Modified. - IDS_PROP_0881, ///< \ru Общий фон. \en Background. - IDS_PROP_0882, ///< \ru Диффузное отражение. \en Diffuse reflection. - IDS_PROP_0883, ///< \ru Зеркальное отражение. \en Specular reflection. - IDS_PROP_0884, ///< \ru Блеск. \en Shininess. - IDS_PROP_0885, ///< \ru Непрозрачность. \en Opacity. - IDS_PROP_0886, ///< \ru Излучение. \en Emission. - IDS_PROP_0887, ///< \ru Количество родительских объектов. \en Number of parent objects. - IDS_PROP_0888, ///< \ru Родительский объект. \en Parent object. - IDS_PROP_0889, ///< \ru Имя топологического объекта. \en Topological object name. - IDS_PROP_0890, ///< \ru Имя объекта. \en Object name. - IDS_PROP_0891, ///< \ru Хэш имени объекта. \en Object name hash. - - IDS_PROP_0900, ///< \ru Сопряжение в точке. \en Conjugation at point. - IDS_PROP_0901, ///< \ru Тип сопряжения. \en Conjugation type. - IDS_PROP_0902, ///< \ru Без сопряжения. \en Without conjugation. - IDS_PROP_0903, ///< \ru По позиции. \en By position. - IDS_PROP_0904, ///< \ru По касательной. \en By tangent. - IDS_PROP_0905, ///< \ru По нормали. \en By normal. - IDS_PROP_0906, ///< \ru По G2. \en By G2. - IDS_PROP_0907, ///< \ru По G3. \en By G3. - IDS_PROP_0908, ///< \ru Касательный вектор. \en Tangent vector. - IDS_PROP_0909, ///< \ru Первая производная касательного вектора. \en First derivative of tangent vector. - IDS_PROP_0910, ///< \ru Вторая производная касательного вектора. \en Second derivative of tangent vector. - IDS_PROP_0911, ///< \ru Можно ли двигать точки. \en Whether points can be moved. - IDS_PROP_0912, ///< \ru Только по направлению. \en Only along the direction. - IDS_PROP_0913, ///< \ru Сопряжение. \en Conjugation. - - IDS_PROP_0920, ///< \ru Тип параметризации. \en Parametrization type. - IDS_PROP_0921, ///< \ru Пользовательская. \en Custom. - IDS_PROP_0922, ///< \ru Равномерная. \en Uniform. - IDS_PROP_0923, ///< \ru По длине хорды. \en By chord length. - IDS_PROP_0924, ///< \ru Центростремительная. \en Centripetal. - - IDS_PROP_0925, ///< \ru Через вершины. \en Through vertices. - IDS_PROP_0926, ///< \ru Равномерная параметризация. \en Uniform parametrization. - - IDS_PROP_0927, ///< \ru Признак текущего исполнения. \en Mark of current embodiment. - -// \ru Конвертеры \en Converters - - IDS_PROP_1000, ///< \ru Заголовок. \en Title. - IDS_PROP_1001, ///< \ru Название. \en Name. - IDS_PROP_1002, ///< \ru Дата и время. \en Date and time. - IDS_PROP_1003, ///< \ru Автор(ы). \en Author(s)). - IDS_PROP_1004, ///< \ru Организация(и). \en Organization(s). - IDS_PROP_1005, ///< \ru Процессор STEP. \en Processor STEP. - IDS_PROP_1006, ///< \ru Система. \en System. - IDS_PROP_1007, ///< \ru Авторизация. \en Authorization. - - IDS_PROP_1010, ///< \ru Лицо и организация. \en Person and organization. - IDS_PROP_1011, ///< \ru Идентификатор лица. \en Identifier of a person. - IDS_PROP_1012, ///< \ru Фамилия. \en Surname. - IDS_PROP_1013, ///< \ru Имя. \en Name. - IDS_PROP_1014, ///< \ru Средние имена. \en Middle names. - IDS_PROP_1015, ///< \ru Титулы предшествующие. \en Prefix titles. - IDS_PROP_1016, ///< \ru Титулы завершающие. \en Suffix titles. - IDS_PROP_1017, ///< \ru Идентификатор организации. \en Identifier of organization. - IDS_PROP_1018, ///< \ru Название организации. \en Name of organization. - IDS_PROP_1019, ///< \ru Описание организации. \en Description of organization. - - IDS_PROP_1030, ///< \ru Изделие. \en Product. - IDS_PROP_1031, ///< \ru Идентификатор. \en Identifier. - IDS_PROP_1032, ///< \ru Название. \en Name. - IDS_PROP_1033, ///< \ru Описание. \en Description. - - IDS_PROP_1043, ///< \ru Элемент описания. \en Description element. - - /* - 1100 .. 1199 is a range for C3D Solver - */ - IDS_PROP_1100, ///< \ru Геометрический решатель. \en Geom solver. - IDS_PROP_1101, ///< \ru Схема сопряжений. \en Scheme of matings. - IDS_PROP_1102, ///< \ru Система ограничений. \en Constraint system. - IDS_PROP_1103, ///< \ru Ограничение. \en Constraint. - IDS_PROP_1104, ///< \ru Тип сопряжения. \en Type of mating. - IDS_PROP_1105, ///< \ru Тип ограничения. \en Type of constraint. - IDS_PROP_1106, ///< \ru Выравнивание. \en Alignment. - IDS_PROP_1107, ///< \ru Количество ограничений. \en Number of constraints. - IDS_PROP_1108, ///< \ru Тип взаимоориентации. \en Coorientation type. - IDS_PROP_1109, ///< \ru Сопряжение. \en Mate. - IDS_PROP_1110, ///< \ru Базовый объект. \en Base object. - IDS_PROP_1111, ///< \ru Объект 1. \en Object 1. - IDS_PROP_1112, ///< \ru Объект 2. \en Object 2. - IDS_PROP_1113, ///< \ru Вещественный параметр. \en Real parameter. - IDS_PROP_1114, ///< \ru Величина взаимоориентации. \en Value of coorientation. - - /* - Types of geometric constraint - */ - IDS_PROP_1130, ///< \ru Совпадение \en Coincident - IDS_PROP_1131, ///< \ru Параллельность \en Parallel - IDS_PROP_1132, ///< \ru Перпендикулярность \en Perpendicular - IDS_PROP_1133, ///< \ru Касание \en Tangent. - IDS_PROP_1134, ///< \ru Концентричность \en Concentric - IDS_PROP_1135, ///< \ru На расстоянии \en Distance - IDS_PROP_1136, ///< \ru По углом \en Angle - IDS_PROP_1137, ///< \ru По месту \en In place - IDS_PROP_1138, ///< \ru Механическая передача \en Transmittion - IDS_PROP_1139, ///< \ru Кулачковый механизм \en Cam mechanism - IDS_PROP_1140, ///< \ru Радиальный размер. \en Radial dimension. - IDS_PROP_1145, ///< \ru Симметричность \en Symmetric - IDS_PROP_1146, ///< \ru Зависимый объект \en Dependent - IDS_PROP_1147, ///< \ru Элемент паттерна \en Patterned - IDS_PROP_1148, ///< \ru Линейный паттерн \en Linear pattern - IDS_PROP_1149, ///< \ru Угловой паттерн \en Angular pattern - IDS_PROP_1199, // The last id for C3D Solver - - /* - \ru Новые описания без группировки \en New unsorted descriptions - */ - - IDS_PROP_2001, ///< \ru Внимание: \en Attention: - IDS_PROP_2002, ///< \ru Начало общих операций. \en Beginning of the shared operations. - IDS_PROP_2003, ///< \ru Начало группы операций. \en Beginning of the operations group. - IDS_PROP_2004, ///< \ru Начало первой группы операций. \en Beginning of the first operations group. - IDS_PROP_2005, ///< \ru Начало второй группы операций. \en Beginning of the second operations group. - IDS_PROP_2006, ///< \ru Начало объекта. \en Beginning of an object. - IDS_PROP_2007, ///< \ru Начало первого объекта. \en Beginning of the first object. - IDS_PROP_2008, ///< \ru Начало второго объекта. \en Beginning of the second object. - IDS_PROP_2009, ///< \ru Копировать атрибуты. \en Copy attributes. - IDS_PROP_2010, ///< \ru Количество общих групп операций. \en Number of shared operations groups. - IDS_PROP_2011, ///< \ru Количество выбранных граней. \en Number of selected faces. - IDS_PROP_2012, ///< \ru Количество выбранных ребёр. \en Number of selected edges. - IDS_PROP_2013, ///< \ru Количество выбранных вершин. \en Number of selected vertices. - IDS_PROP_2014, ///< \ru Секущий эскиз. \en Cutting sketch. - IDS_PROP_2015, ///< \ru Секущие 3D-кривые. \en Cutting 3D-curves. - IDS_PROP_2016, ///< \ru Секущие поверхности. \en Cutting surfaces. - IDS_PROP_2017, ///< \ru Секущее тело. \en Cutting solid. - - IDS_PROP_2018, ///< \ru Поверхность сопряжения на границе 0. \en Adjacent surface on the border 0. - IDS_PROP_2019, ///< \ru Поверхность сопряжения на границе 1. \en Adjacent surface on the border 1. - IDS_PROP_2020, ///< \ru Поверхность сопряжения на границе 2. \en Adjacent surface on the border 2. - IDS_PROP_2021, ///< \ru Поверхность сопряжения на границе 3. \en Adjacent surface on the border 3. - IDS_PROP_2022, ///< \ru Поверхность сопряжения на границе 4. \en Adjacent surface on the border 4. - IDS_PROP_2023, ///< \ru Сопряжение на границе 0. \en Conjugation on the boundary 0. - IDS_PROP_2024, ///< \ru Сопряжение на границе 1. \en Conjugation on the boundary 1. - IDS_PROP_2025, ///< \ru Сопряжение на границе 2. \en Conjugation on the boundary 2. - IDS_PROP_2026, ///< \ru Сопряжение на границе 3. \en Conjugation on the boundary 3. - IDS_PROP_2027, ///< \ru Сопряжение на границе 4. \en Conjugation on the boundary 4. - - IDS_PROP_LAST = 9999, ///< \ru Наибольшее значение. \en The greatest value. -}; - - -#endif // __MB_PROPERTY_TITLE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Свойства математических объектов. + \en Properties of mathematical objects. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __MB_PROPERTY_TITLE_H +#define __MB_PROPERTY_TITLE_H + + +//------------------------------------------------------------------------------ +/** \brief \ru Свойства математических объектов. + \en Properties of mathematical objects. \~ + \attention \ru Целочисленные значения данного перечислительного типа могут быть изменены! + \en Integer values of the enum can be changed! + \ingroup Base_Items +*/ +// --- +enum MbePrompt +{ + IDS_ITEM_0000 = 0, ///< \ru Неопределенный объект. + +// \ru Базовые объекты двумерной математики. \en Base 2D objects. + + IDS_ITEM_0001, ///< \ru Двумерная точка. \en A two-dimensional point. + IDS_ITEM_0002, ///< \ru Двумерный вектор. \en Two-dimensional vector. + IDS_ITEM_0003, ///< \ru Двумерная матрица преобразования. \en Two-dimensional matrix of transformation. + IDS_ITEM_0004, ///< \ru Двумерная локальная система. \en Two-dimensional local system. + IDS_ITEM_0005, ///< \ru Двумерный единичный вектор. \en Two-dimensional unit vector + +// \ru Типы двумерных кривых. \en Types of two-dimensional curves. + + IDS_ITEM_0011, ///< \ru Двумерная кривая. \en Two-dimensional curve + + IDS_ITEM_0013, ///< \ru Двумерная прямая. \en Two-dimensional line. + IDS_ITEM_0014, ///< \ru Двумерный отрезок. \en Two-dimensional segment. + IDS_ITEM_0015, ///< \ru Двумерный отрезок прямой. \en Two-dimensional line segment. + IDS_ITEM_0016, ///< \ru Двумерная дуга окружности. \en Two-dimensional circular arc. + IDS_ITEM_0017, ///< \ru Двумерная усеченная кривая. \en Two-dimensional truncated curve. + IDS_ITEM_0018, ///< \ru Двумерная эквидистантная кривая. \en Two-dimensional offset curve. + IDS_ITEM_0019, ///< \ru Двумерная эквидистанта. \en Two-dimensional equidistant. + IDS_ITEM_0020, ///< \ru Двумерная окружность. \en Two-dimensional circle. + IDS_ITEM_0021, ///< \ru Двумерный эллипс. \en Two-dimensional ellipse. + IDS_ITEM_0022, ///< \ru Двумерная парабола. \en Two-dimensional parabola. + IDS_ITEM_0023, ///< \ru Двумерная дуга эллипса. \en Two-dimensional elliptical arc. + IDS_ITEM_0024, ///< \ru Двумерная ломаная. \en Two-dimensional polyline. + IDS_ITEM_0025, ///< \ru Двумерная NURBS кривая. \en Two-dimensional NURBS curve. + IDS_ITEM_0026, ///< \ru Двумерный сплайн Эрмита. \en Two-dimensional Hermite spline. + IDS_ITEM_0027, ///< \ru Двумерный сплайн Безье. \en Two-dimensional Bezier spline. + IDS_ITEM_0028, ///< \ru Двумерный кубический сплайн. \en Two-dimensional cubic spline. + IDS_ITEM_0029, ///< \ru Двумерная репараметризованная кривая. \en Two-dimensional reparametrized curve. + IDS_ITEM_0030, ///< \ru Двумерный контур. \en Two-dimensional contour. + IDS_ITEM_0031, ///< \ru Двумерная косинусоида. \en Two-dimensional cosine curve. + IDS_ITEM_0032, ///< \ru Двумерная точечная кривая. \en Two-dimensional point curve. + IDS_ITEM_0040, ///< \ru Двумерная область. \en Two-dimensional region. + IDS_ITEM_0050, ///< \ru Двумерный объект. \en Two-dimensional object. + IDS_ITEM_0051, ///< \ru Двумерная мультилиния. \en Two-dimensional multiline. + IDS_ITEM_0052, ///< \ru Двумерная кривая на конусе, соответствующая кривой на коническом изгибе плоскости. \en Two-dimensional curve on cone corresponding to a curve on conic bend of a plane. + IDS_ITEM_0053, ///< \ru Двумерная кривая на плоскости, соответствующая кривой на изгибе конуса. \en Two-dimensional curve on plane corresponding to a curve on a bend of cone + IDS_ITEM_0054, ///< \ru Двумерная кривая, координатные функции которой заданы в символьном виде. \en Functionally defined two-dimensional curve. + IDS_ITEM_0055, ///< \ru Образ трехмерной кривой на поверхности при движении по направляющей. \en Image of a three-dimensional curve on surface while moving along the guide curve. + +// \ru Типы полигональных объектов. \en Types of simplified forms of an object. + + IDS_ITEM_0060, ///< \ru Параметр. \en Parameter. + IDS_ITEM_0061, ///< \ru Вершина. \en Vertex. + IDS_ITEM_0062, ///< \ru Нормаль. \en Normal. + IDS_ITEM_0063, ///< \ru Треугольник. \en Triangle. + IDS_ITEM_0064, ///< \ru Четырехугольник. \en Quadrangle. + IDS_ITEM_0071, ///< \ru Полигональный объект на числах double. \en Polygonal object on double data. + IDS_ITEM_0072, ///< \ru Апекс на числах double. \en Apex on double data. + IDS_ITEM_0073, ///< \ru Полигон на числах double. \en Polygon on double data. + IDS_ITEM_0074, ///< \ru Триангуляция на числах double. \en Triangulation on double data. + IDS_ITEM_0075, ///< \ru Полигональный объект на числах float. \en Polygonal object on float data. + IDS_ITEM_0076, ///< \ru Апекс на числах float. \en Apex on float data. + IDS_ITEM_0077, ///< \ru Полигон на числах float. \en Polygon on float data. + IDS_ITEM_0078, ///< \ru Триангуляция на числах float. \en Triangulation on float data. + +// \ru Базовые объекты трехмерной математики. \en Base objects of three-dimensional mathematics. + + IDS_ITEM_0101, ///< \ru Точка. \en Point. + IDS_ITEM_0102, ///< \ru Вектор. \en Vector. + IDS_ITEM_0103, ///< \ru Матрица преобразования. \en Transformation matrix. + IDS_ITEM_0104, ///< \ru Локальная система координат. \en Local coordinate system. + +// \ru Типы функций \en Types of functions + + IDS_ITEM_0111, ///< \ru Kонстантная функция. \en Constant Function. + IDS_ITEM_0112, ///< \ru Линейная функция. \en Linear Function. + IDS_ITEM_0113, ///< \ru Kубическая функция. \en Cubic Function. + IDS_ITEM_0114, ///< \ru Kубическай сплайн-функция. \en Cubic spline Function. + IDS_ITEM_0115, ///< \ru Символьная функция. \en Symbolic Function. + IDS_ITEM_0116, ///< \ru Степенная функция. \en Power Function. + IDS_ITEM_0117, ///< \ru Синус функция. \en Sinus Function. + +// \ru Типы трехмерных кривы.х \en Types of three-dimensional curves. + + IDS_ITEM_0201, ///< \ru Кривая. \en Curve. + IDS_ITEM_0202, ///< \ru B-сплайн. \en B-spline. + IDS_ITEM_0213, ///< \ru Прямая линия. \en Straight Line. + IDS_ITEM_0214, ///< \ru Отрезок. \en Segment. + IDS_ITEM_0215, ///< \ru Дуга эллипса. \en Elliptic Arc. + IDS_ITEM_0216, ///< \ru Дуга окружности. \en Circular Arc. + IDS_ITEM_0217, ///< \ru Усеченная кривая. \en Truncated Curve. + IDS_ITEM_0218, ///< \ru Эквидистантная кривая. \en Offset Curve. + IDS_ITEM_0219, ///< \ru Коническая спираль. \en Conical Spiral. + IDS_ITEM_0220, ///< \ru Oкружность. \en Circle. + IDS_ITEM_0221, ///< \ru Эллипс. \en Ellipse. + IDS_ITEM_0222, ///< \ru Парабола. \en Parabola. + IDS_ITEM_0223, ///< \ru Гипербола. \en Hyperbola. + IDS_ITEM_0224, ///< \ru Ломаная линия. \en Polyline. + IDS_ITEM_0225, ///< \ru NURBS кривая. \en NURBS Curve. + IDS_ITEM_0226, ///< \ru Сплайн Эрмита. \en Hermite Spline. + IDS_ITEM_0227, ///< \ru Сплайн Безье. \en Bezier Spline. + IDS_ITEM_0228, ///< \ru Кубический сплайн. \en Cubic Spline. + IDS_ITEM_0229, ///< \ru Репараметризованная кривая. \en Reparametrized Curve. + IDS_ITEM_0231, ///< \ru Плоская кривая. \en Plane curve. + IDS_ITEM_0232, ///< \ru Спираль с переменым радиусом. \en Spiral with Variable Radius. + IDS_ITEM_0233, ///< \ru Спираль с криволинейной осью. \en Spiral with a Curved Axis. + IDS_ITEM_0234, ///< \ru Кривая-мостик. \en Bridge Curve. + IDS_ITEM_0235, ///< \ru Символьная кривая. \en Functionally Defined Curve. + IDS_ITEM_0236, ///< \ru Кривая на поверхности. \en Curve on a Surface. + IDS_ITEM_0237, ///< \ru Линия пересечения поверхностей. \en Intersection Curve of Surfaces. + IDS_ITEM_0238, ///< \ru Контур на поверхности. \en Contour on a Surface. + IDS_ITEM_0239, ///< \ru Контур на плоскости. \en Contour on a Plane. + IDS_ITEM_0240, ///< \ru Контур. \en Contour. + IDS_ITEM_0241, ///< \ru Проекционная кривая. \en Projection Curve. + IDS_ITEM_0242, ///< \ru Силуэтная кривая. \en Silhouette Curve. + IDS_ITEM_0243, ///< \ru Кривая сопряжения кривых. \en Curve of Curves Conjugation. + IDS_ITEM_0244, ///< \ru Кривая производных поверхности Кунса. \en Curve of Coons Surface Derivetives. + IDS_ITEM_0249, ///< \ru Направляющая кривая. \en Guide Curve. + IDS_ITEM_0250, ///< \ru Кривая пересечения. \en Intersection Curve. + + IDS_ITEM_0251, ///< \ru Первое направляющее ребро. \en First Guide Edge. + IDS_ITEM_0252, ///< \ru Первая направляющая кривая. \en First Guide Curve. + IDS_ITEM_0253, ///< \ru Первая грань для стыковки. \en First Mating Face. + + IDS_ITEM_0256, ///< \ru Второе направляющее ребро. \en Second Guide Edge. + IDS_ITEM_0257, ///< \ru Вторая направляющая кривая. \en Second Guide Curve. + IDS_ITEM_0258, ///< \ru Вторая грань для стыковки. \en Second Mating Face. + +// \ru Типы параметрических поверхностей. \en Types of parametric surfaces. + + IDS_ITEM_0301, ///< \ru Поверхность. \en Surface. + IDS_ITEM_0302, ///< \ru Поверхность заметания. \en Sweep Surface. + IDS_ITEM_0303, ///< \ru Поверхность сдвига. \en Motion Surface. + IDS_ITEM_0304, ///< \ru Поверхность выдавливания. \en Extrusion Surface. + IDS_ITEM_0305, ///< \ru Поверхность вращения. \en Revolution Surface. + IDS_ITEM_0306, ///< \ru Линейчатая поверхность. \en Ruled Surface. + IDS_ITEM_0307, ///< \ru Поверхность по кривой и точке. \en Surface Defined by a Curve and a Point. + IDS_ITEM_0308, ///< \ru Четырехугольная поверхность. \en Quadrangular Surface. + IDS_ITEM_0309, ///< \ru Треугольная поверхность. \en Triangular Surface. + IDS_ITEM_0310, ///< \ru Поверхность движения с доворотом. \en Sweep with Guide Curve Surface with Rotating Ends. + IDS_ITEM_0311, ///< \ru Поверхность на семействе кривых и напрвляющей. \en Loft Surface with Guide Curve. + IDS_ITEM_0312, ///< \ru Спиральная поверхность. \en Spiral Surface. + IDS_ITEM_0313, ///< \ru Цилиндрически согнутая поверхность. \en Cylindrically Bent Surface. + IDS_ITEM_0314, ///< \ru Цилиндрически разогнутая поверхность. \en Cylindrically Unbent Surface. + IDS_ITEM_0315, ///< \ru Конически согнутая поверхность. \en Conically Bent Surface. + IDS_ITEM_0316, ///< \ru Конически разогнутая поверхность. \en Conically Unbent Surface. + IDS_ITEM_0317, ///< \ru Поверхность заметания с изменением образующей. \en Sweep Surface with Changin Generatin. + IDS_ITEM_0319, ///< \ru Плоскость. \en Plane. + IDS_ITEM_0320, ///< \ru Сферическая поверхность. \en Spherical Surface. + IDS_ITEM_0321, ///< \ru Тороидальная поверхность. \en Toroidal Surface. + IDS_ITEM_0322, ///< \ru Цилиндрическая поверхность. \en Cylindrical Surface. + IDS_ITEM_0323, ///< \ru Коническая поверхность. \en Conical Surface. + IDS_ITEM_0325, ///< \ru NURBS поверхность. \en NURBS Surface. + IDS_ITEM_0326, ///< \ru Треугольная NURBS поверхность. \en Triangular NURBS Surface. + IDS_ITEM_0327, ///< \ru Поверхность Безье. \en Bezier Surface. + IDS_ITEM_0328, ///< \ru Эквидистантная поверхность. \en Offset Surface. + IDS_ITEM_0329, ///< \ru Деформированная поверхность. \en Deformed Surface. + IDS_ITEM_0330, ///< \ru Поверхность Грегори. \en Gregory Surface. + IDS_ITEM_0331, ///< \ru Поверхность соединения. \en Joint Surface. + IDS_ITEM_0332, ///< \ru Поверхность объединения. \en Join Surface. + IDS_ITEM_0333, ///< \ru Поверхность на трех кривых. \en Surface Based on Three Curves. + IDS_ITEM_0334, ///< \ru Поверхность на четырех кривых. \en Surface Based on Four Curves. + IDS_ITEM_0335, ///< \ru Поверхность-фаска. \en Chamfer Surface. + IDS_ITEM_0336, ///< \ru Поверхность скругления. \en Fillet Surface. + IDS_ITEM_0337, ///< \ru Переменная поверхность скругления. \en Variable Fillet Surface. + IDS_ITEM_0338, ///< \ru Поверхность на семействе кривых. \en Lofted Surface. + IDS_ITEM_0339, ///< \ru Поверхность на сетке кривых. \en Surface defined on a mesh of curves. + IDS_ITEM_0340, ///< \ru Поверхность скругления по кромке. \en Surface of Fillet by Border. + IDS_ITEM_0341, ///< \ru Поверхность по замкнутому контуру. \en Surface on Closed Contour. + IDS_ITEM_0342, ///< \ru Плазовая поверхность. \en Spiling Surface. + IDS_ITEM_0343, ///< \ru Поверхность Кунса. \en Coons Surface. + IDS_ITEM_0345, ///< \ru Поверхность на сетке точек. \en Surface Based on a Point Grid. + IDS_ITEM_0346, ///< \ru Треугольная поверхность Безье. \en Triangular Bezier Surface. + IDS_ITEM_0349, ///< \ru Усеченная контурами поверхность. \en Curve bounded Surface. + IDS_ITEM_0350, ///< \ru Поверхность-копия. \en Copy Surface. + IDS_ITEM_0351, ///< \ru Поверхность соединения. \en Joint Surface. + IDS_ITEM_0352, ///< \ru Поверхность полного скругления. \en Full Fillet Surface. + IDS_ITEM_0353, ///< \ru Поверхность заметания с масштабированием. \en Swept Surface with Scaling. + IDS_ITEM_0354, ///< \ru Поверхность переменного сечения. \en The Swept Mutable Section Surface. + +// \ru Типы тел \en Types of solids + + IDS_ITEM_0401, ///< \ru Тело. \en Solid. + IDS_ITEM_0402, ///< \ru Оболочка. \en Shell. + IDS_ITEM_0403, ///< \ru Проволочный каркас. \en Wire Frame. + IDS_ITEM_0404, ///< \ru Точечный каркас. \en Point Frame. + IDS_ITEM_0405, ///< \ru Коллекция элементов. \en Collection of Elements. + +// \ru Типы строителей. \en Types of creators. + + IDS_ITEM_0501, ///< \ru Журнал построения. \en Build log. + IDS_ITEM_0502, ///< \ru Шар. \en Sphere. + IDS_ITEM_0503, ///< \ru Тор. \en Torus. + IDS_ITEM_0504, ///< \ru Цилиндр. \en Cylinder. + IDS_ITEM_0505, ///< \ru Конус. \en Cone. + IDS_ITEM_0506, ///< \ru Блок. \en Block. + IDS_ITEM_0507, ///< \ru Клин. \en Wedge. + IDS_ITEM_0508, ///< \ru Призма. \en Prism. + IDS_ITEM_0509, ///< \ru Пирамида. \en Pyramid. + IDS_ITEM_0510, ///< \ru Твёрдое тело. \en Solid. + IDS_ITEM_0511, ///< \ru Плита. \en Plate. + IDS_ITEM_0512, ///< \ru Икосаэдр. \en Icosahedron. + IDS_ITEM_0513, ///< \ru Многогранник. \en Polyhedron. + + IDS_ITEM_0515, ///< \ru Объединение оболочек. \en Shells union. + IDS_ITEM_0516, ///< \ru Пересечение оболочек. \en Shells intersection. + IDS_ITEM_0517, ///< \ru Разность оболочек. \en Shells subtraction. + + IDS_ITEM_0520, ///< \ru Отверстие. \en Hole. + IDS_ITEM_0521, ///< \ru Карман/Бобышка. \en Pocket/Boss. + IDS_ITEM_0522, ///< \ru Паз. \en Groove. + IDS_ITEM_0523, ///< \ru Заплатка. \en Patch. + IDS_ITEM_0524, ///< \ru Тонкая оболочка. \en Thin Shell. + + IDS_ITEM_0526, ///< \ru Оболочка на семействе кривых. \en Shell Defined by a Set of Curves. + IDS_ITEM_0527, ///< \ru Продолженная оболочка. \en Extended Shell. + IDS_ITEM_0528, ///< \ru Эквидистантная оболочка. \en Offset Shell. + IDS_ITEM_0529, ///< \ru Срединная оболочка. \en Median Shell. + IDS_ITEM_0530, ///< \ru Оболочка переменного сечения. \en The Swept Mutable Section Shell. + IDS_ITEM_0531, ///< \ru Булево объединение тел. \en Boolean Union of Solids. + IDS_ITEM_0532, ///< \ru Булево пересечение тел. \en Boolean Intersection of Solids. + IDS_ITEM_0533, ///< \ru Булевa разность тел. \en Boolean Subtraction of Solids. + IDS_ITEM_0534, ///< \ru Разрезанное тело. \en Cut Solid. + IDS_ITEM_0535, ///< \ru Фаски ребер. \en Edges Chamfers. + IDS_ITEM_0536, ///< \ru Скругление ребер. \en Edges Fillets. + IDS_ITEM_0537, ///< \ru Симметричное тело. \en Symmetric Solid. + IDS_ITEM_0538, ///< \ru Оболочечное тело. \en Thin Shell Solid. + IDS_ITEM_0539, ///< \ru Тело приданием толщины. \en Solid of Thickening. + IDS_ITEM_0540, ///< \ru Оболочка с удалёнными гранями. \en Shell with Removed Faces. + IDS_ITEM_0541, ///< \ru Коробчатое тело. \en Box-like Solid. + IDS_ITEM_0542, ///< \ru Кинематическое тело. \en Sweeping Solid. + IDS_ITEM_0543, ///< \ru Тело заметания. \en Swept Solid. + IDS_ITEM_0544, ///< \ru Тело выдавливания. \en Extrusion Solid. + IDS_ITEM_0545, ///< \ru Тело вращения. \en Revolution Solid. + IDS_ITEM_0546, ///< \ru Тело по сечениям. \en Loft Solid. + IDS_ITEM_0547, ///< \ru Простое тело. \en Simple Solid. + IDS_ITEM_0548, ///< \ru Ребро жесткости тела. \en Rib of a Solid. + IDS_ITEM_0549, ///< \ru Набор тел. \en Set of solids. + IDS_ITEM_0550, ///< \ru Часть набора тел. \en Set of solids part. + IDS_ITEM_0551, ///< \ru Клон граней тела. \en Solid's faces drafting. + IDS_ITEM_0552, ///< \ru Разбивка граней тела. \en Splitting of Solid's faces. + IDS_ITEM_0553, ///< \ru Сшитое из оболочек тело. \en Stitched Solid. + IDS_ITEM_0554, ///< \ru Сшитая из оболочек оболочка. \en Shell stitched from shells. + IDS_ITEM_0555, ///< \ru Оболочка из NURBS-поверхностей. \en Shell from NURBS-surfaces. + IDS_ITEM_0556, ///< \ru Трансформированное тело. \en Transformed Solid. + IDS_ITEM_0557, ///< \ru Модифицированное тело. \en Modified Solid. + IDS_ITEM_0558, ///< \ru Оболочка из линейчатых поверхностей. \en Shell from ruled surfaces. + IDS_ITEM_0559, ///< \ru Усеченная оболочка. \en Truncated Shell. + IDS_ITEM_0560, ///< \ru Оболочка соединения. \en Joint Shell. + IDS_ITEM_0561, ///< \ru Тело с восстановленными боковыми рёбрами. \en Solid with restored lateral edges. + IDS_ITEM_0562, ///< \ru Объединение с кинематическим телом. \en Union with a sweeping Solid. + IDS_ITEM_0563, ///< \ru Модифицированное тело NURBS-поверхностями. \en Solid modified with NURBS-surfaces. + IDS_ITEM_0564, ///< \ru Объединение с телом выдавливания. \en Union with an extrusion Solid. + IDS_ITEM_0565, ///< \ru Объединение с телом вращения. \en Union with a revolution Solid. + IDS_ITEM_0566, ///< \ru Объединение с телом по сечениям. \en Union with a lofted Solid. + IDS_ITEM_0567, ///< \ru Объединение с простым телом. \en Union with an elementary Solid. + IDS_ITEM_0568, ///< \ru Модифицированная NURBS-поверхность грани. \en Modified NURBS-surface of the face. + IDS_ITEM_0569, ///< \ru Объединение с набором тел. \en Union with a Solid set. + IDS_ITEM_0570, ///< \ru Оболочка грани соединения. \en Joint face Shell. + IDS_ITEM_0571, ///< \ru Скругление граней. \en Faces fillet. + IDS_ITEM_0572, ///< \ru Разность с кинематическим телом. \en Subtraction with a sweeping Solid. + IDS_ITEM_0573, ///< \ru Удаление результата операции. \en Delete the result of the operation. + IDS_ITEM_0574, ///< \ru Разность с телом выдавливания. \en Subtraction with an extrusion Solid. + IDS_ITEM_0575, ///< \ru Разность с телом вращения. \en Subtraction with a revolution Solid. + IDS_ITEM_0576, ///< \ru Разность с телом по сечениям. \en Subtraction with a lofted Solid. + IDS_ITEM_0577, ///< \ru Разность с простым телом. \en Subtraction with an elementary Solid. + IDS_ITEM_0578, ///< \ru Упрощение развёртки. \en The flat pattern simplification. + IDS_ITEM_0579, ///< \ru Разность с набором тел. \en Subtraction with a Set of Solids. + IDS_ITEM_0580, ///< \ru Вывернутое тело. \en Reversed Solid. + IDS_ITEM_0581, ///< \ru Сгиб нелистового тела. \en Bend of a non-sheet Solid. + IDS_ITEM_0582, ///< \ru Пересечение с кинематическим телом. \en Intersection with a sweeping Solid. + IDS_ITEM_0583, ///< \ru Сферическая штамповка. \en Spherical stamping. + IDS_ITEM_0584, ///< \ru Пересечение с телом выдавливания. \en Intersection with an extrusion Solid. + IDS_ITEM_0585, ///< \ru Пересечение с телом вращения. \en Intersection with a revolution Solid. + IDS_ITEM_0586, ///< \ru Пересечение с телом по сечениям. \en Intersection with a lofted Solid. + IDS_ITEM_0587, ///< \ru Пересечение с простым телом. \en Intersection with an elementary Solid. + IDS_ITEM_0588, ///< \ru Обечайка. \en Ruled Shell. + IDS_ITEM_0589, ///< \ru Пересечение с набором тел. \en Intersection with a set of solids. + IDS_ITEM_0590, ///< \ru Оболочка по сети кривых. \en Shell defined by a mesh of curves. + IDS_ITEM_0591, ///< \ru Комбинированный сгиб. \en Combined bend. + IDS_ITEM_0592, ///< \ru Кинематическая оболочка. \en Sweep with guide curve Shell. + IDS_ITEM_0593, ///< \ru Жалюзи. \en Jalousie. + IDS_ITEM_0594, ///< \ru Оболочка выдавливания. \en Extrusion Shell. + IDS_ITEM_0595, ///< \ru Оболочка вращения. \en Revolution Shell. + IDS_ITEM_0596, ///< \ru Оболочка по сечениям. \en Loft Shell. + IDS_ITEM_0597, ///< \ru Тонкая оболочка. \en Thin Shell. + IDS_ITEM_0598, ///< \ru Разрезанная оболочка. \en Cut Shell. + IDS_ITEM_0599, ///< \ru Буртик. \en Bead. + IDS_ITEM_0600, ///< \ru Сгиб/разгиб листового тела. \en Bend/Unbend of a Sheet Solid. + IDS_ITEM_0601, ///< \ru Сгиб листового тела по отрезку. \en Bend of a sheet Solid by a segment. + IDS_ITEM_0602, ///< \ru Сгиб листового тела по рёбрам. \en Bend of a sheet Solid along edges. + IDS_ITEM_0603, ///< \ru Замыкание угла листового тела. \en Closure of a sheet Solid corner. + IDS_ITEM_0604, ///< \ru Листовое тело. \en Sheet Solid. + IDS_ITEM_0605, ///< \ru Пластина листового тела. \en Sheet Solid plate. + IDS_ITEM_0606, ///< \ru Вырез листового тела. \en Cut of a sheet Solid. + IDS_ITEM_0607, ///< \ru Пересечение листового тела. \en Sheet Solid intersection. + IDS_ITEM_0608, ///< \ru Подсечка листового тела. \en Jog of a sheet Solid. + IDS_ITEM_0609, ///< \ru Штамповка. \en Stamping. + +// \ru Способы построения оболочек. \en Shells construction methods. + + IDS_ITEM_0610, ///< \ru Оболочка. \en Shell. + IDS_ITEM_0611, ///< \ru Оболочка на базе поверхности. \en Shell based on a surface. + + IDS_ITEM_0614, ///< \ru Оболочка тела. \en Shell. + IDS_ITEM_0615, ///< \ru Журнал построения. \en Build log. + IDS_ITEM_0616, ///< \ru Атрибуты объекта. \en Object Attributes. + + IDS_ITEM_0620, ///< \ru Оболочка тела. \en Shell. + IDS_ITEM_0621, ///< \ru Ориентированное ребро цикла. \en Oriented edge of a loop. + IDS_ITEM_0622, ///< \ru Цикл грани. \en Face loop. + IDS_ITEM_0623, ///< \ru Грань оболочки. \en Face of a Shell. + IDS_ITEM_0624, ///< \ru Вершина. \en Vertex. + IDS_ITEM_0625, ///< \ru Ребро оболочки. \en Edge of a Shell. + IDS_ITEM_0626, ///< \ru Ребро каркаса. \en Edge of a frame. + + IDS_ITEM_0627, ///< \ru Вершина. \en Vertex. + + IDS_ITEM_0628, ///< \ru Разделенная оболочка. \en Divided Shell. + + IDS_ITEM_0630, ///< \ru Оболочка кругового сечения. \en Round Section Shell. + IDS_ITEM_0631, ///< \ru Оболочка лигнейчатого сечения. \en Ruled Section Shell. + IDS_ITEM_0632, ///< \ru Оболочка конического сечения. \en Conic Section Shell. + IDS_ITEM_0633, ///< \ru Оболочка кубического сечения. \en Cubic Section Shell. + IDS_ITEM_0634, ///< \ru Оболочка сплайнового сечения. \en Spline Section Shell. + +// \ru Способы построения проекций тела\оболочки. \en Solid/Shell projections creation methods. + + IDS_ITEM_0650, ///< \ru Проекция тела. \en Solid Projection. + IDS_ITEM_0651, ///< \ru Разрез тела. \en Solid Cutting. + IDS_ITEM_0652, ///< \ru Сечение тела. \en Solid Section. + + IDS_ITEM_0653, ///< \ru Размножение тела. \en Duplication of solids. + +// \ru Вспомогательный объект. \en The helper object. + + IDS_ITEM_0669, ///< \ru Вспомогательный объект. \en The helper object. + +// \ru Резьба. \en A thread. + + IDS_ITEM_0670, ///< \ru Резьба. \en Thread. + +// \ru Обозначение \en Notation + + IDS_ITEM_0671, ///< \ru Условное обозначение. \en Symbolic notation. + +// \ru Объекты. \en Objects. + + IDS_ITEM_0700, ///< \ru Геометрический объект. \en Geometric object. + IDS_ITEM_0701, ///< \ru Переменная уравнения. \en Equation variable. + IDS_ITEM_0702, ///< \ru Объект на плоскости. \en Object on a plane. + IDS_ITEM_0703, ///< \ru Объект в пространстве. \en Object in space. + IDS_ITEM_0704, ///< \ru Объект модели. \en Model object. + IDS_ITEM_0705, ///< \ru Сборочная единица. \en Assembly unit. + IDS_ITEM_0706, ///< \ru Вспомогательный объект. \en Auxiliary object. + IDS_ITEM_0707, ///< \ru Вставка объекта. \en Object instance. + IDS_ITEM_0708, ///< \ru Количество элементов. \en Number of elements. + IDS_ITEM_0709, ///< \ru Геометрическая модель. \en Geometric model. + +// \ru Атрибуты \en Attributes + + IDS_ITEM_0729, ///< \ru Атрибуты модели. \en Model attributes. + IDS_ITEM_0730, ///< \ru Поставщик атрибутов. \en Attributes provider. + IDS_ITEM_0731, ///< \ru Атрибуты объекта. \en Object attributes. + IDS_ITEM_0732, ///< \ru Атрибут. \en Attribute. + IDS_ITEM_0733, ///< \ru Имя примитива. \en Primitive name. + IDS_ITEM_0734, ///< \ru Поведение атрибутов. \en Attributes behavior. + + IDS_ITEM_0751, ///< \ru Механические характеристики. \en Mechanical properties. + IDS_ITEM_0754, ///< \ru Деформации. \en Strains. + + IDS_ITEM_0761, ///< \ru Исполнение (вариант реализации модели). \en Embodiment (variant of model implementation). + IDS_ITEM_0762, ///< \ru Количество u-линий и v-линий отрисовочной сетки. \en The number of u-mesh and v-mesh lines. + IDS_ITEM_0763, ///< \ru Плотность. \en Density. + IDS_ITEM_0764, ///< \ru Цвет. \en Color. + IDS_ITEM_0765, ///< \ru Толщина. \en Thickness. + IDS_ITEM_0766, ///< \ru Стиль. \en Style. + IDS_ITEM_0767, ///< \ru Визуальные свойства. \en Visual properties. + IDS_ITEM_0768, ///< \ru Идентификатор. \en Identifier. + IDS_ITEM_0769, ///< \ru Селектированность. \en Selectivity. + IDS_ITEM_0770, ///< \ru Видимость. \en Visibility. + IDS_ITEM_0771, ///< \ru Измененность. \en Modification. + IDS_ITEM_0772, ///< \ru Топологическое имя. \en Topological name. + IDS_ITEM_0773, ///< \ru Якорь. \en Anchor. + IDS_ITEM_0774, ///< \ru Геометрический атрибут. \en Geometric attribute. + IDS_ITEM_0775, ///< \ru Метка времени обновления. \en Label of update time. + IDS_ITEM_0776, ///< \ru Уникальность ключей. \en Keys uniqueness. + IDS_ITEM_0777, ///< \ru Имя объекта в модели. \en Name of object in the model. + IDS_ITEM_0778, ///< \ru Данные об изделии. \en Product data. + IDS_ITEM_0779, ///< \ru Атрибут ребра жесткости листового тела. \en Attribute of stamp rib of sheet solid. + IDS_ITEM_0780, ///< \ru Атрибут отбортовки листового тела. \en Swept flange attribute of a sheet solid. + + IDS_ITEM_0782, ///< \ru Атрибут пользовательский. \en Custom attribute. + IDS_ITEM_0783, ///< \ru Атрибут обобщенный. \en Generalized attribute. + IDS_ITEM_0784, ///< \ru Атрибут булев. \en Boolean attribute. + IDS_ITEM_0785, ///< \ru Атрибут целочисленный (32-битный). \en (32 bit ) Integer attribute. + IDS_ITEM_0786, ///< \ru Атрибут действительный. \en Real attribute. + IDS_ITEM_0787, ///< \ru Атрибут строковый. \en String attribute. + IDS_ITEM_0788, ///< \ru Атрибут элементарный. \en Elementary attribute. + IDS_ITEM_0789, ///< \ru Пояснение. \en Prompt. + IDS_ITEM_0790, ///< \ru Атрибут int64. \en Int64 attribute. + IDS_ITEM_0791, ///< \ru Атрибут бинарный. \en Binary attribute. + +// \ru Сообщения. \en Messages. + + IDS_ITEM_0900, ///< \ru ! Ошибка !. \en ! Error ! + IDS_ITEM_0901, ///< \ru Остановлено. \en Stopped. + IDS_ITEM_0902, ///< \ru Пропущено. \en Missed. + +// \ru Состав объектов \en Structure of objects + + IDS_PROP_0000, ///< \ru Пусто. \en Empty. + + IDS_PROP_0001, ///< \ru Кривая на плоскости. \en Curve on a plane. + IDS_PROP_0002, ///< \ru Параметр кривой. \en Parameter of a curve. + IDS_PROP_0003, ///< \ru Кривая 1 на плоскости. \en Curve 1 on the plane. + IDS_PROP_0004, ///< \ru Кривая 2 на плоскости. \en Curve 2 on the plane. + IDS_PROP_0005, ///< \ru Параметр кривой 1. \en Parameter of curve 1. + IDS_PROP_0006, ///< \ru Параметр кривой 2. \en Parameter of curve 2. + IDS_PROP_0007, ///< \ru Начальная точка. \en Start point. + IDS_PROP_0008, ///< \ru Направление. \en Direction. + IDS_PROP_0009, ///< \ru Конечная точка. \en End point. + IDS_PROP_0014, ///< \ru Начальный параметр усечения. \en Start parameter of truncation. + IDS_PROP_0015, ///< \ru Конечный параметр усечения. \en End parameter of truncation. + IDS_PROP_0016, ///< \ru Совпадение направления. \en Coincidence of direction. + IDS_PROP_0017, ///< \ru Циклическая частота. \en Cyclic frequency. + IDS_PROP_0018, ///< \ru Начальная фаза (град). \en Initial phase (degrees). + IDS_PROP_0019, ///< \ru Амплитуда. \en Amplitude. + IDS_PROP_0020, ///< \ru Перевернуть направление. \en Reverse the direction. + IDS_PROP_0021, ///< \ru Неподвижная точка. \en Fixed point. + IDS_PROP_0022, ///< \ru Использовать точку. \en Use a point. + IDS_PROP_0023, ///< \ru Равномерное преобразование. \en Uniform transformation. + IDS_PROP_0024, ///< \ru Точность. \en Tolerance. + IDS_PROP_0025, ///< \ru Начальное значение. \en Start value. + IDS_PROP_0026, ///< \ru Коэффициент усиления. \en Scale gain. + IDS_PROP_0027, ///< \ru Амплитуда. \en Amplitude. + IDS_PROP_0028, ///< \ru Сдвиг параметра. \en Parameter shift. + IDS_PROP_0029, ///< \ru Степень возведения. \en Exponent parameter. + IDS_PROP_0030, ///< \ru Циклическая частота. \en Frequency. + IDS_PROP_0031, ///< \ru Эквидистантное смещение. \en The offset range. + IDS_PROP_0032, ///< \ru Тип кривой. \en Type of curve. + + IDS_PROP_0033, ///< \ru Прогиб. \en The sag. + IDS_PROP_0034, ///< \ru Угловое отклонение. \en The angular deviation. + IDS_PROP_0035, ///< \ru Расстояние. \en The distance. + + IDS_PROP_0101, ///< \ru Координата X. \en Coordinate X. + IDS_PROP_0102, ///< \ru Координата Y. \en Coordinate Y. + IDS_PROP_0103, ///< \ru Координата Z. \en Coordinate Z. + IDS_PROP_0104, ///< \ru Матрица. \en Matrix. + IDS_PROP_0107, ///< \ru Угол с осью X. \en Angle with axis X. + IDS_PROP_0108, ///< \ru Угол с осью Y. \en Angle with axis Y. + IDS_PROP_0109, ///< \ru Угол с осью Z. \en Angle with axis Z. + IDS_PROP_0110, ///< \ru Точка. \en Point. + IDS_PROP_0111, ///< \ru Компонента 1.X. \en Component 1.X. + IDS_PROP_0112, ///< \ru Компонента 1.Y. \en Component 1.Y. + IDS_PROP_0113, ///< \ru Компонента 1.Z. \en Component 1.Z. + IDS_PROP_0114, ///< \ru Компонента 2.X. \en Component 2.X. + IDS_PROP_0115, ///< \ru Компонента 2.Y. \en Component 2.Y. + IDS_PROP_0116, ///< \ru Компонента 2.Z. \en Component 2.Z. + IDS_PROP_0117, ///< \ru Компонента 3.X. \en Component 3.X. + IDS_PROP_0118, ///< \ru Компонента 3.Y. \en Component 3.Y. + IDS_PROP_0119, ///< \ru Компонента 3.Z. \en Component 3.Z. + IDS_PROP_0120, ///< \ru Вектор. \en Vector. + IDS_PROP_0121, ///< \ru Сдвиг по X. \en Shift by X. + IDS_PROP_0122, ///< \ru Сдвиг по Y. \en Shift by Y. + IDS_PROP_0123, ///< \ru Сдвиг по Z. \en Shift by Z. + IDS_PROP_0124, ///< \ru Ось X. \en Axis X. + IDS_PROP_0125, ///< \ru Ось Y. \en Axis Y. + IDS_PROP_0126, ///< \ru Ось Z. \en Axis Z. + IDS_PROP_0127, ///< \ru Длина оси X. \en Length of axis X. + IDS_PROP_0128, ///< \ru Длина оси Y. \en Length of axis Y. + IDS_PROP_0129, ///< \ru Длина оси Z. \en Length of axis Z. + IDS_PROP_0130, ///< \ru Центр. \en Center. + IDS_PROP_0131, ///< \ru Левая система. \en Left system. + IDS_PROP_0132, ///< \ru Прямоугольная. \en Rectangular. + IDS_PROP_0133, ///< \ru Базовая точка. \en Base point. + IDS_PROP_0134, ///< \ru Строка. \en String. + IDS_PROP_0135, ///< \ru Шрифт. \en Font. + IDS_PROP_0136, ///< \ru Позиция начала. \en Start position. + IDS_PROP_0137, ///< \ru Высота. \en Height. + IDS_PROP_0138, ///< \ru Сужение. \en Taper. + IDS_PROP_0139, ///< \ru Угол наклона. \en Slope angle. + IDS_PROP_0140, ///< \ru Радиус. \en Radius. + IDS_PROP_0141, ///< \ru Масштаб по X. \en Scale by X. + IDS_PROP_0142, ///< \ru Масштаб по Y. \en Scale by Y. + IDS_PROP_0143, ///< \ru Масштаб по Z. \en Scale by Z. + IDS_PROP_0144, ///< \ru Функция масштабирования. \en The function of scaling. + IDS_PROP_0145, ///< \ru Функция вращения. \en The function of rotation. + IDS_PROP_0146, ///< \ru Толерантность. \en The tolerance. + + IDS_PROP_0150, ///< \ru Угол. \en Angle. + IDS_PROP_0151, ///< \ru Шаг. \en Step. + IDS_PROP_0152, ///< \ru Смещение зазора. \en Gap displacement. + IDS_PROP_0153, ///< \ru Перемещение. \en Translation. + IDS_PROP_0154, ///< \ru Вращение. \en Rotation. + IDS_PROP_0155, ///< \ru Общий масштаб. \en Common scale. + IDS_PROP_0156, ///< \ru Зеркальность. \en Specularity. + IDS_PROP_0157, ///< \ru Только ортогональность. \en Orthogonality only. + IDS_PROP_0158, ///< \ru Объект общего вида. \en General object. + IDS_PROP_0159, ///< \ru Перспектива. \en Perspective. + IDS_PROP_0160, ///< \ru Локальная система координат. \en Local coordinate system. + IDS_PROP_0161, ///< \ru Начальное значение. \en Start value. + IDS_PROP_0162, ///< \ru Конечное значение. \en End value. + IDS_PROP_0163, ///< \ru Значение. \en Value. + IDS_PROP_0164, ///< \ru Функция изменения радиусов. \en Radius function. + IDS_PROP_0165, ///< \ru Функция изменения веса. \en Weight function. + IDS_PROP_0166, ///< \ru Функция. \en Function. + IDS_PROP_0167, ///< \ru Минимум. \en Minimum. + IDS_PROP_0168, ///< \ru Максимум. \en Maximum + + IDS_PROP_0169, ///< \ru Продлевать вверх?. \en Extend upwards?. + IDS_PROP_0170, ///< \ru Поворот оси вокруг нормали. \en Rotation of axis about the normal. + IDS_PROP_0171, ///< \ru Угол между осью и нормалью. \en Angle between the axis and the normal. + IDS_PROP_0172, ///< \ru Диаметр головки. \en Cap diameter. + IDS_PROP_0173, ///< \ru Глубина под головку. \en Depth for a cap. + IDS_PROP_0174, ///< \ru Угол фаски под головку. \en Chamfer angle for a cap. + IDS_PROP_0175, ///< \ru Диаметр отверстия под резьбу. \en Diameter of hole for a cap. + IDS_PROP_0176, ///< \ru Глубина отверстия под резьбу. \en Depth of hole for a cap. + IDS_PROP_0177, ///< \ru Угол конусности отверстия. \en Taper angle of hole. + IDS_PROP_0178, ///< \ru Угол раствора конца отверстия. \en Apical angle of hole end. + IDS_PROP_0179, ///< \ru Тип отверстия. \en Hole type. + IDS_PROP_0180, ///< \ru Способ модификации. \en Method of modification. + + IDS_PROP_0181, ///< \ru Тип(true-бобышка, false- карман). \en Type(true-boss, false- pocket). + IDS_PROP_0182, ///< \ru Тип. \en Type. + IDS_PROP_0183, ///< \ru Ширина. \en Width. + IDS_PROP_0184, ///< \ru Ширина. \en Width. + IDS_PROP_0185, ///< \ru Способ. \en Method. + IDS_PROP_0186, ///< \ru Новая грань. \en New face. + + IDS_PROP_0188, ///< \ru Направление (вниз/вверх). \en Direction (down/up). + IDS_PROP_0189, ///< \ru Радиус дуги. \en Arc radius. + IDS_PROP_0190, ///< \ru Функция дискриминанта. \en Discriminant function. + +// \ru Параметры. \en Parameters. + + IDS_PROP_0201, ///< \ru Объект на числах double. \en Object on double data. + IDS_PROP_0202, ///< \ru Объект на числах float. \en Object on float data. + IDS_PROP_0203, ///< \ru Кривая 1. \en Curve 1. + IDS_PROP_0204, ///< \ru Кривая 2. \en Curve 2. + IDS_PROP_0205, ///< \ru Кривая 3. \en Curve 3. + IDS_PROP_0206, ///< \ru Кривая 4. \en Curve 4 + IDS_PROP_0207, ///< \ru Начальная точка. \en Start point. + IDS_PROP_0208, ///< \ru Направляющий вектор. \en Direction vector. + IDS_PROP_0209, ///< \ru Конечная точка. \en End point. + IDS_PROP_0210, ///< \ru Нормаль к плоскости.\en Normal to surface. + IDS_PROP_0211, ///< \ru Первая полуось. \en First semiaxis. + IDS_PROP_0212, ///< \ru Вторая полуось. \en Second semiaxis. + IDS_PROP_0213, ///< \ru Фокусное расстояние. \en Focal distance. + IDS_PROP_0214, ///< \ru Параметр min. \en Parameter min. + IDS_PROP_0215, ///< \ru Параметр max. \en Parameter max. + IDS_PROP_0216, ///< \ru Действительная полуось. \en Real semiaxis. + IDS_PROP_0217, ///< \ru Мнимая полуось. \en Imaginary semiaxis. + IDS_PROP_0218, ///< \ru Приращение начального параметра. \en Increment of start parameter. + IDS_PROP_0219, ///< \ru Приращение конечного параметра. \en Increment of end parameter. + IDS_PROP_0220, ///< \ru Замкнутость. \en Closedness. + IDS_PROP_0221, ///< \ru Порядок. \en Order. + IDS_PROP_0222, ///< \ru Количество точек. \en Number of points. + IDS_PROP_0223, ///< \ru Точка. \en Point. + IDS_PROP_0224, ///< \ru Начальный параметр. \en Start parameter. + IDS_PROP_0225, ///< \ru Производная параметра. \en Derivative of parameter. + IDS_PROP_0226, ///< \ru Параметрическая длина. \en Parametric length. + IDS_PROP_0227, ///< \ru Положительное направление. \en Positive direction. + IDS_PROP_0228, ///< \ru Вес. \en Weight. + // IDS_PROP_0229, ///< \ru Касание. \en Tangency. (МА 2019 Лишнее?) + IDS_PROP_0230, ///< \ru Расстояние. \en Distance. + IDS_PROP_0232, ///< \ru Число кривых контура. \en Number of curves of contour. + IDS_PROP_0233, ///< \ru Кривая. \en Curve. + IDS_PROP_0234, ///< \ru Начальный параметр кривой. \en Start parameter of a curve. + IDS_PROP_0235, ///< \ru Конечный параметр кривой. \en End parameter of a curve. + IDS_PROP_0236, ///< \ru Число узлов. \en Number of knots. + IDS_PROP_0237, ///< \ru Значение узла. \en Knot value. + IDS_PROP_0238, ///< \ru Вторая производная в точке. \en Second derivative at a point. + IDS_PROP_0239, ///< \ru Производная. \en Derivative. + IDS_PROP_0240, ///< \ru Опорная кривая. \en Reference curve. + IDS_PROP_0241, ///< \ru Зависимость по X. \en Dependency by X. + IDS_PROP_0242, ///< \ru Зависимость по Y. \en Dependency by Y. + IDS_PROP_0243, ///< \ru Зависимость по Z. \en Dependency by Z. + IDS_PROP_0244, ///< \ru Расширение minPar. \en Extension minPar. + IDS_PROP_0245, ///< \ru Расширение maxPar. \en Extension maxPar. + IDS_PROP_0246, ///< \ru Количество сплайнов. \en Number of splines. + IDS_PROP_0250, ///< \ru Базовая кривая. \en Base curve. + IDS_PROP_0260, ///< \ru Двумерная кривая. \en Two-dimensional curve. + IDS_PROP_0263, ///< \ru Угол между OX плоскости и прямой касания ее с конусом. \en Angle of OX axis of a plane and the line of its tangency with a cone. + IDS_PROP_0264, ///< \ru Расстояние от листовой грани до нейтрального слоя. \en Distance from a sheet face to the neutral layer. + IDS_PROP_0265, ///< \ru Угол образующей конуса, касательной к плоскости при разгибе. \en Angle of a cone generatrix tangent to the plane while unbending. + IDS_PROP_0266, ///< \ru Количество образующих кривых. \en Number of generating lines. + IDS_PROP_0267, ///< \ru Объект модифицирован. \en Object is modified. + IDS_PROP_0268, ///< \ru Количество нормалей. \en Number of normals. + IDS_PROP_0269, ///< \ru Нормаль. \en Normal. + IDS_PROP_0270, ///< \ru Аппроксимационная кривая. \en Approximation curve. + + IDS_PROP_0271, ///< \ru Удаление выбранных граней. \en Remove selected faces. + IDS_PROP_0272, ///< \ru Создание тела из выбранных граней. \en Solid creation by selected faces. + IDS_PROP_0273, ///< \ru Перемещение выбранных граней. \en Move selected faces. + IDS_PROP_0274, ///< \ru Смещение выбранных граней по нормали. \en Offset selected faces. + IDS_PROP_0275, ///< \ru Изменение радиусов выбранных скруглений. \en Change selected fillets. + IDS_PROP_0276, ///< \ru Замена выбранных граней деформируемыми. \en Replace selected faces by deformed. + IDS_PROP_0277, ///< \ru Удаление выбранных скруглений. \en Remove selected features. + IDS_PROP_0278, ///< \ru Слияние вершин выбранных ребер. \en Merging vertices of selected edges. + IDS_PROP_0279, ///< \ru Замена гладко стыкующихся граней одной гранью. \en Replacing smoothly joined faces with one face. + + IDS_PROP_0282, ///< \ru Вектор модификации. \en The vector of modification. + IDS_PROP_0283, ///< \ru Количество модифицированных граней. \en Number of modified faces. + IDS_PROP_0284, ///< \ru Положение срединной оболочки тела. \en Position of median shell. + IDS_PROP_0285, ///< \ru Минимальное расстояние между гранями. \en Minimal equidistant value. + IDS_PROP_0286, ///< \ru Максимальное расстояние между гранями. \en Maximal equidistant value. + + IDS_PROP_0287, ///< \ru Окружность в сечении поверхности. \en The surface section is a circle. + IDS_PROP_0288, ///< \ru Отрезок прямой в сечении поверхности. \en The surface section is a line segment. + IDS_PROP_0289, ///< \ru Коническое сечение в сечении поверхности. \en The surface section is a conic section. + IDS_PROP_0290, ///< \ru Кубическая кривая в сечении поверхности. \en The surface section is a cubic curve. + IDS_PROP_0291, ///< \ru Сплайн в сечении поверхности. \en The surface section is a spline. + + IDS_PROP_0301, ///< \ru Начальный параметр U. \en Start parameter U. + IDS_PROP_0302, ///< \ru Начальный параметр V. \en Start parameter V. + IDS_PROP_0303, ///< \ru Положительное направление U. \en Positive direction by U. + IDS_PROP_0304, ///< \ru Положительное направление V. \en Positive direction by V. + IDS_PROP_0305, ///< \ru Параметрическая длина U. \en Parametric length by U. + IDS_PROP_0306, ///< \ru Параметрическая длина V. \en Parametric length by V. + IDS_PROP_0307, ///< \ru Положительное направление 1. \en Positive direction 1. + IDS_PROP_0308, ///< \ru Положительное направление 2. \en Positive direction 2. + IDS_PROP_0309, ///< \ru Тип сопряжения. \en Conjugation type. + IDS_PROP_0310, ///< \ru Точка. \en Point. + IDS_PROP_0311, ///< \ru Замкнутость по U. \en Closedness by U. + IDS_PROP_0312, ///< \ru Замкнутость по V. \en Closedness by V. + IDS_PROP_0313, ///< \ru Число порций по U. \en Number of portions by U. + IDS_PROP_0314, ///< \ru Число порций по V. \en Number of portions by V. + IDS_PROP_0315, ///< \ru Порядок по U. \en Order by U. + IDS_PROP_0316, ///< \ru Порядок по V. \en Order by V. + IDS_PROP_0317, ///< \ru Число точек по U. \en Number of points by U. + IDS_PROP_0318, ///< \ru Число точек по V. \en Number of points by V. + IDS_PROP_0320, ///< \ru Локальная система. \en Local system. + IDS_PROP_0321, ///< \ru Радиус основания. \en Radius of base. + IDS_PROP_0322, ///< \ru Высота. \en Height. + IDS_PROP_0323, ///< \ru Половина угла. \en Half-angle. + IDS_PROP_0324, ///< \ru Большой радиус. \en Major radius. + IDS_PROP_0325, ///< \ru Малый радиус. \en Minor radius. + IDS_PROP_0326, ///< \ru Длина. \en Length. + IDS_PROP_0327, ///< \ru Смещение. \en Shift. + IDS_PROP_0328, ///< \ru Форма. \en Shape. + IDS_PROP_0329, ///< \ru Закрепление границы поверхности. \en Surface boundary fixation. + IDS_PROP_0330, ///< \ru Отличается от базовой поверхности. \en Differs from the base surface. + IDS_PROP_0331, ///< \ru Видимая длина Xmin. \en Visible length Xmin. + IDS_PROP_0332, ///< \ru Видимая длина Ymin. \en Visible length Ymin. + IDS_PROP_0333, ///< \ru Видимая длина Xmax. \en Visible length Xmax. + IDS_PROP_0334, ///< \ru Видимая длина Ymax. \en Visible length Ymax. + IDS_PROP_0336, ///< \ru Число узлов по U. \en Number of knots by U. + IDS_PROP_0337, ///< \ru Значение U узла. \en Value of U knot. + IDS_PROP_0338, ///< \ru Число узлов по V. \en Number of knots by V. + IDS_PROP_0339, ///< \ru Значение V узла. \en Value of V knot. + IDS_PROP_0340, ///< \ru Поверхность. \en Surface. + IDS_PROP_0341, ///< \ru Направляющая кривая. \en Guide curve . + IDS_PROP_0342, ///< \ru Образующая кривая. \en Generating curve. + IDS_PROP_0343, ///< \ru Вектор смещения. \en Translation vector. + IDS_PROP_0344, ///< \ru Вектор направления. \en Direction vector. + IDS_PROP_0345, ///< \ru Точка оси вращения. \en Point of the rotation axis. + IDS_PROP_0346, ///< \ru Направление оси. \en Axis direction. + IDS_PROP_0347, ///< \ru Угол вращения. \en Rotation angle. + IDS_PROP_0348, ///< \ru Вершина. \en Vertex. + IDS_PROP_0350, ///< \ru Базовая поверхность. \en Base surface. + IDS_PROP_0351, ///< \ru Поверхность 1. \en Surface 1. + IDS_PROP_0352, ///< \ru Поверхность 2. \en Surface 2. + IDS_PROP_0353, ///< \ru Контур. \en Contour. + IDS_PROP_0354, ///< \ru Число контуров. \en Number of contours. + IDS_PROP_0355, ///< \ru Двумерный контур. \en Two-dimensional contour. + IDS_PROP_0356, ///< \ru Двумерная кривая. \en Two-dimensional curve. + IDS_PROP_0357, ///< \ru Контур 1. \en Contour 1. + IDS_PROP_0358, ///< \ru Контур 2. \en Contour 2. + IDS_PROP_0360, ///< \ru Плоскость. \en Plane. + IDS_PROP_0361, ///< \ru Вес поверхности 1. \en Weight of surface 1. + IDS_PROP_0362, ///< \ru Вес поверхности 2. \en Weight of surface 2. + IDS_PROP_0363, ///< \ru Производная в начале. \en Derivative at the beginning. + IDS_PROP_0364, ///< \ru Производная в конце. \en Derivative at the end. + IDS_PROP_0365, ///< \ru Установлена нормаль в начале. \en The normal is set at the beginning. + IDS_PROP_0366, ///< \ru Установлена нормаль в конце. \en The normal is set at the end. + IDS_PROP_0367, ///< \ru Множитель длины производной в начале. \en The derivative length modifier at the beginning. + IDS_PROP_0368, ///< \ru Множитель длины производной в конце. \en The derivative length modifier at the end. + IDS_PROP_0370, ///< \ru Кривая на поверхности 0. \en Curve on surface 0. + IDS_PROP_0371, ///< \ru Кривая на поверхности 1. \en Curve on surface 1. + IDS_PROP_0372, ///< \ru Кривая на поверхности 2. \en Curve on surface 2. + IDS_PROP_0373, ///< \ru Кривая вершин. \en Curve of vertices. + IDS_PROP_0374, ///< \ru Параметр Umin. \en Parameter Umin. + IDS_PROP_0375, ///< \ru Параметр Umax. \en Parameter Umax. + IDS_PROP_0376, ///< \ru Параметр Vmin. \en Parameter Vmin. + IDS_PROP_0377, ///< \ru Параметр Vmax. \en Parameter Vmax. + IDS_PROP_0378, ///< \ru Производная dU. \en Derivative dU. + IDS_PROP_0379, ///< \ru Производная dV. \en Derivative dV. + IDS_PROP_0380, ///< \ru Кромка проходит по Vmin. \en Boundary passes through Vmin. + IDS_PROP_0384, ///< \ru Расширение minUPar. \en Extension minUPar. + IDS_PROP_0385, ///< \ru Расширение maxUPar. \en Extension maxUPar. + IDS_PROP_0386, ///< \ru Расширение minVPar. \en Extension minVPar. + IDS_PROP_0387, ///< \ru Расширение maxVPar. \en Extension maxVPar. + IDS_PROP_0390, ///< \ru Число кривых. \en Number of curves. + IDS_PROP_0391, ///< \ru Кривая по U. \en Curve by U + IDS_PROP_0392, ///< \ru Кривая по V. \en Curve by V. + IDS_PROP_0393, ///< \ru Число кривых по U. \en Number of curves by U. + IDS_PROP_0394, ///< \ru Число кривых по V. \en Number of curves by V. + IDS_PROP_0395, ///< \ru Тип поверхности. \en Type of surface. + IDS_PROP_0397, ///< \ru Нейтральная плоскость. \en Neutral plane. + IDS_PROP_0398, ///< \ru Плоскость контура. \en Plane of the contour. + IDS_PROP_0399, ///< \ru Через элементы. \en Through elements. + IDS_PROP_0400, ///< \ru Сопряжение на границе. \en Conjugation on the boundary. + IDS_PROP_0401, ///< \ru Натяжение на границе. \en Tension on the boundary. + IDS_PROP_0402, ///< \ru Параметр определения длины производных. \en Parameter of derivatives length definition. + IDS_PROP_0403, ///< \ru Не сохранять длину производной. \en Do not keep the derivative length. + IDS_PROP_0404, ///< \ru Тип сопряжения (0-4). \en Conjugation type (0-4). + IDS_PROP_0405, ///< \ru Использовать готовый узловой вектор. \en Use prepared knot vector. + IDS_PROP_0406, ///< \ru Количество узлов. \en Number of knots. + IDS_PROP_0407, ///< \ru Проверка самопересечений. \en Check for self-intersections. + IDS_PROP_0408, ///< \ru Используется общий вес точек. \en The common weight of points is used. + IDS_PROP_0409, ///< \ru Построена по пласту точек. \en Build from a cloud of points. + IDS_PROP_0410, ///< \ru Построена по сети точек. \en Build from a mesh of points. + IDS_PROP_0411, ///< \ru В виде набора треугольников. \en As a set of triangles. + IDS_PROP_0412, ///< \ru Использовать проекционную кривую. \en Use projection curve. + IDS_PROP_0413, ///< \ru Усекать границами. \en Truncate by bounds. + IDS_PROP_0414, ///< \ru Привязка к началу. \en Binding to the beginning. + IDS_PROP_0415, ///< \ru Соединять скруглениями.\en Join by fillets. + IDS_PROP_0416, ///< \ru Сохранять радиус. \en Keep the radius. + IDS_PROP_0417, ///< \ru Притуплять острый угол. \en Blunt a sharp angle. + IDS_PROP_0418, ///< \ru Проверка пересечений. \en Check for intersections. + IDS_PROP_0419, ///< \ru Слияние подобных граней. \en Merging of similar faces. + IDS_PROP_0420, ///< \ru Слияние подобных ребер. \en Merging of similar edges. + + IDS_PROP_0421, ///< \ru Номер соседнего объекта. \en The number of neighbour object. + + IDS_PROP_0450, ///< \ru Начальный радиус (поверхность). \en Start radius (surface). + IDS_PROP_0451, ///< \ru Конечный радиус (резьба). \en End radius (thread). + IDS_PROP_0452, ///< \ru Длина резьбы. \en Thread length. + IDS_PROP_0453, ///< \ru Угол коничности резьбы. \en Taper angle of the thread. + + IDS_PROP_0461, ///< \ru Триангуляция. \en Triangulation. + IDS_PROP_0462, ///< \ru Количество точек триангуляции. \en Number of points of triangulation. + IDS_PROP_0463, ///< \ru Количество двумерных точек. \en Number of two-dimension points. + IDS_PROP_0464, ///< \ru Количество точек полигонов. \en Number of points of polygons. + + IDS_PROP_0501, ///< \ru Число вершин. \en Number of vertices. + IDS_PROP_0502, ///< \ru Число ребер. \en Number of edges. + IDS_PROP_0503, ///< \ru Число граней. \en Number of faces. + IDS_PROP_0504, ///< \ru Ориентация вершины. \en Vertex orientation. + IDS_PROP_0505, ///< \ru Ориентация ребра. \en Edge orientation. + IDS_PROP_0506, ///< \ru Ориентация грани. \en Face orientation. + IDS_PROP_0508, ///< \ru Ребро. \en Edge. + IDS_PROP_0509, ///< \ru Грань. \en Face. + IDS_PROP_0510, ///< \ru Число циклов. \en Number of loops. + IDS_PROP_0511, ///< \ru Цикл. \en Loop. + IDS_PROP_0512, ///< \ru Автоопределение. \en Automatic identification. + IDS_PROP_0513, ///< \ru Автоматическое. \en Automatic. + + IDS_PROP_0514, ///< \ru Тип размножения. \en Type of duplication. + IDS_PROP_0515, ///< \ru Кол-во шагов. \en Number of steps. + IDS_PROP_0516, ///< \ru Шаг. \en Step. + IDS_PROP_0517, ///< \ru Количество угловых шагов. \en Number of angular step. + IDS_PROP_0518, ///< \ru Элемент. \en Element. + IDS_PROP_0519, ///< \ru Сегмент полигональной сетки. \en Segment of polygonal mesh. + + IDS_PROP_0521, ///< \ru Длина Lx. \en Length Lx. + IDS_PROP_0522, ///< \ru Ширина Ly. \en Width Ly. + IDS_PROP_0523, ///< \ru Высота Lz. \en Height Lz. + IDS_PROP_0524, ///< \ru Малая длина lx. \en Minor length lx. + IDS_PROP_0525, ///< \ru Толщина. \en Thickness. + IDS_PROP_0526, ///< \ru Толщина стенки. \en Wall thickness. + IDS_PROP_0527, ///< \ru Число вскрытых граней. \en Number of opened faces. + IDS_PROP_0528, ///< \ru Форма. \en Shape. + IDS_PROP_0529, ///< \ru Сохранять кромку\поверхность\автоопределение. \en Keep the boundary\surface\auto. + IDS_PROP_0530, ///< \ru Продолжить далее. \en Continue. + IDS_PROP_0531, ///< \ru Катет 1. \en Cathetus 1. + IDS_PROP_0532, ///< \ru Катет 2. \en Cathetus 2. + IDS_PROP_0533, ///< \ru Число фасок. \en Number of chamfers. + IDS_PROP_0534, ///< \ru Число скруглений. \en Number of fillets. + IDS_PROP_0535, ///< \ru Радиус скругления. \en Fillet radius. + IDS_PROP_0536, ///< \ru Номер грани. \en Face number. + IDS_PROP_0537, ///< \ru Параметр U. \en Parameter U. + IDS_PROP_0538, ///< \ru Параметр V. \en Parameter V. + IDS_PROP_0539, ///< \ru Число модифицированных граней. \en Number of modified faces. + IDS_PROP_0540, ///< \ru Тело. \en Solid. + IDS_PROP_0541, ///< \ru Строитель тела. \en Construct solid. + IDS_PROP_0542, ///< \ru Количество четырёхугольников. \en Number of quadrangles. + IDS_PROP_0543, ///< \ru Остановка от начала. \en Termination from the start. + IDS_PROP_0544, ///< \ru Остановка до конца. \en Termination to the end. + IDS_PROP_0545, ///< \ru Радиус скругления 1. \en Fillet radius 1. + IDS_PROP_0546, ///< \ru Радиус скругления 2. \en Fillet radius 2. + IDS_PROP_0547, ///< \ru Коэффициент полноты. \en Coefficient of completeness. + IDS_PROP_0548, ///< \ru Способ обработки углов стыковки рёбер. \en Method of processing corners of edges connection. + IDS_PROP_0549, ///< \ru Четырехугольник. \en Quadrangle. + + IDS_PROP_0550, ///< \ru Базовое тело. \en Base solid. + IDS_PROP_0551, ///< \ru Тело 1. \en Solid 1. + IDS_PROP_0552, ///< \ru Тело 2. \en Solid 2. + IDS_PROP_0553, ///< \ru Исходное тело. \en Initial solid. + IDS_PROP_0554, ///< \ru Режущая поверхность. \en Cutting surface. + IDS_PROP_0555, ///< \ru Оставляемая часть. \en A part to keep. + IDS_PROP_0556, ///< \ru Точка симметрии. \en Symmetry point. + IDS_PROP_0557, ///< \ru Ось X симметрии. \en Axis X of symmetry. + IDS_PROP_0558, ///< \ru Ось Y симметрии. \en Axis Y of symmetry. + IDS_PROP_0559, ///< \ru Поверхность. \en Surface. + IDS_PROP_0560, ///< \ru Внешняя оболочка. \en Outer shell. + IDS_PROP_0561, ///< \ru Пустотная оболочка. \en Void shell. + IDS_PROP_0562, ///< \ru Число пустот. \en Number of voids. + IDS_PROP_0563, ///< \ru Глубина 1. \en Depth 1. + IDS_PROP_0564, ///< \ru Глубина 2. \en Depth 2. + IDS_PROP_0565, ///< \ru Угол уклона 1. \en Slope angle 1. + IDS_PROP_0566, ///< \ru Угол уклона 2. \en Slope angle 2. + IDS_PROP_0567, ///< \ru Толщина стенки 1. \en Wall thickness 1. + IDS_PROP_0568, ///< \ru Толщина стенки 2. \en Wall thickness 2. + IDS_PROP_0569, ///< \ru Толщина. \en Thickness. + IDS_PROP_0570, ///< \ru Глубина. \en Depth. + + IDS_PROP_0571, ///< \ru Способ построения. \en Method of construction. + IDS_PROP_0572, ///< \ru Число оболочек. \en Number of shells. + IDS_PROP_0573, ///< \ru Количество треугольников. \en Number of triangles. + IDS_PROP_0575, ///< \ru Угол вращения 1. \en Rotation angle 1. + IDS_PROP_0576, ///< \ru Угол вращения 2. \en Rotation angle 2. + + IDS_PROP_0577, ///< \ru Первая вершина. \en The first vertex. + IDS_PROP_0578, ///< \ru Вторая вершина. \en The second vertex. + IDS_PROP_0579, ///< \ru Третья вершина. \en The third vertex. + IDS_PROP_0580, ///< \ru Четвертая вершина. \en The fourth vertex. + + IDS_PROP_0581, ///< \ru Способ построения 1. \en Method of construction 1. + IDS_PROP_0582, ///< \ru Способ построения 2. \en Method of construction 2. + IDS_PROP_0583, ///< \ru Расстояние 1. \en Distance 1. + IDS_PROP_0584, ///< \ru Расстояние 2. \en Distance 2. + IDS_PROP_0585, ///< \ru Треугольник. \en Triangle. + IDS_PROP_0586, ///< \ru Количество апексов. \en Number of apices. + IDS_PROP_0587, ///< \ru Количество полигонов. \en Number of polygons. + IDS_PROP_0588, ///< \ru Количество триангуляций. \en Number of triangulations. + IDS_PROP_0589, ///< \ru Контур. \en Contour. + IDS_PROP_0590, ///< \ru Число сечений. \en Number of sections. + IDS_PROP_0591, ///< \ru Сечение. \en Section. + IDS_PROP_0592, ///< \ru Параллельность. \en Parallelization. + IDS_PROP_0593, ///< \ru Ориентация образующей. \en Generatrix orientation. + IDS_PROP_0594, ///< \ru Положение образующей. \en Generatrix position. + IDS_PROP_0595, ///< \ru Сфероид (0) или тороид (1). \en Spheroid (0) or toroid (1). + IDS_PROP_0596, ///< \ru Полюс в начале. \en Pole at the beginning. + IDS_PROP_0597, ///< \ru Полюс в конце. \en Pole at the end. + IDS_PROP_0598, ///< \ru Продолжение. \en Extension. + IDS_PROP_0599, ///< \ru Смещение. \en Shift. + + IDS_PROP_0600, ///< \ru Оболочка тела. \en Shell. + IDS_PROP_0601, ///< \ru Ориентация ребра в цикле. \en Sense of edge in the loop. + IDS_PROP_0602, ///< \ru Ориентация кривой ребра. \en Edge curve orientation. + IDS_PROP_0603, ///< \ru Ориентация нормали оболочки. \en Orientation of a shell normal. + IDS_PROP_0604, ///< \ru Кривая ребра. \en Edge curve. + IDS_PROP_0605, ///< \ru Двумерная кривая ребра. \en Two-dimensional edge curve. + IDS_PROP_0606, ///< \ru Поверхность грани. \en Surface of a face. + IDS_PROP_0607, ///< \ru Вершина-начало. \en Start vertex. + IDS_PROP_0608, ///< \ru Вершина-конец. \en End vertex. + IDS_PROP_0609, ///< \ru Количество ссылок. \en References count. + IDS_PROP_0611, ///< \ru Грань плюс. \en Face plus. + IDS_PROP_0612, ///< \ru Грань минус. \en Face minus. + IDS_PROP_0613, ///< \ru Указатель на грань. \en Pointer to a face. + IDS_PROP_0614, ///< \ru Номер по порядку. \en Number by and index. + IDS_PROP_0615, ///< \ru Сортировка. \en Sorting. + IDS_PROP_0616, ///< \ru Пуансон или матрица. \en Punch or die. + IDS_PROP_0617, ///< \ru Постоянность толщины штамповки. \en Stamp constant thickness. + IDS_PROP_0651, ///< \ru Разрезанное тело. \en Cutting solid. + IDS_PROP_0652, ///< \ru Плоскость раскроя. \en Cutting plane. + IDS_PROP_0654, ///< \ru Наличие штриховки. \en Whether there is hatching. + IDS_PROP_0655, ///< \ru Шаг штриховки. \en Hatching step. + IDS_PROP_0656, ///< \ru Угол штриховки. \en Hatching angle. + IDS_PROP_0657, ///< \ru Проекционная плоскость. \en Projection plane. + IDS_PROP_0658, ///< \ru Наличие невидимых линий. \en Whether there are invisible lines. + IDS_PROP_0659, ///< \ru Hash имени. \en Hash of name. + + IDS_PROP_0660, ///< \ru Коэффициент нейтрального слоя. \en Neutral layer coefficient. + IDS_PROP_0661, ///< \ru Радиус сгиба. \en Bend radius. + IDS_PROP_0662, ///< \ru Угол сгиба. \en Bend angle. + IDS_PROP_0663, ///< \ru Длина продолжения сгиба. \en Bend extension length. + IDS_PROP_0664, ///< \ru Смещение сгиба. \en Bend shift. + IDS_PROP_0665, ///< \ru Отступ от края сгиба 1. \en Distance from the bound of bend 1. + IDS_PROP_0666, ///< \ru Отступ от края сгиба 2. \en Distance from the bound of bend 2. + IDS_PROP_0667, ///< \ru Угол уклона края сгиба 1. \en Slope angle of the bound of bend 1. + IDS_PROP_0668, ///< \ru Угол уклона края сгиба 2. \en Slope angle of the bound of bend 2. + IDS_PROP_0669, ///< \ru Угол уклона продолжения сгиба 1. \en Slope angle of bend extension 1. + IDS_PROP_0670, ///< \ru Угол уклона продолжения сгиба 2. \en Slope angle of bend extension 2. + IDS_PROP_0671, ///< \ru Расширение продолжения сгиба 1. \en Expansion of extension of bend 1. + IDS_PROP_0672, ///< \ru Расширение продолжения сгиба 2. \en Expansion of extension of bend 2. + IDS_PROP_0673, ///< \ru Ширина разгрузки сгиба. \en Width of bend relief. + IDS_PROP_0674, ///< \ru Глубина разгрузки сгиба. \en Depth of bend relief. + IDS_PROP_0675, ///< \ru Радиус скругления разгрузки. \en Radius of relief rounding. + IDS_PROP_0676, ///< \ru Способ освобождения углов. \en Method of freeing the corners. + IDS_PROP_0677, ///< \ru Фиксированная часть грани слева. \en Fixed part of a face on the left. + IDS_PROP_0678, ///< \ru Строить разогнутым. \en Build in unbent state. + IDS_PROP_0679, ///< \ru Зазор. \en Gap. + IDS_PROP_0680, ///< \ru Перехлёстывающая сторона слева. \en Overlapping side on the left. + IDS_PROP_0681, ///< \ru С добавлением материала. \en With addition of material. + IDS_PROP_0682, ///< \ru Высота. \en Height. + IDS_PROP_0683, ///< \ru Коэффициент сгиба 1. \en Coefficient of bend 1. + IDS_PROP_0684, ///< \ru Радиус сгиба 1. \en Radius of bend 1. + IDS_PROP_0685, ///< \ru Коэффициент сгиба 2. \en Coefficient of bend 2. + IDS_PROP_0686, ///< \ru Радиус сгиба 2. \en Radius of bend 2. + IDS_PROP_0687, ///< \ru Радиус скругления эскиза. \en Radius of a sketch fillet. + IDS_PROP_0688, ///< \ru Радиус скругления основания. \en Radius of a base fillet. + IDS_PROP_0689, ///< \ru Радиус скругления дна. \en Radius of a bottom fillet. + IDS_PROP_0690, ///< \ru Открытая штамповка. \en Open stamping. + IDS_PROP_0691, ///< \ru Боковая стенка внутри. \en Side wall is inside. + IDS_PROP_0692, ///< \ru Ширина основания. \en Width of base. + IDS_PROP_0693, ///< \ru Ширина выпуклой части. \en Width of a salient part. + IDS_PROP_0694, ///< \ru Зазор рубленой законцовки. \en Gap of a cropped tip. + IDS_PROP_0695, ///< \ru Тип буртика. \en Bead type. + IDS_PROP_0696, ///< \ru Тип законцовки. \en Type of a tip. + IDS_PROP_0697, ///< \ru Вытяжка. \en Stretch. + IDS_PROP_0698, ///< \ru По нормали к толщине. \en By the normal to thickness. + IDS_PROP_0699, ///< \ru Способ замыкания цилиндрических частей. \en Method of cylindric parts closure. + IDS_PROP_0700, ///< \ru Разрешение на замыкание углов. \en Permission for corners closure. + + IDS_PROP_0701, ///< \ru Имя. \en Name. + IDS_PROP_0702, ///< \ru Значение. \en Value. + IDS_PROP_0703, ///< \ru Положение. \en Position. + IDS_PROP_0704, ///< \ru Число. \en Number. + IDS_PROP_0705, ///< \ru Ориентация. \en Orientation. + IDS_PROP_0706, ///< \ru Длина. \en Length. + IDS_PROP_0707, ///< \ru Толщина. \en Thickness. + IDS_PROP_0708, ///< \ru Угол. \en Angle. + IDS_PROP_0709, ///< \ru Параметр. \en Parameter. + IDS_PROP_0710, ///< \ru Геометрический объект. \en Geometric object. + IDS_PROP_0711, ///< \ru Точка. \en Point. + IDS_PROP_0712, ///< \ru Контрольная кривая. \en Control curve. + IDS_PROP_0713, ///< \ru Поверхность. \en Surface. + IDS_PROP_0714, ///< \ru Вершина. \en Vertex. + IDS_PROP_0715, ///< \ru Ребро грани. \en Edge of a face. + IDS_PROP_0716, ///< \ru Цикл грани. \en Face loop. + IDS_PROP_0717, ///< \ru Грань. \en Face. + IDS_PROP_0718, ///< \ru Полюс при umin. \en Pole at umin. + IDS_PROP_0719, ///< \ru Полюс при umax. \en Pole at umax. + IDS_PROP_0720, ///< \ru Полюс при vmin. \en Pole at vmin. + IDS_PROP_0721, ///< \ru Полюс при vmax. \en Pole at vmax. + IDS_PROP_0724, ///< \ru Вершина. \en Vertex. + IDS_PROP_0725, ///< \ru Ребро. \en Edge. + IDS_PROP_0726, ///< \ru Цикл. \en Loop. + IDS_PROP_0727, ///< \ru Грань. \en Face. + IDS_PROP_0729, ///< \ru Количество граней. \en Number of faces. + IDS_PROP_0730, ///< \ru Количество операций. \en Number of operations. + IDS_PROP_0731, ///< \ru Количество объектов. \en Number of objects. + IDS_PROP_0732, ///< \ru Объединение граней. \en Faces unification. + IDS_PROP_0733, ///< \ru Обработка углов. \en Corners treatment. + IDS_PROP_0734, ///< \ru Операция объединения. \en Union operation. + IDS_PROP_0735, ///< \ru Операция пересечения. \en Intersection operation. + IDS_PROP_0736, ///< \ru Операция разности. \en Subtraction operation. + IDS_PROP_0737, ///< \ru Базовая операция. \en Base operations. + IDS_PROP_0738, ///< \ru Флаг состояния. \en Flag of state. + IDS_PROP_0739, ///< \ru Параметр полюса по U. \en Parameter of a pole by U. + IDS_PROP_0740, ///< \ru Номер грани. \en Number of a face. + IDS_PROP_0741, ///< \ru Номер ребра. \en Number of an edge. + IDS_PROP_0742, ///< \ru Номер грани плюс. \en Number of a face plus. + IDS_PROP_0743, ///< \ru Номер грани минус. \en Number of a face minus. + IDS_PROP_0744, ///< \ru Формировать твёрдое тело. \en Create a solid. + IDS_PROP_0745, ///< \ru Точность сшивки. \en Stitching tolerance. + IDS_PROP_0746, ///< \ru Через сгиб. \en Through a bend. + IDS_PROP_0747, ///< \ru Полюс. \en Pole. + IDS_PROP_0748, ///< \ru Край. \en Border. + IDS_PROP_0749, ///< \ru Шов. \en Seam. + IDS_PROP_0750, ///< \ru Линия перехода. \en Transition line. + IDS_PROP_0751, ///< \ru Адрес начальной вершины. \en Start vertex address. + IDS_PROP_0752, ///< \ru Адрес конечной вершины. \en End vertex address. + IDS_PROP_0753, ///< \ru Адрес грани слева. \en Address of a face on the left. + IDS_PROP_0754, ///< \ru Адрес грани справа. \en Address of a face on the right. + IDS_PROP_0755, ///< \ru Примитив разрезан. \en Primitive is cut. + IDS_PROP_0756, ///< \ru Листовой примитив. \en Sheet primitive. + IDS_PROP_0757, ///< \ru Внутренняя грань сгиба. \en Internal face of a bend. + IDS_PROP_0758, ///< \ru Внешняя грань сгиба. \en External face of a bend. + IDS_PROP_0759, ///< \ru Угол раствора конуса. \en Cone angle. + +// \ru Версии \en Versions + + IDS_PROP_0760, ///< \ru Версия. \en Version. + IDS_PROP_0761, ///< \ru Версия имени. \en Version of name. + IDS_PROP_0762, ///< \ru Версия операции. \en Version of operation. + IDS_PROP_0763, ///< \ru Версия объекта. \en Version of object. + +// \ru Информация от геометрической модели \en Information from geometric model + + IDS_PROP_0771, ///< \ru Количество вершин. \en Number of vertices. + IDS_PROP_0772, ///< \ru Количество кривых. \en Number of curves. + IDS_PROP_0773, ///< \ru Количество поверхностей. \en Number of surfaces. + IDS_PROP_0774, ///< \ru Количество тел. \en Number of solids. + IDS_PROP_0775, ///< \ru Количество полигональных объектов. \en Number of polygonal objects. + IDS_PROP_0776, ///< \ru Количество проволочных каркасов. \en Number of wireframes. + IDS_PROP_0777, ///< \ru Количество точечных каркасов. \en Number of point frames. + IDS_PROP_0778, ///< \ru Количество сборочных единиц. \en Number of assembly units. + IDS_PROP_0779, ///< \ru Количество вставок. \en Number of instances. + IDS_PROP_0780, ///< \ru Количество других объектов. \en Number of other objects. + IDS_PROP_0781, ///< \ru Количество регионов. \en Number of regions. + IDS_PROP_0782, ///< \ru Количество элементов. \en Number of elements. + IDS_PROP_0783, ///< \ru Количество сегментов. \en Number of segments. + IDS_PROP_0784, ///< \ru Количество всех граней. \en Number of all faces. + IDS_PROP_0785, ///< \ru Количество всех уникальных граней. \en Number of all unique faces. + + IDS_PROP_0791, ///< \ru Первая деформация. \en First strain. + IDS_PROP_0792, ///< \ru Вторая деформация. \en Second strain. + IDS_PROP_0793, ///< \ru Третья деформация. \en Third strain. + + IDS_PROP_0797, ///< \ru Модуль Юнга. \en Young's modulus. + IDS_PROP_0798, ///< \ru Коэффициент Пуассона. \en Poisson's ratio. + + IDS_PROP_0830, ///< \ru Число радиусов эквидистант. \en Number of offsets radii. + IDS_PROP_0831, ///< \ru Гладкий стык. \en Smooth joint. + IDS_PROP_0832, ///< \ru Тип обхода угла. \en Type of corner bypass. + IDS_PROP_0833, ///< \ru Радиус специального скругления. \en Radius of a special fillet. + IDS_PROP_0834, ///< \ru Тип законцовки. \en Type of a tip. + IDS_PROP_0835, ///< \ru Законцовка первого сегмента. \en Tip of the first segment. + IDS_PROP_0836, ///< \ru Тип законцовки в начале. \en Type of tip at the beginning. + IDS_PROP_0837, ///< \ru Тип законцовки в конце. \en Type of tip at the end. + IDS_PROP_0838, ///< \ru Параметр законцовки. \en Parameter of a tip. + + IDS_PROP_0839, ///< \ru Параметр построения заплатки. \en Parameter of a patch construction. + IDS_PROP_0840, ///< \ru Число кривых образующего контура. \en Number of curves of a generating contour. + IDS_PROP_0841, ///< \ru Даны образующие грани. \en The generating faces are given. + IDS_PROP_0842, ///< \ru Кривая образующего контура. \en Curve of a generating contour. + IDS_PROP_0843, ///< \ru Ориентация кривой контура. \en Orientation of a curve of the contour. + IDS_PROP_0844, ///< \ru Сторона существующей грани оболочки. \en Side of an existent face of the shell. + IDS_PROP_0845, ///< \ru Да. \en Yes. + IDS_PROP_0846, ///< \ru Нет. \en No. + +// \ru Контейнер атрибутов \en Attribute container + + IDS_PROP_0847, ///< \ru Поставщик атрибутов. \en Attribute provider. + IDS_PROP_0848, ///< \ru Количество контейнеров. \en Number of containers. + IDS_PROP_0849, ///< \ru Контейнер. \en Container. + + IDS_PROP_0851, ///< \ru Количество атрибутов. \en Number of attributes. + + IDS_PROP_0853, ///< \ru При изменении. \en While changing. + IDS_PROP_0854, ///< \ru При конвертации. \en While convertation. + IDS_PROP_0855, ///< \ru При трансформировании. \en While transforming. + IDS_PROP_0856, ///< \ru При копировании. \en While copying. + IDS_PROP_0857, ///< \ru При объединении. \en While joining. + IDS_PROP_0858, ///< \ru При замене. \en While replacing. + IDS_PROP_0859, ///< \ru При разделении. \en While splitting. + IDS_PROP_0860, ///< \ru При удалении. \en While deleting. + IDS_PROP_0861, ///< \ru Объект свободен. \en The object is free. + IDS_PROP_0862, ///< \ru Копируемость. \en Whether it can be copied. + IDS_PROP_0863, ///< \ru Количество u-линий. \en The number of u-lines. + IDS_PROP_0864, ///< \ru Количество v-линий. \en The number of v-lines. + + IDS_PROP_0869, ///< \ru Имя исполнения. \en Embodiment name. + IDS_PROP_0870, ///< \ru Имя родительского исполнения. \en Parent embodiment name. + IDS_PROP_0871, ///< \ru Красный. \en Red. + IDS_PROP_0872, ///< \ru Зелёный. \en Green. + IDS_PROP_0873, ///< \ru Синий. \en Blue. + IDS_PROP_0874, ///< \ru Толщина. \en Thickness. + IDS_PROP_0875, ///< \ru Стиль. \en Style. + IDS_PROP_0876, ///< \ru Плотность. \en Density. + IDS_PROP_0877, ///< \ru Идентификатор. \en Identifier. + IDS_PROP_0878, ///< \ru Селектированность. \en Selectivity. + IDS_PROP_0879, ///< \ru Видимость. \en Visibility. + IDS_PROP_0880, ///< \ru Изменённость. \en Modified. + IDS_PROP_0881, ///< \ru Общий фон. \en Background. + IDS_PROP_0882, ///< \ru Диффузное отражение. \en Diffuse reflection. + IDS_PROP_0883, ///< \ru Зеркальное отражение. \en Specular reflection. + IDS_PROP_0884, ///< \ru Блеск. \en Shininess. + IDS_PROP_0885, ///< \ru Непрозрачность. \en Opacity. + IDS_PROP_0886, ///< \ru Излучение. \en Emission. + IDS_PROP_0887, ///< \ru Количество родительских объектов. \en Number of parent objects. + IDS_PROP_0888, ///< \ru Родительский объект. \en Parent object. + IDS_PROP_0889, ///< \ru Имя топологического объекта. \en Topological object name. + IDS_PROP_0890, ///< \ru Имя объекта. \en Object name. + IDS_PROP_0891, ///< \ru Хэш имени объекта. \en Object name hash. + + IDS_PROP_0900, ///< \ru Сопряжение в точке. \en Conjugation at point. + IDS_PROP_0901, ///< \ru Тип сопряжения. \en Conjugation type. + IDS_PROP_0902, ///< \ru Без сопряжения. \en Without conjugation. + IDS_PROP_0903, ///< \ru По позиции. \en By position. + IDS_PROP_0904, ///< \ru По касательной. \en By tangent. + IDS_PROP_0905, ///< \ru По нормали. \en By normal. + IDS_PROP_0906, ///< \ru По G2. \en By G2. + IDS_PROP_0907, ///< \ru По G3. \en By G3. + IDS_PROP_0908, ///< \ru Касательный вектор. \en Tangent vector. + IDS_PROP_0909, ///< \ru Первая производная касательного вектора. \en First derivative of tangent vector. + IDS_PROP_0910, ///< \ru Вторая производная касательного вектора. \en Second derivative of tangent vector. + IDS_PROP_0911, ///< \ru Можно ли двигать точки. \en Whether points can be moved. + IDS_PROP_0912, ///< \ru Только по направлению. \en Only along the direction. + IDS_PROP_0913, ///< \ru Сопряжение. \en Conjugation. + + IDS_PROP_0920, ///< \ru Тип параметризации. \en Parametrization type. + IDS_PROP_0921, ///< \ru Пользовательская. \en Custom. + IDS_PROP_0922, ///< \ru Равномерная. \en Uniform. + IDS_PROP_0923, ///< \ru По длине хорды. \en By chord length. + IDS_PROP_0924, ///< \ru Центростремительная. \en Centripetal. + + IDS_PROP_0925, ///< \ru Через вершины. \en Through vertices. + IDS_PROP_0926, ///< \ru Равномерная параметризация. \en Uniform parametrization. + + IDS_PROP_0927, ///< \ru Признак текущего исполнения. \en Mark of current embodiment. + +// \ru Конвертеры \en Converters + + IDS_PROP_1000, ///< \ru Заголовок. \en Title. + IDS_PROP_1001, ///< \ru Название. \en Name. + IDS_PROP_1002, ///< \ru Дата и время. \en Date and time. + IDS_PROP_1003, ///< \ru Автор(ы). \en Author(s)). + IDS_PROP_1004, ///< \ru Организация(и). \en Organization(s). + IDS_PROP_1005, ///< \ru Процессор STEP. \en Processor STEP. + IDS_PROP_1006, ///< \ru Система. \en System. + IDS_PROP_1007, ///< \ru Авторизация. \en Authorization. + + IDS_PROP_1010, ///< \ru Лицо и организация. \en Person and organization. + IDS_PROP_1011, ///< \ru Идентификатор лица. \en Identifier of a person. + IDS_PROP_1012, ///< \ru Фамилия. \en Surname. + IDS_PROP_1013, ///< \ru Имя. \en Name. + IDS_PROP_1014, ///< \ru Средние имена. \en Middle names. + IDS_PROP_1015, ///< \ru Титулы предшествующие. \en Prefix titles. + IDS_PROP_1016, ///< \ru Титулы завершающие. \en Suffix titles. + IDS_PROP_1017, ///< \ru Идентификатор организации. \en Identifier of organization. + IDS_PROP_1018, ///< \ru Название организации. \en Name of organization. + IDS_PROP_1019, ///< \ru Описание организации. \en Description of organization. + + IDS_PROP_1030, ///< \ru Изделие. \en Product. + IDS_PROP_1031, ///< \ru Идентификатор. \en Identifier. + IDS_PROP_1032, ///< \ru Название. \en Name. + IDS_PROP_1033, ///< \ru Описание. \en Description. + + IDS_PROP_1043, ///< \ru Элемент описания. \en Description element. + + /* + 1100 .. 1199 is a range for C3D Solver + */ + IDS_PROP_1100, ///< \ru Геометрический решатель. \en Geom solver. + IDS_PROP_1101, ///< \ru Схема сопряжений. \en Scheme of matings. + IDS_PROP_1102, ///< \ru Система ограничений. \en Constraint system. + IDS_PROP_1103, ///< \ru Ограничение. \en Constraint. + IDS_PROP_1104, ///< \ru Тип сопряжения. \en Type of mating. + IDS_PROP_1105, ///< \ru Тип ограничения. \en Type of constraint. + IDS_PROP_1106, ///< \ru Выравнивание. \en Alignment. + IDS_PROP_1107, ///< \ru Количество ограничений. \en Number of constraints. + IDS_PROP_1108, ///< \ru Тип взаимоориентации. \en Coorientation type. + IDS_PROP_1109, ///< \ru Сопряжение. \en Mate. + IDS_PROP_1110, ///< \ru Базовый объект. \en Base object. + IDS_PROP_1111, ///< \ru Объект 1. \en Object 1. + IDS_PROP_1112, ///< \ru Объект 2. \en Object 2. + IDS_PROP_1113, ///< \ru Вещественный параметр. \en Real parameter. + IDS_PROP_1114, ///< \ru Величина взаимоориентации. \en Value of coorientation. + + /* + Types of geometric constraint + */ + IDS_PROP_1130, ///< \ru Совпадение \en Coincident + IDS_PROP_1131, ///< \ru Параллельность \en Parallel + IDS_PROP_1132, ///< \ru Перпендикулярность \en Perpendicular + IDS_PROP_1133, ///< \ru Касание \en Tangent. + IDS_PROP_1134, ///< \ru Концентричность \en Concentric + IDS_PROP_1135, ///< \ru На расстоянии \en Distance + IDS_PROP_1136, ///< \ru По углом \en Angle + IDS_PROP_1137, ///< \ru По месту \en In place + IDS_PROP_1138, ///< \ru Механическая передача \en Transmittion + IDS_PROP_1139, ///< \ru Кулачковый механизм \en Cam mechanism + IDS_PROP_1140, ///< \ru Радиальный размер. \en Radial dimension. + IDS_PROP_1141, ///< \ru Точка по проценту. \en Percent point. + IDS_PROP_1145, ///< \ru Симметричность. \en Symmetric. + IDS_PROP_1146, ///< \ru Зависимый объект. \en Dependent. + IDS_PROP_1147, ///< \ru Элемент паттерна. \en Patterned. + IDS_PROP_1148, ///< \ru Линейный паттерн. \en Linear pattern. + IDS_PROP_1149, ///< \ru Угловой паттерн. \en Angular pattern. + IDS_PROP_1150, ///< \ru Фиксация. \en Fixing. + IDS_ITEM_1151, // "Горизонтальность" + IDS_ITEM_1152, // "Вертикальность" + IDS_ITEM_1153, // "Длина" + IDS_ITEM_1154, // "Угол наклона" + IDS_PROP_1199, // The last id for C3D Solver + + /* + \ru Новые описания без группировки \en New unsorted descriptions + */ + + IDS_PROP_2001, ///< \ru Внимание: \en Attention: + IDS_PROP_2002, ///< \ru Начало общих операций. \en Beginning of the shared operations. + IDS_PROP_2003, ///< \ru Начало группы операций. \en Beginning of the operations group. + IDS_PROP_2004, ///< \ru Начало первой группы операций. \en Beginning of the first operations group. + IDS_PROP_2005, ///< \ru Начало второй группы операций. \en Beginning of the second operations group. + IDS_PROP_2006, ///< \ru Начало объекта. \en Beginning of an object. + IDS_PROP_2007, ///< \ru Начало первого объекта. \en Beginning of the first object. + IDS_PROP_2008, ///< \ru Начало второго объекта. \en Beginning of the second object. + IDS_PROP_2009, ///< \ru Копировать атрибуты. \en Copy attributes. + IDS_PROP_2010, ///< \ru Количество общих групп операций. \en Number of shared operations groups. + IDS_PROP_2011, ///< \ru Количество выбранных граней. \en Number of selected faces. + IDS_PROP_2012, ///< \ru Количество выбранных ребёр. \en Number of selected edges. + IDS_PROP_2013, ///< \ru Количество выбранных вершин. \en Number of selected vertices. + IDS_PROP_2014, ///< \ru Секущий эскиз. \en Cutting sketch. + IDS_PROP_2015, ///< \ru Секущие 3D-кривые. \en Cutting 3D-curves. + IDS_PROP_2016, ///< \ru Секущие поверхности. \en Cutting surfaces. + IDS_PROP_2017, ///< \ru Секущее тело. \en Cutting solid. + + IDS_PROP_2018, ///< \ru Поверхность сопряжения на границе 0. \en Adjacent surface on the border 0. + IDS_PROP_2019, ///< \ru Поверхность сопряжения на границе 1. \en Adjacent surface on the border 1. + IDS_PROP_2020, ///< \ru Поверхность сопряжения на границе 2. \en Adjacent surface on the border 2. + IDS_PROP_2021, ///< \ru Поверхность сопряжения на границе 3. \en Adjacent surface on the border 3. + IDS_PROP_2022, ///< \ru Поверхность сопряжения на границе 4. \en Adjacent surface on the border 4. + IDS_PROP_2023, ///< \ru Сопряжение на границе 0. \en Conjugation on the boundary 0. + IDS_PROP_2024, ///< \ru Сопряжение на границе 1. \en Conjugation on the boundary 1. + IDS_PROP_2025, ///< \ru Сопряжение на границе 2. \en Conjugation on the boundary 2. + IDS_PROP_2026, ///< \ru Сопряжение на границе 3. \en Conjugation on the boundary 3. + IDS_PROP_2027, ///< \ru Сопряжение на границе 4. \en Conjugation on the boundary 4. + + IDS_PROP_LAST = 9999, ///< \ru Наибольшее значение. \en The greatest value. +}; + + +#endif // __MB_PROPERTY_TITLE_H diff --git a/C3d/Include/mb_rect.h b/C3d/Include/mb_rect.h index 2e87a8a..9289f56 100644 --- a/C3d/Include/mb_rect.h +++ b/C3d/Include/mb_rect.h @@ -246,16 +246,16 @@ public: /// \ru Включить в себя координату Y. \en Enclose an Y-coordinate. void IncludeY( double y ); /// \ru Включить в себя интервал от X - dx до X + dx. \en Enclose a range from X - dx to X + dx. - void IncludeXInterval( double x, double dx ); + void IncludeXInterval( double x, double dx ); /// \ru Включить в себя интервал от Y - dy до Y + dy. \en Enclose a range from Y - dy to Y + dy. - void IncludeYInterval( double y, double dy ); + void IncludeYInterval( double y, double dy ); // \ru Сдвинуть прямоугольник \en Move rectangle /// \ru Cдвинуть прямоугольник. \en Move rectangle. void Move( const MbVector & to ); /// \ru Cдвинуть прямоугольник. \en Move rectangle. void Move( double dx, double dy ); /// \ru Масштабировать относительно 0. \en Scale relative to 0. - void Scale( double scale ); + void Scale( double scale ); /// \ru Расширить прямоугольник. \en Extend rectangle. void Enlarge( double x, double y ); @@ -290,9 +290,11 @@ public: /// \ru Количество координат точки. \en The number of point coordinates. static size_t GetDimension() { return 2; } /// \ru Доступ к координате по индексу. \en Access to a coordinate by an index. - double GetMin( size_t k ) const { return k ? bottom : left; }; + double GetMin( size_t k ) const { return k ? bottom : left; } /// \ru Доступ к координате по индексу. \en Access to a coordinate by an index. - double GetMax( size_t k ) const { return k ? top : right; }; + double GetMax( size_t k ) const { return k ? top : right; } + /// \ru Дать длина по стороне. \en Get side length. + double GetSideLength( size_t k ) const { return k ? ::fabs( GetLengthY() ) : ::fabs( GetLengthX() ); } /// \ru Получить ссылку на себя. \en Get reference to itself. const MbRect & GetCube() const { return *this; } diff --git a/C3d/Include/mb_thread.h b/C3d/Include/mb_thread.h index faf7b58..0ac9375 100644 --- a/C3d/Include/mb_thread.h +++ b/C3d/Include/mb_thread.h @@ -788,26 +788,32 @@ bool CheckThreads( ThreadsVector & threads, const MbPlacement3D * placeSec, bool for ( size_t i = threads.size(); i--; ) { MbThread * thr = threads[i]; - if ( (thr == NULL) || !thr->IsValid() ) + if ( (thr == C3D_NULL_PTR) || !thr->IsValid() ) { threads.erase( threads.begin() + i ); - else if ( checkThreadNames && (thr->GetName() == NULL) ) // C3D-695 : KOMPAS-25125 + thr = C3D_NULL_PTR; + } + else if ( checkThreadNames && (thr->GetName() == C3D_NULL_PTR) ) { // C3D-695 : KOMPAS-25125 threads.erase( threads.begin() + i ); + thr = C3D_NULL_PTR; + } else { if ( threads.size() > 1 ) { for ( ptrdiff_t j = i - 1; j >= 0; j-- ) { if ( thr == threads[j] ) { - threads[i] = NULL; + threads[i] = C3D_NULL_PTR; threads.erase( threads.begin() + i ); C3D_ASSERT_UNCONDITIONAL( false ); // Error case! + thr = C3D_NULL_PTR; break; } } } } - thr->DetachWrongBodies(); + if ( thr != C3D_NULL_PTR ) // KOMPAS-37171 + thr->DetachWrongBodies(); } - if ( !threads.empty() && (placeSec != NULL) ) { + if ( !threads.empty() && (placeSec != C3D_NULL_PTR) ) { const MbVector3D & axisZsec = placeSec->GetAxisZ(); for ( size_t i = threads.size(); i--; ) { const MbThread * thr = threads[i]; diff --git a/C3d/Include/mb_variables.h b/C3d/Include/mb_variables.h index e6396ec..25e08e5 100644 --- a/C3d/Include/mb_variables.h +++ b/C3d/Include/mb_variables.h @@ -19,182 +19,188 @@ /** \ru \name Общие константы \en \name Common constants \{ */ -#define MB_MAXDOUBLE (1.0E+300) ///< \ru Максимальное значение double 1.7976931348623158E+308. \en Maximum value of double 1.7976931348623158E+308. -#define MB_MINDOUBLE (1.0E-300) ///< \ru Минимальное значение double 2.2250738585072014E-308. \en Minimum value of double 2.2250738585072014E-308. +const_expr double MB_MAXDOUBLE = 1.0E+300; ///< \ru Максимальное значение double 1.7976931348623158E+308. \en Maximum value of double 1.7976931348623158E+308. +const_expr double MB_MINDOUBLE = 1.0E-300; ///< \ru Минимальное значение double 2.2250738585072014E-308. \en Minimum value of double 2.2250738585072014E-308. // \ru Математические константы, округленные до 21 значащей цифры, определены в math.h \en Mathematical constants rounded to 21 significant digits, defined in math.h #if !defined( _MATH_DEFINES_DEFINED ) #ifdef C3D_WINDOWS// _MSC_VER // \ru Константы определены в math.h [Linux] \en Constants defined in math.h [Linux] -#define M_E 2.71828182845904523536 ///< \ru Экспонента. \en Exponent. -#define M_LOG2E 1.44269504088896340736 ///< \ru Логарифм M_E по основанию 2. \en Logarithm M_E to base 2. -#define M_LOG10E 0.434294481903251827651 ///< \ru Логарифм M_E по основанию 10. \en Logarithm M_E to base 10. -#define M_LN2 0.693147180559945309417 ///< \ru Натуральный логарифм 2. \en Natural logarithm 2. -#define M_PI_4 0.785398163397448309616 ///< M_PI / 4.0 -#define M_1_PI 0.318309886183790671538 ///< 1.0 / M_PI -#define M_2_PI 0.636619772367581343076 ///< 2.0 / M_PI -#define M_SQRT1_2 0.707106781186547524401 ///< \ru Корень из одной второй, sqrt(1/2). \en Root of one half, sqrt(1/2). +const_expr double M_E = 2.71828182845904523536; ///< \ru Экспонента. \en Exponent. +const_expr double M_LOG2E = 1.44269504088896340736; ///< \ru Логарифм M_E по основанию 2. \en Logarithm M_E to base 2. +const_expr double M_LOG10E = 0.434294481903251827651; ///< \ru Логарифм M_E по основанию 10. \en Logarithm M_E to base 10. +const_expr double M_LN2 = 0.693147180559945309417; ///< \ru Натуральный логарифм 2. \en Natural logarithm 2. +const_expr double M_PI_4 = 0.785398163397448309616; ///< M_PI / 4.0 +const_expr double M_1_PI = 0.318309886183790671538; ///< 1.0 / M_PI +const_expr double M_2_PI = 0.636619772367581343076; ///< 2.0 / M_PI +const_expr double M_SQRT1_2 = 0.707106781186547524401; ///< \ru Корень из одной второй, sqrt(1/2). \en Root of one half, sqrt(1/2). #else // C3D_WINDOWS - #ifndef M_E - #define M_E 2.71828182845904523536 ///< \ru Экспонента. \en Exponent. - #endif // M_E - #ifndef M_LOG2E - #define M_LOG2E 1.44269504088896340736 ///< \ru Логарифм M_E по основанию 2. \en Logarithm M_E to base 2. - #endif // M_LOG2E - #ifndef M_LOG10E - #define M_LOG10E 0.434294481903251827651 ///< \ru Логарифм M_E по основанию 10. \en Logarithm M_E to base 10. - #endif // M_LOG10E - #ifndef M_LN2 - #define M_LN2 0.693147180559945309417 ///< \ru Натуральный логарифм 2. \en Natural logarithm 2. - #endif // M_LN2 - #ifndef M_PI_4 - #define M_PI_4 0.785398163397448309616 ///< M_PI / 4.0 - #endif // M_PI_4 - #ifndef M_1_PI - #define M_1_PI 0.318309886183790671538 ///< 1.0 / M_PI - #endif // M_1_PI - #ifndef M_2_PI - #define M_2_PI 0.636619772367581343076 ///< 2.0 / M_PI - #endif // M_2_PI - #ifndef M_SQRT1_2 - #define M_SQRT1_2 0.707106781186547524401 ///< \ru Корень из одной второй, sqrt(1/2). \en Root of one half, sqrt(1/2). - #endif // M_SQRT1_2 +#ifndef M_E +const_expr double M_E = 2.71828182845904523536; ///< \ru Экспонента. \en Exponent. +#endif // M_E +#ifndef M_LOG2E +const_expr double M_LOG2E = 1.44269504088896340736; ///< \ru Логарифм M_E по основанию 2. \en Logarithm M_E to base 2. +#endif // M_LOG2E +#ifndef M_LOG10E +const_expr double M_LOG10E = 0.434294481903251827651; ///< \ru Логарифм M_E по основанию 10. \en Logarithm M_E to base 10. +#endif // M_LOG10E +#ifndef M_LN2 +const_expr double M_LN2 = 0.693147180559945309417; ///< \ru Натуральный логарифм 2. \en Natural logarithm 2. +#endif // M_LN2 +#ifndef M_PI_4 +const_expr double M_PI_4 = 0.785398163397448309616; ///< M_PI / 4.0 +#endif // M_PI_4 +#ifndef M_1_PI +const_expr double M_1_PI = 0.318309886183790671538; ///< 1.0 / M_PI +#endif // M_1_PI +#ifndef M_2_PI +const_expr double M_2_PI = 0.636619772367581343076; ///< 2.0 / M_PI +#endif // M_2_PI +#ifndef M_SQRT1_2 +const_expr double M_SQRT1_2 = 0.707106781186547524401; ///< \ru Корень из одной второй, sqrt(1/2). \en Root of one half, sqrt(1/2). +#endif // M_SQRT1_2 #endif // C3D_WINDOWS + #ifndef M_LN10 - #define M_LN10 2.30258509299404568402 ///< \ru Натуральный логарифм 10, ln(10). \en Natural logarithm 10, ln(10). +const_expr double M_LN10 = 2.30258509299404568402; ///< \ru Натуральный логарифм 10, ln(10). \en Natural logarithm 10, ln(10). #endif #ifndef M_PI - #define M_PI 3.14159265358979323846 ///< \ru Отношение длины окружности к её диаметру, pi. \en Relation between circle length and its diameter, pi. +const_expr double M_PI = 3.14159265358979323846; ///< \ru Отношение длины окружности к её диаметру, pi. \en Relation between circle length and its diameter, pi. #endif #ifndef M_PI_2 - #define M_PI_2 1.57079632679489661923 ///< M_PI / 2.0 +const_expr double M_PI_2 = 1.57079632679489661923; ///< M_PI / 2.0 #endif #ifndef M_2_SQRTPI - #define M_2_SQRTPI 1.12837916709551257390 ///< \ru Два разделить на корень из числа пи, 2/sqrt(pi). \en Two divided by root of pi, 2/sqrt(pi). +const_expr double M_2_SQRTPI = 1.12837916709551257390; ///< \ru Два разделить на корень из числа пи, 2/sqrt(pi). \en Two divided by root of pi, 2/sqrt(pi). #endif #ifndef M_SQRT2 - #define M_SQRT2 1.41421356237309504880 ///< \ru Корень из двух, sqrt(2). \en Root of two, sqrt(2). +const_expr double M_SQRT2 = 1.41421356237309504880; ///< \ru Корень из двух, sqrt(2). \en Root of two, sqrt(2). #endif #endif // _MATH_DEFINES_DEFINED -#define M_1_SQRTPI 0.564189583547756286948 ///< \ru Единица, деленная на корень из числа пи, 1/sqrt(pi). \en One divided by root of pi, 1/sqrt(pi). -#define M_PI2 (M_PI*2.0) ///< \ru Отношение длины окружности к её радиусу, 2.0 * M_PI, 6.28318530717958647692 \en Relation between circle length and its radius, 2.0 * M_PI, 6.28318530717958647692 -#define M_DEGRAD (M_PI/180.0) ///< \ru Коэффициент перевода градусов в радианы. \en Factor of conversion from degrees to radians. -#define M_RADDEG (180.0/M_PI) ///< \ru Коэффициент перевода радиан в градусы. \en Factor of conversion from radians to degrees. -#define M_FI 1.61803398874989484 ///< \ru Число золотого сечения, 1/M_FI = 0.6180339887499. \en Golden ratio, 1/M_FI = 0.6180339887499. -#define MM_INCH 25.4 ///< \ru Количество миллиметров в дюйме. \en Millimeters per inch. + +const_expr double M_1_SQRTPI = 0.564189583547756286948; ///< \ru Единица, деленная на корень из числа пи, 1/sqrt(pi). \en One divided by root of pi, 1/sqrt(pi). +const_expr double M_FI = 1.61803398874989484; ///< \ru Число золотого сечения, 1/M_FI = 0.6180339887499. \en Golden ratio, 1/M_FI = 0.6180339887499. +const_expr double MM_INCH = 25.4; ///< \ru Количество миллиметров в дюйме. \en Millimeters per inch. -#define EPSILON 1E-10 ///< \ru Погрешность. \en Tolerance. -#define MAXIMON 1E+10 ///< \ru 10 в 10-й степени. \en 10 to the power of 10. -#define MAX_OVERALL_DIM 1E+12 ///< \ru Максимальное значение габарита. \en Maximal value of bounding box. +const_expr double EPSILON = 1E-10; ///< \ru Погрешность. \en Tolerance. +const_expr double MAXIMON = 1E+10; ///< \ru 10 в 10-й степени. \en 10 to the power of 10. +const_expr double MAX_OVERALL_DIM = 1E+12; ///< \ru Максимальное значение габарита. \en Maximal value of bounding box. -#define DETERMINANT_MAX 1E+137 ///< \ru Максимальная величина. \en Maximal value. -#define DETERMINANT_MIN 1E-171 ///< \ru Минимальная величина. \en Minimal value. +const_expr double DETERMINANT_MAX = 1E+137; ///< \ru Максимальная величина. \en Maximal value. +const_expr double DETERMINANT_MIN = 1E-171; ///< \ru Минимальная величина. \en Minimal value. -#define UNDEFINED_DBL -MB_MAXDOUBLE ///< \ru Неопределенный double. \en Undefined double. -#define UNDEFINED_INT_T SYS_MIN_ST ///< \ru Неопределенный int. \en Undefined int. +const_expr double NULL_EPSILON = 1E-30; ///< \ru Погрешность для проверки на равенство нулю. \en Tolerance for equality to zero. +const_expr double NULL_REGION = 1E-20; ///< \ru Погрешность для проверки на равенство нулю. \en Tolerance for equality to zero. -#define DEVIATION_SAG M_PI * 0.04 ///< \ru Угловая толерантность. \en Angular tolerance. +const_expr double DOUBLE_EPSILON = 1E-16; ///< \ru Погрешность. \en Tolerance. +const_expr double DOUBLE_REGION = 1E-15; ///< \ru Погрешность. \en Tolerance. +#ifdef C3D_STANDARD_CXX_11 +const_expr double EXTENT_EQUAL = 1E-14; ///< \ru Погрешность. \en Tolerance. +#else +#define EXTENT_EQUAL 1E-14 ///< \ru Погрешность. \en Tolerance. +#endif +const_expr double EXTENT_EPSILON = 1E-12; ///< \ru Погрешность. \en Tolerance. +const_expr double EXTENT_REGION = 1E-11; ///< \ru Погрешность. \en Tolerance. +const_expr double LENGTH_EPSILON = 1E-10; ///< \ru Погрешность длины. \en Tolerance for length. +const_expr double LENGTH_REGION = 1E-9; ///< \ru Погрешность региона. \en Tolerance for region. -#define NULL_EPSILON 1E-30 ///< \ru Погрешность для проверки на равенство нулю. \en Tolerance for equality to zero. -#define NULL_REGION 1E-20 ///< \ru Погрешность для проверки на равенство нулю. \en Tolerance for equality to zero. +const_expr double METRIC_EPSILON = 1E-8; ///< \ru Погрешность расстояния в итерационных функциях. \en Tolerance for distance in iterative functions. +const_expr double METRIC_REGION = 1E-7; ///< \ru Неразличимая метрическая область. \en Indistinguishable metric region. +const_expr double METRIC_PRECISION = 1E-6; ///< \ru Метрическая погрешность. \en Metric tolerance. +const_expr double METRIC_ACCURACY = 1E-5; ///< \ru Наибольшая метрическая погрешность (абсолютная точность в мм ("размер" атома 5e-8 мм)). \en The largest metric tolerance (absolute tolerance expressed in mm ("size" of atom is 5e-8 mm)). +const_expr double METRIC_NEAR = 1E-4; ///< \ru Метрическая близость. \en Metric proximity tolerance. -#define DOUBLE_EPSILON 1E-16 ///< \ru Погрешность. \en Tolerance. -#define DOUBLE_REGION 1E-15 ///< \ru Погрешность. \en Tolerance. -#define EXTENT_EQUAL 1E-14 ///< \ru Погрешность. \en Tolerance. -#define EXTENT_EPSILON 1E-12 ///< \ru Погрешность. \en Tolerance. -#define EXTENT_REGION 1E-11 ///< \ru Погрешность. \en Tolerance. -#define LENGTH_EPSILON 1E-10 ///< \ru Погрешность длины. \en Tolerance for length. -#define LENGTH_REGION 1E-9 ///< \ru Погрешность региона. \en Tolerance for region. +const_expr double PARAM_EPSILON = 1E-8; ///< \ru Погрешность параметра в итерационных функциях. \en Tolerance for parameter in iterative functions. +const_expr double PARAM_REGION = 1E-7; ///< \ru Неразличимая параметрическая область. \en Indistinguishable parametric region. +const_expr double PARAM_PRECISION = 1E-6; ///< \ru Параметрическая погрешность. \en Parametric tolerance. +const_expr double PARAM_ACCURACY = 1E-5; ///< \ru Наибольшая параметрическая погрешность. \en The largest parametric tolerance. +const_expr double PARAM_NEAR = 1E-4; ///< \ru Параметрическая близость. \en Parametric proximity. -#define METRIC_EPSILON 1E-8 ///< \ru Погрешность расстояния в итерационных функциях. \en Tolerance for distance in iterative functions. -#define METRIC_REGION 1E-7 ///< \ru Неразличимая метрическая область. \en Indistinguishable metric region. -#define METRIC_PRECISION 1E-6 ///< \ru Метрическая погрешность. \en Metric tolerance. -#define METRIC_ACCURACY 1E-5 ///< \ru Наибольшая метрическая погрешность (абсолютная точность в мм ("размер" атома 5e-8 мм)). \en The largest metric tolerance (absolute tolerance expressed in mm ("size" of atom is 5e-8 mm)). -#define METRIC_NEAR 1E-4 ///< \ru Метрическая близость. \en Metric proximity tolerance. +const_expr ptrdiff_t UNDEFINED_INT_T = SYS_MIN_ST; ///< \ru Неопределенный int. \en Undefined int. +const_expr size_t FAIR_MAX_DEGREE = 11; ///< \ru Максимальный порядок NURBS при аппроксимации. \en Maxinum degree of the NURBS approximation. -#define PARAM_EPSILON 1E-8 ///< \ru Погрешность параметра в итерационных функциях. \en Tolerance for parameter in iterative functions. -#define PARAM_REGION 1E-7 ///< \ru Неразличимая параметрическая область. \en Indistinguishable parametric region. -#define PARAM_PRECISION 1E-6 ///< \ru Параметрическая погрешность. \en Parametric tolerance. -#define PARAM_ACCURACY 1E-5 ///< \ru Наибольшая параметрическая погрешность. \en The largest parametric tolerance. -#define PARAM_NEAR 1E-4 ///< \ru Параметрическая близость. \en Parametric proximity. - -#define ANGLE_EPSILON PARAM_EPSILON*M_PI ///< \ru Погрешность угла. \en Angular tolerance. -#define ANGLE_REGION ANGLE_EPSILON*40 ///< \ru Погрешность угла, при которой углы считаются равными. \en Angular tolerance for equality of angles. - -#define FAIR_MAX_DEGREE 11 ///< \ru Максимальный порядок NURBS при аппроксимации. \en Maxinum degree of the NURBS approximation. +const_expr double M_PI2 = M_PI * 2.0; ///< \ru Отношение длины окружности к её радиусу, 2.0 * M_PI, 6.28318530717958647692 \en Relation between circle length and its radius, 2.0 * M_PI, 6.28318530717958647692 +const_expr double M_DEGRAD = M_PI / 180.0; ///< \ru Коэффициент перевода градусов в радианы. \en Factor of conversion from degrees to radians. +const_expr double M_RADDEG = 180.0 / M_PI; ///< \ru Коэффициент перевода радиан в градусы. \en Factor of conversion from radians to degrees. +const_expr double UNDEFINED_DBL = -MB_MAXDOUBLE; ///< \ru Неопределенный double. \en Undefined double. +const_expr double DEVIATION_SAG = M_PI * 0.04; ///< \ru Угловая толерантность. \en Angular tolerance. +const_expr double ANGLE_EPSILON = PARAM_EPSILON * M_PI; ///< \ru Погрешность угла. \en Angular tolerance. +const_expr double ANGLE_REGION = ANGLE_EPSILON * 40; ///< \ru Погрешность угла, при которой углы считаются равными. \en Angular tolerance for equality of angles. namespace c3d // namespace C3D { -const double METRIC_DELTA = 0.05; ///< \ru Величина отшагивания. \en Metric offset. -const double PARAM_DELTA_MIN = 0.005; ///< \ru Минимальная доля приращения параметра. \en Minimal portion of parameter increment. -const double PARAM_DELTA_MAX = 1.0; ///< \ru Максимальная доля приращения параметра. \en Maximal portion of parameter increment. +const_expr double METRIC_PORTION = 0.075; ///< \ru Доля расстояния между контрольными точками. \en Proportion of distance between control points. +const_expr double METRIC_DELTA = 0.05; ///< \ru Величина отшагивания. \en Metric offset. +const_expr double PARAM_DELTA_MIN = 0.005; ///< \ru Минимальная доля приращения параметра. \en Minimal portion of parameter increment. +const_expr double PARAM_DELTA_MAX = 1.0; ///< \ru Максимальная доля приращения параметра. \en Maximal portion of parameter increment. -const double MIN_LENGTH = 1.0E-4; ///< \ru Минимальная длина объекта. \en Minimal object length. -const double MAX_LENGTH = 5.0E+7; ///< \ru Максимальная длина объекта. \en Maximal object length. -const double MIN_RADIUS = 1.0E-4; ///< \ru Минимальный радиус объекта. \en Minimal object radius. -const double MAX_RADIUS = 2.5E+7; ///< \ru Максимальный радиус объекта. \en Maximal object radius. +const_expr double MIN_LENGTH = 1.0E-4; ///< \ru Минимальная длина объекта. \en Minimal object length. +const_expr double MAX_LENGTH = 5.0E+7; ///< \ru Максимальная длина объекта. \en Maximal object length. +const_expr double MIN_RADIUS = 1.0E-4; ///< \ru Минимальный радиус объекта. \en Minimal object radius. +const_expr double MAX_RADIUS = 2.5E+7; ///< \ru Максимальный радиус объекта. \en Maximal object radius. -const double DELTA_MIN = 1E-3; ///< \ru Коэффициент уменьшения. \en Reduction factor. -const double DELTA_MID = 1E-2; ///< \ru Коэффициент уменьшения. \en Reduction factor. -const double DELTA_MOD = 1E-1; ///< \ru Коэффициент уменьшения. \en Reduction factor. -const double DELTA_MAX = 1E+3; ///< \ru Коэффициент увеличения. \en Magnification factor. -const double POWER_1 = 1E+1; ///< \ru Коэффициент увеличения. \en Magnification factor. -const double POWER_2 = 1E+2; ///< \ru Коэффициент увеличения. \en Magnification factor. -const double POWER_3 = 1E+3; ///< \ru Коэффициент увеличения. \en Magnification factor. -const double POWER_4 = 1E+4; ///< \ru Коэффициент увеличения. \en Magnification factor. -const double POWER_5 = 1E+5; ///< \ru Коэффициент увеличения. \en Magnification factor. +const_expr double DELTA_MIN = 1E-3; ///< \ru Коэффициент уменьшения. \en Reduction factor. +const_expr double DELTA_MID = 1E-2; ///< \ru Коэффициент уменьшения. \en Reduction factor. +const_expr double DELTA_MOD = 1E-1; ///< \ru Коэффициент уменьшения. \en Reduction factor. +const_expr double DELTA_MAX = 1E+3; ///< \ru Коэффициент увеличения. \en Magnification factor. +const_expr double POWER_1 = 1E+1; ///< \ru Коэффициент увеличения. \en Magnification factor. +const_expr double POWER_2 = 1E+2; ///< \ru Коэффициент увеличения. \en Magnification factor. +const_expr double POWER_3 = 1E+3; ///< \ru Коэффициент увеличения. \en Magnification factor. +const_expr double POWER_4 = 1E+4; ///< \ru Коэффициент увеличения. \en Magnification factor. +const_expr double POWER_5 = 1E+5; ///< \ru Коэффициент увеличения. \en Magnification factor. -const double ONE_THIRD = 0.33333333333333333333; ///< 1/3. -const double TWO_THIRD = 0.66666666666666666666; ///< 2/3. -const double ONE_SIXTH = 0.166666666666666666667; ///< 1/6. -const double ONE_FIFTH = 0.2; ///< 1/5. -const double TWO_FIFTH = 0.4; ///< 2/5. -const double ONE_QUARTER = 0.25; ///< 1/4. -const double ONE_EIGHTH = 0.125; ///< 1/8. -const double ONE_HALF = 0.5; ///< 1/2. +const_expr double ONE_THIRD = 0.33333333333333333333; ///< 1/3. +const_expr double TWO_THIRD = 0.66666666666666666666; ///< 2/3. +const_expr double ONE_SIXTH = 0.166666666666666666667; ///< 1/6. +const_expr double ONE_FIFTH = 0.2; ///< 1/5. +const_expr double TWO_FIFTH = 0.4; ///< 2/5. +const_expr double ONE_QUARTER = 0.25; ///< 1/4. +const_expr double ONE_HALF = 0.5; ///< 1/2. +const_expr double ONE_SEVENTH = 0.14285714285714285714; ///< 1/7. +const_expr double ONE_EIGHTH = 0.125; ///< 1/8. // \ru Способы построения поверхности сопряжения (скругления или фаски). \en Ways for construction of smooth surface (fillet or chamfer). -const double _CONIC_MIN_ = 0.05; ///< \ru Минимальный коэффициент полноты сечения поверхности сопряжения (при 0.5 - парабола, меньше - эллипс). \en Minimum factor of smooth surface section completeness (0.5 for parabola, less for ellipse). -const double _CONIC_MAX_ = 0.95; ///< \ru Максимальный коэффициент полноты сечения поверхности сопряжения (при 0.5 - парабола, больше - гипербола). \en Maximum factor of smooth surface section completeness (0.5 for parabola, greater for hyperbola). -const double _ARC_ = 0.0; ///< \ru Коэффициент полноты сечения поверхности скругления при u = const соответствует дуге окружности. \en Factor of smooth surface section completeness in case of u = const corresponds to circle arc. +const_expr double _CONIC_MIN_ = 0.05; ///< \ru Минимальный коэффициент полноты сечения поверхности сопряжения (при 0.5 - парабола, меньше - эллипс). \en Minimum factor of smooth surface section completeness (0.5 for parabola, less for ellipse). +const_expr double _CONIC_MAX_ = 0.95; ///< \ru Максимальный коэффициент полноты сечения поверхности сопряжения (при 0.5 - парабола, больше - гипербола). \en Maximum factor of smooth surface section completeness (0.5 for parabola, greater for hyperbola). +const_expr double _ARC_ = 0.0; ///< \ru Коэффициент полноты сечения поверхности скругления при u = const соответствует дуге окружности. \en Factor of smooth surface section completeness in case of u = const corresponds to circle arc. -const int32 TEN = 10; ///< \ru Число 10. \en Number 10. -const int32 TWENTY = 20; ///< \ru Число 20. \en Number 20. -const int32 TESSERA_MAX = 4000; ///< \ru Максимальное количество ячеек в строке и ряду триангуляционной сетки. \en Maximum count of cell in rows and columns for triangulation grid. -const int32 COUNT_MAX = 512; ///< \ru Коэффициент увеличения. \en Magnification factor. -const int32 COUNT_MID = 256; ///< \ru Коэффициент увеличения. \en Magnification factor. -const int32 COUNT_MIN = 128; ///< \ru Коэффициент увеличения. \en Magnification factor. -const int32 COUNT_BIN = 64; ///< \ru Уровень вложенности. \en Inclusion level. -const int32 WIRE_MAX = 256; ///< \ru Максимальное количество линий отрисовочной сетки. \en The maximum number of mesh lines. +const_expr int32 TEN = 10; ///< \ru Число 10. \en Number 10. +const_expr int32 TWENTY = 20; ///< \ru Число 20. \en Number 20. +const_expr int32 TESSERA_MAX = 4000; ///< \ru Максимальное количество ячеек в строке и ряду триангуляционной сетки. \en Maximum count of cell in rows and columns for triangulation grid. +const_expr int32 COUNT_MAX = 512; ///< \ru Коэффициент увеличения. \en Magnification factor. +const_expr int32 COUNT_MID = 256; ///< \ru Коэффициент увеличения. \en Magnification factor. +const_expr int32 COUNT_MIN = 128; ///< \ru Коэффициент увеличения. \en Magnification factor. +const_expr int32 COUNT_BIN = 64; ///< \ru Уровень вложенности. \en Inclusion level. +const_expr int32 WIRE_MAX = 256; ///< \ru Максимальное количество линий отрисовочной сетки. \en The maximum number of mesh lines. -const int32 ITERATE_COUNT = 16; ///< \ru Число приближений в итерационном методе. \en Number of approximations in iterative method. -const int32 ITERATE_LIMIT = 32; ///< \ru Количество итераций для построения касательных окружностей. \en Count of iterations for construction of tangent circles. +const_expr int32 ITERATE_COUNT = 16; ///< \ru Число приближений в итерационном методе. \en Number of approximations in iterative method. +const_expr int32 ITERATE_LIMIT = 32; ///< \ru Количество итераций для построения касательных окружностей. \en Count of iterations for construction of tangent circles. -const int32 NEWTON_COUNT = 8; ///< \ru Число приближений в итерационном методе. \en Number of approximations in iterative method. -const int32 NEWTON_COUNT_2X = 16; ///< \ru Число приближений в итерационном методе. \en Number of approximations in iterative method. -const int32 NEWTON_COUNT_3X = 24; ///< \ru Число приближений в итерационном методе. \en Number of approximations in iterative method. -const int32 NEWTON_COUNT_4X = 32; ///< \ru Число приближений в итерационном методе. \en Number of approximations in iterative method. -const int32 NEWTON_COUNT_8X = 64; ///< \ru Число приближений в итерационном методе. \en Number of approximations in iterative method. +const_expr int32 NEWTON_COUNT = 8; ///< \ru Число приближений в итерационном методе. \en Number of approximations in iterative method. +const_expr int32 NEWTON_COUNT_2X = 16; ///< \ru Число приближений в итерационном методе. \en Number of approximations in iterative method. +const_expr int32 NEWTON_COUNT_3X = 24; ///< \ru Число приближений в итерационном методе. \en Number of approximations in iterative method. +const_expr int32 NEWTON_COUNT_4X = 32; ///< \ru Число приближений в итерационном методе. \en Number of approximations in iterative method. +const_expr int32 NEWTON_COUNT_8X = 64; ///< \ru Число приближений в итерационном методе. \en Number of approximations in iterative method. -const int32 LIMIT_COUNT = 4; ///< \ru Число приближений в итерационном методе. \en Number of approximations in iterative method. -const int32 COUNT_DELTA = 10; ///< \ru Коэффициент увеличения или уменьшения. \en Reduction or magnification factor. -const int32 COUNT_DELTA_2X = 20; ///< \ru Коэффициент увеличения или уменьшения. \en Reduction or magnification factor. -const int32 ITEMS_COUNT = 12; ///< \ru Число точек в шаговом методе. \en Number of points in step method. +const_expr int32 LIMIT_COUNT = 4; ///< \ru Число приближений в итерационном методе. \en Number of approximations in iterative method. +const_expr int32 COUNT_DELTA = 10; ///< \ru Коэффициент увеличения или уменьшения. \en Reduction or magnification factor. +const_expr int32 COUNT_DELTA_2X = 20; ///< \ru Коэффициент увеличения или уменьшения. \en Reduction or magnification factor. +const_expr int32 ITEMS_COUNT = 12; ///< \ru Число точек в шаговом методе. \en Number of points in step method. -const int32 BEZIER_DEGREE = 4; ///< \ru Порядок Безье-сплайна по умолчанию. \en Default degree of Bezier-spline. -const int32 NURBS_DEGREE = 4; ///< \ru Порядок NURBS по умолчанию. \en Degree of NURBS. +const_expr int32 BEZIER_DEGREE = 4; ///< \ru Порядок Безье-сплайна по умолчанию. \en Default degree of Bezier-spline. +const_expr int32 NURBS_DEGREE = 4; ///< \ru Порядок NURBS по умолчанию. \en Degree of NURBS. -const int32 NURBS_POINTS_COUNT = 6; ///< \ru Число точек для NURBS по умолчанию для прямого редактирования. \en Default number of points for NURBS direct editing. -const int32 NURBS_POINTS_MAX_COUNT = 100; ///< \ru Максимальное число точек для NURBS по умолчанию для прямого редактирования. \en Default maximum number of points for NURBS direct editing. -const int32 APPROX_POINTS_MUL_COEFF = 3; ///< \ru Коэффициент увеличения количества точек для метода наименьших квадратов. \en Factor of points count incrementing in method of least squares. +const_expr int32 NURBS_POINTS_COUNT = 6; ///< \ru Число точек для NURBS по умолчанию для прямого редактирования. \en Default number of points for NURBS direct editing. +const_expr int32 NURBS_POINTS_MAX_COUNT = 100; ///< \ru Максимальное число точек для NURBS по умолчанию для прямого редактирования. \en Default maximum number of points for NURBS direct editing. +const_expr int32 APPROX_POINTS_MUL_COEFF = 3; ///< \ru Коэффициент увеличения количества точек для метода наименьших квадратов. \en Factor of points count incrementing in method of least squares. -const int32 SPACE_DIM = 3; ///< \ru Размерность 3D-пространства. \en Dimension of 3D space. +const_expr int32 SPACE_DIM = 3; ///< \ru Размерность 3D-пространства. \en Dimension of 3D space. -const int32 TRT_FREE = 0; ///< \ru Сопряжение отсутствует. \en No conjugation. -const int32 TRT_TANGENT = 1; ///< \ru Сопряжение по касательной. \en Tangent conjugation. -const int32 TRT_NORMAL = 2; ///< \ru Сопряжение по нормали. \en Normal conjugation. +const_expr int32 TRT_FREE = 0; ///< \ru Сопряжение отсутствует. \en No conjugation. +const_expr int32 TRT_TANGENT = 1; ///< \ru Сопряжение по касательной. \en Tangent conjugation. +const_expr int32 TRT_NORMAL = 2; ///< \ru Сопряжение по нормали. \en Normal conjugation. /** \ru \name Способ информирования о нарушении требований.~ @@ -209,27 +215,27 @@ enum eAssertViolationNotify { } // namespace C3D -#define MB_AMBIENT 0.4 ///< \ru Коэффициент рассеянного освещения (фон). \en Coefficient of backlighting. -#define MB_DIFFUSE 0.7 ///< \ru Коэффициент диффузного отражения. \en Coefficient of diffuse reflection. -#define MB_SPECULARITY 0.8 ///< \ru Коэффициент зеркального отражения. \en Coefficient of specular reflection. -#define MB_SHININESS 50.0 ///< \ru Блеск (показатель степени в законе зеркального отражения). \en Shininess (index according to the law of specular reflection). -#define MB_OPACITY 1.0 ///< \ru Коэффициент суммарного отражения (коэффициент непрозрачности). \en Coefficient of total reflection (opacity coefficient). -#define MB_EMISSION 0.0 ///< \ru Коэффициент излучения. \en Emissivity coefficient. +const_expr float MB_AMBIENT = 0.4f; ///< \ru Коэффициент рассеянного освещения (фон). \en Coefficient of backlighting. +const_expr float MB_DIFFUSE = 0.7f; ///< \ru Коэффициент диффузного отражения. \en Coefficient of diffuse reflection. +const_expr float MB_SPECULARITY = 0.8f; ///< \ru Коэффициент зеркального отражения. \en Coefficient of specular reflection. +const_expr float MB_SHININESS = 50.0f; ///< \ru Блеск (показатель степени в законе зеркального отражения). \en Shininess (index according to the law of specular reflection). +const_expr float MB_OPACITY = 1.0f; ///< \ru Коэффициент суммарного отражения (коэффициент непрозрачности). \en Coefficient of total reflection (opacity coefficient). +const_expr float MB_EMISSION = 0.0f; ///< \ru Коэффициент излучения. \en Emissivity coefficient. -#define MB_DEFCOLOR 0x7F7F7F ///< \ru Цвет по умолчанию при импорте и экспорте (серый). \en Default color for import and export (grey). -#define MB_C3DCOLOR 0xFF7F00 ///< \ru Цвет по умолчанию для геометрических объектов. \en Default color for geometric objects. +const_expr uint32 MB_DEFCOLOR = 0x7F7F7F; ///< \ru Цвет по умолчанию при импорте и экспорте (серый). \en Default color for import and export (grey). +const_expr uint32 MB_C3DCOLOR = 0xFF7F00; ///< \ru Цвет по умолчанию для геометрических объектов. \en Default color for geometric objects. /// \ru Битовые флаги для матрицы и локальной системы координат. \en Bit flags for matrix and local coordinate system. -#define MB_IDENTITY 0x00 ///< \ru Единичная матрица. \en Identity. -#define MB_TRANSLATION 0x01 ///< \ru Присутствует смещение. \en Translation. -#define MB_ROTATION 0x02 ///< \ru Присутствует вращение. \en Rotation. -#define MB_SCALING 0x04 ///< \ru Присутствует масштабирование (компонент не 1.0). \en Scaling (factor is not equal to 1.0). -#define MB_REFLECTION 0x08 ///< \ru Присутствует зеркальная инверсия. \en Reflection. -#define MB_LEFT 0x08 ///< \ru Присутствует зеркальная инверсия (признак левой системы координат). \en Reflection (left coordinate system attribute). -#define MB_ORTOGONAL 0x10 ///< \ru Присутствует ортогональность, взводится только в случае аффинности. \en Orthogonality, is set up in case of affinity. -#define MB_AFFINE 0x20 ///< \ru Отсутствует ортогональность и нормированность (аффинное преобразование). \en Absence of orthogonality and normalization (affine transformation). -#define MB_PERSPECTIVE 0x40 ///< \ru Присутствует вектор перспективы (не нулевой). \en Vector of perspective (non-zero). -#define MB_UNSET 0x80 ///< \ru Битовые флаги не установлены. \en Bit flags not set. +const_expr uint8 MB_IDENTITY = 0x00; ///< \ru Единичная матрица. \en Identity. +const_expr uint8 MB_TRANSLATION = 0x01; ///< \ru Присутствует смещение. \en Translation. +const_expr uint8 MB_ROTATION = 0x02; ///< \ru Присутствует вращение. \en Rotation. +const_expr uint8 MB_SCALING = 0x04; ///< \ru Присутствует масштабирование (компонент не 1.0). \en Scaling (factor is not equal to 1.0). +const_expr uint8 MB_REFLECTION = 0x08; ///< \ru Присутствует зеркальная инверсия. \en Reflection. +const_expr uint8 MB_LEFT = 0x08; ///< \ru Присутствует зеркальная инверсия (признак левой системы координат). \en Reflection (left coordinate system attribute). +const_expr uint8 MB_ORTOGONAL = 0x10; ///< \ru Присутствует ортогональность, взводится только в случае аффинности. \en Orthogonality, is set up in case of affinity. +const_expr uint8 MB_AFFINE = 0x20; ///< \ru Отсутствует ортогональность и нормированность (аффинное преобразование). \en Absence of orthogonality and normalization (affine transformation). +const_expr uint8 MB_PERSPECTIVE = 0x40; ///< \ru Присутствует вектор перспективы (не нулевой). \en Vector of perspective (non-zero). +const_expr uint8 MB_UNSET = 0x80; ///< \ru Битовые флаги не установлены. \en Bit flags not set. /** \} */ @@ -330,6 +336,7 @@ private: static bool namesComplete; ///< \ru Флаг полного именования объекта. \en Flag of object full naming. ///< \ru Проименовать грани, рёбра, вершины оболочки после её создания (true) \en Name faces, edges, vertices of shell after creation (true) ///< \ru Проименовать только грани оболочки после её создания (false) \en Name only faces of shell after creation (false) + static bool supressCreators; ///< \ru Флаг отключения работы построителей. \en Constructor disable flag. static MbeMultithreadedMode multithreadedMode; ///< \ru Флаг режима многопоточных вычислений (по умолчанию максимальный). \en Flag of multithreading mode (maximum by default). ///< \ru mtm_Off - Многопоточные вычисления отключены. \en Multithreading is off. ///< \ru mtm_Standard - Включена многопоточность ядра при обработке независимых объектов. \en Kernel multithreading is ON for independent objects. @@ -390,6 +397,22 @@ public: */ static void SetNamesComplete( bool b ); +/** \brief \ru Необходимо ли отключить работу построителей. + \en Is it necessary to disable the work of constructors. \~ + \details \ru Необходимо ли отключить работу построителей. \n + \en Is it necessary to disable the work of constructors. \n \~ + \ingroup Base_Items +*/ + static bool SupressCreators(); + +/** \brief \ru Установить флаг отключения работы построителей. + \en Set the flag to disable the constructors. \~ + \details \ru Установить флаг отключения работы построителей. \n + \en Set the flag to disable the constructors. \~ + \ingroup Base_Items +*/ + static void SetSupressCreators( bool b ); + /** \brief \ru Используются ли многопоточные вычисления? \en Are multithreaded calculations used? \~ \details \ru Используются ли многопоточные вычисления? \n @@ -460,6 +483,9 @@ MATH_FUNC( const char* ) C3DFileNameOnly( const char* path ); #define C3D_ASSERT_AS_CERR(expr) fprintf(stderr, "C3D ASSERT VIOLATION in file %s, %d:\n `%s' in function: %s.\n", C3DFileNameOnly(__FILE__), __LINE__, #expr, __PRETTY_FUNCTION__); #endif +// Supress a warning "unreferenced formal parameter" +#define C3D_UNUSED_PARAMETER( param ) (void*)(¶m) + #ifdef C3D_DEBUG #define C3D_ASSERT_UNCONDITIONAL(expr) \ { const c3d::eAssertViolationNotify notify = Math::CheckAssertNotify(); \ @@ -480,5 +506,11 @@ MATH_FUNC( const char* ) C3DFileNameOnly( const char* path ); #define C3D_ASSERT(expr) ((void)0) #endif +extern "C" +{ + extern MATH_SYMBOL const char * const c3dVersionInfo; ///< \ru Информация о версии c3d.dll \en c3d.dll version information + extern MATH_SYMBOL const char * const c3dBuildInfo; ///< \ru Информация о сборке c3d.dll \en c3d.dll building information +} + #endif // __MB_VARIABLES_H diff --git a/C3d/Include/mb_vector.h b/C3d/Include/mb_vector.h index 83eae5d..08185f1 100644 --- a/C3d/Include/mb_vector.h +++ b/C3d/Include/mb_vector.h @@ -53,7 +53,7 @@ public : /// \ru Конструктор копирования. \en Copy constructor. MbVector ( const MbVector & dir ) : x( dir.x ), y( dir.y ) {} /// \ru Конструктор по двум точкам. \en The constructor by two points. - MbVector ( const MbCartPoint & p1, const MbCartPoint & p2 ) { Init( p1, p2 ); } + MbVector ( const MbCartPoint & p1, const MbCartPoint & p2 ) { Init( p1, p2 ); } /// \ru Конструктор по точке. \en Constructor by point. MbVector ( const MbCartPoint & p ); /// \ru Конструктор по углу. \en Constructor by angle. @@ -700,7 +700,7 @@ public : /// \ru Найти положение вектора относительно текущего вектора. \en Find vector location relative to current vector. int Relative( const MbDirection & ) const; /// \ru Проверить на вырожденность. \en Check for degeneracy. - bool IsDegenerate( double lenEps = Math::LengthEps ) const; + bool IsDegenerate( double lenEps = Math::LengthEps ) const; /// \ru Выдать свойства объекта. \en Get properties of the object. void GetProperties( MbProperties &properties ); diff --git a/C3d/Include/mesh.h b/C3d/Include/mesh.h index c9afdfc..e1bf88f 100644 --- a/C3d/Include/mesh.h +++ b/C3d/Include/mesh.h @@ -185,12 +185,12 @@ public: gr->DecRef(); gridsVector.push_back( gr ); } - grids.clear(); -#ifdef STANDARD_C11 - grids.shrink_to_fit(); -#endif - cube.SetEmpty(); } + grids.clear(); +#ifdef C3D_STANDARD_CXX_11_PARTIAL + grids.shrink_to_fit(); +#endif + cube.SetEmpty(); } /// \ru Вернуть указатель на триангуляцию по её номеру. \en Return pointer to triangulation by it number. const MbGrid * GetGrid( size_t i ) const { return ( (i < grids.size()) ? grids[i]: NULL ); } @@ -224,12 +224,12 @@ public: pl->DecRef(); polyVector.push_back( pl ); } - wires.clear(); -#ifdef STANDARD_C11 - wires.shrink_to_fit(); -#endif - cube.SetEmpty(); } + wires.clear(); +#ifdef C3D_STANDARD_CXX_11_PARTIAL + wires.shrink_to_fit(); +#endif + cube.SetEmpty(); } /// \ru Вернуть указатель на полигон по его номеру. \en Return the pointer to polygon by its number. const MbPolygon3D * GetPolygon( size_t i ) const { return ( (i < wires.size()) ? wires[i]: NULL ); } @@ -263,12 +263,12 @@ public: peak->DecRef(); peakVector.push_back( peak ); } - peaks.clear(); -#ifdef STANDARD_C11 - peaks.shrink_to_fit(); -#endif - cube.SetEmpty(); } + peaks.clear(); +#ifdef C3D_STANDARD_CXX_11_PARTIAL + peaks.shrink_to_fit(); +#endif + cube.SetEmpty(); } /// \ru Вернуть указатель на апекс по его номеру. \en Return the pointer to apex by its number. const MbApex3D * GetApex( size_t i ) const { return ( (i < peaks.size()) ? peaks[i]: NULL ); } diff --git a/C3d/Include/mesh_float_point3d.h b/C3d/Include/mesh_float_point3d.h index 4203509..63a7fd5 100644 --- a/C3d/Include/mesh_float_point3d.h +++ b/C3d/Include/mesh_float_point3d.h @@ -13,7 +13,7 @@ #include -#define MB_MAXFLOAT MAXIMON // \ru Максимальное значение. \en Maximum value. +const_expr float MB_MAXFLOAT = (float)MAXIMON; // \ru Максимальное значение. \en Maximum value. class MATH_CLASS MbFloatVector3D; diff --git a/C3d/Include/mesh_grid.h b/C3d/Include/mesh_grid.h index 5e37c7b..0f3d9e1 100644 --- a/C3d/Include/mesh_grid.h +++ b/C3d/Include/mesh_grid.h @@ -1,902 +1,906 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Tриангуляция. - \en Triangulation. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __MESH_GRID_H -#define __MESH_GRID_H - - -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbRect; - - -//////////////////////////////////////////////////////////////////////////////// -/** \brief \ru Триангуляция на числах double. - \en Triangulation on double data. \~ - \details \ru Триангуляция представляет собой набор треугольных и четырёхугольных пластин, стыкующихся друг с другом по общим сторонам.\n - Триангуляция состоит из согласованных наборов точек, нормалей, параметров триангулируемой поверхности и наборов треугольников и четырехугольников. - Каждый треугольник - это три номера из набора точек, определяющих вершины треугольника, каждый четырехугольник - это четыре номера из набора точек, определяющих вершины четырехугольника. \n - \en Triangulation represents a set of triangular and quadrangular plates which are joined to each other by their common sides.\n - The triangulation consists of a sets of points, normals, surface parameters and a sets of triangles and quadrangles. - The triangle is represented as three indices from the set of points defining vertices of triangle, the quadrangle is represented as four indices from the set of points defining vertices of quadrangle.\n \~ - \ingroup Polygonal_Objects -*/ -//////////////////////////////////////////////////////////////////////////////// -class MATH_CLASS MbExactGrid : public MbGrid { -private: - std::vector points; ///< \ru Множество контрольных точек триангуляции (согласовано с множеством параметров, если последнее не пустое, или пусто, если не пусто множество параметров). \en Set of control points of triangulation (synchronized with set of parameters if the last is not empty, or empty if the set of parameters isn't empty). - std::vector normals; ///< \ru Множество нормалей в контрольных точках согласовано с множеством контрольных точек. \en Set of normals at control points is synchronized with the set of control points. - std::vector params; ///< \ru Множество параметров - двумерных точек на параметрической области триангулируемой поверхности (может быть пустым). \en Set of parameters of two-dimensional points in parametric domain of surface being triangulated(can be empty). - std::vector escorts; ///< \ru Множество значений для дополнительной информации в точках. \en The set of values for additional information of points. - -protected: - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. - MbExactGrid( const MbExactGrid & init ); - // \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. - explicit MbExactGrid( const MbExactGrid & init, MbRegDuplicate * iReg ); -public: - // \ru Конструктор без параметров. \en Constructor without parameters. - MbExactGrid(); - // \ru Деструктор. \en Destructor. - virtual ~MbExactGrid(); - -public: - - // \ru \name Общие функции примитива. \en \name Common functions of primitive. - - virtual MbePrimitiveType IsA() const; // \ru Тип объекта. \en A type of an object. - virtual MbExactGrid & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Создать копию объекта. \en Create a copy of the object. - virtual void Transform( const MbMatrix3D & matr ); // \ru Преобразовать сетку согласно матрице. \en Transform mesh according to the matrix. - virtual void Move ( const MbVector3D & to ); // \ru Сдвиг сетки. \en Move mesh. - virtual void Rotate ( const MbAxis3D & axis, double angle ); // \ru Поворот сетки вокруг оси. \en Rotation of mesh about an axis. - virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Расширить присланный габаритный куб так, чтобы он включал в себя данный объект. \en Extend given bounding box so that it encloses the given object. - virtual double DistanceToPoint( const MbCartPoint3D & pnt ) const; // \ru Вычислить расстояние до точки. \en Calculate distance to point. - virtual double DistanceToLine( const MbAxis3D & axis, double maxDistance, double & t ) const; // \ru Вычислить расстояние до оси. \en Calculate the distance to an axis. - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта. \en Set properties of the object. - - // \ru Выдать количество точек. \en Get the number of points. - virtual size_t PointsCount() const { return points.size(); } - // \ru Выдать количество нормалей. \en Get the number of normals. - virtual size_t NormalsCount() const { return normals.size(); } - // \ru Выдать количество параметров. \en Get the number of parameters. - virtual size_t ParamsCount() const { return params.size(); } - // \ru Выдать количество значений. \en Get count of values. - virtual size_t EscortsCount() const { return escorts.size(); } - // \ru Выдать количество точек минус 1 (максимальный индекс). \en Get the number of points minus one (maximal index). - virtual ptrdiff_t PointsMaxIndex() const { ptrdiff_t n = points.size(); return ( n - 1 ); } - // \ru Выдать количество нормалей минус 1 (максимальный индекс). \en Get the number of normals minus one (maximal index). - virtual ptrdiff_t NormalsMaxIndex() const { ptrdiff_t n = normals.size(); return ( n - 1 ); } - // \ru Выдать количество параметров минус 1 (максимальный индекс). \en Get the number of parameters minus one (maximal index). - virtual ptrdiff_t ParamsMaxIndex() const { ptrdiff_t n = params.size(); return ( n - 1 ); } - - // \ru Добавить в триангуляцию параметры, точку и нормаль триангулируемой поверхности в точке. \en Add parameters, point and normal of triangulated surface at point to triangulation. - virtual void AddPoint ( const MbCartPoint & p2D, const MbCartPoint3D & p3D, const MbVector3D & n3D ); - // \ru Добавить в триангуляцию параметры и точку. \en Add parameters and a point to triangulation. - virtual void AddPoint ( const MbCartPoint & p2D, const MbCartPoint3D & p3D ); - // \ru Добавить в триангуляцию точку и нормаль в точке. \en Add a point and normal at the point to triangulation. - virtual void AddPoint ( const MbCartPoint3D & p3D, const MbVector3D & n3D ); - // \ru Добавить в триангуляцию точку. \en Add a point to triangulation. - virtual void AddPoint ( const MbCartPoint3D & p3D ); - // \ru Добавить в триангуляцию нормаль. \en Add a normal to triangulation. - virtual void AddNormal( const MbVector3D & n3D ); - // \ru Добавить в триангуляцию параметры триангулируемой поверхности. \en Add parameters of triangulated surface to triangulation. - virtual void AddParam ( const MbCartPoint & p2D ); - - // \ru Добавить в триангуляцию параметры, точку и нормаль триангулируемой поверхности в точке. \en Add parameters, point and normal of triangulated surface at point to triangulation. - virtual void AddPoint ( const MbFloatPoint & p2D, const MbFloatPoint3D & p3D, const MbFloatVector3D & n3D ); - // \ru Добавить в триангуляцию параметры и точку. \en Add parameters and a point to triangulation. - virtual void AddPoint ( const MbFloatPoint & p2D, const MbFloatPoint3D & p3D ); - // \ru Добавить в триангуляцию точку и нормаль в точке. \en Add a point and normal at the point to triangulation. - virtual void AddPoint ( const MbFloatPoint3D & p3D, const MbFloatVector3D & n3D ); - // \ru Добавить в триангуляцию точку. \en Add a point to triangulation. - virtual void AddPoint( const MbFloatPoint3D & p3D ); - // \ru Добавить в триангуляцию нормаль. \en Add a normal to triangulation. - virtual void AddNormal( const MbFloatVector3D & n3D ); - // \ru Добавить в триангуляцию параметры триангулируемой поверхности. \en Add parameters of triangulated surface to triangulation. - virtual void AddParam( const MbFloatPoint & p2D ); - - // \ru Добавить в триангуляцию точки. \en Add points to triangulation. - template - void AddPoints ( const PointsVector & pnts ) { - size_t addCnt = pnts.size(); - points.reserve( points.size() + addCnt ); - for ( size_t k = 0; k < addCnt; k++ ) - points.push_back( pnts[k] ); - } - // \ru Добавить в триангуляцию нормали. \en Add normals to triangulation. - template - void AddNormals( const NormalsVector & nrms ) { - size_t addCnt = nrms.size(); - normals.reserve( normals.size() + addCnt ); - for ( size_t k = 0; k < addCnt; k++ ) - normals.push_back( nrms[k] ); - } - // \ru Добавить в триангуляцию параметры триангулируемой поверхности. \en Add parameters of triangulated surface to triangulation. - template - void AddParams( const ParamsVector & prms ) { - size_t addCnt = prms.size(); - params.reserve( params.size() + addCnt ); - for ( size_t k = 0; k < addCnt; k++ ) - params.push_back( prms[k] ); - } - - // \ru Добавить в коллекцию данных. \en Add scores to collection. - virtual void AddEscorts( const std::vector & scores ) { escorts.insert(escorts.end(), scores.begin(), scores.end()); } - - // \ru Выдать точку по её номеру. \en Get point by its index. - virtual void GetPoint ( size_t i, MbCartPoint3D & p ) const; - // \ru Выдать нормаль по её номеру. \en Get normal by its index. - virtual void GetNormal( size_t i, MbVector3D & n ) const; - // \ru Выдать параметр по его номеру. \en Get parameter by its index. - virtual void GetParam ( size_t i, MbCartPoint & p ) const; - // \ru Выдать дополнительную информацию по её номеру. \en Get additional information by its index. - virtual const uint32 & GetEscort( size_t i ) const; - - /// \ru Выдать точку по её номеру. \en Get point by its index. - virtual void GetPoint ( size_t i, MbFloatPoint3D & p ) const; - /// \ru Выдать нормаль по её номеру. \en Get normal by its index. - virtual void GetNormal( size_t i, MbFloatVector3D & n ) const; - /// \ru Выдать параметр по его номеру. \en Get parameter by its index. - virtual void GetParam ( size_t i, MbFloatPoint & p ) const; - - // \ru Выдать точку с заданным номером. \en Get point by the given index. - const MbCartPoint3D & GetPoint ( size_t i ) const; - // \ru Выдать нормаль с заданным номером. \en Get normal by the given index. - const MbVector3D & GetNormal( size_t i ) const; - // \ru Выдать параметр с заданным номером. \en Get parameter by the given index. - const MbCartPoint & GetParam ( size_t i ) const; - - // \ru Установить точку с заданным номером. \en Set point by the given index. - virtual void SetPoint ( size_t i, const MbCartPoint3D & p ); - // \ru Установить нормаль с заданным номером. \en Set normal by the given index. - virtual void SetNormal( size_t i, const MbVector3D & n ); - // \ru Установить параметр с заданным номером. \en Set parameter by the given index. - virtual void SetParam ( size_t i, const MbCartPoint & p ); - // \ru Установить дополнительную информацию по её номеру. \en Set additional information by its index. - virtual void SetEscort( size_t i, const uint32 & e ); - - // \ru Удалить точку с заданным номером. \en Delete point by the given index. - virtual void PointRemove ( size_t i ); - // \ru Удалить нормаль с заданным номером. \en Delete normal by the given index. - virtual void NormalRemove( size_t i ); - // \ru Удалить параметры поверхности с заданным номером. \en Delete parameters of surface by the given index. - virtual void ParamRemove ( size_t i ); - - // \ru Удалить точки. \en Delete points. - virtual void PointsDelete(); - // \ru Удалить нормали. \en Delete normal. - virtual void NormalsDelete(); - // \ru Удалить параметры. \en Delete papams. - virtual void PapamsDelete(); - // \ru Удалить дополнительную информацию. \en Delete additional information. - virtual void EscortsDelete(); - - /// \ru Инвертировать нормали. \en Invert normals. - virtual void NormalsInvert(); - - // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) параметры поверхности. \en Get parameters of surface for i-th triangle in general numbering (with strips). - virtual bool GetTriangleParams ( size_t i, MbCartPoint & r0, MbCartPoint & r1, MbCartPoint & r2 ) const; - // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th triangle in general numbering (with strips). - virtual bool GetTrianglePoints ( size_t i, MbCartPoint3D & p0, MbCartPoint3D & p1, MbCartPoint3D & p2 ) const; - // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th triangle in general numbering (with strips). - virtual bool GetTrianglePoints ( size_t i, MbFloatPoint3D & p0, MbFloatPoint3D & p1, MbFloatPoint3D & p2 ) const; - // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th triangle in general numbering (with strips). - virtual bool GetTriangleNormals ( size_t i, MbVector3D & n0, MbVector3D & n1, MbVector3D & n2 ) const; - // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th triangle in general numbering (with strips). - virtual bool GetTriangleNormals ( size_t i, MbFloatVector3D & n0, MbFloatVector3D & n1, MbFloatVector3D & n2 ) const; - - // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) параметры поверхности. \en Get parameters of surface for i-th quadrangle in general numbering (with strips). - virtual bool GetQuadrangleParams ( size_t i, MbCartPoint & r0, MbCartPoint & r1, MbCartPoint & r2, MbCartPoint & r3 ) const; - // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th quadrangle in general numbering (with strips). - virtual bool GetQuadranglePoints ( size_t i, MbCartPoint3D & p0, MbCartPoint3D & p1, MbCartPoint3D & p2, MbCartPoint3D & p3 ) const; - // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th quadrangle in general numbering (with strips). - virtual bool GetQuadranglePoints ( size_t i, MbFloatPoint3D & p0, MbFloatPoint3D & p1, MbFloatPoint3D & p2, MbFloatPoint3D & n3 ) const; - // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th quadrangle in general numbering (with strips). - virtual bool GetQuadrangleNormals( size_t i, MbVector3D & n0, MbVector3D & n1, MbVector3D &n2, MbVector3D & n3 ) const; - // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th quadrangle in general numbering (with strips). - virtual bool GetQuadrangleNormals( size_t i, MbFloatVector3D & n0, MbFloatVector3D & n1, MbFloatVector3D & n2, MbFloatVector3D & n3 ) const; - - // \ru Выдать первую нормаль для плоской триангуляции, если количество точек больше количества нормалей (только для плоской триангуляции). \en Get first normal for flat triangulation if count of points is greater than count of normals (only for planar triangulation). - virtual bool GetSingleNormal ( MbVector3D & ) const; - // \ru Выдать первую нормаль для плоской триангуляции, если количество точек больше количества нормалей (только для плоской триангуляции). \en Get first normal for flat triangulation if count of points is greater than count of normals (only for planar triangulation). - virtual bool GetSingleNormal ( MbFloatVector3D & ) const; - // \ru Если количество точек больше количества нормалей, то добавить недостающие нормали (только для плоской триангуляции). \en If count of points is greater than count of normals, then add missing normals (only for planar triangulation). - virtual void SynchronizNormals (); - - // \ru Выдать контейнер параметров. \en Get the container of parameters. - template - void GetParams( ParamsVector & paramsVector ) const { - paramsVector.reserve( paramsVector.size() + params.size() ); - for ( size_t i = 0, iCount = params.size(); i < iCount; i++ ) - paramsVector.push_back( params[i] ); - } - // \ru Выдать контейнер точек. \en Get the container of points. - template - void GetPoints( PointsVector & pointsVector ) const { - pointsVector.reserve( pointsVector.size() + points.size() ); - for ( size_t i = 0, iCount = points.size(); i < iCount; i++ ) - pointsVector.push_back( points[i] ); - } - // \ru Выдать контейнер нормалей. \en Get the container of normals. - template - void GetNormals( NormalsVector & normalsVector ) const { - normalsVector.reserve( normalsVector.size() + normals.size() ); - for ( size_t i = 0, iCount = normals.size(); i < iCount; i++ ) - normalsVector.push_back( normals[i] ); - } - - // \ru Расширить присланный габаритный прямоугольник так, чтобы он включал в себя проекцию данного объекта на глобальную плоскость XY. \en Extend given bounding box so that it enclose projection of this object to the global XY-plane. - virtual void AddRect( MbRect & rect ) const; - // \ru Расширить присланный габаритный куб так, чтобы он включал в себя данный объект. \en Extend given bounding box so that it encloses the given object. - virtual void AddCube( MbCube & r ) const; - - // \ru Определить, пересекается ли проекция на глобальную плоскость XY треугольника с заданным номером с присланным прямоугольником. \en Determine whether the projection of triangle with a given index to the global XY-plane intersects the given rectangle. - bool TriangleIntersectRect( size_t i, MbRect & rect ) const { return (i points; ///< \ru Множество контрольных точек триангуляции (согласовано с множеством параметров, если последнее не пустое, или пусто, если не пусто множество параметров). \en Set of control points of triangulation (synchronized with set of parameters if the last is not empty, or empty if the set of parameters isn't empty). - std::vector normals; ///< \ru Множество нормалей в контрольных точках согласовано с множеством контрольных точек. \en Set of normals at control points is synchronized with the set of control points. - std::vector params; ///< \ru Множество параметров - двумерных точек на параметрической области триангулируемой поверхности (может быть пустым). \en Set of parameters of two-dimensional points in parametric domain of surface being triangulated(can be empty). - std::vector escorts; ///< \ru Множество значений для дополнительной информации в точках. \en The set of values for additional information of points. - -protected: - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. - MbFloatGrid( const MbFloatGrid & init ); - // \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. - explicit MbFloatGrid( const MbFloatGrid & init, MbRegDuplicate * iReg ); -public: - // \ru Конструктор без параметров. \en Constructor without parameters. - MbFloatGrid(); - // \ru Деструктор. \en Destructor. - virtual ~MbFloatGrid(); - -public: - - // \ru \name Общие функции примитива. \en \name Common functions of primitive. - - virtual MbePrimitiveType IsA() const; // \ru Тип объекта. \en A type of an object. - virtual MbFloatGrid & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Создать копию объекта. \en Create a copy of the object. - virtual void Transform( const MbMatrix3D & matr ); // \ru Преобразовать сетку согласно матрице. \en Transform mesh according to the matrix. - virtual void Move ( const MbVector3D & to ); // \ru Сдвиг сетки. \en Move mesh. - virtual void Rotate ( const MbAxis3D & axis, double angle ); // \ru Поворот сетки вокруг оси. \en Rotation of mesh about an axis. - virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Расширить присланный габаритный куб так, чтобы он включал в себя данный объект. \en Extend given bounding box so that it encloses the given object. - virtual double DistanceToPoint( const MbCartPoint3D & pnt ) const; // \ru Вычислить расстояние до точки. \en Calculate distance to point. - virtual double DistanceToLine( const MbAxis3D & axis, double maxDistance, double & t ) const; // \ru Вычислить расстояние до оси. \en Calculate the distance to an axis. - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта. \en Set properties of the object. - - // \ru Выдать количество точек. \en Get the number of points. - virtual size_t PointsCount() const { return points.size(); } - // \ru Выдать количество нормалей. \en Get the number of normals. - virtual size_t NormalsCount() const { return normals.size(); } - // \ru Выдать количество параметров. \en Get the number of parameters. - virtual size_t ParamsCount() const { return params.size(); } - // \ru Выдать количество значений. \en Get count of values. - virtual size_t EscortsCount() const { return escorts.size(); } - // \ru Выдать количество точек минус 1 (максимальный индекс). \en Get the number of points minus one (maximal index). - virtual ptrdiff_t PointsMaxIndex() const { ptrdiff_t n = points.size(); return ( n - 1 ); } - // \ru Выдать количество нормалей минус 1 (максимальный индекс). \en Get the number of normals minus one (maximal index). - virtual ptrdiff_t NormalsMaxIndex() const { ptrdiff_t n = normals.size(); return ( n - 1 ); } - // \ru Выдать количество параметров минус 1 (максимальный индекс). \en Get the number of parameters minus one (maximal index). - virtual ptrdiff_t ParamsMaxIndex() const { ptrdiff_t n = params.size(); return ( n - 1 ); } - - // \ru Добавить в триангуляцию параметры, точку и нормаль триангулируемой поверхности в точке. \en Add parameters, point and normal of triangulated surface at point to triangulation. - virtual void AddPoint ( const MbCartPoint & p2D, const MbCartPoint3D & p3D, const MbVector3D & n3D ); - // \ru Добавить в триангуляцию параметры и точку. \en Add parameters and a point to triangulation. - virtual void AddPoint ( const MbCartPoint & p2D, const MbCartPoint3D & p3D ); - // \ru Добавить в триангуляцию точку и нормаль в точке. \en Add a point and normal at the point to triangulation. - virtual void AddPoint ( const MbCartPoint3D & p3D, const MbVector3D & n3D ); - // \ru Добавить в триангуляцию точку. \en Add a point to triangulation. - virtual void AddPoint ( const MbCartPoint3D & p3D ); - // \ru Добавить в триангуляцию нормаль. \en Add a normal to triangulation. - virtual void AddNormal( const MbVector3D & n3D ); - // \ru Добавить в триангуляцию параметры триангулируемой поверхности. \en Add parameters of triangulated surface to triangulation. - virtual void AddParam ( const MbCartPoint & p2D ); - - // \ru Добавить в триангуляцию параметры, точку и нормаль триангулируемой поверхности в точке. \en Add parameters, point and normal of triangulated surface at point to triangulation. - virtual void AddPoint ( const MbFloatPoint & p2D, const MbFloatPoint3D & p3D, const MbFloatVector3D & n3D ); - // \ru Добавить в триангуляцию параметры и точку. \en Add parameters and a point to triangulation. - virtual void AddPoint ( const MbFloatPoint & p2D, const MbFloatPoint3D & p3D ); - // \ru Добавить в триангуляцию точку и нормаль в точке. \en Add a point and normal at the point to triangulation. - virtual void AddPoint ( const MbFloatPoint3D & p3D, const MbFloatVector3D & n3D ); - // \ru Добавить в триангуляцию точку. \en Add a point to triangulation. - virtual void AddPoint( const MbFloatPoint3D & p3D ); - // \ru Добавить в триангуляцию нормаль. \en Add a normal to triangulation. - virtual void AddNormal( const MbFloatVector3D & n3D ); - // \ru Добавить в триангуляцию параметры триангулируемой поверхности. \en Add parameters of triangulated surface to triangulation. - virtual void AddParam( const MbFloatPoint & p2D ); - - // \ru Добавить в триангуляцию точки. \en Add points to triangulation. - template - void AddPoints ( const PointsVector & pnts ) { - size_t addCnt = pnts.size(); - points.reserve( points.size() + addCnt ); - for ( size_t k = 0; k < addCnt; k++ ) - points.push_back( pnts[k] ); - } - // \ru Добавить в триангуляцию нормали. \en Add normals to triangulation. - template - void AddNormals( const NormalsVector & nrms ) { - size_t addCnt = nrms.size(); - normals.reserve( normals.size() + addCnt ); - for ( size_t k = 0; k < addCnt; k++ ) - normals.push_back( nrms[k] ); - } - // \ru Добавить в триангуляцию параметры триангулируемой поверхности. \en Add parameters of triangulated surface to triangulation. - template - void AddParams( const ParamsVector & prms ) { - size_t addCnt = prms.size(); - params.reserve( params.size() + addCnt ); - for ( size_t k = 0; k < addCnt; k++ ) - params.push_back( prms[k] ); - } - - // \ru Добавить в коллекцию данных. \en Add scores to collection. - virtual void AddEscorts( const std::vector & scores ) { escorts.insert(escorts.end(), scores.begin(), scores.end()); } - - // \ru Выдать точку по её номеру. \en Get point by its index. - virtual void GetPoint ( size_t i, MbCartPoint3D & p ) const; - // \ru Выдать нормаль по её номеру. \en Get normal by its index. - virtual void GetNormal( size_t i, MbVector3D & n ) const; - // \ru Выдать параметр по его номеру. \en Get parameter by its index. - virtual void GetParam ( size_t i, MbCartPoint & p ) const; - // \ru Выдать дополнительную информацию по её номеру. \en Get additional information by its index. - virtual const uint32 & GetEscort( size_t i ) const; - - /// \ru Выдать точку по её номеру. \en Get point by its index. - virtual void GetPoint ( size_t i, MbFloatPoint3D & p ) const; - /// \ru Выдать нормаль по её номеру. \en Get normal by its index. - virtual void GetNormal( size_t i, MbFloatVector3D & n ) const; - /// \ru Выдать параметр по его номеру. \en Get parameter by its index. - virtual void GetParam ( size_t i, MbFloatPoint & p ) const; - - // \ru Выдать точку с заданным номером. \en Get point by the given index. - const MbFloatPoint3D & GetPoint ( size_t i ) const; - // \ru Выдать нормаль с заданным номером. \en Get normal by the given index. - const MbFloatVector3D & GetNormal( size_t i ) const; - // \ru Выдать параметр с заданным номером. \en Get parameter by the given index. - const MbFloatPoint & GetParam ( size_t i ) const; - - // \ru Установить точку с заданным номером. \en Set point by the given index. - virtual void SetPoint ( size_t i, const MbCartPoint3D & p ); - // \ru Установить нормаль с заданным номером. \en Set normal by the given index. - virtual void SetNormal( size_t i, const MbVector3D & n ); - // \ru Установить параметр с заданным номером. \en Set parameter by the given index. - virtual void SetParam ( size_t i, const MbCartPoint & p ); - // \ru Установить дополнительную информацию по её номеру. \en Set additional information by its index. - virtual void SetEscort( size_t i, const uint32 & e ); - - // \ru Удалить точку с заданным номером. \en Delete point by the given index. - virtual void PointRemove ( size_t i ); - // \ru Удалить нормаль с заданным номером. \en Delete normal by the given index. - virtual void NormalRemove( size_t i ); - // \ru Удалить параметры поверхности с заданным номером. \en Delete parameters of surface by the given index. - virtual void ParamRemove ( size_t i ); - - // \ru Удалить точки. \en Delete points. - virtual void PointsDelete(); - // \ru Удалить нормали. \en Delete normal. - virtual void NormalsDelete(); - // \ru Удалить параметры. \en Delete papams. - virtual void PapamsDelete(); - // \ru Удалить дополнительную информацию. \en Delete additional information. - virtual void EscortsDelete(); - - /// \ru Инвертировать нормали. \en Invert normals. - virtual void NormalsInvert(); - - // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) параметры поверхности. \en Get parameters of surface for i-th triangle in general numbering (with strips). - virtual bool GetTriangleParams ( size_t i, MbCartPoint & r0, MbCartPoint & r1, MbCartPoint & r2 ) const; - // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th triangle in general numbering (with strips). - virtual bool GetTrianglePoints ( size_t i, MbCartPoint3D & p0, MbCartPoint3D & p1, MbCartPoint3D & p2 ) const; - // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th triangle in general numbering (with strips). - virtual bool GetTrianglePoints ( size_t i, MbFloatPoint3D & p0, MbFloatPoint3D & p1, MbFloatPoint3D & p2 ) const; - // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th triangle in general numbering (with strips). - virtual bool GetTriangleNormals ( size_t i, MbVector3D & n0, MbVector3D & n1, MbVector3D & n2 ) const; - // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th triangle in general numbering (with strips). - virtual bool GetTriangleNormals ( size_t i, MbFloatVector3D & n0, MbFloatVector3D & n1, MbFloatVector3D & n2 ) const; - - // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) параметры поверхности. \en Get parameters of surface for i-th quadrangle in general numbering (with strips). - virtual bool GetQuadrangleParams ( size_t i, MbCartPoint & r0, MbCartPoint & r1, MbCartPoint & r2, MbCartPoint & r3 ) const; - // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th quadrangle in general numbering (with strips). - virtual bool GetQuadranglePoints ( size_t i, MbCartPoint3D & p0, MbCartPoint3D & p1, MbCartPoint3D & p2, MbCartPoint3D & p3 ) const; - // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th quadrangle in general numbering (with strips). - virtual bool GetQuadranglePoints ( size_t i, MbFloatPoint3D & p0, MbFloatPoint3D & p1, MbFloatPoint3D & p2, MbFloatPoint3D & n3 ) const; - // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th quadrangle in general numbering (with strips). - virtual bool GetQuadrangleNormals( size_t i, MbVector3D & n0, MbVector3D & n1, MbVector3D &n2, MbVector3D & n3 ) const; - // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th quadrangle in general numbering (with strips). - virtual bool GetQuadrangleNormals( size_t i, MbFloatVector3D & n0, MbFloatVector3D & n1, MbFloatVector3D & n2, MbFloatVector3D & n3 ) const; - - // \ru Выдать первую нормаль для плоской триангуляции, если количество точек больше количества нормалей (только для плоской триангуляции). \en Get first normal for flat triangulation if count of points is greater than count of normals (only for planar triangulation). - virtual bool GetSingleNormal ( MbVector3D & ) const; - // \ru Выдать первую нормаль для плоской триангуляции, если количество точек больше количества нормалей (только для плоской триангуляции). \en Get first normal for flat triangulation if count of points is greater than count of normals (only for planar triangulation). - virtual bool GetSingleNormal ( MbFloatVector3D & ) const; - // \ru Если количество точек больше количества нормалей, то добавить недостающие нормали (только для плоской триангуляции). \en If count of points is greater than count of normals, then add missing normals (only for planar triangulation). - virtual void SynchronizNormals (); - - // \ru Выдать контейнер параметров. \en Get the container of parameters. - template - void GetParams( ParamsVector & paramsVector ) const { - paramsVector.reserve( paramsVector.size() + params.size() ); - for ( size_t i = 0, iCount = params.size(); i < iCount; i++ ) - paramsVector.push_back( params[i] ); - } - // \ru Выдать контейнер точек. \en Get the container of points. - template - void GetPoints( PointsVector & pointsVector ) const { - pointsVector.reserve( pointsVector.size() + points.size() ); - for ( size_t i = 0, iCount = points.size(); i < iCount; i++ ) - pointsVector.push_back( points[i] ); - } - // \ru Выдать контейнер нормалей. \en Get the container of normals. - template - void GetNormals( NormalsVector & normalsVector ) const { - normalsVector.reserve( normalsVector.size() + normals.size() ); - for ( size_t i = 0, iCount = normals.size(); i < iCount; i++ ) - normalsVector.push_back( normals[i] ); - } - - // \ru Расширить присланный габаритный прямоугольник так, чтобы он включал в себя проекцию данного объекта на глобальную плоскость XY. \en Extend given bounding box so that it enclose projection of this object to the global XY-plane. - virtual void AddRect( MbRect & rect ) const; - // \ru Расширить присланный габаритный куб так, чтобы он включал в себя данный объект. \en Extend given bounding box so that it encloses the given object. - virtual void AddCube( MbCube & r ) const; - - // \ru Определить, пересекается ли проекция на глобальную плоскость XY треугольника с заданным номером с присланным прямоугольником. \en Determine whether the projection of triangle with a given index to the global XY-plane intersects the given rectangle. - virtual bool TriangleIntersectRect( size_t i, MbRect & rect ) const { return (i & cutPlaces, - MbFloatPoint3D & crossPnt, - float & tRes ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Найти пересечение прямой линии и триангуляции. - \en Find the intersection of a straight line with the triangulation. \~ - \details \ru Для всех треугольников определяется пересечение с прямой линии и вычисляется минимальное значение - параметра точки пересечения на секущей прямой линии. \n - \en For all the triangles the intersection with the straight line is determined and the minimum value of - the intersection point parameter on the secant straight line is calculated. \n \~ - \param[in] grid - \ru Триангуляция. - \en Triangulation. \~ - \param[in] line - \ru Прямая линия, для которой вычисляется пересечение с триангуляцией. - \en Straight line to calculate the intersection of triangulation with. \~ - \param[out] tRes - \ru Параметр точки пересечения линии. - \en Parameter of the intersection point on the line. \~ - \return \ru Найдено ли пересечение (true - В случае успеха). - \en Whether the intersection is found (true if success). \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC (bool) LineGridIntersect( const MbGrid & grid, - const MbFloatAxis3D & line, - float & tRes ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Вычислить квадрат расстояния от линии до полигона. - \en Calculate squared distance from a line to a polygon. \~ - \details \ru При вычислении квадрата расстояния от линии до полигона проверяется расстояние от каждого - сегмента полигона до первого попадания в окрестность delta. - Возвращается значение параметра ближайшей точки на линии tRes и квадрат расстояния - от этой точки до сегмента полигона. \n - \en During calculation of squared distance from a line to a polygon the distance from each - segment of the polygon is checked until the first getting to 'delta' neighborhood. - Returns the value of the nearest point parameter on tRes line and the squared distance - from this point to a segment of the polygon. \n \~ - \param[in] grid - \ru Триангуляция. - \en Triangulation. \~ - \param[in] edgeInd - \ru Индекс тестируемого полигона. - \en Index of polygon to check. \~ - \param[in] line - \ru Линия, до которой вычисляется расстояние. - \en Line to calculate the distance to. \~ - \param[in] delta - \ru Радиус окрестности вокруг линии. - \en Neighborhood radius around the line. \~ - \param[out] tRes - \ru Значение параметра ближайшей точки линии. - \en The value of parameter of the nearest point on the line. \~ - \return \ru Квадрат расстояния ближайшей точки до линии. - \en Squared distance between the nearest point and the line. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC(float) LineToGridEdgeDistanceSquared( const MbGrid & grid, - size_t edgeInd, - const MbFloatAxis3D & line, - float delta, - float & tRes ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Вычислить квадрат расстояния от линии до полигона. - \en Calculate squared distance from a line to a polygon. \~ - \details \ru При вычислении квадрата расстояния от линии до полигона проверяется расстояние от каждого - сегмента полигона до первого попадания в окрестность delta. - Возвращается значение параметра ближайшей точки на линии tRes, вектор между ближайшими точками и квадрат - расстояния от этой точки до сегмента полигона. \n - \en During calculation of squared distance from a line to a polygon the distance from each - segment of the polygon is checked until the first getting to 'delta' neighborhood. - Returns the value of the nearest point parameter on the line, the vector between the nearest points - and the squared distance from this point to a segment of the polygon. \n \~ - \param[in] grid - \ru Триангуляция. - \en Triangulation. \~ - \param[in] edgeInd - \ru Индекс тестируемого полигона. - \en Index of polygon to check. \~ - \param[in] line - \ru Линия, до которой вычисляется расстояние. - \en Line to calculate the distance to. \~ - \param[in] vDelta - \ru Габарит окрестности вокруг линии. - \en The dimensions of the area around the line. \~ - \param[out] vRes - \ru Вектор от ближайшей точки на линии до ближайшей точки на полигоне. - \en Vector from the nearest point on the line to the nearest point on the polygon. \~ - \param[out] tRes - \ru Значение параметра ближайшей точки линии. - \en The value of parameter of the nearest point on the line. \~ - \return \ru Квадрат расстояния ближайшей точки до линии. - \en Squared distance between the nearest point and the line. \~ - \ingroup Algorithms_3D -*/ -// --- -MATH_FUNC(float) LineToGridEdgeDistanceSquared( const MbGrid & grid, - size_t edgeInd, - const MbFloatAxis3D & line, - const MbFloatVector3D & vDelta, - MbFloatVector3D & vRes, - float & tRes ); - - -#endif // __MESH_GRID_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Tриангуляция. + \en Triangulation. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __MESH_GRID_H +#define __MESH_GRID_H + + +#include +#include +#include +#include +#include +#include + + +class MATH_CLASS MbRect; + + +//////////////////////////////////////////////////////////////////////////////// +/** \brief \ru Триангуляция на числах double. + \en Triangulation on double data. \~ + \details \ru Триангуляция представляет собой набор треугольных и четырёхугольных пластин, стыкующихся друг с другом по общим сторонам.\n + Триангуляция состоит из согласованных наборов точек, нормалей, параметров триангулируемой поверхности и наборов треугольников и четырехугольников. + Каждый треугольник - это три номера из набора точек, определяющих вершины треугольника, каждый четырехугольник - это четыре номера из набора точек, определяющих вершины четырехугольника. \n + \en Triangulation represents a set of triangular and quadrangular plates which are joined to each other by their common sides.\n + The triangulation consists of a sets of points, normals, surface parameters and a sets of triangles and quadrangles. + The triangle is represented as three indices from the set of points defining vertices of triangle, the quadrangle is represented as four indices from the set of points defining vertices of quadrangle.\n \~ + \ingroup Polygonal_Objects +*/ +//////////////////////////////////////////////////////////////////////////////// +class MATH_CLASS MbExactGrid : public MbGrid { +private: + std::vector points; ///< \ru Множество контрольных точек триангуляции (согласовано с множеством параметров, если последнее не пустое, или пусто, если не пусто множество параметров). \en Set of control points of triangulation (synchronized with set of parameters if the last is not empty, or empty if the set of parameters isn't empty). + std::vector normals; ///< \ru Множество нормалей в контрольных точках согласовано с множеством контрольных точек. \en Set of normals at control points is synchronized with the set of control points. + std::vector params; ///< \ru Множество параметров - двумерных точек на параметрической области триангулируемой поверхности (может быть пустым). \en Set of parameters of two-dimensional points in parametric domain of surface being triangulated(can be empty). + std::vector escorts; ///< \ru Множество значений для дополнительной информации в точках. \en The set of values for additional information of points. + +protected: + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. + MbExactGrid( const MbExactGrid & init ); + // \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. + explicit MbExactGrid( const MbExactGrid & init, MbRegDuplicate * iReg ); +public: + // \ru Конструктор без параметров. \en Constructor without parameters. + MbExactGrid(); + // \ru Деструктор. \en Destructor. + virtual ~MbExactGrid(); + +public: + + // \ru \name Общие функции примитива. \en \name Common functions of primitive. + + virtual MbePrimitiveType IsA() const; // \ru Тип объекта. \en A type of an object. + virtual MbExactGrid & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Создать копию объекта. \en Create a copy of the object. + virtual void Transform( const MbMatrix3D & matr ); // \ru Преобразовать сетку согласно матрице. \en Transform mesh according to the matrix. + virtual void Move ( const MbVector3D & to ); // \ru Сдвиг сетки. \en Move mesh. + virtual void Rotate ( const MbAxis3D & axis, double angle ); // \ru Поворот сетки вокруг оси. \en Rotation of mesh about an axis. + virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Расширить присланный габаритный куб так, чтобы он включал в себя данный объект. \en Extend given bounding box so that it encloses the given object. + virtual double DistanceToPoint( const MbCartPoint3D & pnt ) const; // \ru Вычислить расстояние до точки. \en Calculate distance to point. + virtual double DistanceToLine( const MbAxis3D & axis, double maxDistance, double & t ) const; // \ru Вычислить расстояние до оси. \en Calculate the distance to an axis. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта. \en Set properties of the object. + + // \ru Выдать количество точек. \en Get the number of points. + virtual size_t PointsCount() const { return points.size(); } + // \ru Выдать количество нормалей. \en Get the number of normals. + virtual size_t NormalsCount() const { return normals.size(); } + // \ru Выдать количество параметров. \en Get the number of parameters. + virtual size_t ParamsCount() const { return params.size(); } + // \ru Выдать количество значений. \en Get count of values. + virtual size_t EscortsCount() const { return escorts.size(); } + // \ru Выдать количество точек минус 1 (максимальный индекс). \en Get the number of points minus one (maximal index). + virtual ptrdiff_t PointsMaxIndex() const { ptrdiff_t n = points.size(); return ( n - 1 ); } + // \ru Выдать количество нормалей минус 1 (максимальный индекс). \en Get the number of normals minus one (maximal index). + virtual ptrdiff_t NormalsMaxIndex() const { ptrdiff_t n = normals.size(); return ( n - 1 ); } + // \ru Выдать количество параметров минус 1 (максимальный индекс). \en Get the number of parameters minus one (maximal index). + virtual ptrdiff_t ParamsMaxIndex() const { ptrdiff_t n = params.size(); return ( n - 1 ); } + + // \ru Добавить в триангуляцию параметры, точку и нормаль триангулируемой поверхности в точке. \en Add parameters, point and normal of triangulated surface at point to triangulation. + virtual void AddPoint ( const MbCartPoint & p2D, const MbCartPoint3D & p3D, const MbVector3D & n3D ); + // \ru Добавить в триангуляцию параметры и точку. \en Add parameters and a point to triangulation. + virtual void AddPoint ( const MbCartPoint & p2D, const MbCartPoint3D & p3D ); + // \ru Добавить в триангуляцию точку и нормаль в точке. \en Add a point and normal at the point to triangulation. + virtual void AddPoint ( const MbCartPoint3D & p3D, const MbVector3D & n3D ); + // \ru Добавить в триангуляцию точку. \en Add a point to triangulation. + virtual void AddPoint ( const MbCartPoint3D & p3D ); + // \ru Добавить в триангуляцию нормаль. \en Add a normal to triangulation. + virtual void AddNormal( const MbVector3D & n3D ); + // \ru Добавить в триангуляцию параметры триангулируемой поверхности. \en Add parameters of triangulated surface to triangulation. + virtual void AddParam ( const MbCartPoint & p2D ); + + // \ru Добавить в триангуляцию параметры, точку и нормаль триангулируемой поверхности в точке. \en Add parameters, point and normal of triangulated surface at point to triangulation. + virtual void AddPoint ( const MbFloatPoint & p2D, const MbFloatPoint3D & p3D, const MbFloatVector3D & n3D ); + // \ru Добавить в триангуляцию параметры и точку. \en Add parameters and a point to triangulation. + virtual void AddPoint ( const MbFloatPoint & p2D, const MbFloatPoint3D & p3D ); + // \ru Добавить в триангуляцию точку и нормаль в точке. \en Add a point and normal at the point to triangulation. + virtual void AddPoint ( const MbFloatPoint3D & p3D, const MbFloatVector3D & n3D ); + // \ru Добавить в триангуляцию точку. \en Add a point to triangulation. + virtual void AddPoint( const MbFloatPoint3D & p3D ); + // \ru Добавить в триангуляцию нормаль. \en Add a normal to triangulation. + virtual void AddNormal( const MbFloatVector3D & n3D ); + // \ru Добавить в триангуляцию параметры триангулируемой поверхности. \en Add parameters of triangulated surface to triangulation. + virtual void AddParam( const MbFloatPoint & p2D ); + + // \ru Добавить в триангуляцию точки. \en Add points to triangulation. + template + void AddPoints ( const PointsVector & pnts ) { + size_t addCnt = pnts.size(); + points.reserve( points.size() + addCnt ); + for ( size_t k = 0; k < addCnt; k++ ) + points.push_back( pnts[k] ); + } + // \ru Добавить в триангуляцию нормали. \en Add normals to triangulation. + template + void AddNormals( const NormalsVector & nrms ) { + size_t addCnt = nrms.size(); + normals.reserve( normals.size() + addCnt ); + for ( size_t k = 0; k < addCnt; k++ ) + normals.push_back( nrms[k] ); + } + // \ru Добавить в триангуляцию параметры триангулируемой поверхности. \en Add parameters of triangulated surface to triangulation. + template + void AddParams( const ParamsVector & prms ) { + size_t addCnt = prms.size(); + params.reserve( params.size() + addCnt ); + for ( size_t k = 0; k < addCnt; k++ ) + params.push_back( prms[k] ); + } + + // \ru Добавить в коллекцию данных. \en Add scores to collection. + virtual void AddEscorts( const std::vector & scores ) { escorts.insert(escorts.end(), scores.begin(), scores.end()); } + // \ru Добавить в коллекцию данных цвет точки (0 <= r,g,b,a <= 1). \en Add color to collection (0 <= r,g,b,a <= 1). + virtual void AddColor( float r, float g, float b, float a ); + + // \ru Выдать точку по её номеру. \en Get point by its index. + virtual void GetPoint ( size_t i, MbCartPoint3D & p ) const; + // \ru Выдать нормаль по её номеру. \en Get normal by its index. + virtual void GetNormal( size_t i, MbVector3D & n ) const; + // \ru Выдать параметр по его номеру. \en Get parameter by its index. + virtual void GetParam ( size_t i, MbCartPoint & p ) const; + // \ru Выдать дополнительную информацию по её номеру. \en Get additional information by its index. + virtual const uint32 & GetEscort( size_t i ) const; + + /// \ru Выдать точку по её номеру. \en Get point by its index. + virtual void GetPoint ( size_t i, MbFloatPoint3D & p ) const; + /// \ru Выдать нормаль по её номеру. \en Get normal by its index. + virtual void GetNormal( size_t i, MbFloatVector3D & n ) const; + /// \ru Выдать параметр по его номеру. \en Get parameter by its index. + virtual void GetParam ( size_t i, MbFloatPoint & p ) const; + + // \ru Выдать точку с заданным номером. \en Get point by the given index. + const MbCartPoint3D & GetPoint ( size_t i ) const; + // \ru Выдать нормаль с заданным номером. \en Get normal by the given index. + const MbVector3D & GetNormal( size_t i ) const; + // \ru Выдать параметр с заданным номером. \en Get parameter by the given index. + const MbCartPoint & GetParam ( size_t i ) const; + + // \ru Установить точку с заданным номером. \en Set point by the given index. + virtual void SetPoint ( size_t i, const MbCartPoint3D & p ); + // \ru Установить нормаль с заданным номером. \en Set normal by the given index. + virtual void SetNormal( size_t i, const MbVector3D & n ); + // \ru Установить параметр с заданным номером. \en Set parameter by the given index. + virtual void SetParam ( size_t i, const MbCartPoint & p ); + // \ru Установить дополнительную информацию по её номеру. \en Set additional information by its index. + virtual void SetEscort( size_t i, const uint32 & e ); + + // \ru Удалить точку с заданным номером. \en Delete point by the given index. + virtual void PointRemove ( size_t i ); + // \ru Удалить нормаль с заданным номером. \en Delete normal by the given index. + virtual void NormalRemove( size_t i ); + // \ru Удалить параметры поверхности с заданным номером. \en Delete parameters of surface by the given index. + virtual void ParamRemove ( size_t i ); + + // \ru Удалить точки. \en Delete points. + virtual void PointsDelete(); + // \ru Удалить нормали. \en Delete normal. + virtual void NormalsDelete(); + // \ru Удалить параметры. \en Delete papams. + virtual void PapamsDelete(); + // \ru Удалить дополнительную информацию. \en Delete additional information. + virtual void EscortsDelete(); + + /// \ru Инвертировать нормали. \en Invert normals. + virtual void NormalsInvert(); + + // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) параметры поверхности. \en Get parameters of surface for i-th triangle in general numbering (with strips). + virtual bool GetTriangleParams ( size_t i, MbCartPoint & r0, MbCartPoint & r1, MbCartPoint & r2 ) const; + // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th triangle in general numbering (with strips). + virtual bool GetTrianglePoints ( size_t i, MbCartPoint3D & p0, MbCartPoint3D & p1, MbCartPoint3D & p2 ) const; + // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th triangle in general numbering (with strips). + virtual bool GetTrianglePoints ( size_t i, MbFloatPoint3D & p0, MbFloatPoint3D & p1, MbFloatPoint3D & p2 ) const; + // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th triangle in general numbering (with strips). + virtual bool GetTriangleNormals ( size_t i, MbVector3D & n0, MbVector3D & n1, MbVector3D & n2 ) const; + // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th triangle in general numbering (with strips). + virtual bool GetTriangleNormals ( size_t i, MbFloatVector3D & n0, MbFloatVector3D & n1, MbFloatVector3D & n2 ) const; + + // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) параметры поверхности. \en Get parameters of surface for i-th quadrangle in general numbering (with strips). + virtual bool GetQuadrangleParams ( size_t i, MbCartPoint & r0, MbCartPoint & r1, MbCartPoint & r2, MbCartPoint & r3 ) const; + // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th quadrangle in general numbering (with strips). + virtual bool GetQuadranglePoints ( size_t i, MbCartPoint3D & p0, MbCartPoint3D & p1, MbCartPoint3D & p2, MbCartPoint3D & p3 ) const; + // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th quadrangle in general numbering (with strips). + virtual bool GetQuadranglePoints ( size_t i, MbFloatPoint3D & p0, MbFloatPoint3D & p1, MbFloatPoint3D & p2, MbFloatPoint3D & n3 ) const; + // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th quadrangle in general numbering (with strips). + virtual bool GetQuadrangleNormals( size_t i, MbVector3D & n0, MbVector3D & n1, MbVector3D &n2, MbVector3D & n3 ) const; + // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th quadrangle in general numbering (with strips). + virtual bool GetQuadrangleNormals( size_t i, MbFloatVector3D & n0, MbFloatVector3D & n1, MbFloatVector3D & n2, MbFloatVector3D & n3 ) const; + + // \ru Выдать первую нормаль для плоской триангуляции, если количество точек больше количества нормалей (только для плоской триангуляции). \en Get first normal for flat triangulation if count of points is greater than count of normals (only for planar triangulation). + virtual bool GetSingleNormal ( MbVector3D & ) const; + // \ru Выдать первую нормаль для плоской триангуляции, если количество точек больше количества нормалей (только для плоской триангуляции). \en Get first normal for flat triangulation if count of points is greater than count of normals (only for planar triangulation). + virtual bool GetSingleNormal ( MbFloatVector3D & ) const; + // \ru Если количество точек больше количества нормалей, то добавить недостающие нормали (только для плоской триангуляции). \en If count of points is greater than count of normals, then add missing normals (only for planar triangulation). + virtual void SynchronizNormals (); + + // \ru Выдать контейнер параметров. \en Get the container of parameters. + template + void GetParams( ParamsVector & paramsVector ) const { + paramsVector.reserve( paramsVector.size() + params.size() ); + for ( size_t i = 0, iCount = params.size(); i < iCount; i++ ) + paramsVector.push_back( params[i] ); + } + // \ru Выдать контейнер точек. \en Get the container of points. + template + void GetPoints( PointsVector & pointsVector ) const { + pointsVector.reserve( pointsVector.size() + points.size() ); + for ( size_t i = 0, iCount = points.size(); i < iCount; i++ ) + pointsVector.push_back( points[i] ); + } + // \ru Выдать контейнер нормалей. \en Get the container of normals. + template + void GetNormals( NormalsVector & normalsVector ) const { + normalsVector.reserve( normalsVector.size() + normals.size() ); + for ( size_t i = 0, iCount = normals.size(); i < iCount; i++ ) + normalsVector.push_back( normals[i] ); + } + + // \ru Расширить присланный габаритный прямоугольник так, чтобы он включал в себя проекцию данного объекта на глобальную плоскость XY. \en Extend given bounding box so that it enclose projection of this object to the global XY-plane. + virtual void AddRect( MbRect & rect ) const; + // \ru Расширить присланный габаритный куб так, чтобы он включал в себя данный объект. \en Extend given bounding box so that it encloses the given object. + virtual void AddCube( MbCube & r ) const; + + // \ru Определить, пересекается ли проекция на глобальную плоскость XY треугольника с заданным номером с присланным прямоугольником. \en Determine whether the projection of triangle with a given index to the global XY-plane intersects the given rectangle. + bool TriangleIntersectRect( size_t i, MbRect & rect ) const { return (i points; ///< \ru Множество контрольных точек триангуляции (согласовано с множеством параметров, если последнее не пустое, или пусто, если не пусто множество параметров). \en Set of control points of triangulation (synchronized with set of parameters if the last is not empty, or empty if the set of parameters isn't empty). + std::vector normals; ///< \ru Множество нормалей в контрольных точках согласовано с множеством контрольных точек. \en Set of normals at control points is synchronized with the set of control points. + std::vector params; ///< \ru Множество параметров - двумерных точек на параметрической области триангулируемой поверхности (может быть пустым). \en Set of parameters of two-dimensional points in parametric domain of surface being triangulated(can be empty). + std::vector escorts; ///< \ru Множество значений для дополнительной информации в точках. \en The set of values for additional information of points. + +protected: + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. + MbFloatGrid( const MbFloatGrid & init ); + // \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. + explicit MbFloatGrid( const MbFloatGrid & init, MbRegDuplicate * iReg ); +public: + // \ru Конструктор без параметров. \en Constructor without parameters. + MbFloatGrid(); + // \ru Деструктор. \en Destructor. + virtual ~MbFloatGrid(); + +public: + + // \ru \name Общие функции примитива. \en \name Common functions of primitive. + + virtual MbePrimitiveType IsA() const; // \ru Тип объекта. \en A type of an object. + virtual MbFloatGrid & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Создать копию объекта. \en Create a copy of the object. + virtual void Transform( const MbMatrix3D & matr ); // \ru Преобразовать сетку согласно матрице. \en Transform mesh according to the matrix. + virtual void Move ( const MbVector3D & to ); // \ru Сдвиг сетки. \en Move mesh. + virtual void Rotate ( const MbAxis3D & axis, double angle ); // \ru Поворот сетки вокруг оси. \en Rotation of mesh about an axis. + virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Расширить присланный габаритный куб так, чтобы он включал в себя данный объект. \en Extend given bounding box so that it encloses the given object. + virtual double DistanceToPoint( const MbCartPoint3D & pnt ) const; // \ru Вычислить расстояние до точки. \en Calculate distance to point. + virtual double DistanceToLine( const MbAxis3D & axis, double maxDistance, double & t ) const; // \ru Вычислить расстояние до оси. \en Calculate the distance to an axis. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта. \en Set properties of the object. + + // \ru Выдать количество точек. \en Get the number of points. + virtual size_t PointsCount() const { return points.size(); } + // \ru Выдать количество нормалей. \en Get the number of normals. + virtual size_t NormalsCount() const { return normals.size(); } + // \ru Выдать количество параметров. \en Get the number of parameters. + virtual size_t ParamsCount() const { return params.size(); } + // \ru Выдать количество значений. \en Get count of values. + virtual size_t EscortsCount() const { return escorts.size(); } + // \ru Выдать количество точек минус 1 (максимальный индекс). \en Get the number of points minus one (maximal index). + virtual ptrdiff_t PointsMaxIndex() const { ptrdiff_t n = points.size(); return ( n - 1 ); } + // \ru Выдать количество нормалей минус 1 (максимальный индекс). \en Get the number of normals minus one (maximal index). + virtual ptrdiff_t NormalsMaxIndex() const { ptrdiff_t n = normals.size(); return ( n - 1 ); } + // \ru Выдать количество параметров минус 1 (максимальный индекс). \en Get the number of parameters minus one (maximal index). + virtual ptrdiff_t ParamsMaxIndex() const { ptrdiff_t n = params.size(); return ( n - 1 ); } + + // \ru Добавить в триангуляцию параметры, точку и нормаль триангулируемой поверхности в точке. \en Add parameters, point and normal of triangulated surface at point to triangulation. + virtual void AddPoint ( const MbCartPoint & p2D, const MbCartPoint3D & p3D, const MbVector3D & n3D ); + // \ru Добавить в триангуляцию параметры и точку. \en Add parameters and a point to triangulation. + virtual void AddPoint ( const MbCartPoint & p2D, const MbCartPoint3D & p3D ); + // \ru Добавить в триангуляцию точку и нормаль в точке. \en Add a point and normal at the point to triangulation. + virtual void AddPoint ( const MbCartPoint3D & p3D, const MbVector3D & n3D ); + // \ru Добавить в триангуляцию точку. \en Add a point to triangulation. + virtual void AddPoint ( const MbCartPoint3D & p3D ); + // \ru Добавить в триангуляцию нормаль. \en Add a normal to triangulation. + virtual void AddNormal( const MbVector3D & n3D ); + // \ru Добавить в триангуляцию параметры триангулируемой поверхности. \en Add parameters of triangulated surface to triangulation. + virtual void AddParam ( const MbCartPoint & p2D ); + + // \ru Добавить в триангуляцию параметры, точку и нормаль триангулируемой поверхности в точке. \en Add parameters, point and normal of triangulated surface at point to triangulation. + virtual void AddPoint ( const MbFloatPoint & p2D, const MbFloatPoint3D & p3D, const MbFloatVector3D & n3D ); + // \ru Добавить в триангуляцию параметры и точку. \en Add parameters and a point to triangulation. + virtual void AddPoint ( const MbFloatPoint & p2D, const MbFloatPoint3D & p3D ); + // \ru Добавить в триангуляцию точку и нормаль в точке. \en Add a point and normal at the point to triangulation. + virtual void AddPoint ( const MbFloatPoint3D & p3D, const MbFloatVector3D & n3D ); + // \ru Добавить в триангуляцию точку. \en Add a point to triangulation. + virtual void AddPoint( const MbFloatPoint3D & p3D ); + // \ru Добавить в триангуляцию нормаль. \en Add a normal to triangulation. + virtual void AddNormal( const MbFloatVector3D & n3D ); + // \ru Добавить в триангуляцию параметры триангулируемой поверхности. \en Add parameters of triangulated surface to triangulation. + virtual void AddParam( const MbFloatPoint & p2D ); + + // \ru Добавить в триангуляцию точки. \en Add points to triangulation. + template + void AddPoints ( const PointsVector & pnts ) { + size_t addCnt = pnts.size(); + points.reserve( points.size() + addCnt ); + for ( size_t k = 0; k < addCnt; k++ ) + points.push_back( pnts[k] ); + } + // \ru Добавить в триангуляцию нормали. \en Add normals to triangulation. + template + void AddNormals( const NormalsVector & nrms ) { + size_t addCnt = nrms.size(); + normals.reserve( normals.size() + addCnt ); + for ( size_t k = 0; k < addCnt; k++ ) + normals.push_back( nrms[k] ); + } + // \ru Добавить в триангуляцию параметры триангулируемой поверхности. \en Add parameters of triangulated surface to triangulation. + template + void AddParams( const ParamsVector & prms ) { + size_t addCnt = prms.size(); + params.reserve( params.size() + addCnt ); + for ( size_t k = 0; k < addCnt; k++ ) + params.push_back( prms[k] ); + } + + // \ru Добавить в коллекцию данных. \en Add scores to collection. + virtual void AddEscorts( const std::vector & scores ) { escorts.insert(escorts.end(), scores.begin(), scores.end()); } + // \ru Добавить в коллекцию данных цвет точки (0 <= r,g,b,a <= 1). \en Add color to collection (0 <= r,g,b,a <= 1). + virtual void AddColor( float r, float g, float b, float a ); + + // \ru Выдать точку по её номеру. \en Get point by its index. + virtual void GetPoint ( size_t i, MbCartPoint3D & p ) const; + // \ru Выдать нормаль по её номеру. \en Get normal by its index. + virtual void GetNormal( size_t i, MbVector3D & n ) const; + // \ru Выдать параметр по его номеру. \en Get parameter by its index. + virtual void GetParam ( size_t i, MbCartPoint & p ) const; + // \ru Выдать дополнительную информацию по её номеру. \en Get additional information by its index. + virtual const uint32 & GetEscort( size_t i ) const; + + /// \ru Выдать точку по её номеру. \en Get point by its index. + virtual void GetPoint ( size_t i, MbFloatPoint3D & p ) const; + /// \ru Выдать нормаль по её номеру. \en Get normal by its index. + virtual void GetNormal( size_t i, MbFloatVector3D & n ) const; + /// \ru Выдать параметр по его номеру. \en Get parameter by its index. + virtual void GetParam ( size_t i, MbFloatPoint & p ) const; + + // \ru Выдать точку с заданным номером. \en Get point by the given index. + const MbFloatPoint3D & GetPoint ( size_t i ) const; + // \ru Выдать нормаль с заданным номером. \en Get normal by the given index. + const MbFloatVector3D & GetNormal( size_t i ) const; + // \ru Выдать параметр с заданным номером. \en Get parameter by the given index. + const MbFloatPoint & GetParam ( size_t i ) const; + + // \ru Установить точку с заданным номером. \en Set point by the given index. + virtual void SetPoint ( size_t i, const MbCartPoint3D & p ); + // \ru Установить нормаль с заданным номером. \en Set normal by the given index. + virtual void SetNormal( size_t i, const MbVector3D & n ); + // \ru Установить параметр с заданным номером. \en Set parameter by the given index. + virtual void SetParam ( size_t i, const MbCartPoint & p ); + // \ru Установить дополнительную информацию по её номеру. \en Set additional information by its index. + virtual void SetEscort( size_t i, const uint32 & e ); + + // \ru Удалить точку с заданным номером. \en Delete point by the given index. + virtual void PointRemove ( size_t i ); + // \ru Удалить нормаль с заданным номером. \en Delete normal by the given index. + virtual void NormalRemove( size_t i ); + // \ru Удалить параметры поверхности с заданным номером. \en Delete parameters of surface by the given index. + virtual void ParamRemove ( size_t i ); + + // \ru Удалить точки. \en Delete points. + virtual void PointsDelete(); + // \ru Удалить нормали. \en Delete normal. + virtual void NormalsDelete(); + // \ru Удалить параметры. \en Delete papams. + virtual void PapamsDelete(); + // \ru Удалить дополнительную информацию. \en Delete additional information. + virtual void EscortsDelete(); + + /// \ru Инвертировать нормали. \en Invert normals. + virtual void NormalsInvert(); + + // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) параметры поверхности. \en Get parameters of surface for i-th triangle in general numbering (with strips). + virtual bool GetTriangleParams ( size_t i, MbCartPoint & r0, MbCartPoint & r1, MbCartPoint & r2 ) const; + // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th triangle in general numbering (with strips). + virtual bool GetTrianglePoints ( size_t i, MbCartPoint3D & p0, MbCartPoint3D & p1, MbCartPoint3D & p2 ) const; + // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th triangle in general numbering (with strips). + virtual bool GetTrianglePoints ( size_t i, MbFloatPoint3D & p0, MbFloatPoint3D & p1, MbFloatPoint3D & p2 ) const; + // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th triangle in general numbering (with strips). + virtual bool GetTriangleNormals ( size_t i, MbVector3D & n0, MbVector3D & n1, MbVector3D & n2 ) const; + // \ru Выдать для треугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th triangle in general numbering (with strips). + virtual bool GetTriangleNormals ( size_t i, MbFloatVector3D & n0, MbFloatVector3D & n1, MbFloatVector3D & n2 ) const; + + // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) параметры поверхности. \en Get parameters of surface for i-th quadrangle in general numbering (with strips). + virtual bool GetQuadrangleParams ( size_t i, MbCartPoint & r0, MbCartPoint & r1, MbCartPoint & r2, MbCartPoint & r3 ) const; + // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th quadrangle in general numbering (with strips). + virtual bool GetQuadranglePoints ( size_t i, MbCartPoint3D & p0, MbCartPoint3D & p1, MbCartPoint3D & p2, MbCartPoint3D & p3 ) const; + // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) точки вершин. \en Get points of vertices for i-th quadrangle in general numbering (with strips). + virtual bool GetQuadranglePoints ( size_t i, MbFloatPoint3D & p0, MbFloatPoint3D & p1, MbFloatPoint3D & p2, MbFloatPoint3D & n3 ) const; + // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th quadrangle in general numbering (with strips). + virtual bool GetQuadrangleNormals( size_t i, MbVector3D & n0, MbVector3D & n1, MbVector3D &n2, MbVector3D & n3 ) const; + // \ru Выдать для четырёхугольника с номером i в общей нумерации (с полосами) нормали в вершинах. \en Get normals at vertices for i-th quadrangle in general numbering (with strips). + virtual bool GetQuadrangleNormals( size_t i, MbFloatVector3D & n0, MbFloatVector3D & n1, MbFloatVector3D & n2, MbFloatVector3D & n3 ) const; + + // \ru Выдать первую нормаль для плоской триангуляции, если количество точек больше количества нормалей (только для плоской триангуляции). \en Get first normal for flat triangulation if count of points is greater than count of normals (only for planar triangulation). + virtual bool GetSingleNormal ( MbVector3D & ) const; + // \ru Выдать первую нормаль для плоской триангуляции, если количество точек больше количества нормалей (только для плоской триангуляции). \en Get first normal for flat triangulation if count of points is greater than count of normals (only for planar triangulation). + virtual bool GetSingleNormal ( MbFloatVector3D & ) const; + // \ru Если количество точек больше количества нормалей, то добавить недостающие нормали (только для плоской триангуляции). \en If count of points is greater than count of normals, then add missing normals (only for planar triangulation). + virtual void SynchronizNormals (); + + // \ru Выдать контейнер параметров. \en Get the container of parameters. + template + void GetParams( ParamsVector & paramsVector ) const { + paramsVector.reserve( paramsVector.size() + params.size() ); + for ( size_t i = 0, iCount = params.size(); i < iCount; i++ ) + paramsVector.push_back( params[i] ); + } + // \ru Выдать контейнер точек. \en Get the container of points. + template + void GetPoints( PointsVector & pointsVector ) const { + pointsVector.reserve( pointsVector.size() + points.size() ); + for ( size_t i = 0, iCount = points.size(); i < iCount; i++ ) + pointsVector.push_back( points[i] ); + } + // \ru Выдать контейнер нормалей. \en Get the container of normals. + template + void GetNormals( NormalsVector & normalsVector ) const { + normalsVector.reserve( normalsVector.size() + normals.size() ); + for ( size_t i = 0, iCount = normals.size(); i < iCount; i++ ) + normalsVector.push_back( normals[i] ); + } + + // \ru Расширить присланный габаритный прямоугольник так, чтобы он включал в себя проекцию данного объекта на глобальную плоскость XY. \en Extend given bounding box so that it enclose projection of this object to the global XY-plane. + virtual void AddRect( MbRect & rect ) const; + // \ru Расширить присланный габаритный куб так, чтобы он включал в себя данный объект. \en Extend given bounding box so that it encloses the given object. + virtual void AddCube( MbCube & r ) const; + + // \ru Определить, пересекается ли проекция на глобальную плоскость XY треугольника с заданным номером с присланным прямоугольником. \en Determine whether the projection of triangle with a given index to the global XY-plane intersects the given rectangle. + virtual bool TriangleIntersectRect( size_t i, MbRect & rect ) const { return (i & cutPlaces, + MbFloatPoint3D & crossPnt, + float & tRes ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Найти пересечение прямой линии и триангуляции. + \en Find the intersection of a straight line with the triangulation. \~ + \details \ru Для всех треугольников определяется пересечение с прямой линии и вычисляется минимальное значение + параметра точки пересечения на секущей прямой линии. \n + \en For all the triangles the intersection with the straight line is determined and the minimum value of + the intersection point parameter on the secant straight line is calculated. \n \~ + \param[in] grid - \ru Триангуляция. + \en Triangulation. \~ + \param[in] line - \ru Прямая линия, для которой вычисляется пересечение с триангуляцией. + \en Straight line to calculate the intersection of triangulation with. \~ + \param[out] tRes - \ru Параметр точки пересечения линии. + \en Parameter of the intersection point on the line. \~ + \return \ru Найдено ли пересечение (true - В случае успеха). + \en Whether the intersection is found (true if success). \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC (bool) LineGridIntersect( const MbGrid & grid, + const MbFloatAxis3D & line, + float & tRes ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Вычислить квадрат расстояния от линии до полигона. + \en Calculate squared distance from a line to a polygon. \~ + \details \ru При вычислении квадрата расстояния от линии до полигона проверяется расстояние от каждого + сегмента полигона до первого попадания в окрестность delta. + Возвращается значение параметра ближайшей точки на линии tRes и квадрат расстояния + от этой точки до сегмента полигона. \n + \en During calculation of squared distance from a line to a polygon the distance from each + segment of the polygon is checked until the first getting to 'delta' neighborhood. + Returns the value of the nearest point parameter on tRes line and the squared distance + from this point to a segment of the polygon. \n \~ + \param[in] grid - \ru Триангуляция. + \en Triangulation. \~ + \param[in] edgeInd - \ru Индекс тестируемого полигона. + \en Index of polygon to check. \~ + \param[in] line - \ru Линия, до которой вычисляется расстояние. + \en Line to calculate the distance to. \~ + \param[in] delta - \ru Радиус окрестности вокруг линии. + \en Neighborhood radius around the line. \~ + \param[out] tRes - \ru Значение параметра ближайшей точки линии. + \en The value of parameter of the nearest point on the line. \~ + \return \ru Квадрат расстояния ближайшей точки до линии. + \en Squared distance between the nearest point and the line. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC(float) LineToGridEdgeDistanceSquared( const MbGrid & grid, + size_t edgeInd, + const MbFloatAxis3D & line, + float delta, + float & tRes ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Вычислить квадрат расстояния от линии до полигона. + \en Calculate squared distance from a line to a polygon. \~ + \details \ru При вычислении квадрата расстояния от линии до полигона проверяется расстояние от каждого + сегмента полигона до первого попадания в окрестность delta. + Возвращается значение параметра ближайшей точки на линии tRes, вектор между ближайшими точками и квадрат + расстояния от этой точки до сегмента полигона. \n + \en During calculation of squared distance from a line to a polygon the distance from each + segment of the polygon is checked until the first getting to 'delta' neighborhood. + Returns the value of the nearest point parameter on the line, the vector between the nearest points + and the squared distance from this point to a segment of the polygon. \n \~ + \param[in] grid - \ru Триангуляция. + \en Triangulation. \~ + \param[in] edgeInd - \ru Индекс тестируемого полигона. + \en Index of polygon to check. \~ + \param[in] line - \ru Линия, до которой вычисляется расстояние. + \en Line to calculate the distance to. \~ + \param[in] vDelta - \ru Габарит окрестности вокруг линии. + \en The dimensions of the area around the line. \~ + \param[out] vRes - \ru Вектор от ближайшей точки на линии до ближайшей точки на полигоне. + \en Vector from the nearest point on the line to the nearest point on the polygon. \~ + \param[out] tRes - \ru Значение параметра ближайшей точки линии. + \en The value of parameter of the nearest point on the line. \~ + \return \ru Квадрат расстояния ближайшей точки до линии. + \en Squared distance between the nearest point and the line. \~ + \ingroup Algorithms_3D +*/ +// --- +MATH_FUNC(float) LineToGridEdgeDistanceSquared( const MbGrid & grid, + size_t edgeInd, + const MbFloatAxis3D & line, + const MbFloatVector3D & vDelta, + MbFloatVector3D & vRes, + float & tRes ); + + +#endif // __MESH_GRID_H diff --git a/C3d/Include/mesh_plane_grid.h b/C3d/Include/mesh_plane_grid.h index 9d630cd..10328c4 100644 --- a/C3d/Include/mesh_plane_grid.h +++ b/C3d/Include/mesh_plane_grid.h @@ -1,356 +1,356 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Tриангуляция двумерной области. - \en Triangulation of two-dimensional region. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -// "PGrid.cpp" -// \ru PGrid.h - заголовочный файл триангуляции массива двумерных точек. \en PGrid.h - header file of two-dimensional point array triangulation. -// \ru Содержит следующие разделы: \en Contains the following sections: -// \ru - Триангуляция двумерной области \en - Triangulation of a two-dimensional region -// \ru - Вершина полигона \en - Vertex of a polygon -// \ru - Многоугольник \en - Polygon -// \ru - Аппроксимация плоской области треугольными пластинами \en - Approximation of a planar region by triangular plates -// \ru - Трингуляция двумерного региона \en - Triangulation of a two-dimensional region -// \ru - Выпуклая триангуляция неупорядоченного массива двумерных точек \en - Convex triangulation of unordered array of two-dimensional points -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __MESH_PLANE_GRID_H -#define __MESH_PLANE_GRID_H - -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbRegion; -class MATH_CLASS MbMatrix3D; -class MATH_CLASS ProgressBarWrapper; - - -//------------------------------------------------------------------------------ -/** \brief \ru Tреугольник. - \en Triangle. \~ - \details \ru Tреугольник определен, как тройка точек, заданных индексами - вершин триангуляции MbPlanarGrid. \n - \en Triangle is defined as triple of points defined by indices - of vertices of MbPlanarGrid triangulation. \n \~ - \ingroup Algorithms_2D -*/ -// --- -class MbTri { -protected : - size_t pIndex[3]; // \ru Номера вершин треугольника в массиве точек \en Indices of triangle vertices in array of points - -public : - MbTri() { pIndex[0] = pIndex[1] = pIndex[2] = SYS_MAX_T; } - MbTri( size_t j0, size_t j1, size_t j2, bool orientation ) { Init( j0, j1, j2, orientation ); } - ~MbTri() {} -private: - MbTri( const MbTri & ); // \ru Не реализовано \en Not implemented - void operator = ( const MbTri & ); // \ru Не реализовано \en Not implemented -public : - - void Init( size_t j0, size_t j1, size_t j2, bool orientation ) - { - if ( orientation ) { // \ru Совпадает направление обхода \en Traverse direction coincides - pIndex[0] = j0; - pIndex[1] = j1; - pIndex[2] = j2; - } - else { - pIndex[1] = j1; - pIndex[2] = j0; - pIndex[0] = j2; - } - } - - bool GetTriangle ( size_t & i0, size_t & i1, size_t & i2 ) const - { - i0 = pIndex[0]; - i1 = pIndex[1]; - i2 = pIndex[2]; - return true; - } - - size_t GetIndex( size_t n ) const { return pIndex[n % 3]; } - - bool IsTriangleEdge( size_t k0, size_t k1, size_t & eInd ) const - { - eInd = SYS_MAX_T; - - if ( k0 == pIndex[0] && k1 == pIndex[1] ) - eInd = 0; - else if ( k0 == pIndex[1] && k1 == pIndex[2] ) - eInd = 1; - else if ( k0 == pIndex[2] && k1 == pIndex[0] ) - eInd = 2; - - return (eInd != SYS_MAX_T); - } -}; // MbTri - - -//------------------------------------------------------------------------------ -/** \brief \ru Tриангуляция двумерной области. - \en Triangulation of a two-dimensional region. \~ - \details \ru Tриангуляция двумерной области. \n - \en Triangulation of a two-dimensional region. \n \~ - \ingroup Algorithms_2D -*/ -// --- -class MATH_CLASS MbPlanarGrid { -private: - SArray points; ///< \ru Вершины триангуляции. \en Vertices of triangulation. - SArray triangles; ///< \ru Множество треугольников. \en Array of triangles. - -public: - MbPlanarGrid() : points( 0, 1 ), triangles( 0, 1 ) {} - ~MbPlanarGrid() {}; - -public: - void PointsReserve ( size_t size ) { points.Reserve( size ); } - void TrianglesReserve ( size_t size ) { triangles.Reserve( size ); } - void PointsSetMaxDelta ( uint16 size ) { points.SetMaxDelta( size ); } - void TrianglesSetMaxDelta( uint16 size ) { triangles.SetMaxDelta( size ); } - void PointsAdjust() { points.Adjust(); } - void TrianglesAdjust() { triangles.Adjust(); } - - size_t GetPointsCount () const { return points.Count(); } - size_t GetTrianglesCount() const { return triangles.Count(); } - - void AddTriangle( size_t j0, size_t j1, size_t j2, bool o ) { triangles.Add()->Init( j0, j1, j2,o ); } - void AddPoint ( const MbFloatPoint & p ) { points.Add( p ); } - void AddPoint ( const MbCartPoint & p ) { points.Add( p ); } - /// \ru Выдать декартову точку. \en Get Cartesian point. - bool GetPoint ( size_t k, MbCartPoint & p ) const; - /// \ru Выдать точку. \en Get point. - bool GetPoint ( size_t k, MbFloatPoint & p ) const; - /// \ru Получить индексы точек треугольника. \en Get indices of points of the triangle. - bool GetTriangle( size_t k, size_t & i0, size_t & i1, size_t & i2 ) const; - /// \ru Получить точки треугольника. \en Get points of the triangle. - bool GetTrianglePoints( size_t k, MbCartPoint & p0, MbCartPoint & p1, MbCartPoint & p2 ) const; - -OBVIOUS_PRIVATE_COPY( MbPlanarGrid ) -}; - - -//------------------------------------------------------------------------------ -/// \ru Выдать декартову точку \en Get Cartesian point -// --- -inline bool MbPlanarGrid::GetPoint( size_t k, MbCartPoint & p ) const -{ - if ( k < points.Count() ) { - p = points[k]; - return true; - } - return false; -} - - -//------------------------------------------------------------------------------ -/// \ru Выдать точку \en Get point -// --- -inline bool MbPlanarGrid::GetPoint( size_t k, MbFloatPoint & p ) const -{ - if ( k < points.Count() ) { - p = points[k]; - return true; - } - return false; -} - - -//------------------------------------------------------------------------------ -/// \ru Вершина полигона \en Vertex of a polygon -// --- -class TriVertex { -private: - TriVertex * next; ///< \ru Следующая вершина \en Next vertex - TriVertex * prev; ///< \ru Предыдущая вершина \en Previous vertex - size_t index; ///< \ru Номер точки в массиве точек триангуляции \en Index of point in array of points of triangulation - -public: - TriVertex( size_t i ) : index(i) { next = this; prev = this; } - -public: - ~TriVertex() {} - -public: - /// \ru Смежная вершина (следующая или предыдущая). \en Adjacent vertex (next or previous). - TriVertex * Neighbor ( MbeMoveType ) const; - /// \ru Следующая вершина. \en Next vertex. - TriVertex * Next () const { return next; } - /// \ru Предыдущая вершина. \en Previous vertex. - TriVertex * Prev () const { return prev; } - /// \ru Номер точки текущей вершины. \en Index of point of current vertex. - size_t Index () const { return index; } - /// \ru Вставить вершину после текущей. \en Insert vertex after the current one. - TriVertex * Insert ( TriVertex & ); - /// \ru Исключить текущую вершину (возвращает исключенную, текущей становится предыдущая) \en Exclude the current vertex (returns excluded vertex, previous vertex becomes the current one) - TriVertex * Detach (); - /// \ru Соединить две цепочки вершин. \en Connect two vertex chains. - void Splice ( TriVertex & ); - /// \ru Разделить цепочку вершин вершиной. \en Disconnect vertex chain by a vertex. - TriVertex * Split ( TriVertex & ); - -OBVIOUS_PRIVATE_COPY( TriVertex ) -}; - - -//------------------------------------------------------------------------------ -/// \ru Многоугольник \en Polygon -// --- -class TriPoly { -private: - TriVertex * vertex; ///< \ru Текущая вершина. \en Current vertex. - intptr_t size; ///< \ru Количество вершин. \en Count of vertices. - -public: - TriPoly() : vertex( NULL ), size( 0 ) {} - TriPoly( TriVertex * vert ) : vertex( vert ), size( 0 ) { Resize(); } - -public: - ~TriPoly(); - -public: - intptr_t Size() const { return size; } ///< \ru Размер цепочки вершин \en Size of vertex chain - size_t Index() const { return (vertex != NULL) ? vertex->Index() : SYS_MAX_T; } ///< \ru Индекс вершины \en Index of vertex - - TriVertex * This() const { return vertex; } ///< \ru Текущая вершина \en Current vertex - TriVertex * Next() const; ///< \ru Следующая вершина \en Next vertex - TriVertex * Prev() const; ///< \ru Предыдущая вершина \en Previous vertex - TriVertex * Neighbor( MbeMoveType rotation ) const; ///< \ru Соседняя вершина \en Neighboring vertex - - TriVertex * Advance ( MbeMoveType rotation ); ///< \ru Перемещение указателя текущей вершины на соседа \en Move current vertex pointer to the neighbor - TriVertex * SetVertex( TriVertex * ); ///< \ru Перемещение указателя текущей вершины на указанную \en Move current vertex pointer to the given one - - TriVertex * Insert( TriVertex & ); ///< \ru Вставка новой вершины \en Insert new vertex - void Remove(); ///< \ru Удаление текущей вершины \en Remove the current vertex - - TriPoly * Split( TriVertex &, bool createNew ); ///< \ru Резка многоугольника вдоль хорды \en Cutting of polygon along a chord - bool GetTriangle( size_t & k0, size_t & k1, size_t & k2 ) const; ///< \ru Получить индексы вершин треугольника (когда вершин больше 2) \en Get indices of triangle vertices (when there are more than 2 vertices) - - template - bool CalculateGab( const SArray & pnts, Gab & rect ) const; - -private: - void Resize(); ///< \ru Обновление размера \en Set size to zero - -OBVIOUS_PRIVATE_COPY( TriPoly ) -}; - - -//------------------------------------------------------------------------------ -// \ru Посчитать габарит цепочки вершин \en Calculate bounding box of vertex chain -// --- -template -bool TriPoly::CalculateGab( const SArray & pnts, Gab & rect ) const -{ - bool isDone = false; - - rect.SetEmpty(); - size_t pntsCnt = pnts.Count(); - - if ( Size() > 0 && pntsCnt > 0 ) { - const TriVertex * v0 = This(); - size_t ind0 = v0->Index(); - - if ( ind0 < pntsCnt ) { - isDone = true; - const Point & pnt0 = pnts[ind0]; - rect |= pnt0; - - TriVertex * v = v0->Next(); - while( v0 != v && isDone ) { - isDone = false; - size_t ind = v->Index(); - if ( ind < pntsCnt ) { - const Point & pnt = pnts[ind]; - rect |= pnt; - v = v->Next(); - isDone = true; - } - } - } - } - - C3D_ASSERT( isDone ); - return isDone; -} - - -/* -//------------------------------------------------------------------------------ -// \ru Треугольник со ссылками на смежные(соседние) с ним треугольники \en Triangle with references to triangles adjacent (neighboring) to it -// --- -class MbLinkedTri : public MbTri { -protected: - MbTri * neighbors[3]; // \ru Соседние треугольники \en Neighboring triangles - // \ru Нумерация соседних треугольников: \en Numeration of neighboring triangles: - // \ru 0 - смежный через ребро на вершинах 0,1 \en 0 - adjacent at edge with vertices 0,1 - // \ru 1 - смежный через ребро на вершинах 1,2 \en 1 - adjacent at edge with vertices 1,2 - // \ru 2 - смедный через ребра на вершинах 2,0 \en 2 - adjacent at edge with vertices 2,0 - -public: - MbLinkedTri() : MbTri() { neighbors[0] = neighbors[1] = neighbors[2] = NULL; }; - ~MbLinkedTri() {}; -public: - MbTri * GetNeighbor( size_t n ) const { return neighbors[n % 3]; } - void SetNeighbor( size_t n, MbTri * neighbor ) { neighbors[n % 3] = neighbor; } - bool IsBoundary() const; // \ru Является ли треугольник граничным \en Whether the triangle is boundary - -OBVIOUS_PRIVATE_COPY( MbLinkedTri ) -}; - - -//------------------------------------------------------------------------------ -// \ru Является ли треугольник граничным \en Whether the triangle is boundary -// --- -inline bool MbLinkedTri::IsBoundary() const -{ - bool isBoundary = (neighbors[0] == NULL) || - (neighbors[1] == NULL) || - (neighbors[2] == NULL); - return isBoundary; -} -// */ - - -//------------------------------------------------------------------------------ -// \ru Аппроксимация плоской области треугольными пластинами \en Approximation of planar region by triangular plates -// \ru Функция удаляет полигионы точек из массива \en The function removes polygons of points from the array -// --- -MATH_FUNC (void) CalculatePlanarGrid( PArray< SArray > & poly, MbPlanarGrid & grid ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Трингуляция двумерного региона - \en Triangulation of a two-dimensional region \~ - \details \ru Трингуляция двумерного региона. - Регион region должен быть корректным (на некорректном работает неправильно) - \en Triangulation of a two-dimensional region. - 'region' region has to be correct (improper handling of incorrect ones) \~ - \ingroup Polygonal_Objects -*/ -// --- -MATH_FUNC (void) TriangulateRegion( const MbRegion & region, double sag, MbPlanarGrid & grid ); - - -//------------------------------------------------------------------------------ -/// \ru Выпуклая триангуляция неупорядоченного массива двумерных точек \en Convex triangulation of unordered array of two-dimensional points -// --- -MATH_FUNC (bool) TriangulateConvexCloud( const SArray & uvPnts, double xEps, double yEps, const MbMatrix3D & from, - SArray & triangles, - ProgressBarWrapper * progBar ); - - -#endif // __PGRID_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Tриангуляция двумерной области. + \en Triangulation of two-dimensional region. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// "PGrid.cpp" +// \ru PGrid.h - заголовочный файл триангуляции массива двумерных точек. \en PGrid.h - header file of two-dimensional point array triangulation. +// \ru Содержит следующие разделы: \en Contains the following sections: +// \ru - Триангуляция двумерной области \en - Triangulation of a two-dimensional region +// \ru - Вершина полигона \en - Vertex of a polygon +// \ru - Многоугольник \en - Polygon +// \ru - Аппроксимация плоской области треугольными пластинами \en - Approximation of a planar region by triangular plates +// \ru - Трингуляция двумерного региона \en - Triangulation of a two-dimensional region +// \ru - Выпуклая триангуляция неупорядоченного массива двумерных точек \en - Convex triangulation of unordered array of two-dimensional points +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __MESH_PLANE_GRID_H +#define __MESH_PLANE_GRID_H + +#include +#include +#include +#include +#include +#include + + +class MATH_CLASS MbRegion; +class MATH_CLASS MbMatrix3D; +class MATH_CLASS ProgressBarWrapper; + + +//------------------------------------------------------------------------------ +/** \brief \ru Tреугольник. + \en Triangle. \~ + \details \ru Tреугольник определен, как тройка точек, заданных индексами + вершин триангуляции MbPlanarGrid. \n + \en Triangle is defined as triple of points defined by indices + of vertices of MbPlanarGrid triangulation. \n \~ + \ingroup Algorithms_2D +*/ +// --- +class MbTri { +protected : + size_t pIndex[3]; // \ru Номера вершин треугольника в массиве точек \en Indices of triangle vertices in array of points + +public : + MbTri() { pIndex[0] = pIndex[1] = pIndex[2] = SYS_MAX_T; } + MbTri( size_t j0, size_t j1, size_t j2, bool orientation ) { Init( j0, j1, j2, orientation ); } + ~MbTri() {} +private: + MbTri( const MbTri & ); // \ru Не реализовано \en Not implemented + void operator = ( const MbTri & ); // \ru Не реализовано \en Not implemented +public : + + void Init( size_t j0, size_t j1, size_t j2, bool orientation ) + { + if ( orientation ) { // \ru Совпадает направление обхода \en Traverse direction coincides + pIndex[0] = j0; + pIndex[1] = j1; + pIndex[2] = j2; + } + else { + pIndex[1] = j1; + pIndex[2] = j0; + pIndex[0] = j2; + } + } + + bool GetTriangle ( size_t & i0, size_t & i1, size_t & i2 ) const + { + i0 = pIndex[0]; + i1 = pIndex[1]; + i2 = pIndex[2]; + return true; + } + + size_t GetIndex( size_t n ) const { return pIndex[n % 3]; } + + bool IsTriangleEdge( size_t k0, size_t k1, size_t & eInd ) const + { + eInd = SYS_MAX_T; + + if ( k0 == pIndex[0] && k1 == pIndex[1] ) + eInd = 0; + else if ( k0 == pIndex[1] && k1 == pIndex[2] ) + eInd = 1; + else if ( k0 == pIndex[2] && k1 == pIndex[0] ) + eInd = 2; + + return (eInd != SYS_MAX_T); + } +}; // MbTri + + +//------------------------------------------------------------------------------ +/** \brief \ru Tриангуляция двумерной области. + \en Triangulation of a two-dimensional region. \~ + \details \ru Tриангуляция двумерной области. \n + \en Triangulation of a two-dimensional region. \n \~ + \ingroup Algorithms_2D +*/ +// --- +class MATH_CLASS MbPlanarGrid { +private: + SArray points; ///< \ru Вершины триангуляции. \en Vertices of triangulation. + SArray triangles; ///< \ru Множество треугольников. \en Array of triangles. + +public: + MbPlanarGrid() : points( 0, 1 ), triangles( 0, 1 ) {} + ~MbPlanarGrid() {}; + +public: + void PointsReserve ( size_t size ) { points.Reserve( size ); } + void TrianglesReserve ( size_t size ) { triangles.Reserve( size ); } + void PointsSetMaxDelta ( uint16 size ) { points.SetMaxDelta( size ); } + void TrianglesSetMaxDelta( uint16 size ) { triangles.SetMaxDelta( size ); } + void PointsAdjust() { points.Adjust(); } + void TrianglesAdjust() { triangles.Adjust(); } + + size_t GetPointsCount () const { return points.Count(); } + size_t GetTrianglesCount() const { return triangles.Count(); } + + void AddTriangle( size_t j0, size_t j1, size_t j2, bool o ) { triangles.Add()->Init( j0, j1, j2,o ); } + void AddPoint ( const MbFloatPoint & p ) { points.Add( p ); } + void AddPoint ( const MbCartPoint & p ) { points.Add( p ); } + /// \ru Выдать декартову точку. \en Get Cartesian point. + bool GetPoint ( size_t k, MbCartPoint & p ) const; + /// \ru Выдать точку. \en Get point. + bool GetPoint ( size_t k, MbFloatPoint & p ) const; + /// \ru Получить индексы точек треугольника. \en Get indices of points of the triangle. + bool GetTriangle( size_t k, size_t & i0, size_t & i1, size_t & i2 ) const; + /// \ru Получить точки треугольника. \en Get points of the triangle. + bool GetTrianglePoints( size_t k, MbCartPoint & p0, MbCartPoint & p1, MbCartPoint & p2 ) const; + +OBVIOUS_PRIVATE_COPY( MbPlanarGrid ) +}; + + +//------------------------------------------------------------------------------ +/// \ru Выдать декартову точку \en Get Cartesian point +// --- +inline bool MbPlanarGrid::GetPoint( size_t k, MbCartPoint & p ) const +{ + if ( k < points.Count() ) { + p = points[k]; + return true; + } + return false; +} + + +//------------------------------------------------------------------------------ +/// \ru Выдать точку \en Get point +// --- +inline bool MbPlanarGrid::GetPoint( size_t k, MbFloatPoint & p ) const +{ + if ( k < points.Count() ) { + p = points[k]; + return true; + } + return false; +} + + +//------------------------------------------------------------------------------ +/// \ru Вершина полигона \en Vertex of a polygon +// --- +class TriVertex { +private: + TriVertex * next; ///< \ru Следующая вершина \en Next vertex + TriVertex * prev; ///< \ru Предыдущая вершина \en Previous vertex + size_t index; ///< \ru Номер точки в массиве точек триангуляции \en Index of point in array of points of triangulation + +public: + TriVertex( size_t i ) : index(i) { next = this; prev = this; } + +public: + ~TriVertex() {} + +public: + /// \ru Смежная вершина (следующая или предыдущая). \en Adjacent vertex (next or previous). + TriVertex * Neighbor ( MbeMoveType ) const; + /// \ru Следующая вершина. \en Next vertex. + TriVertex * Next () const { return next; } + /// \ru Предыдущая вершина. \en Previous vertex. + TriVertex * Prev () const { return prev; } + /// \ru Номер точки текущей вершины. \en Index of point of current vertex. + size_t Index () const { return index; } + /// \ru Вставить вершину после текущей. \en Insert vertex after the current one. + TriVertex * Insert ( TriVertex & ); + /// \ru Исключить текущую вершину (возвращает исключенную, текущей становится предыдущая) \en Exclude the current vertex (returns excluded vertex, previous vertex becomes the current one) + TriVertex * Detach (); + /// \ru Соединить две цепочки вершин. \en Connect two vertex chains. + void Splice ( TriVertex & ); + /// \ru Разделить цепочку вершин вершиной. \en Disconnect vertex chain by a vertex. + TriVertex * Split ( TriVertex & ); + +OBVIOUS_PRIVATE_COPY( TriVertex ) +}; + + +//------------------------------------------------------------------------------ +/// \ru Многоугольник \en Polygon +// --- +class TriPoly { +private: + TriVertex * vertex; ///< \ru Текущая вершина. \en Current vertex. + intptr_t size; ///< \ru Количество вершин. \en Count of vertices. + +public: + TriPoly() : vertex( NULL ), size( 0 ) {} + TriPoly( TriVertex * vert ) : vertex( vert ), size( 0 ) { Resize(); } + +public: + ~TriPoly(); + +public: + intptr_t Size() const { return size; } ///< \ru Размер цепочки вершин \en Size of vertex chain + size_t Index() const { return (vertex != NULL) ? vertex->Index() : SYS_MAX_T; } ///< \ru Индекс вершины \en Index of vertex + + TriVertex * This() const { return vertex; } ///< \ru Текущая вершина \en Current vertex + TriVertex * Next() const; ///< \ru Следующая вершина \en Next vertex + TriVertex * Prev() const; ///< \ru Предыдущая вершина \en Previous vertex + TriVertex * Neighbor( MbeMoveType rotation ) const; ///< \ru Соседняя вершина \en Neighboring vertex + + TriVertex * Advance ( MbeMoveType rotation ); ///< \ru Перемещение указателя текущей вершины на соседа \en Move current vertex pointer to the neighbor + TriVertex * SetVertex( TriVertex * ); ///< \ru Перемещение указателя текущей вершины на указанную \en Move current vertex pointer to the given one + + TriVertex * Insert( TriVertex & ); ///< \ru Вставка новой вершины \en Insert new vertex + void Remove(); ///< \ru Удаление текущей вершины \en Remove the current vertex + + TriPoly * Split( TriVertex &, bool createNew ); ///< \ru Резка многоугольника вдоль хорды \en Cutting of polygon along a chord + bool GetTriangle( size_t & k0, size_t & k1, size_t & k2 ) const; ///< \ru Получить индексы вершин треугольника (когда вершин больше 2) \en Get indices of triangle vertices (when there are more than 2 vertices) + + template + bool CalculateGab( const SArray & pnts, Gab & rect ) const; + +private: + void Resize(); ///< \ru Обновление размера \en Set size to zero + +OBVIOUS_PRIVATE_COPY( TriPoly ) +}; + + +//------------------------------------------------------------------------------ +// \ru Посчитать габарит цепочки вершин \en Calculate bounding box of vertex chain +// --- +template +bool TriPoly::CalculateGab( const SArray & pnts, Gab & rect ) const +{ + bool isDone = false; + + rect.SetEmpty(); + size_t pntsCnt = pnts.Count(); + + if ( Size() > 0 && pntsCnt > 0 ) { + const TriVertex * v0 = This(); + size_t ind0 = v0->Index(); + + if ( ind0 < pntsCnt ) { + isDone = true; + const Point & pnt0 = pnts[ind0]; + rect |= pnt0; + + TriVertex * v = v0->Next(); + while( v0 != v && isDone ) { + isDone = false; + size_t ind = v->Index(); + if ( ind < pntsCnt ) { + const Point & pnt = pnts[ind]; + rect |= pnt; + v = v->Next(); + isDone = true; + } + } + } + } + + C3D_ASSERT( isDone ); + return isDone; +} + + +/* +//------------------------------------------------------------------------------ +// \ru Треугольник со ссылками на смежные(соседние) с ним треугольники \en Triangle with references to triangles adjacent (neighboring) to it +// --- +class MbLinkedTri : public MbTri { +protected: + MbTri * neighbors[3]; // \ru Соседние треугольники \en Neighboring triangles + // \ru Нумерация соседних треугольников: \en Numeration of neighboring triangles: + // \ru 0 - смежный через ребро на вершинах 0,1 \en 0 - adjacent at edge with vertices 0,1 + // \ru 1 - смежный через ребро на вершинах 1,2 \en 1 - adjacent at edge with vertices 1,2 + // \ru 2 - смедный через ребра на вершинах 2,0 \en 2 - adjacent at edge with vertices 2,0 + +public: + MbLinkedTri() : MbTri() { neighbors[0] = neighbors[1] = neighbors[2] = NULL; }; + ~MbLinkedTri() {}; +public: + MbTri * GetNeighbor( size_t n ) const { return neighbors[n % 3]; } + void SetNeighbor( size_t n, MbTri * neighbor ) { neighbors[n % 3] = neighbor; } + bool IsBoundary() const; // \ru Является ли треугольник граничным \en Whether the triangle is boundary + +OBVIOUS_PRIVATE_COPY( MbLinkedTri ) +}; + + +//------------------------------------------------------------------------------ +// \ru Является ли треугольник граничным \en Whether the triangle is boundary +// --- +inline bool MbLinkedTri::IsBoundary() const +{ + bool isBoundary = (neighbors[0] == NULL) || + (neighbors[1] == NULL) || + (neighbors[2] == NULL); + return isBoundary; +} +// */ + + +//------------------------------------------------------------------------------ +// \ru Аппроксимация плоской области треугольными пластинами \en Approximation of planar region by triangular plates +// \ru Функция удаляет полигионы точек из массива \en The function removes polygons of points from the array +// --- +MATH_FUNC (void) CalculatePlanarGrid( PArray< SArray > & poly, MbPlanarGrid & grid ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Трингуляция двумерного региона + \en Triangulation of a two-dimensional region \~ + \details \ru Трингуляция двумерного региона. + Регион region должен быть корректным (на некорректном работает неправильно) + \en Triangulation of a two-dimensional region. + 'region' region has to be correct (improper handling of incorrect ones) \~ + \ingroup Polygonal_Objects +*/ +// --- +MATH_FUNC (void) TriangulateRegion( const MbRegion & region, double sag, MbPlanarGrid & grid ); + + +//------------------------------------------------------------------------------ +/// \ru Выпуклая триангуляция неупорядоченного массива двумерных точек \en Convex triangulation of unordered array of two-dimensional points +// --- +MATH_FUNC (bool) TriangulateConvexCloud( const SArray & uvPnts, double xEps, double yEps, const MbMatrix3D & from, + SArray & triangles, + ProgressBarWrapper * progBar ); + + +#endif // __PGRID_H diff --git a/C3d/Include/mesh_polygon.h b/C3d/Include/mesh_polygon.h index 0711805..915bfcc 100644 --- a/C3d/Include/mesh_polygon.h +++ b/C3d/Include/mesh_polygon.h @@ -69,7 +69,7 @@ public: virtual void Reserve( size_t cnt ) { points.reserve( points.size() + cnt ); } // \ru Удалить лишнюю память. \en Free the unnecessary memory. virtual void Adjust() { - #ifdef STANDARD_C11 + #ifdef C3D_STANDARD_CXX_11_PARTIAL points.shrink_to_fit(); #endif } @@ -195,7 +195,7 @@ public: virtual void Reserve( size_t cnt ) { points.reserve( points.size() + cnt ); } // \ru Удалить лишнюю память. \en Free the unnecessary memory. virtual void Adjust() { - #ifdef STANDARD_C11 + #ifdef C3D_STANDARD_CXX_11_PARTIAL points.shrink_to_fit(); #endif } diff --git a/C3d/Include/mesh_primitive.h b/C3d/Include/mesh_primitive.h index 4180359..2dfc6f2 100644 --- a/C3d/Include/mesh_primitive.h +++ b/C3d/Include/mesh_primitive.h @@ -88,7 +88,7 @@ enum MbePrimitiveType { \ingroup Polygonal_Objects */ //////////////////////////////////////////////////////////////////////////////// -class MATH_CLASS MbPrimitive : public MbAttributeContainer, public MbRefItem { +class MATH_CLASS MbPrimitive : public MbAttributeContainer, public MbRefItem, public MbNestSyncItem { protected: SimpleName name; ///< \ru Имя примитива. \en Name of primitive. const MbRefItem * parentItem; ///< \ru Породивший объект (не владеем). \en Begetter object (don't own). @@ -743,6 +743,14 @@ public: /// \ru Добавить в коллекцию данных. \en Add scores to collection. virtual void AddEscorts( const std::vector & scores ) = 0; + /** \brief \ru Добавить цвет как дополнительную информацию в точке (0 <= r,g,b,a <= 1). + \en Add color as an additional information of point (0 <= r,g,b,a <= 1). \~ + \details + \warning \ru Значения компонент цвета должны лежать в диапазоне [ 0; 1 ]. + \en Values of color components should belong to the range [ 0; 1 ]. \~ + \ingroup Polygonal_Objects +*/ + virtual void AddColor( float r, float g, float b, float a ) = 0; /// \ru Выдать точку по её номеру. \en Get point by its index. virtual void GetPoint ( size_t i, MbCartPoint3D & p ) const = 0; diff --git a/C3d/Include/mesh_triangle.h b/C3d/Include/mesh_triangle.h index f3fccde..9dbcaab 100644 --- a/C3d/Include/mesh_triangle.h +++ b/C3d/Include/mesh_triangle.h @@ -1,483 +1,501 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Структуры данных триангуляции. - \en Triangulation data structures. \~ -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __MESH_TRIANGLE_H -#define __MESH_TRIANGLE_H - - -#include -#include -#include -#include - - -//------------------------------------------------------------------------------ -/// \ru Направление движения \en Motion direction -/** - \ingroup Polygonal_Objects -*/ -// --- -enum MbeMoveType { - mt_Forward = 0, ///< \ru Вперед. \en Forward. - mt_Backward = 1, ///< \ru Назад. \en Backward. -}; - - -//////////////////////////////////////////////////////////////////////////////// -/** \brief \ru Tреугольник. - \en Triangle. \~ - \details \ru Tреугольник определен, как тройка точек, заданных индексами - вершин триангуляции MbGrid. \n - \en Triangle is defined as a triple of points defined by indices - of vertices of MbGrid triangulation. \n \~ - \ingroup Polygonal_Objects -*/ -// --- -class MATH_CLASS MbTriangle { -protected : - uint pIndex[3]; ///< \ru Номера вершин треугольника в массиве точек. \en Indices of triangle vertices in the array of points. - -public : - /// \ru Конструктор. \en Constructor. - MbTriangle() { pIndex[0] = pIndex[1] = pIndex[2] = SYS_MAX_UINT; } - /// \ru Конструктор. \en Constructor. - MbTriangle( uint j0, uint j1, uint j2, bool orientation ); - /// \ru Конструктор копирования. \en The copy-constructor. - MbTriangle( const MbTriangle & init ) { - pIndex[0] = init.pIndex[0]; - pIndex[1] = init.pIndex[1]; - pIndex[2] = init.pIndex[2]; - } - /// \ru Деструктор. \en Destructor. - ~MbTriangle(); - /// \ru Оператора присваивания. \en The assignment operator. - MbTriangle & operator = ( const MbTriangle & init ) { - pIndex[0] = init.pIndex[0]; - pIndex[1] = init.pIndex[1]; - pIndex[2] = init.pIndex[2]; - return *this; - } -public : - - /// \ru Инициализация. \en Initialization. - void Init( uint j0, uint j1, uint j2, bool orientation ); - /// \ru Выдать номера вершин треугольника в массиве точек. \en Get indices of triangle vertices in the array of points. - bool GetTriangle ( uint & i0, uint & i1, uint & i2 ) const; - /// \ru Выдать номер вершины n треугольника в массиве точек. \en Get index of n-th triangle vertex in the array of points. - uint GetIndex( size_t n ) const { return pIndex[n % 3]; } - /// \ru Инвертировать последовательность вершин. \en Reverse the sequence of vertices. - void Reverse(); - - /// \ru Определить, пересекается ли проекция на глобальную плоскость XY треугольника с присланным прямоугольником. \en Determine whether the projection of the triangle to the global XY-plane intersects the given rectangle. - template - bool IntersectRect( const MbRect & rect, const ParamPoints & points ) const - { - if ( points.size() < 3 ) - return false; - - double x1 = std_min( points[pIndex[0]].x, std_min( points[pIndex[1]].x, points[pIndex[2]].x ) ); - double x2 = std_max( points[pIndex[0]].x, std_max( points[pIndex[1]].x, points[pIndex[2]].x ) ); - - double y1 = std_min( points[pIndex[0]].y, std_min( points[pIndex[1]].y, points[pIndex[2]].y ) ); - double y2 = std_max( points[pIndex[0]].y, std_max( points[pIndex[1]].y, points[pIndex[2]].y ) ); - - return std_max( x1, rect.left ) <= std_min( x2, rect.right ) && - std_max( y1, rect.bottom ) <= std_min( y2, rect.top ); - } - - /// \ru Рассчитать габаритный прямоугольник проекции на глобальную плоскость XY треугольника. \en Calculate bounding rectangle of the projection of the triangle to the global XY-plane. - template - void GetGabRect ( MbRect & rect, const ParamPoints & points ) const - { - if ( points.size() < 3 ) - return; - - rect.left = std_min( points[pIndex[0]].x, std_min( points[pIndex[1]].x, points[pIndex[2]].x ) ); - rect.right = std_max( points[pIndex[0]].x, std_max( points[pIndex[1]].x, points[pIndex[2]].x ) ); - - rect.bottom = std_min( points[pIndex[0]].y, std_min( points[pIndex[1]].y, points[pIndex[2]].y ) ); - rect.top = std_max( points[pIndex[0]].y, std_max( points[pIndex[1]].y, points[pIndex[2]].y ) ); - } - - /// \ru Принадлежит ли ребро треугольнику. \en Is triangle's edge? - bool IsTriangleEdge( uint k0, uint k1, size_t & eInd ) const - { - eInd = SYS_MAX_T; - - if ( k0 == pIndex[0] && k1 == pIndex[1] ) - eInd = 0; - else if ( k0 == pIndex[1] && k1 == pIndex[2] ) - eInd = 1; - else if ( k0 == pIndex[2] && k1 == pIndex[0] ) - eInd = 2; - - return (eInd != SYS_MAX_T); - } - - /// \ru Выдать свойства объекта. \en Get properties of the object. - void GetProperties( MbProperties & properties ); - /// \ru Записать свойства объекта. \en Set properties of the object. - void SetProperties( const MbProperties & properties ); - - KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbTriangle, MATH_FUNC_EX ); - KNOWN_OBJECTS_RW_PTR_OPERATORS_EX( MbTriangle, MATH_FUNC_EX ); -}; // MbTriangle - - -//------------------------------------------------------------------------------ -// \ru Инициализация \en Initialization -// --- -inline void MbTriangle::Init( uint j0, uint j1, uint j2, bool orientation ) -{ - if ( orientation ) { // \ru Совпадает направление обхода \en Traverse direction coincides - pIndex[0] = j0; - pIndex[1] = j1; - pIndex[2] = j2; - } - else { - pIndex[1] = j1; - pIndex[2] = j0; - pIndex[0] = j2; - } -} - - -//------------------------------------------------------------------------------ -// \ru Получить индексы треугольной пластины \en Get indices of triangle plate -// --- -inline bool MbTriangle::GetTriangle ( uint & i0, uint & i1, uint & i2 ) const -{ - i0 = pIndex[0]; - i1 = pIndex[1]; - i2 = pIndex[2]; - return true; -} - - -//------------------------------------------------------------------------------ -// \ru Инвертировать последовательность вершин. \en Reverse the sequence of vertices. -// --- -inline void MbTriangle::Reverse() -{ - uint ind = pIndex[1]; - pIndex[1] = pIndex[2]; - pIndex[2] = ind; -} - - -//////////////////////////////////////////////////////////////////////////////// -/** \brief \ru Четырёхугольник. - \en Quadrangle. \~ - \details \ru Четырёхугольник задан, как четверка индексов элементов из массива - вершин триангуляции MbGrid. \n - \en Quadrangle defined as a quadruple of elements' indices form the array - of vertices of MbGrid triangulation. \n \~ - \ingroup Polygonal_Objects -*/ -// --- -class MATH_CLASS MbQuadrangle { -protected : - uint pIndex[4]; ///< \ru Номера вершин четырёхугольника в массиве точек. //-V112 \en Indices of quadrangle vertices in the array of points. //-V112 - -public : - /// \ru Конструктор. \en Constructor. - MbQuadrangle(); - /// \ru Конструктор. \en Constructor. - MbQuadrangle( uint j0, uint j1, uint j2, uint j3, bool orientation ); - /// \ru Конструктор копирования. \en The copy-constructor. - MbQuadrangle( const MbQuadrangle & init ) { - pIndex[0] = init.pIndex[0]; - pIndex[1] = init.pIndex[1]; - pIndex[2] = init.pIndex[2]; - pIndex[3] = init.pIndex[3]; - } - /// \ru Деструктор. \en Destructor. - ~MbQuadrangle(); - /// \ru Оператора присваивания. \en The assignment operator. - MbQuadrangle & operator = ( const MbQuadrangle & init ) { - pIndex[0] = init.pIndex[0]; - pIndex[1] = init.pIndex[1]; - pIndex[2] = init.pIndex[2]; - pIndex[3] = init.pIndex[3]; - return *this; - } -public : - - /// \ru Инициализация. \en Initialization. - void Init( uint j0, uint j1, uint j2, uint j3, bool orientation ); - /// \ru Выдать номера вершин четырёхугольника в массиве точек. \en Get indices of quadrangle vertices in the array of points. - bool GetQuadrangle ( uint & i0, uint & i1, uint & i2, uint & i3 ) const; - /// \ru Выдать номер вершины n четырёхугольника в массиве точек. \en Get index of n-th quadrangle vertex in the array of points. - uint GetIndex( size_t n ) const { return pIndex[n % 4]; } //-V112 - /// \ru Инвертировать последовательность вершин. \en Reverse the sequence of vertices. - void Reverse(); - - /// \ru Определить, пересекается ли проекция на глобальную плоскость XY четырёхугольника с присланным прямоугольником. \en Determine whether the projection of the quadrangle to the global XY-plane intersects the given rectangle. - template - bool IntersectRect( const MbRect & rect, const ParamPoints & points ) const - { - if ( points.size() < 4 ) //-V112 - return false; - - double x1 = std_min( points[pIndex[0]].x, std_min( points[pIndex[1]].x, std_min( points[pIndex[2]].x, points[pIndex[3]].x ) ) ); - double x2 = std_max( points[pIndex[0]].x, std_max( points[pIndex[1]].x, std_max( points[pIndex[2]].x, points[pIndex[3]].x ) ) ); - - double y1 = std_min( points[pIndex[0]].y, std_min( points[pIndex[1]].y, std_min( points[pIndex[2]].y, points[pIndex[3]].y ) ) ); - double y2 = std_max( points[pIndex[0]].y, std_max( points[pIndex[1]].y, std_max( points[pIndex[2]].y, points[pIndex[3]].y ) ) ); - - return std_max( x1, rect.left ) <= std_min( x2, rect.right ) && - std_max( y1, rect.bottom ) <= std_min( y2, rect.top ); - } - - /// \ru Рассчитать габаритный прямоугольник проекции на глобальную плоскость XY четырёхугольника. \en Calculate bounding rectangle of the projection of quadrangle to the global XY-plane. - template - void GetGabRect ( MbRect & rect, const ParamPoints & points ) const - { - if ( points.size() < 4 ) //-V112 - return; - - rect.left = std_min( points[pIndex[0]].x, std_min( points[pIndex[1]].x, std_min( points[pIndex[2]].x, points[pIndex[3]].x ) ) ); - rect.right = std_max( points[pIndex[0]].x, std_max( points[pIndex[1]].x, std_max( points[pIndex[2]].x, points[pIndex[3]].x ) ) ); - - rect.bottom = std_min( points[pIndex[0]].y, std_min( points[pIndex[1]].y, std_min( points[pIndex[2]].y, points[pIndex[3]].y ) ) ); - rect.top = std_max( points[pIndex[0]].y, std_max( points[pIndex[1]].y, std_max( points[pIndex[2]].y, points[pIndex[3]].y ) ) ); - } - /// \ru Выдать свойства объекта. \en Get properties of the object. - void GetProperties( MbProperties &properties ); - /// \ru Записать свойства объекта. \en Set properties of the object. - void SetProperties( const MbProperties &properties ); - - KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbQuadrangle, MATH_FUNC_EX ); - KNOWN_OBJECTS_RW_PTR_OPERATORS_EX( MbQuadrangle, MATH_FUNC_EX ); -}; // MbQuadrangle - - -//------------------------------------------------------------------------------ -// \ru Инициализация \en Initialization -// --- -inline void MbQuadrangle::Init( uint j0, uint j1, uint j2, uint j3, bool orientation ) -{ - if ( orientation ) { // \ru Совпадает направление обхода \en Traverse direction coincides - pIndex[0] = j0; - pIndex[1] = j1; - pIndex[2] = j2; - pIndex[3] = j3; - } - else { - pIndex[2] = j1; - pIndex[3] = j0; - pIndex[0] = j3; - pIndex[1] = j2; - } -} - - -//------------------------------------------------------------------------------ -/// \ru Получить индексы четырехугольной пластины \en Get indices of quadrangle plate -// --- -inline bool MbQuadrangle::GetQuadrangle ( uint & i0, uint & i1, uint & i2, uint & i3 ) const -{ - i0 = pIndex[0]; - i1 = pIndex[1]; - i2 = pIndex[2]; - i3 = pIndex[3]; - return true; -} - - -//------------------------------------------------------------------------------ -// \ru Инвертировать последовательность вершин. \en Reverse the sequence of vertices. -// --- -inline void MbQuadrangle::Reverse() -{ - uint ind = pIndex[1]; - pIndex[1] = pIndex[3]; - pIndex[3] = ind; -} - - -//////////////////////////////////////////////////////////////////////////////// -/** \brief \ru Объемный элемент. - \en Element of tesselation of solid volume. \~ - \details \ru Элемент задан, как восемь индексов точек из массива вершин объекта MbGrid. \n - \en Element defined as an elements' indices form the array of vertices of MbGrid. \n \~ - \ingroup Polygonal_Objects -*/ -// --- -class MATH_CLASS MbElement { -protected : - uint pIndex[8]; ///< \ru Номера вершин елемента в массиве точек. \en Indices of element vertices in the array of points. - size_t estate; ///< \ru Свойство элемента. \en Estate of element. - double props; ///< \ru Характеристика элемента. \en Property of element. - -public : - /// \ru Конструктор. \en Constructor. - MbElement(); - /// \ru Конструктор. \en Constructor. - MbElement( uint j0, uint j1, uint j2, uint j3, uint j4, uint j5, uint j6, uint j7 ); - /// \ru Конструктор копирования. \en Copy-constructor. - MbElement( const MbElement & ); - /// \ru Деструктор. \en Destructor. - ~MbElement(); - // \ru Оператор присваивания. \en Assignment operator. - MbElement & operator = ( const MbElement & ); - -public : - - /// \ru Инициализация. \en Initialization. - void Init( uint j0, uint j1, uint j2, uint j3, uint j4, uint j5, uint j6, uint j7 ); - /// \ru Выдать номера вершин четырёхугольника в массиве точек. \en Get indices of quadrangle vertices in the array of points. - bool GetElement ( uint & i0, uint & i1, uint & i2, uint & i3, uint & i4, uint & i5, uint & i6, uint & i7 ) const; - /// \ru Выдать номер вершины n четырёхугольника в массиве точек. \en Get index of n-th quadrangle vertex in the array of points. - uint GetIndex( size_t n ) const { return pIndex[n % 8]; } - /// \ru Дать свойство элемента. \en Get estate of element. - size_t GetEstate() const { return estate; } - /// \ru Изменить свойство элемента. \en Set estate of element. - void SetEstate( uint32 e ) { estate = e; } - /// \ru Дать характеристику элемента. \en Get property of element. - double GetProps() const { return props; } - /// \ru Изменить характеристику элемента. \en Get property of element. - void SetProps( double p ) { props = p; } - - /// \ru Выдать свойства объекта. \en Get properties of the object. - void GetProperties( MbProperties &properties ); - /// \ru Записать свойства объекта. \en Set properties of the object. - void SetProperties( const MbProperties &properties ); - - KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbElement, MATH_FUNC_EX ); - KNOWN_OBJECTS_RW_PTR_OPERATORS_EX( MbElement, MATH_FUNC_EX ); -}; // MbElement - - -//------------------------------------------------------------------------------ -// \ru Инициализация \en Initialization -// --- -inline void MbElement::Init( uint j0, uint j1, uint j2, uint j3, uint j4, uint j5, uint j6, uint j7 ) -{ - pIndex[0] = j0; - pIndex[1] = j1; - pIndex[2] = j2; - pIndex[3] = j3; - pIndex[4] = j4; - pIndex[5] = j5; - pIndex[6] = j6; - pIndex[7] = j7; -} - - -//------------------------------------------------------------------------------ -/// \ru Получить индексы четырехугольной пластины \en Get indices of quadrangle plate -// --- -inline bool MbElement::GetElement ( uint & i0, uint & i1, uint & i2, uint & i3, uint & i4, uint & i5, uint & i6, uint & i7 ) const -{ - i0 = pIndex[0]; - i1 = pIndex[1]; - i2 = pIndex[2]; - i3 = pIndex[3]; - i4 = pIndex[4]; - i5 = pIndex[5]; - i6 = pIndex[6]; - i7 = pIndex[7]; - return true; -} - - -//////////////////////////////////////////////////////////////////////////////// -/** \brief \ru Граница триангуляции. - \en Border of triangulation. \~ - \details \ru Граница триангуляции используется для описания набора ребер грани оболочки. \n - Граница триангуляции содержит номера последовательности вершины. - \en Border of triangulation is used to describe edge sequence of shell's face. \n - Border of triangulation contains indices of vertex sequence. \~ - \ingroup Polygonal_Objects -*/ -// --- -class MATH_CLASS MbGridLoop { -private: - std::vector pIndices; ///< \ru Номера вершин в массиве точек. \en Indices of vertices in the array of points. - -public: - /// \ru Конструктор. \en Constructor. - MbGridLoop( size_t n = 0 ) : pIndices() { if ( n > 0 ) pIndices.reserve( n ); } - /// \ru Конструктор. \en Constructor. - template - explicit MbGridLoop( const UintVector & init ) : pIndices() { - pIndices.reserve( init.size() ); - for ( size_t i = 0, iCount = init.size(); i < iCount; i++ ) - pIndices.push_back( init[i] ); - } - /// \ru Деструктор. \en Destructor. - ~MbGridLoop() {} - -public: - /// \ru Инициализация. \en Initialization. - template - void Init( const UintVector & init ) { - pIndices.clear(); pIndices.reserve( init.size() ); - for ( size_t i = 0, iCount = init.size(); i < iCount; i++ ) - pIndices.push_back( init[i] ); - } - /// \ru Выдать количество вершин полосы. \en Get the count of strip vertices. - size_t Count() const { return pIndices.size(); } - /// \ru Добавить номер вершины. \en Add vertex number. - void Add( uint n ) { pIndices.push_back(n); } - /// \ru Выдать количество вершин полосы. \en Get the count of strip vertices. - uint GetIndex( size_t i ) const { return pIndices[i]; } - /// \ru Выдать количество вершин полосы. \en Get the count of strip vertices. - uint & SetIndex( size_t i ) { return pIndices[i]; } - /// \ru Очистить полосу. \en Clear the strip. - void Flush() { pIndices.clear(); } - /// \ru Выдать адрес начала массива. \en Get the address of the beginning of the array. - const uint * GetIndicesAddr() const { return &(pIndices[0]); } - /// \ru Выдать адрес начала массива. \en Get the address of the beginning of the array. - const std::vector & GetIndices() const { return pIndices; } - /// \ru Выдать контейнер номеров вершин. \en Get the container of vertex numbers. - template - void GetIndices( IndicesVector & iVector ) const { - iVector.reserve( iVector.size() + pIndices.size() ); - for ( size_t i = 0, iCount = pIndices.size(); i < iCount; i++ ) - iVector.push_back( pIndices[i] ); - } - /// \ru Есть ли такой индекс в цикле? \en Is exist index n in the loop? - bool IsExist( uint n ) const { return ( std::find(pIndices.begin(), pIndices.end(), n) != pIndices.end() ); } - - KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbGridLoop, MATH_FUNC_EX ); - KNOWN_OBJECTS_RW_PTR_OPERATORS_EX( MbGridLoop, MATH_FUNC_EX ); - OBVIOUS_PRIVATE_COPY( MbGridLoop ) -}; - - -//////////////////////////////////////////////////////////////////////////////// -/** \brief \ru Сегмент(результат сегментации) полигональной сетки. - \en A polygonal mesh segment (segmentation result). \~ - \details \ru Сегмент определен множеством треугольников полигональной сетки. \n - \en Segment is defined as a set of triangles of polygonal mesh. \n \~ - \ingroup Polygonal_Objects -*/ -// --- -class MATH_CLASS MbGridSegment { -private: - std::vector faces; ///< \ru Вектор индексов треугольников сегмента. \en A vector of segment triangles indicies. - -public: - /// \ru Конструктор. \en Constructor. - MbGridSegment() : faces() {} - /// \ru Конструктор. \en Constructor. - MbGridSegment( const std::vector & initFaces ) : faces( initFaces ) {} - /// \ru Выдать вектор индексов треугольников сегмента. \en Get the vector of segment triangles indicies. - const std::vector & GetFaces() const { return faces; } - /// \ru Выдать количество треугольников сегмента. \en Get the count of of segment triangles. - size_t GetFaceCount() const { return faces.size(); } - /// \ru Выдать индекс треугольника сегмента. \en Get the index of segment triangle. - size_t GetFace( size_t idx ) const { return faces[idx]; } - - KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbGridSegment, MATH_FUNC_EX ); - KNOWN_OBJECTS_RW_PTR_OPERATORS_EX( MbGridSegment, MATH_FUNC_EX ); -}; - -#endif // __MESH_TRIANGLE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Структуры данных триангуляции. + \en Triangulation data structures. \~ +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __MESH_TRIANGLE_H +#define __MESH_TRIANGLE_H + + +#include +#include +#include +#include + + +class MATH_CLASS MbTriangle; + +namespace c3d // namespace C3D +{ + typedef MbTriangle MeshTriangle; ///< \ru Треугольник. \en Triangles vector. + typedef std::vector MeshTrianglesVector; ///< \ru Вектор треугольников. \en Mesh triangles vector. +} + + +//------------------------------------------------------------------------------ +/// \ru Направление движения. \en Motion direction. +/** + \ingroup Polygonal_Objects +*/ +// --- +enum MbeMoveType { + mt_Forward = 0, ///< \ru Вперед. \en Forward. + mt_Backward = 1, ///< \ru Назад. \en Backward. +}; + + +//////////////////////////////////////////////////////////////////////////////// +/** \brief \ru Tреугольник. + \en Triangle. \~ + \details \ru Tреугольник определен, как тройка точек, заданных индексами + вершин триангуляции MbGrid. \n + \en Triangle is defined as a triple of points defined by indices + of vertices of MbGrid triangulation. \n \~ + \ingroup Polygonal_Objects +*/ +// --- +class MATH_CLASS MbTriangle { +protected : + uint pIndex[3]; ///< \ru Номера вершин треугольника в массиве точек. \en Indices of triangle vertices in the array of points. + +public : + /// \ru Конструктор. \en Constructor. + MbTriangle() { pIndex[0] = pIndex[1] = pIndex[2] = SYS_MAX_UINT; } + /// \ru Конструктор. \en Constructor. + MbTriangle( uint j0, uint j1, uint j2, bool orientation ); + /// \ru Конструктор копирования. \en The copy-constructor. + MbTriangle( const MbTriangle & init ) { + pIndex[0] = init.pIndex[0]; + pIndex[1] = init.pIndex[1]; + pIndex[2] = init.pIndex[2]; + } + /// \ru Деструктор. \en Destructor. + ~MbTriangle(); + /// \ru Оператора присваивания. \en The assignment operator. + MbTriangle & operator = ( const MbTriangle & init ) { + pIndex[0] = init.pIndex[0]; + pIndex[1] = init.pIndex[1]; + pIndex[2] = init.pIndex[2]; + return *this; + } +public : + + /// \ru Инициализация. \en Initialization. + void Init( uint j0, uint j1, uint j2, bool orientation ); + /// \ru Выдать номера вершин треугольника в массиве точек. \en Get indices of triangle vertices in the array of points. + bool GetTriangle ( uint & i0, uint & i1, uint & i2 ) const; + /// \ru Выдать номер вершины n треугольника в массиве точек. \en Get index of n-th triangle vertex in the array of points. + uint GetIndex( size_t n ) const { return pIndex[n % 3]; } + /// \ru Инвертировать последовательность вершин. \en Reverse the sequence of vertices. + void Reverse(); + + /// \ru Определить, пересекается ли проекция на глобальную плоскость XY треугольника с присланным прямоугольником. \en Determine whether the projection of the triangle to the global XY-plane intersects the given rectangle. + template + bool IntersectRect( const MbRect & rect, const ParamPoints & points ) const + { + if ( points.size() < 3 ) + return false; + + double x1 = std_min( points[pIndex[0]].x, std_min( points[pIndex[1]].x, points[pIndex[2]].x ) ); + double x2 = std_max( points[pIndex[0]].x, std_max( points[pIndex[1]].x, points[pIndex[2]].x ) ); + + double y1 = std_min( points[pIndex[0]].y, std_min( points[pIndex[1]].y, points[pIndex[2]].y ) ); + double y2 = std_max( points[pIndex[0]].y, std_max( points[pIndex[1]].y, points[pIndex[2]].y ) ); + + return std_max( x1, rect.left ) <= std_min( x2, rect.right ) && + std_max( y1, rect.bottom ) <= std_min( y2, rect.top ); + } + + /// \ru Рассчитать габаритный прямоугольник проекции на глобальную плоскость XY треугольника. \en Calculate bounding rectangle of the projection of the triangle to the global XY-plane. + template + void GetGabRect ( MbRect & rect, const ParamPoints & points ) const + { + if ( points.size() < 3 ) + return; + + rect.left = std_min( points[pIndex[0]].x, std_min( points[pIndex[1]].x, points[pIndex[2]].x ) ); + rect.right = std_max( points[pIndex[0]].x, std_max( points[pIndex[1]].x, points[pIndex[2]].x ) ); + + rect.bottom = std_min( points[pIndex[0]].y, std_min( points[pIndex[1]].y, points[pIndex[2]].y ) ); + rect.top = std_max( points[pIndex[0]].y, std_max( points[pIndex[1]].y, points[pIndex[2]].y ) ); + } + + /// \ru Принадлежит ли ребро треугольнику. \en Is triangle's edge? + bool IsTriangleEdge( uint k0, uint k1, size_t & eInd ) const + { + eInd = SYS_MAX_T; + + if ( k0 == pIndex[0] && k1 == pIndex[1] ) + eInd = 0; + else if ( k0 == pIndex[1] && k1 == pIndex[2] ) + eInd = 1; + else if ( k0 == pIndex[2] && k1 == pIndex[0] ) + eInd = 2; + + return (eInd != SYS_MAX_T); + } + + /// \ru Выдать свойства объекта. \en Get properties of the object. + void GetProperties( MbProperties & properties ); + /// \ru Записать свойства объекта. \en Set properties of the object. + void SetProperties( const MbProperties & properties ); + + KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbTriangle, MATH_FUNC_EX ); + KNOWN_OBJECTS_RW_PTR_OPERATORS_EX( MbTriangle, MATH_FUNC_EX ); +}; // MbTriangle + + +//------------------------------------------------------------------------------ +// \ru Инициализация \en Initialization +// --- +inline void MbTriangle::Init( uint j0, uint j1, uint j2, bool orientation ) +{ + if ( orientation ) { // \ru Совпадает направление обхода \en Traverse direction coincides + pIndex[0] = j0; + pIndex[1] = j1; + pIndex[2] = j2; + } + else { + pIndex[1] = j1; + pIndex[2] = j0; + pIndex[0] = j2; + } +} + + +//------------------------------------------------------------------------------ +// \ru Получить индексы треугольной пластины \en Get indices of triangle plate +// --- +inline bool MbTriangle::GetTriangle ( uint & i0, uint & i1, uint & i2 ) const +{ + i0 = pIndex[0]; + i1 = pIndex[1]; + i2 = pIndex[2]; + return true; +} + + +//------------------------------------------------------------------------------ +// \ru Инвертировать последовательность вершин. \en Reverse the sequence of vertices. +// --- +inline void MbTriangle::Reverse() +{ + uint ind = pIndex[1]; + pIndex[1] = pIndex[2]; + pIndex[2] = ind; +} + + +//////////////////////////////////////////////////////////////////////////////// +/** \brief \ru Четырёхугольник. + \en Quadrangle. \~ + \details \ru Четырёхугольник задан, как четверка индексов элементов из массива + вершин триангуляции MbGrid. \n + \en Quadrangle defined as a quadruple of elements' indices form the array + of vertices of MbGrid triangulation. \n \~ + \ingroup Polygonal_Objects +*/ +// --- +class MATH_CLASS MbQuadrangle { +protected : + uint pIndex[4]; ///< \ru Номера вершин четырёхугольника в массиве точек. //-V112 \en Indices of quadrangle vertices in the array of points. //-V112 + +public : + /// \ru Конструктор. \en Constructor. + MbQuadrangle(); + /// \ru Конструктор. \en Constructor. + MbQuadrangle( uint j0, uint j1, uint j2, uint j3, bool orientation ); + /// \ru Конструктор копирования. \en The copy-constructor. + MbQuadrangle( const MbQuadrangle & init ) { + pIndex[0] = init.pIndex[0]; + pIndex[1] = init.pIndex[1]; + pIndex[2] = init.pIndex[2]; + pIndex[3] = init.pIndex[3]; + } + /// \ru Деструктор. \en Destructor. + ~MbQuadrangle(); + /// \ru Оператора присваивания. \en The assignment operator. + MbQuadrangle & operator = ( const MbQuadrangle & init ) { + pIndex[0] = init.pIndex[0]; + pIndex[1] = init.pIndex[1]; + pIndex[2] = init.pIndex[2]; + pIndex[3] = init.pIndex[3]; + return *this; + } +public : + + /// \ru Инициализация. \en Initialization. + void Init( uint j0, uint j1, uint j2, uint j3, bool orientation ); + /// \ru Выдать номера вершин четырёхугольника в массиве точек. \en Get indices of quadrangle vertices in the array of points. + bool GetQuadrangle ( uint & i0, uint & i1, uint & i2, uint & i3 ) const; + /// \ru Выдать пару треугольников, составляющих четырехугольник. \en Get a pair of triangles consisting the quadrangle. + void GetTriangles( MbTriangle &, MbTriangle & ) const; + /// \ru Выдать номер вершины n четырёхугольника в массиве точек. \en Get index of n-th quadrangle vertex in the array of points. + uint GetIndex( size_t n ) const { return pIndex[n % 4]; } //-V112 + /// \ru Инвертировать последовательность вершин. \en Reverse the sequence of vertices. + void Reverse(); + /// \ru Определить, пересекается ли проекция на глобальную плоскость XY четырёхугольника с присланным прямоугольником. \en Determine whether the projection of the quadrangle to the global XY-plane intersects the given rectangle. + template + bool IntersectRect( const MbRect & rect, const ParamPoints & points ) const + { + if ( points.size() < 4 ) //-V112 + return false; + + double x1 = std_min( points[pIndex[0]].x, std_min( points[pIndex[1]].x, std_min( points[pIndex[2]].x, points[pIndex[3]].x ) ) ); + double x2 = std_max( points[pIndex[0]].x, std_max( points[pIndex[1]].x, std_max( points[pIndex[2]].x, points[pIndex[3]].x ) ) ); + + double y1 = std_min( points[pIndex[0]].y, std_min( points[pIndex[1]].y, std_min( points[pIndex[2]].y, points[pIndex[3]].y ) ) ); + double y2 = std_max( points[pIndex[0]].y, std_max( points[pIndex[1]].y, std_max( points[pIndex[2]].y, points[pIndex[3]].y ) ) ); + + return std_max( x1, rect.left ) <= std_min( x2, rect.right ) && + std_max( y1, rect.bottom ) <= std_min( y2, rect.top ); + } + + /// \ru Рассчитать габаритный прямоугольник проекции на глобальную плоскость XY четырёхугольника. \en Calculate bounding rectangle of the projection of quadrangle to the global XY-plane. + template + void GetGabRect ( MbRect & rect, const ParamPoints & points ) const + { + if ( points.size() < 4 ) //-V112 + return; + + rect.left = std_min( points[pIndex[0]].x, std_min( points[pIndex[1]].x, std_min( points[pIndex[2]].x, points[pIndex[3]].x ) ) ); + rect.right = std_max( points[pIndex[0]].x, std_max( points[pIndex[1]].x, std_max( points[pIndex[2]].x, points[pIndex[3]].x ) ) ); + + rect.bottom = std_min( points[pIndex[0]].y, std_min( points[pIndex[1]].y, std_min( points[pIndex[2]].y, points[pIndex[3]].y ) ) ); + rect.top = std_max( points[pIndex[0]].y, std_max( points[pIndex[1]].y, std_max( points[pIndex[2]].y, points[pIndex[3]].y ) ) ); + } + /// \ru Выдать свойства объекта. \en Get properties of the object. + void GetProperties( MbProperties &properties ); + /// \ru Записать свойства объекта. \en Set properties of the object. + void SetProperties( const MbProperties &properties ); + + KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbQuadrangle, MATH_FUNC_EX ); + KNOWN_OBJECTS_RW_PTR_OPERATORS_EX( MbQuadrangle, MATH_FUNC_EX ); +}; // MbQuadrangle + + +//------------------------------------------------------------------------------ +// \ru Инициализация \en Initialization +// --- +inline void MbQuadrangle::Init( uint j0, uint j1, uint j2, uint j3, bool orientation ) +{ + if ( orientation ) { // \ru Совпадает направление обхода \en Traverse direction coincides + pIndex[0] = j0; + pIndex[1] = j1; + pIndex[2] = j2; + pIndex[3] = j3; + } + else { + pIndex[2] = j1; + pIndex[3] = j0; + pIndex[0] = j3; + pIndex[1] = j2; + } +} + + +//------------------------------------------------------------------------------ +/// \ru Получить индексы четырехугольной пластины \en Get indices of quadrangle plate +// --- +inline bool MbQuadrangle::GetQuadrangle ( uint & i0, uint & i1, uint & i2, uint & i3 ) const +{ + i0 = pIndex[0]; + i1 = pIndex[1]; + i2 = pIndex[2]; + i3 = pIndex[3]; + return true; +} + +//------------------------------------------------------------------------------ +// \ru Выдать пару треугольников, составляющих четырехугольник. \en Get a pair of triangles consisting the quadrangle. +//--- +inline void MbQuadrangle::GetTriangles( MbTriangle & fTri, MbTriangle & sTri ) const +{ + fTri.Init( pIndex[0], pIndex[1], pIndex[2], true ); + sTri.Init( pIndex[2], pIndex[3], pIndex[0], true ); +} + +//------------------------------------------------------------------------------ +// \ru Инвертировать последовательность вершин. \en Reverse the sequence of vertices. +// --- +inline void MbQuadrangle::Reverse() +{ + uint ind = pIndex[1]; + pIndex[1] = pIndex[3]; + pIndex[3] = ind; +} + + +//////////////////////////////////////////////////////////////////////////////// +/** \brief \ru Объемный элемент. + \en Element of tesselation of solid volume. \~ + \details \ru Элемент задан, как восемь индексов точек из массива вершин объекта MbGrid. \n + \en Element defined as an elements' indices form the array of vertices of MbGrid. \n \~ + \ingroup Polygonal_Objects +*/ +// --- +class MATH_CLASS MbElement { +protected : + uint pIndex[8]; ///< \ru Номера вершин елемента в массиве точек. \en Indices of element vertices in the array of points. + size_t estate; ///< \ru Свойство элемента. \en Estate of element. + double props; ///< \ru Характеристика элемента. \en Property of element. + +public : + /// \ru Конструктор. \en Constructor. + MbElement(); + /// \ru Конструктор. \en Constructor. + MbElement( uint j0, uint j1, uint j2, uint j3, uint j4, uint j5, uint j6, uint j7 ); + /// \ru Конструктор копирования. \en Copy-constructor. + MbElement( const MbElement & ); + /// \ru Деструктор. \en Destructor. + ~MbElement(); + // \ru Оператор присваивания. \en Assignment operator. + MbElement & operator = ( const MbElement & ); + +public : + + /// \ru Инициализация. \en Initialization. + void Init( uint j0, uint j1, uint j2, uint j3, uint j4, uint j5, uint j6, uint j7 ); + /// \ru Выдать номера вершин четырёхугольника в массиве точек. \en Get indices of quadrangle vertices in the array of points. + bool GetElement ( uint & i0, uint & i1, uint & i2, uint & i3, uint & i4, uint & i5, uint & i6, uint & i7 ) const; + /// \ru Выдать номер вершины n четырёхугольника в массиве точек. \en Get index of n-th quadrangle vertex in the array of points. + uint GetIndex( size_t n ) const { return pIndex[n % 8]; } + /// \ru Дать свойство элемента. \en Get estate of element. + size_t GetEstate() const { return estate; } + /// \ru Изменить свойство элемента. \en Set estate of element. + void SetEstate( uint32 e ) { estate = e; } + /// \ru Дать характеристику элемента. \en Get property of element. + double GetProps() const { return props; } + /// \ru Изменить характеристику элемента. \en Get property of element. + void SetProps( double p ) { props = p; } + + /// \ru Выдать свойства объекта. \en Get properties of the object. + void GetProperties( MbProperties &properties ); + /// \ru Записать свойства объекта. \en Set properties of the object. + void SetProperties( const MbProperties &properties ); + + KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbElement, MATH_FUNC_EX ); + KNOWN_OBJECTS_RW_PTR_OPERATORS_EX( MbElement, MATH_FUNC_EX ); +}; // MbElement + + +//------------------------------------------------------------------------------ +// \ru Инициализация \en Initialization +// --- +inline void MbElement::Init( uint j0, uint j1, uint j2, uint j3, uint j4, uint j5, uint j6, uint j7 ) +{ + pIndex[0] = j0; + pIndex[1] = j1; + pIndex[2] = j2; + pIndex[3] = j3; + pIndex[4] = j4; + pIndex[5] = j5; + pIndex[6] = j6; + pIndex[7] = j7; +} + + +//------------------------------------------------------------------------------ +/// \ru Получить индексы четырехугольной пластины \en Get indices of quadrangle plate +// --- +inline bool MbElement::GetElement ( uint & i0, uint & i1, uint & i2, uint & i3, uint & i4, uint & i5, uint & i6, uint & i7 ) const +{ + i0 = pIndex[0]; + i1 = pIndex[1]; + i2 = pIndex[2]; + i3 = pIndex[3]; + i4 = pIndex[4]; + i5 = pIndex[5]; + i6 = pIndex[6]; + i7 = pIndex[7]; + return true; +} + + +//////////////////////////////////////////////////////////////////////////////// +/** \brief \ru Граница триангуляции. + \en Border of triangulation. \~ + \details \ru Граница триангуляции используется для описания набора ребер грани оболочки. \n + Граница триангуляции содержит номера последовательности вершины. + \en Border of triangulation is used to describe edge sequence of shell's face. \n + Border of triangulation contains indices of vertex sequence. \~ + \ingroup Polygonal_Objects +*/ +// --- +class MATH_CLASS MbGridLoop { +private: + std::vector pIndices; ///< \ru Номера вершин в массиве точек. \en Indices of vertices in the array of points. + +public: + /// \ru Конструктор. \en Constructor. + MbGridLoop( size_t n = 0 ) : pIndices() { if ( n > 0 ) pIndices.reserve( n ); } + /// \ru Конструктор. \en Constructor. + template + explicit MbGridLoop( const UintVector & init ) : pIndices() { + pIndices.reserve( init.size() ); + for ( size_t i = 0, iCount = init.size(); i < iCount; i++ ) + pIndices.push_back( init[i] ); + } + /// \ru Деструктор. \en Destructor. + ~MbGridLoop() {} + +public: + /// \ru Инициализация. \en Initialization. + template + void Init( const UintVector & init ) { + pIndices.clear(); pIndices.reserve( init.size() ); + for ( size_t i = 0, iCount = init.size(); i < iCount; i++ ) + pIndices.push_back( init[i] ); + } + /// \ru Выдать количество вершин полосы. \en Get the count of strip vertices. + size_t Count() const { return pIndices.size(); } + /// \ru Добавить номер вершины. \en Add vertex number. + void Add( uint n ) { pIndices.push_back(n); } + /// \ru Выдать количество вершин полосы. \en Get the count of strip vertices. + uint GetIndex( size_t i ) const { return pIndices[i]; } + /// \ru Выдать количество вершин полосы. \en Get the count of strip vertices. + uint & SetIndex( size_t i ) { return pIndices[i]; } + /// \ru Очистить полосу. \en Clear the strip. + void Flush() { pIndices.clear(); } + /// \ru Выдать адрес начала массива. \en Get the address of the beginning of the array. + const uint * GetIndicesAddr() const { return &(pIndices[0]); } + /// \ru Выдать адрес начала массива. \en Get the address of the beginning of the array. + const std::vector & GetIndices() const { return pIndices; } + /// \ru Выдать контейнер номеров вершин. \en Get the container of vertex numbers. + template + void GetIndices( IndicesVector & iVector ) const { + iVector.reserve( iVector.size() + pIndices.size() ); + for ( size_t i = 0, iCount = pIndices.size(); i < iCount; i++ ) + iVector.push_back( pIndices[i] ); + } + /// \ru Есть ли такой индекс в цикле? \en Is exist index n in the loop? + bool IsExist( uint n ) const { return ( std::find(pIndices.begin(), pIndices.end(), n) != pIndices.end() ); } + + KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbGridLoop, MATH_FUNC_EX ); + KNOWN_OBJECTS_RW_PTR_OPERATORS_EX( MbGridLoop, MATH_FUNC_EX ); + OBVIOUS_PRIVATE_COPY( MbGridLoop ) +}; + + +//////////////////////////////////////////////////////////////////////////////// +/** \brief \ru Сегмент(результат сегментации) полигональной сетки. + \en A polygonal mesh segment (segmentation result). \~ + \details \ru Сегмент определен множеством треугольников полигональной сетки. \n + \en Segment is defined as a set of triangles of polygonal mesh. \n \~ + \ingroup Polygonal_Objects +*/ +// --- +class MATH_CLASS MbGridSegment { +private: + std::vector faces; ///< \ru Вектор индексов треугольников сегмента. \en A vector of segment triangles indicies. + +public: + /// \ru Конструктор. \en Constructor. + MbGridSegment() : faces() {} + /// \ru Конструктор. \en Constructor. + MbGridSegment( const std::vector & initFaces ) : faces( initFaces ) {} + /// \ru Выдать вектор индексов треугольников сегмента. \en Get the vector of segment triangles indicies. + const std::vector & GetFaces() const { return faces; } + /// \ru Выдать количество треугольников сегмента. \en Get the count of of segment triangles. + size_t GetFaceCount() const { return faces.size(); } + /// \ru Выдать индекс треугольника сегмента. \en Get the index of segment triangle. + size_t GetFace( size_t idx ) const { return faces[idx]; } + + KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbGridSegment, MATH_FUNC_EX ); + KNOWN_OBJECTS_RW_PTR_OPERATORS_EX( MbGridSegment, MATH_FUNC_EX ); +}; + +#endif // __MESH_TRIANGLE_H diff --git a/C3d/Include/mip_solid_mass_inertia.h b/C3d/Include/mip_solid_mass_inertia.h index 095d5f2..066d60c 100644 --- a/C3d/Include/mip_solid_mass_inertia.h +++ b/C3d/Include/mip_solid_mass_inertia.h @@ -20,14 +20,14 @@ struct IfProgressIndicator; /// \ru Неинициализированное значение double. \en Uninitialized value of double. -#define NOT_INITIAL_DBL -DETERMINANT_MAX +const_expr double NOT_INITIAL_DBL = -DETERMINANT_MAX; //------------------------------------------------------------------------------ /** \brief \ru Инерционные характеристики тела. \en Inertial properties of solid. \~ \details \ru Инерционные характеристики тела.\n - Векторы direction дают направления главных осей инерции. \n + Векторы direction дают направления главных осей инерции. \n Если все главные моменты инерции general[i] i=1,2,3 разные, то все векторы direction[i] i=1,2,3 не равны нулю. \n Если все главные моменты инерции general[i] i=1,2,3 одинаковые, diff --git a/C3d/Include/model.h b/C3d/Include/model.h index cda4e12..32062f1 100644 --- a/C3d/Include/model.h +++ b/C3d/Include/model.h @@ -1,669 +1,669 @@ -////////////////////////////////////////////////////////////////////////////////////////// -/** \file - \brief \ru Геометрическая модель. - \en Geometric model. \~ -*/ -////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef __MODEL_H -#define __MODEL_H - - -#include -#include - -struct ItModelVisitor; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Геометрическая модель. - \en Geometric model. \~ - \details \ru Геометрическая модель - контейнер геометрических объектов. \n - Модель состоит из массивов указателей на объекты геометрической модели MbItem. - Модель может содержать вспомогательные объекты MbAssistingItem, - точки MbPointFrame, каркасы MbWireFrame, - твердые тела MbSolid, полигональные объекты MbMesh, - объекты MbSpaceInstance и MbPlaneInstance.\n - Модель используется для описания геометрических свойств реальных и - воображаемых объектов, визуализации моделируемых объектов, - вычисления геометрических характеристик моделируемых объектов.\n - Имя объекта геометрической модели представляет собой контейнер простых имён. - В начале контейнера содержится простое имя SimpleName, - которое совпадает с первым полем std::multimap геометрической модели. \n - Если объект не держит в себе других объектов, то контейнер содержит одно простое имя SimpleName. - Ели объект держит в себе другие объекты (MbAssembly или MbInstance), - то имя внутренних объектов представляет собой контейнер, содержащий как минимум два простых имени. - Количество элементов имени объекта отражают количество уровней вложенности объект относительно модели. - \en Geometric model is a container of geometric objects. \n - The model consists of arrays of pointers to geometric model objects MbItem. - The model can contain MbAssistingItem assisting items, - MbPointFrame points, MbWireFrame frames, - MbSolid solids, polygonal objects MbMesh, - MbSpaceInstance and MbPlaneInstance objects.\n - Model is used to describe geometric properties of real and - imaginary objects, to visualize modeled objects, - to calculate geometric properties of modeled objects.\n - The name of an object of a geometric model is represented as a container of simple names. - In the beginning of the container there is a SimpleName simple name - which coincides with the first field std::multimap of the geometric model. \n - If the object doesn't contain other objects, then the container contains one SimpleName simple name. - If the object contains other objects (MbAssembly or MbInstance), - then the internal objects name is represented as a container with at least two simple names. - Number of the elements of an object's name corresponds to the number of levels of objects inclusion relative to the model. \~ - \ingroup Model -*/ -//--- -class MATH_CLASS MbModel : public TapeBase, - public MbRefItem, - public MbTransactions, - public MbAttributeContainer -{ -public: - typedef std::map NameItemArray; - -private: - NameItemArray modelItems; ///< \ru Множество объектов модели. \en Set of the model objects. - SimpleName name; ///< \ru Имя объекта. \en A name of an object. - -protected: - /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. - explicit MbModel( const MbModel &, MbRegDuplicate * ); - -public: - /// \ru Конструктор по имени объекта. \en Constructor by object's name. - MbModel( SimpleName n = 0 ); - /// \ru Деструктор \en Destructor - virtual ~MbModel(); - -public : - - /** \ru \name Общие функции геометрического объекта - \en \name Common functions of a geometric object - \{ */ - /// \ru Тип контейнера атрибутов - классификатор наследников. \en Type of an attribute container is a classifier of inheritors. - virtual MbeImplicationType ImplicationType() const; - - /// \ru Создать копию. \en Create a copy. - MbModel & Duplicate( MbRegDuplicate * = NULL ) const; - /// \ru Преобразовать согласно матрице. \en Transform according to the matrix. - void Transform( const MbMatrix3D &, MbRegTransform * iReg = NULL ); - /// \ru Сдвинуть вдоль вектора. \en Translate along a vector. - void Move ( const MbVector3D &, MbRegTransform * iReg = NULL ); - /// \ru Повернуть вокруг оси. \en Rotate about an axis. - void Rotate ( const MbAxis3D &, double angle, MbRegTransform * iReg = NULL ); - /// \ru Вычислить расстояние до точки. \en Calculate the distance to a point. - double DistanceToPoint ( const MbCartPoint3D & ) const; - /// \ru Добавь свой габарит в габаритный куб. \en Include your own bounding box into bounding box. - void AddYourGabaritTo( MbCube & ) const; - - /// \ru Создать собственное свойство с заданием его имени. \en Create your own property with specified name. - MbProperty & CreateProperty( MbePrompt ) const; - /// \ru Выдать свойства объекта. \en Get properties of the object. - void GetProperties( MbProperties & ); - /// \ru Установить свойства объекта. \en Set properties of the object. - void SetProperties( const MbProperties & ); - /** \} */ - - /// \ru Выдать имя модели. \en Get name of a model. - SimpleName GetModelName() const { return name; } - /// \ru Установить имя модели. \en Set name of a model. - void SetModelName( SimpleName n ) { name = n; } - - /** \brief \ru Добавить объект в модель. - \en Add object to the model. \~ - \details \ru Добавить объект в модель с указанным именем. - \en Add object to the model with a given name. \~ - \param[in] item - \ru Объект модели. - \en A model object. \~ - \param[in] n - \ru Имя объекта. Если указанное имя равно нулю, то модель именует объект своим уникальным именем. - \en A name of an object. If a given name is equal to zero, then the model names an object with its unique name. \~ - \return \ru Добавленный объект. - \en Added object. \~ - */ - MbItem * AddItem( MbItem & item, SimpleName n = UNDEFINED_SNAME ); - /// \ru Добавить объекты модели item в модель. \en Add item objects of to the model. \~ - bool AddModel( const MbModel & ); - - // \ru Выдать объект модели по индексу. \en Get item of model by index. - const MbItem * GetItem( size_t ind ) const; - // \ru Выдать непосредственный объект модели по идентификатору. \en Get the immediate item of model by identifier. - const MbItem * SubItem( SimpleName n ) const; - - /** \brief \ru Заменить объект. - \en Replace the object. \~ - \details \ru Заменить объект новым. - \en Replace the object by a new one. \~ - \param[in] item - \ru Заменяемый объект. - \en An object to replace. \~ - \param[in] newItem - \ru Новый объект. - \en A new object. \~ - \return \ru Возвращает true, если замена была выполнена. - \en Returns true if the replacement has been done. \~ - */ - bool ReplaceItem( const MbItem & item, MbItem & newItem, bool saveName = false ); - - /// \ru Дать все объекты. \en Get all the objects. - template - void GetItems( Items & ) const; - /// \ru Отцепить объект, если он есть в модели. \en Detach an object if it is in the model. - bool DetachItem ( MbItem *, bool resetName = true ); - /// \ru Отцепить все объекты. \en Detach all the objects. - template - void DetachItems( Items & ); - - /// \ru Удалить объект, если он есть в модели. \en Delete an object if it is in the model. - bool DeleteItem ( MbItem *, bool resetName = true ); - /// \ru Удалить все объекты модели. \en Delete all the model objects. - void DeleteItems(); - - /// \ru Разрушить сборки с подсборками на составляющие. \en Decompose assemblies with subassemblies into components. - bool DecomposeAssemblies(); - - /** \brief \ru Наполнить присланную модель полигональными копиями объектов модели. - \en Fill the given model with polygonal copies of the model objects. \~ - \details \ru Наполнить присланную модель полигональными копиями объектов оригинальной модели. - Присланная модель опустошается и наполняется полигональными копиями объектов оригинальной модели. - Присланная модель заполняется аналогично оригинальной модели с той разницей, что вместо - тел, проволочных каркасов, точечных каркасов и других конечных объектов модели - присланную модель заполняют соответствующие полигональные копии объектов (MbMesh). - Сборки и вставки в присланной модели сохраняются аналогичными оригинальной модели. - Присланная модель может использоваться для визуализации модели и расчетов. - \en Fill the given model with polygonal copies of the original model objects. - Given model is being cleared and filled with polygonal copies of objects of the original model. - Given model is to be filled similarly to original model, but instead of - solids, wire-frames, point-frames and other finite objects of model - the given model is filled by the corresponding polygonal copies of objects (MbMesh). - Assemblies and instances in the given model remains similar to the original model. - Given model can be used for calculations and visualization of the model. \~ - \note \ru В многопоточном режиме выполняется параллельно. \en In multithreaded mode runs in parallel. \~ - - \param[in] stepData - \ru Данные для вычисления шага при триангуляции. - \en Data for step calculation during triangulation. \~ - \param[in] note - \ru Способ построения полигонального объекта. - \en Way for polygonal object constructing. \~ - \param[out] meshModel - \ru Присланная модель для наполнения. - \en Given model to be filled. \~ - \return \ru Не было ошибок во время построения - true, были ошибки - false. - \en If there were no errors during construction, then true, otherwise false. \~ - */ - bool FillMeshModel( const MbStepData & stepData, const MbFormNote & note, MbModel & meshModel ) const; - - /** \brief \ru Добавить полигональный объект. - \en Add polygonal object. \~ - \details \ru Добавить полигональную копию модели в присланный полигональный объект (MbMesh). - Все объекты модели, её сборки и вставки помещаются в единый плоскогранный полигональный объект. - Один и тот же объект, вставленный несколько раз в сборки и вставки модели, получает несколько копий, - так как каждая копия трансформируется по матрице локальной системы координат cjjndtncnde.otq сборки и вставки. - \en Add polygonal copy of the model to the given polygonal object (MbMesh). - All the objects, assemblies and instances of the model are placed in a unified planar polygonal object. - The same object inserted several times in assemblies and instances of the model gets several copies - because each copy is transformed by the matrix of local coordinate system of the corresponding assembly and instance. \~ - \param[in] stepData - \ru Данные для вычисления шага при триангуляции. - \en Data for step calculation during triangulation. \~ - \param[in] note - \ru Способ построения полигонального объекта. - \en Way for polygonal object constructing. \~ - \param[out] mesh - \ru Присланный полигональный объект. - \en Given polygonal object. \~ - \return \ru Добавлен ли объект. - \en Whether the object is added. \~ - */ - bool AddYourMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; - - /** \brief \ru Разрезать модель полигональных объектов одной или двумя параллельными плоскостями. - \en Cut model of polygonal objects by one or two parallel planes. \~ - \details \ru Создать новую модель полигональных объектов и наполнить её частями исходной модели, - лежащими под плоскостью XY локальной системы координат на заданном расстоянии.\n - Функция "режет" только модель полигональных объектов MbMesh. - Функция "режет" модель двумя плоскостями: - плоскостью XY локальной системы координат place и плоскостью, параллельной ей и - расположенной на расстоянии distance ниже неё. - Если distance<=0, то функция "режет" объект только одной плоскостью XY локальной системы.\n - Содержимое исходных полигональных объектов, - необходимое для построения разрезанного объекта и не затронутое режущими плоскостями, - добавляется в возвращаемый разрезанный объект без копирования.\n - \en Create new model of polygonal objects and fill it by the source model parts - lying under XY plane of the local coordinate system at the given distance.\n - Function "cuts" only MbMesh model of polygonal objects. - Function "cuts" the model by two planes: - XY plane of 'place' local coordinate system and plane parallel to it and - located at 'distance' distance below it. - If 'distance' is less than or equal to zero, then the function "cuts" an object only by one XY plane of local coordinate system.\n - Contents of the source polygonal objects - that are necessary for creation of cut object and not affected by cutting planes - are added to returned cut object without copying.\n \~ - \param[in] place - \ru Локальная система координат, плоскость XY которой задаёт режущую плоскость. - \en A local coordinate system which XY plane defines a cutting plane. \~ - \param[in] distance - \ru Расстояние до параллельной режущей плоскости откладывается в отрицательную сторону оси Z локальной системы. - \en Distance to a parallel cutting plane is measured in negative direction of Z-axis of local coordinate system. \~ - \result \ru Возвращает новую модель полигональных объектов, лежащую под плоскость XY локальной системы координат на заданном расстоянии. - \en Returns a new model of polygonal objects that lies under XY plane of local coordinate system at given distance. \~ - */ - MbModel * CutMeshModel( const MbPlacement3D & cutPlace, double distance ) const; - - /** \brief \ru Найти ближайший объект или имя ближайшего объекта. - \en Find the nearest object or name of the nearest object. \~ - \details \ru Найти ближайший трехмерный объект или его имя по типу объекта и - составляющий элемент искомого объекта или его имя по топологическому или двумерному типу элемента (по требованию) - на расстоянии от прямой, не превышающем заданной величины. - Функция предназначена для идентификации геометрического объекта, породившего полигональный объект. - Реальный поиск выполняется для элементов MbPrimitive полигонального объекта MbMesh, - у которых берётся информация о породившем примитив геометрическом объекте. - \en Find the nearest three-dimensional object or its name by type of object and - component of the required object or its name by topological or two-dimensional type of the element (on demand) - at distance from line less than or equal to the given value. - Function is intended for identification of a geometric object which is begetter of a polygonal object. - The real search is performed for MbMesh polygonal object's MbPrimitive elements - from which the information is taken about geometric object which is begetter of the primitive. \~ - \note \ru В многопоточном режиме выполняется параллельно. \en In multithreaded mode runs in parallel. \~ - \param[in] sType - \ru Тип искомого объекта. - \en Type of required object. \~ - \param[in] tType - \ru Топологический тип составляющего элемента искомого объекта. - \en Topological type of the required object's component. \~ - \param[in] pType - \ru Двумерный тип составляющего элемента искомого объекта. - \en Two-dimensional type of the required object's component. \~ - \param[in] axis - \ru Прямая поиска. - \en Line of search. \~ - \param[in] maxDistance - \ru Расстояние от прямой, на котором ищется объект. - \en Distance from the line on which the object is looked for. \~ - \param[in] gridPriority - \ru Повышенный приоритет триангуляционной сетки при поиске. - \en Increased priority triangulation grid when searching. \~ - \param[out] find - \ru Найденный объект. - \en Found object. \~ - \param[out] findName - \ru Имя найденного объекта. - \en Name of the found object. \~ - \param[out] element - \ru Найденный составляющий элемент объекта. - \en Found component of the object. \~ - \param[out] elementName - \ru Имя найденного составляющего элемента объекта. - \en Name of found component of the object. \~ - \param[out] path - \ru Путь к объекту в модели. - \en Path to the object in model. \~ - \param[out] from - \ru Матрица преобразования найденного объекта в глобальную систему координат. - \en Transformation matrix of the found object to the global coordinate system. \~ - \return \ru Найден ли объект или его имя. - \en Whether the object or its name is found. \~ - */ - bool NearestMesh( MbeSpaceType sType, MbeTopologyType tType, MbePlaneType pType, - const MbAxis3D & axis, double maxDistance, bool gridPriority, - MbItem *& find, SimpleName & findName, - MbRefItem *& element, SimpleName & elementName, - MbPath & path, MbMatrix3D & from ) const; - - /** \brief \ru Дать все объекты указанного типа. - \en Get all objects by type. \~ - \details \ru Дать все объекты указанного типа, - а также матрицы преобразования их в глобальную систему координат. \n - \en Get all objects by type - and get transformation matrix to the global coordinate system. \n \~ - \param[in] type - \ru Тип объекта. - \en Object's type. \~ - \param[out] items - \ru Множество найденных объектов. - \en Found objects. \~ - \param[out] matrs - \ru Матрицы преобразования найденных объектов в глобальную систему координат. - \en Transformation matrix of found objects to the global coordinate system. \~ - \ingroup Model_Items - */ - virtual void GetItems( MbeSpaceType type, RPArray & items, SArray & matrs ); - - /** \brief \ru Дать все объекты указанного типа. - \en Get all objects by type. \~ - \details \ru Дать все объекты указанного типа, - а также матрицы преобразования их в глобальную систему координат. \n - \en Get all objects by type - and get transformation matrix to the global coordinate system. \n \~ - \param[in] type - \ru Тип объекта. - \en Object's type. \~ - \param[out] items - \ru Множество найденных объектов. - \en Found objects. \~ - \param[out] matrs - \ru Матрицы преобразования найденных объектов в глобальную систему координат. - \en Transformation matrix of found objects to the global coordinate system. \~ - \ingroup Model_Items - */ - virtual void GetItems( MbeSpaceType type, RPArray & items, SArray & matrs ) const; - - /** \brief \ru Дать все уникальные объекты указанного типа. - \en Get all unique objects by type. \~ - \details \ru Дать все уникальные объекты указанного типа. \n - \en Get all unique objects by type. \n \~ - \param[in] type - \ru Тип объекта. - \en Object's type. \~ - \param[out] items - \ru Множество найденных объектов. - \en Found objects. \~ - \ingroup Model_Items - */ - virtual void GetUniqItems( MbeSpaceType type, CSSArray & items ) const; - - /** \brief \ru Построить путь положения объекта. - \en Create path of object's position. \~ - \details \ru Построить путь положения объекта в модели и - дать матрицу преобразования объекта в глобальную систему координат. - Объект может содержаться в другом объекте (в сборке или вставке). - \en Create path of object's position in the model and - get transformation matrix of the object to the global coordinate system. - Object can be contained in other object (in assembly or in instance). \~ - \param[in] obj - \ru Объект. - \en Object. \~ - \param[out] path - \ru Путь к объекту в модели. - \en Path to the object in model. \~ - \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. - \en Transformation matrix of object to the global coordinate system. \~ - \return \ru Найден ли путь и матрица объекта. - \en Whether the path and the matrix of object are found. \~ - */ - bool MakePath( const MbItem & obj, MbPath & path, MbMatrix3D & from ) const; - - /** \brief \ru Дать объект по его пути. - \en Get the object by its path. \~ - \details \ru Дать объект по его пути положения в модели и - дать матрицу преобразования объекта в глобальную систему координат. - Объект может содержаться в другом объекте (в сборке или вставке). - \en Get the object by path of its position in the model and - get transformation matrix of the object to the global coordinate system. - Object can be contained in other object (in assembly or in instance). \~ - \param[in] path - \ru Путь к объекту в модели. - \en Path to object in the model. \~ - \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. - \en Transformation matrix of object to the global coordinate system. \~ - \return \ru Найден ли путь и матрица объекта. - \en Whether the path and the matrix of object are found. \~ - */ - const MbItem * GetItemByPath( const MbPath & path, MbMatrix3D & from ) const; - - /** \brief \ru Найти объект по геометрическому объекту. - \en Find object by geometric object. \~ - \details \ru Найти объект по геометрическому объекту, а также получить путь к - объекту в модели и матрицу преобразования в глобальную систему координат. - \en Find object by geometric object and also get the path to the - object in model and get transformation matrix to the global coordinate system. \~ - \param[in] s - \ru Геометрический объект. - \en Geometric object. \~ - \param[out] path - \ru Путь к объекту в модели. - \en Path to object in the model. \~ - \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. - \en Transformation matrix of object to the global coordinate system. \~ - \return \ru Найден ли путь и матрица объекта. - \en Whether the path and the matrix of object are found. \~ - */ - const MbItem * FindItem( const MbSpaceItem * s, MbPath & path, MbMatrix3D & from ) const; - - /** \brief \ru Найти объект по геометрическому объекту. - \en Find object by geometric object. \~ - \details \ru Найти объект по геометрическому объекту, - а также получить путь к объекту в модели - и матрицу преобразования в глобальную систему координат. \n - \en Find object by geometric object - and also get the path to the object in model - and get transformation matrix to the global coordinate system. \n \~ - \param[in] s - \ru Геометрический объект. - \en Geometric object. \~ - \param[out] path - \ru Путь к объекту в модели. - \en Path to object in the model. \~ - \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. - \en Transformation matrix of object to the global coordinate system. \~ - \return \ru Найден ли путь и матрица объекта. - \en Whether the path and the matrix of object are found. \~ - */ - const MbItem * FindItem( const MbPlaneItem * s, MbPath & path, MbMatrix3D & from ) const; - - /** \brief \ru Найти объект по объекту геометрической модели. - \en Find object by object of geometric model \~ - \details \ru Найти объект по объекту геометрической модели. - а также получить путь к объекту в модели - и матрицу преобразования в глобальную систему координат. \n - \en Find object by object of geometric model - and also get the path to the object in model - and get transformation matrix to the global coordinate system. \n \~ - \param[in] s - \ru Геометрический объект. - \en Geometric object. \~ - \param[out] path - \ru Путь к объекту в модели. - \en Path to object in the model. \~ - \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. - \en Transformation matrix of object to the global coordinate system. \~ - \return \ru Найден ли путь и матрица объекта. - \en Whether the path and the matrix of object are found. \~ - */ - const MbItem * FindItem( const MbItem * s, MbPath & path, MbMatrix3D & from ) const; - - /** \brief \ru Найти объект по имени. - \en Find object by name. \~ - \details \ru Найти объект по имени, а также получить путь к объекту в модели - и матрицу преобразования в глобальную систему координат. \n - \en Find object by name and also get path to object in model - and get transformation matrix to the global coordinate system. \n \~ - \param[in] n - \ru Имя объекта. - \en A name of an object. \~ - \param[out] path - \ru Путь к объекту в модели. - \en Path to object in the model. \~ - \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. - \en Transformation matrix of object to the global coordinate system. \~ - \return \ru Найден ли путь и матрица объекта. - \en Whether the path and the matrix of object are found. \~ - */ - const MbItem * GetItemByName( SimpleName n, MbPath & path, MbMatrix3D & from ) const; - - /** \brief \ru Найти объект по имени для редактирования. - \en Find object by name for editing. \~ - \details \ru Найти объект по имени для редактирования, а также получить путь к объекту в модели - и матрицу преобразования в глобальную систему координат. \n - \en Find object by name for editing and also get path to object in model - and get transformation matrix to the global coordinate system. \n \~ - \param[in] n - \ru Имя объекта. - \en A name of an object. \~ - \param[out] path - \ru Путь к объекту в модели. - \en Path to object in the model. \~ - \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. - \en Transformation matrix of object to the global coordinate system. \~ - \return \ru Найден ли путь и матрица объекта. - \en Whether the path and the matrix of object are found. \~ - */ - MbItem * SetItemByName( SimpleName n, MbPath & path, MbMatrix3D & from ); - /** \brief \ru Алгоритм общего назначения для обхода дерева модели в глубину. - \en General-purpose algorithm traversing the model graph in depth. */ - void Traverse( ItModelVisitor & ) const; - /// \ru Преобразовать селектирование объекты по матрице. \en Transform selected objects by matrix. - void TransformSelected( const MbMatrix3D &, MbRegTransform * = NULL ); - /// \ru Сдвинуть выбранные объекты. \en Move selected objects. - void MoveSelected( const MbVector3D &, MbRegTransform * = NULL ); - /// \ru Повернуть выбранные объекты вокруг оси. \en Rotate selected objects around an axis. - void RotateSelected( const MbAxis3D &, double angle, MbRegTransform * = NULL ); - - /** \brief \ru Отцепить все выбранные объекты. - \en Detach all selected objects. \~ - \details \ru Отцепить все выбранные объекты модели, в том числе и - содержащиеся в сложных составных объектах, таких как сборка. \n - \en Detach all selected objects of model including ones - contained in complex composite objects such as assembly. \n \~ - \param[out] items - \ru Отцепленные объекты. - \en Detach the objects. \~ - \param[out] matrs - \ru Матрицы преобразования отцепленных объектов в глобальную систему координат. - \en Transformation matrices of detached objects to global coordinate system. \~ - */ - void DetachSelected( RPArray & , SArray & , bool selected, bool resetName = true ); - /// \ru Отцепить все видимые или невидимые объекты. \en Detach all visible or invisible objects. \~ - void DetachInvisible( RPArray & , SArray & , bool invisible, bool resetName = true ); - - /// \ru Выдать количество объектов модели. \en Get the count of objects of model. - size_t ItemsCount() const { return modelItems.size(); } - - /// \ru Содержится ли объект в модели? \en Whether the object is contained in model. - bool ContainsItem( const MbItem * ) const; - /// \ru Добавить в модель объекты другой модели. \en Add objects of other model to the model. - bool AddModelItems( const MbModel & ); - /// \ru Добавить в массив выбранные объекты модели без поиска в сложных составных объектах. \en Add selected objects of model to array without search in complex composite objects. - size_t GetSelected( RPArray & ) const; - - /// \ru Вычислить габарит по всем объектам модели. \en Calculate bounding box for all the objects of model. - void CalculateGabarit( MbCube & ) const; - - private: - // Отдать все объекты с указанным свойством. - void DetachByAttribute( RPArray & items, SArray & matrs, int attribute, bool resetName ); - -public: - /// \ru Простой итератор по объектам модели. \en Simple iterator on objects of model. - class ItemIterator { - private: - NameItemArray::iterator currIter; - - private: - ItemIterator( const NameItemArray::iterator & iter ) : currIter( iter ) {} - - public: - MbItem * operator * () { return currIter->second; } - MbItem * operator -> () { return currIter->second; } - ItemIterator & operator ++ () { ++currIter; return *this; } - ItemIterator operator ++ ( int ) { return ItemIterator(currIter++); } - - bool operator == ( const ItemIterator & other) const { return currIter == other.currIter; } - bool operator != ( const ItemIterator & other) const { return currIter != other.currIter; } - - friend class MbModel; - }; // ItemIterator - - - /// \ru Константный итератор по объектам модели. \en Constant iterator on objects of the model. - class ItemConstIterator { - private: - NameItemArray::const_iterator currIter; - - private: - ItemConstIterator( const NameItemArray::const_iterator & iter ) : currIter( iter ) {} - -// public: -// ItemConstIterator( const ItemIterator& iIter ) : currIter( iIter.currIter ) {} - - public: - const MbItem * operator * () const { return currIter->second; } - const MbItem * operator -> () const { return currIter->second; } - ItemConstIterator & operator ++ () { ++currIter; return *this; } - ItemConstIterator operator ++ ( int ) { return ItemConstIterator(currIter++); } - - bool operator == ( const ItemConstIterator & other) const { return currIter == other.currIter; } - bool operator != ( const ItemConstIterator & other) const { return currIter != other.currIter; } - - friend class MbModel; - }; // ItemConstIterator - -public: - /// \ru Выдать константный итератор по всем объектам с указанием на начало. \en Get constant iterator on all objects pointing to the first element. - ItemConstIterator CBegin() const { return ItemConstIterator( modelItems.begin() ); } - /// \ru Выдать константный итератор по всем объектам с указанием за конец. \en Get constant iterator on all objects pointing to the past-the-end element. - ItemConstIterator CEnd() const { return ItemConstIterator( modelItems.end() ); } - /// \ru Выдать константный итератор для указанного имени с указанием на начало. \en Get constant iterator by the given name pointing to the first element. - ItemConstIterator CBegin( SimpleName n ) const { return ItemConstIterator( modelItems.lower_bound(n) ); } - /// \ru Выдать константный итератор для указанного имени с указанием на конец. \en Get constant iterator by the given name pointing to the past-the-end element. - ItemConstIterator CEnd( SimpleName n ) const { return ItemConstIterator( modelItems.upper_bound(n) ); } - /// \ru Выдать не константный итератор по всем объектам с указанием на начало. \en Get non-constant iterator on all the objects pointing to the first element. - ItemIterator Begin() { return ItemIterator( modelItems.begin() ); } - /// \ru Выдать не константный итератор по всем объектам с указанием за конец. \en Get non-constant iterator on all the objects pointing to the past-the-end element. - ItemIterator End() { return ItemIterator( modelItems.end() ); } - /// \ru Выдать не константный итератор для указанного имени с указанием на начало. \en Get non-constant iterator by the given name pointing to the first element. - ItemIterator Begin( SimpleName n ) { return ItemIterator( modelItems.lower_bound(n) ); } - /// \ru Выдать не константный итератор для указанного имени с указанием на конец. \en Get non-constant iterator by the given name pointing to the past-the-end element. - ItemIterator End( SimpleName n ) { return ItemIterator( modelItems.upper_bound(n) ); } - -private: - void CreateItemsMeshes( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * ) const; - - -private: // Устаревшие методы. // Deprecated methods (it will be deleted in future revisions) - /// \ru Установить первое простое имя существующему объекту модели. \en Set the first simple name for existing object of model. - bool SetItemMainName( MbItem *, SimpleName ); - // The function is deprecated. Use ItemIterator instead indexed access. - const MbItem * GetModelItem( size_t i ) const; - -private: // \ru Закрытые методы. // \en Internal use methods. - // \ru Выдать имя последнего объекта в контейнере. \en Get name of the last object in container. - SimpleName _LastItemName() const { return modelItems.empty() ? 0 : modelItems.rbegin()->first; } - // \ru Выдать имя для следующего за последним объекта. \en Get name of past-the-end object (next object to the last one). - SimpleName _NextItemName() const; - // \ru Генерация имени для нового элемента. \en Generate identifier for a new item. - SimpleName _NewItemName( SimpleName & startName ) const; - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbModel ); - OBVIOUS_PRIVATE_COPY( MbModel ); -}; // MbModel - -IMPL_PERSISTENT_OPS( MbModel ) - -//---------------------------------------------------------------------------------------- -// \ru Дать все объекты. \en Get all the objects. -// --- -template -void MbModel::GetItems( Items & items ) const -{ - items.reserve( modelItems.size() ); - for ( NameItemArray::const_iterator iter = modelItems.begin(); iter != modelItems.end(); ++iter ) { - if ( iter->second != NULL ) - items.push_back( iter->second ); - } -} - -//---------------------------------------------------------------------------------------- -// \ru Отцепить все объекты. \en Detach all the objects. -// --- -template -void MbModel::DetachItems( Items & items ) -{ - items.reserve( modelItems.size() ); - NameItemArray::iterator iter = modelItems.begin(); - NameItemArray::const_iterator endItem = modelItems.end(); - for ( ; iter != endItem; ++iter ) { - MbItem * item = iter->second; - if ( item != NULL ) { - item->DecRef(); - items.push_back( item ); - } - } - modelItems.clear(); - AttributesChange(); -} - - -//---------------------------------------------------------------------------------------- -/** \brief \ru Чтение модели MbModel из потока #reader. - \en Read MbModel model from #reader stream. \~ - \details \ru Чтение модели MbModel из потока #reader. \n - \en Read MbModel model from #reader stream. \n \~ - \ingroup Model -*/ -// --- -MATH_FUNC (bool) ReadModelItems( reader &, MbModel & ); - -//---------------------------------------------------------------------------------------- -/** \brief \ru Чтение из потока объектов, определенных в данном дереве модели. - \en Read items, defined in the given model tree, from a stream. - \details \ru Чтение из потока всех объектов, определенных в данном дереве модели, включая его корень (корни). - \en Read items, defined in the given model tree including its root(s), from a stream. - \param[in] in - \ru Поток для чтения. \en Stream to read from. \~ - \param[in] tree - \ru Дерево модели. \en Model tree. \~ - \param[out] items - \ru Прочитанные объекты. \en Read objects. \~ -*/ -// --- -MATH_FUNC (void) ReadModelItemsFromTree( reader & in, const c3d::IModelTree * tree, std::vector< SPtr > & items ); - -//---------------------------------------------------------------------------------------- -/** \brief \ru Чтение из потока объектов, определенных в поддереве с корнем в данном узле. - \en Read items, defined in a subtree with a root at the given node, from a stream. - \details \ru Чтение из потока объектов, определенных в поддереве с корнем в данном узле, исключая сам узел. - Если определено флагом (addAttr == true), то из объекта, определенного заданным узлом, читаются атрибуты и добавляются в модель. - \en Read items, defined in a subtree with a root at the given node, from a stream, excluding the node itself. - If defined by the flag (addAttr == true), attributes are read from an object defined by the given node and added to the model. - \param[in] in - \ru Поток для чтения. \en Stream to read from. \~ - \param[in] node - \ru Узел дерева модели. \en Node of Model tree. \~ - \param[out] model - \ru Модель, куда добавлять прочитанные объекты. \en Model where to add read objects. \~ - \param[in] addAttr - \ru Флаг чтения атрибутов. \en Attributes read flag. \~ -*/ -// --- -MATH_FUNC (void) ReadModelItemsFromTree( reader & in, const c3d::IModelTreeNode * node, MbModel & model, bool addAttr = false ); - -//---------------------------------------------------------------------------------------- -/** \brief \ru Запись модели MbModel в поток #writer. - \en Write MbModel model to #writer stream. \~ - \details \ru Запись модели MbModel в поток #writer. \n - \en Write MbModel model to #writer stream. \n \~ - \ingroup Model -*/ -// --- -MATH_FUNC (void) WriteModelItems( writer &, const MbModel & ); - - -#endif // __MODEL_H +////////////////////////////////////////////////////////////////////////////////////////// +/** \file + \brief \ru Геометрическая модель. + \en Geometric model. \~ +*/ +////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef __MODEL_H +#define __MODEL_H + + +#include +#include + +struct ItModelVisitor; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Геометрическая модель. + \en Geometric model. \~ + \details \ru Геометрическая модель - контейнер геометрических объектов. \n + Модель состоит из массивов указателей на объекты геометрической модели MbItem. + Модель может содержать вспомогательные объекты MbAssistingItem, + точки MbPointFrame, каркасы MbWireFrame, + твердые тела MbSolid, полигональные объекты MbMesh, + объекты MbSpaceInstance и MbPlaneInstance.\n + Модель используется для описания геометрических свойств реальных и + воображаемых объектов, визуализации моделируемых объектов, + вычисления геометрических характеристик моделируемых объектов.\n + Имя объекта геометрической модели представляет собой контейнер простых имён. + В начале контейнера содержится простое имя SimpleName, + которое совпадает с первым полем std::multimap геометрической модели. \n + Если объект не держит в себе других объектов, то контейнер содержит одно простое имя SimpleName. + Ели объект держит в себе другие объекты (MbAssembly или MbInstance), + то имя внутренних объектов представляет собой контейнер, содержащий как минимум два простых имени. + Количество элементов имени объекта отражают количество уровней вложенности объект относительно модели. + \en Geometric model is a container of geometric objects. \n + The model consists of arrays of pointers to geometric model objects MbItem. + The model can contain MbAssistingItem assisting items, + MbPointFrame points, MbWireFrame frames, + MbSolid solids, polygonal objects MbMesh, + MbSpaceInstance and MbPlaneInstance objects.\n + Model is used to describe geometric properties of real and + imaginary objects, to visualize modeled objects, + to calculate geometric properties of modeled objects.\n + The name of an object of a geometric model is represented as a container of simple names. + In the beginning of the container there is a SimpleName simple name + which coincides with the first field std::multimap of the geometric model. \n + If the object doesn't contain other objects, then the container contains one SimpleName simple name. + If the object contains other objects (MbAssembly or MbInstance), + then the internal objects name is represented as a container with at least two simple names. + Number of the elements of an object's name corresponds to the number of levels of objects inclusion relative to the model. \~ + \ingroup Model +*/ +//--- +class MATH_CLASS MbModel : public TapeBase, + public MbRefItem, + public MbTransactions, + public MbAttributeContainer +{ +public: + typedef std::map NameItemArray; + +private: + NameItemArray modelItems; ///< \ru Множество объектов модели. \en Set of the model objects. + SimpleName name; ///< \ru Имя объекта. \en A name of an object. + +protected: + /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. + explicit MbModel( const MbModel &, MbRegDuplicate * ); + +public: + /// \ru Конструктор по имени объекта. \en Constructor by object's name. + MbModel( SimpleName n = 0 ); + /// \ru Деструктор \en Destructor + virtual ~MbModel(); + +public : + + /** \ru \name Общие функции геометрического объекта + \en \name Common functions of a geometric object + \{ */ + /// \ru Тип контейнера атрибутов - классификатор наследников. \en Type of an attribute container is a classifier of inheritors. + virtual MbeImplicationType ImplicationType() const; + + /// \ru Создать копию. \en Create a copy. + MbModel & Duplicate( MbRegDuplicate * = NULL ) const; + /// \ru Преобразовать согласно матрице. \en Transform according to the matrix. + void Transform( const MbMatrix3D &, MbRegTransform * iReg = NULL ); + /// \ru Сдвинуть вдоль вектора. \en Translate along a vector. + void Move ( const MbVector3D &, MbRegTransform * iReg = NULL ); + /// \ru Повернуть вокруг оси. \en Rotate about an axis. + void Rotate ( const MbAxis3D &, double angle, MbRegTransform * iReg = NULL ); + /// \ru Вычислить расстояние до точки. \en Calculate the distance to a point. + double DistanceToPoint ( const MbCartPoint3D & ) const; + /// \ru Добавь свой габарит в габаритный куб. \en Include your own bounding box into bounding box. + void AddYourGabaritTo( MbCube & ) const; + + /// \ru Создать собственное свойство с заданием его имени. \en Create your own property with specified name. + MbProperty & CreateProperty( MbePrompt ) const; + /// \ru Выдать свойства объекта. \en Get properties of the object. + void GetProperties( MbProperties & ); + /// \ru Установить свойства объекта. \en Set properties of the object. + void SetProperties( const MbProperties & ); + /** \} */ + + /// \ru Выдать имя модели. \en Get name of a model. + SimpleName GetModelName() const { return name; } + /// \ru Установить имя модели. \en Set name of a model. + void SetModelName( SimpleName n ) { name = n; } + + /** \brief \ru Добавить объект в модель. + \en Add object to the model. \~ + \details \ru Добавить объект в модель с указанным именем. + \en Add object to the model with a given name. \~ + \param[in] item - \ru Объект модели. + \en A model object. \~ + \param[in] n - \ru Имя объекта. Если указанное имя равно нулю, то модель именует объект своим уникальным именем. + \en A name of an object. If a given name is equal to zero, then the model names an object with its unique name. \~ + \return \ru Добавленный объект. + \en Added object. \~ + */ + MbItem * AddItem( MbItem & item, SimpleName n = c3d::UNDEFINED_SNAME ); + /// \ru Добавить объекты модели item в модель. \en Add item objects of to the model. \~ + bool AddModel( const MbModel & ); + + // \ru Выдать объект модели по индексу. \en Get item of model by index. + const MbItem * GetItem( size_t ind ) const; + // \ru Выдать непосредственный объект модели по идентификатору. \en Get the immediate item of model by identifier. + const MbItem * SubItem( SimpleName n ) const; + + /** \brief \ru Заменить объект. + \en Replace the object. \~ + \details \ru Заменить объект новым. + \en Replace the object by a new one. \~ + \param[in] item - \ru Заменяемый объект. + \en An object to replace. \~ + \param[in] newItem - \ru Новый объект. + \en A new object. \~ + \return \ru Возвращает true, если замена была выполнена. + \en Returns true if the replacement has been done. \~ + */ + bool ReplaceItem( const MbItem & item, MbItem & newItem, bool saveName = false ); + + /// \ru Дать все объекты. \en Get all the objects. + template + void GetItems( Items & ) const; + /// \ru Отцепить объект, если он есть в модели. \en Detach an object if it is in the model. + bool DetachItem ( MbItem *, bool resetName = true ); + /// \ru Отцепить все объекты. \en Detach all the objects. + template + void DetachItems( Items & ); + + /// \ru Удалить объект, если он есть в модели. \en Delete an object if it is in the model. + bool DeleteItem ( MbItem *, bool resetName = true ); + /// \ru Удалить все объекты модели. \en Delete all the model objects. + void DeleteItems(); + + /// \ru Разрушить сборки с подсборками на составляющие. \en Decompose assemblies with subassemblies into components. + bool DecomposeAssemblies(); + + /** \brief \ru Наполнить присланную модель полигональными копиями объектов модели. + \en Fill the given model with polygonal copies of the model objects. \~ + \details \ru Наполнить присланную модель полигональными копиями объектов оригинальной модели. + Присланная модель опустошается и наполняется полигональными копиями объектов оригинальной модели. + Присланная модель заполняется аналогично оригинальной модели с той разницей, что вместо + тел, проволочных каркасов, точечных каркасов и других конечных объектов модели + присланную модель заполняют соответствующие полигональные копии объектов (MbMesh). + Сборки и вставки в присланной модели сохраняются аналогичными оригинальной модели. + Присланная модель может использоваться для визуализации модели и расчетов. + \en Fill the given model with polygonal copies of the original model objects. + Given model is being cleared and filled with polygonal copies of objects of the original model. + Given model is to be filled similarly to original model, but instead of + solids, wire-frames, point-frames and other finite objects of model + the given model is filled by the corresponding polygonal copies of objects (MbMesh). + Assemblies and instances in the given model remains similar to the original model. + Given model can be used for calculations and visualization of the model. \~ + \note \ru В многопоточном режиме выполняется параллельно. \en In multithreaded mode runs in parallel. \~ + + \param[in] stepData - \ru Данные для вычисления шага при триангуляции. + \en Data for step calculation during triangulation. \~ + \param[in] note - \ru Способ построения полигонального объекта. + \en Way for polygonal object constructing. \~ + \param[out] meshModel - \ru Присланная модель для наполнения. + \en Given model to be filled. \~ + \return \ru Не было ошибок во время построения - true, были ошибки - false. + \en If there were no errors during construction, then true, otherwise false. \~ + */ + bool FillMeshModel( const MbStepData & stepData, const MbFormNote & note, MbModel & meshModel ) const; + + /** \brief \ru Добавить полигональный объект. + \en Add polygonal object. \~ + \details \ru Добавить полигональную копию модели в присланный полигональный объект (MbMesh). + Все объекты модели, её сборки и вставки помещаются в единый плоскогранный полигональный объект. + Один и тот же объект, вставленный несколько раз в сборки и вставки модели, получает несколько копий, + так как каждая копия трансформируется по матрице локальной системы координат cjjndtncnde.otq сборки и вставки. + \en Add polygonal copy of the model to the given polygonal object (MbMesh). + All the objects, assemblies and instances of the model are placed in a unified planar polygonal object. + The same object inserted several times in assemblies and instances of the model gets several copies + because each copy is transformed by the matrix of local coordinate system of the corresponding assembly and instance. \~ + \param[in] stepData - \ru Данные для вычисления шага при триангуляции. + \en Data for step calculation during triangulation. \~ + \param[in] note - \ru Способ построения полигонального объекта. + \en Way for polygonal object constructing. \~ + \param[out] mesh - \ru Присланный полигональный объект. + \en Given polygonal object. \~ + \return \ru Добавлен ли объект. + \en Whether the object is added. \~ + */ + bool AddYourMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; + + /** \brief \ru Разрезать модель полигональных объектов одной или двумя параллельными плоскостями. + \en Cut model of polygonal objects by one or two parallel planes. \~ + \details \ru Создать новую модель полигональных объектов и наполнить её частями исходной модели, + лежащими под плоскостью XY локальной системы координат на заданном расстоянии.\n + Функция "режет" только модель полигональных объектов MbMesh. + Функция "режет" модель двумя плоскостями: + плоскостью XY локальной системы координат place и плоскостью, параллельной ей и + расположенной на расстоянии distance ниже неё. + Если distance<=0, то функция "режет" объект только одной плоскостью XY локальной системы.\n + Содержимое исходных полигональных объектов, + необходимое для построения разрезанного объекта и не затронутое режущими плоскостями, + добавляется в возвращаемый разрезанный объект без копирования.\n + \en Create new model of polygonal objects and fill it by the source model parts + lying under XY plane of the local coordinate system at the given distance.\n + Function "cuts" only MbMesh model of polygonal objects. + Function "cuts" the model by two planes: + XY plane of 'place' local coordinate system and plane parallel to it and + located at 'distance' distance below it. + If 'distance' is less than or equal to zero, then the function "cuts" an object only by one XY plane of local coordinate system.\n + Contents of the source polygonal objects + that are necessary for creation of cut object and not affected by cutting planes + are added to returned cut object without copying.\n \~ + \param[in] place - \ru Локальная система координат, плоскость XY которой задаёт режущую плоскость. + \en A local coordinate system which XY plane defines a cutting plane. \~ + \param[in] distance - \ru Расстояние до параллельной режущей плоскости откладывается в отрицательную сторону оси Z локальной системы. + \en Distance to a parallel cutting plane is measured in negative direction of Z-axis of local coordinate system. \~ + \result \ru Возвращает новую модель полигональных объектов, лежащую под плоскость XY локальной системы координат на заданном расстоянии. + \en Returns a new model of polygonal objects that lies under XY plane of local coordinate system at given distance. \~ + */ + MbModel * CutMeshModel( const MbPlacement3D & cutPlace, double distance ) const; + + /** \brief \ru Найти ближайший объект или имя ближайшего объекта. + \en Find the nearest object or name of the nearest object. \~ + \details \ru Найти ближайший трехмерный объект или его имя по типу объекта и + составляющий элемент искомого объекта или его имя по топологическому или двумерному типу элемента (по требованию) + на расстоянии от прямой, не превышающем заданной величины. + Функция предназначена для идентификации геометрического объекта, породившего полигональный объект. + Реальный поиск выполняется для элементов MbPrimitive полигонального объекта MbMesh, + у которых берётся информация о породившем примитив геометрическом объекте. + \en Find the nearest three-dimensional object or its name by type of object and + component of the required object or its name by topological or two-dimensional type of the element (on demand) + at distance from line less than or equal to the given value. + Function is intended for identification of a geometric object which is begetter of a polygonal object. + The real search is performed for MbMesh polygonal object's MbPrimitive elements + from which the information is taken about geometric object which is begetter of the primitive. \~ + \note \ru В многопоточном режиме выполняется параллельно. \en In multithreaded mode runs in parallel. \~ + \param[in] sType - \ru Тип искомого объекта. + \en Type of required object. \~ + \param[in] tType - \ru Топологический тип составляющего элемента искомого объекта. + \en Topological type of the required object's component. \~ + \param[in] pType - \ru Двумерный тип составляющего элемента искомого объекта. + \en Two-dimensional type of the required object's component. \~ + \param[in] axis - \ru Прямая поиска. + \en Line of search. \~ + \param[in] maxDistance - \ru Расстояние от прямой, на котором ищется объект. + \en Distance from the line on which the object is looked for. \~ + \param[in] gridPriority - \ru Повышенный приоритет триангуляционной сетки при поиске. + \en Increased priority triangulation grid when searching. \~ + \param[out] find - \ru Найденный объект. + \en Found object. \~ + \param[out] findName - \ru Имя найденного объекта. + \en Name of the found object. \~ + \param[out] element - \ru Найденный составляющий элемент объекта. + \en Found component of the object. \~ + \param[out] elementName - \ru Имя найденного составляющего элемента объекта. + \en Name of found component of the object. \~ + \param[out] path - \ru Путь к объекту в модели. + \en Path to the object in model. \~ + \param[out] from - \ru Матрица преобразования найденного объекта в глобальную систему координат. + \en Transformation matrix of the found object to the global coordinate system. \~ + \return \ru Найден ли объект или его имя. + \en Whether the object or its name is found. \~ + */ + bool NearestMesh( MbeSpaceType sType, MbeTopologyType tType, MbePlaneType pType, + const MbAxis3D & axis, double maxDistance, bool gridPriority, + MbItem *& find, SimpleName & findName, + MbRefItem *& element, SimpleName & elementName, + MbPath & path, MbMatrix3D & from ) const; + + /** \brief \ru Дать все объекты указанного типа. + \en Get all objects by type. \~ + \details \ru Дать все объекты указанного типа, + а также матрицы преобразования их в глобальную систему координат. \n + \en Get all objects by type + and get transformation matrix to the global coordinate system. \n \~ + \param[in] type - \ru Тип объекта. + \en Object's type. \~ + \param[out] items - \ru Множество найденных объектов. + \en Found objects. \~ + \param[out] matrs - \ru Матрицы преобразования найденных объектов в глобальную систему координат. + \en Transformation matrix of found objects to the global coordinate system. \~ + \ingroup Model_Items + */ + virtual void GetItems( MbeSpaceType type, RPArray & items, SArray & matrs ); + + /** \brief \ru Дать все объекты указанного типа. + \en Get all objects by type. \~ + \details \ru Дать все объекты указанного типа, + а также матрицы преобразования их в глобальную систему координат. \n + \en Get all objects by type + and get transformation matrix to the global coordinate system. \n \~ + \param[in] type - \ru Тип объекта. + \en Object's type. \~ + \param[out] items - \ru Множество найденных объектов. + \en Found objects. \~ + \param[out] matrs - \ru Матрицы преобразования найденных объектов в глобальную систему координат. + \en Transformation matrix of found objects to the global coordinate system. \~ + \ingroup Model_Items + */ + virtual void GetItems( MbeSpaceType type, RPArray & items, SArray & matrs ) const; + + /** \brief \ru Дать все уникальные объекты указанного типа. + \en Get all unique objects by type. \~ + \details \ru Дать все уникальные объекты указанного типа. \n + \en Get all unique objects by type. \n \~ + \param[in] type - \ru Тип объекта. + \en Object's type. \~ + \param[out] items - \ru Множество найденных объектов. + \en Found objects. \~ + \ingroup Model_Items + */ + virtual void GetUniqItems( MbeSpaceType type, CSSArray & items ) const; + + /** \brief \ru Построить путь положения объекта. + \en Create path of object's position. \~ + \details \ru Построить путь положения объекта в модели и + дать матрицу преобразования объекта в глобальную систему координат. + Объект может содержаться в другом объекте (в сборке или вставке). + \en Create path of object's position in the model and + get transformation matrix of the object to the global coordinate system. + Object can be contained in other object (in assembly or in instance). \~ + \param[in] obj - \ru Объект. + \en Object. \~ + \param[out] path - \ru Путь к объекту в модели. + \en Path to the object in model. \~ + \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. + \en Transformation matrix of object to the global coordinate system. \~ + \return \ru Найден ли путь и матрица объекта. + \en Whether the path and the matrix of object are found. \~ + */ + bool MakePath( const MbItem & obj, MbPath & path, MbMatrix3D & from ) const; + + /** \brief \ru Дать объект по его пути. + \en Get the object by its path. \~ + \details \ru Дать объект по его пути положения в модели и + дать матрицу преобразования объекта в глобальную систему координат. + Объект может содержаться в другом объекте (в сборке или вставке). + \en Get the object by path of its position in the model and + get transformation matrix of the object to the global coordinate system. + Object can be contained in other object (in assembly or in instance). \~ + \param[in] path - \ru Путь к объекту в модели. + \en Path to object in the model. \~ + \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. + \en Transformation matrix of object to the global coordinate system. \~ + \return \ru Найден ли путь и матрица объекта. + \en Whether the path and the matrix of object are found. \~ + */ + const MbItem * GetItemByPath( const MbPath & path, MbMatrix3D & from ) const; + + /** \brief \ru Найти объект по геометрическому объекту. + \en Find object by geometric object. \~ + \details \ru Найти объект по геометрическому объекту, а также получить путь к + объекту в модели и матрицу преобразования в глобальную систему координат. + \en Find object by geometric object and also get the path to the + object in model and get transformation matrix to the global coordinate system. \~ + \param[in] s - \ru Геометрический объект. + \en Geometric object. \~ + \param[out] path - \ru Путь к объекту в модели. + \en Path to object in the model. \~ + \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. + \en Transformation matrix of object to the global coordinate system. \~ + \return \ru Найден ли путь и матрица объекта. + \en Whether the path and the matrix of object are found. \~ + */ + const MbItem * FindItem( const MbSpaceItem * s, MbPath & path, MbMatrix3D & from ) const; + + /** \brief \ru Найти объект по геометрическому объекту. + \en Find object by geometric object. \~ + \details \ru Найти объект по геометрическому объекту, + а также получить путь к объекту в модели + и матрицу преобразования в глобальную систему координат. \n + \en Find object by geometric object + and also get the path to the object in model + and get transformation matrix to the global coordinate system. \n \~ + \param[in] s - \ru Геометрический объект. + \en Geometric object. \~ + \param[out] path - \ru Путь к объекту в модели. + \en Path to object in the model. \~ + \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. + \en Transformation matrix of object to the global coordinate system. \~ + \return \ru Найден ли путь и матрица объекта. + \en Whether the path and the matrix of object are found. \~ + */ + const MbItem * FindItem( const MbPlaneItem * s, MbPath & path, MbMatrix3D & from ) const; + + /** \brief \ru Найти объект по объекту геометрической модели. + \en Find object by object of geometric model \~ + \details \ru Найти объект по объекту геометрической модели. + а также получить путь к объекту в модели + и матрицу преобразования в глобальную систему координат. \n + \en Find object by object of geometric model + and also get the path to the object in model + and get transformation matrix to the global coordinate system. \n \~ + \param[in] s - \ru Геометрический объект. + \en Geometric object. \~ + \param[out] path - \ru Путь к объекту в модели. + \en Path to object in the model. \~ + \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. + \en Transformation matrix of object to the global coordinate system. \~ + \return \ru Найден ли путь и матрица объекта. + \en Whether the path and the matrix of object are found. \~ + */ + const MbItem * FindItem( const MbItem * s, MbPath & path, MbMatrix3D & from ) const; + + /** \brief \ru Найти объект по имени. + \en Find object by name. \~ + \details \ru Найти объект по имени, а также получить путь к объекту в модели + и матрицу преобразования в глобальную систему координат. \n + \en Find object by name and also get path to object in model + and get transformation matrix to the global coordinate system. \n \~ + \param[in] n - \ru Имя объекта. + \en A name of an object. \~ + \param[out] path - \ru Путь к объекту в модели. + \en Path to object in the model. \~ + \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. + \en Transformation matrix of object to the global coordinate system. \~ + \return \ru Найден ли путь и матрица объекта. + \en Whether the path and the matrix of object are found. \~ + */ + const MbItem * GetItemByName( SimpleName n, MbPath & path, MbMatrix3D & from ) const; + + /** \brief \ru Найти объект по имени для редактирования. + \en Find object by name for editing. \~ + \details \ru Найти объект по имени для редактирования, а также получить путь к объекту в модели + и матрицу преобразования в глобальную систему координат. \n + \en Find object by name for editing and also get path to object in model + and get transformation matrix to the global coordinate system. \n \~ + \param[in] n - \ru Имя объекта. + \en A name of an object. \~ + \param[out] path - \ru Путь к объекту в модели. + \en Path to object in the model. \~ + \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. + \en Transformation matrix of object to the global coordinate system. \~ + \return \ru Найден ли путь и матрица объекта. + \en Whether the path and the matrix of object are found. \~ + */ + MbItem * SetItemByName( SimpleName n, MbPath & path, MbMatrix3D & from ); + /** \brief \ru Алгоритм общего назначения для обхода дерева модели в глубину. + \en General-purpose algorithm traversing the model graph in depth. */ + void Traverse( ItModelVisitor & ) const; + /// \ru Преобразовать селектирование объекты по матрице. \en Transform selected objects by matrix. + void TransformSelected( const MbMatrix3D &, MbRegTransform * = NULL ); + /// \ru Сдвинуть выбранные объекты. \en Move selected objects. + void MoveSelected( const MbVector3D &, MbRegTransform * = NULL ); + /// \ru Повернуть выбранные объекты вокруг оси. \en Rotate selected objects around an axis. + void RotateSelected( const MbAxis3D &, double angle, MbRegTransform * = NULL ); + + /** \brief \ru Отцепить все выбранные объекты. + \en Detach all selected objects. \~ + \details \ru Отцепить все выбранные объекты модели, в том числе и + содержащиеся в сложных составных объектах, таких как сборка. \n + \en Detach all selected objects of model including ones + contained in complex composite objects such as assembly. \n \~ + \param[out] items - \ru Отцепленные объекты. + \en Detach the objects. \~ + \param[out] matrs - \ru Матрицы преобразования отцепленных объектов в глобальную систему координат. + \en Transformation matrices of detached objects to global coordinate system. \~ + */ + void DetachSelected( RPArray & , SArray & , bool selected, bool resetName = true ); + /// \ru Отцепить все видимые или невидимые объекты. \en Detach all visible or invisible objects. \~ + void DetachInvisible( RPArray & , SArray & , bool invisible, bool resetName = true ); + + /// \ru Выдать количество объектов модели. \en Get the count of objects of model. + size_t ItemsCount() const { return modelItems.size(); } + + /// \ru Содержится ли объект в модели? \en Whether the object is contained in model. + bool ContainsItem( const MbItem * ) const; + /// \ru Добавить в модель объекты другой модели. \en Add objects of other model to the model. + bool AddModelItems( const MbModel & ); + /// \ru Добавить в массив выбранные объекты модели без поиска в сложных составных объектах. \en Add selected objects of model to array without search in complex composite objects. + size_t GetSelected( RPArray & ) const; + + /// \ru Вычислить габарит по всем объектам модели. \en Calculate bounding box for all the objects of model. + void CalculateGabarit( MbCube & ) const; + + private: + // Отдать все объекты с указанным свойством. + void DetachByAttribute( RPArray & items, SArray & matrs, int attribute, bool resetName ); + +public: + /// \ru Простой итератор по объектам модели. \en Simple iterator on objects of model. + class ItemIterator { + private: + NameItemArray::iterator currIter; + + private: + ItemIterator( const NameItemArray::iterator & iter ) : currIter( iter ) {} + + public: + MbItem * operator * () { return currIter->second; } + MbItem * operator -> () { return currIter->second; } + ItemIterator & operator ++ () { ++currIter; return *this; } + ItemIterator operator ++ ( int ) { return ItemIterator(currIter++); } + + bool operator == ( const ItemIterator & other) const { return currIter == other.currIter; } + bool operator != ( const ItemIterator & other) const { return currIter != other.currIter; } + + friend class MbModel; + }; // ItemIterator + + + /// \ru Константный итератор по объектам модели. \en Constant iterator on objects of the model. + class ItemConstIterator { + private: + NameItemArray::const_iterator currIter; + + private: + ItemConstIterator( const NameItemArray::const_iterator & iter ) : currIter( iter ) {} + +// public: +// ItemConstIterator( const ItemIterator& iIter ) : currIter( iIter.currIter ) {} + + public: + const MbItem * operator * () const { return currIter->second; } + const MbItem * operator -> () const { return currIter->second; } + ItemConstIterator & operator ++ () { ++currIter; return *this; } + ItemConstIterator operator ++ ( int ) { return ItemConstIterator(currIter++); } + + bool operator == ( const ItemConstIterator & other) const { return currIter == other.currIter; } + bool operator != ( const ItemConstIterator & other) const { return currIter != other.currIter; } + + friend class MbModel; + }; // ItemConstIterator + +public: + /// \ru Выдать константный итератор по всем объектам с указанием на начало. \en Get constant iterator on all objects pointing to the first element. + ItemConstIterator CBegin() const { return ItemConstIterator( modelItems.begin() ); } + /// \ru Выдать константный итератор по всем объектам с указанием за конец. \en Get constant iterator on all objects pointing to the past-the-end element. + ItemConstIterator CEnd() const { return ItemConstIterator( modelItems.end() ); } + /// \ru Выдать константный итератор для указанного имени с указанием на начало. \en Get constant iterator by the given name pointing to the first element. + ItemConstIterator CBegin( SimpleName n ) const { return ItemConstIterator( modelItems.lower_bound(n) ); } + /// \ru Выдать константный итератор для указанного имени с указанием на конец. \en Get constant iterator by the given name pointing to the past-the-end element. + ItemConstIterator CEnd( SimpleName n ) const { return ItemConstIterator( modelItems.upper_bound(n) ); } + /// \ru Выдать не константный итератор по всем объектам с указанием на начало. \en Get non-constant iterator on all the objects pointing to the first element. + ItemIterator Begin() { return ItemIterator( modelItems.begin() ); } + /// \ru Выдать не константный итератор по всем объектам с указанием за конец. \en Get non-constant iterator on all the objects pointing to the past-the-end element. + ItemIterator End() { return ItemIterator( modelItems.end() ); } + /// \ru Выдать не константный итератор для указанного имени с указанием на начало. \en Get non-constant iterator by the given name pointing to the first element. + ItemIterator Begin( SimpleName n ) { return ItemIterator( modelItems.lower_bound(n) ); } + /// \ru Выдать не константный итератор для указанного имени с указанием на конец. \en Get non-constant iterator by the given name pointing to the past-the-end element. + ItemIterator End( SimpleName n ) { return ItemIterator( modelItems.upper_bound(n) ); } + +private: + void CreateItemsMeshes( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * ) const; + + +private: // Устаревшие методы. // Deprecated methods (it will be deleted in future revisions) + /// \ru Установить первое простое имя существующему объекту модели. \en Set the first simple name for existing object of model. + bool SetItemMainName( MbItem *, SimpleName ); + // The function is deprecated. Use ItemIterator instead indexed access. + const MbItem * GetModelItem( size_t i ) const; + +private: // \ru Закрытые методы. // \en Internal use methods. + // \ru Выдать имя последнего объекта в контейнере. \en Get name of the last object in container. + SimpleName _LastItemName() const { return modelItems.empty() ? 0 : modelItems.rbegin()->first; } + // \ru Выдать имя для следующего за последним объекта. \en Get name of past-the-end object (next object to the last one). + SimpleName _NextItemName() const; + // \ru Генерация имени для нового элемента. \en Generate identifier for a new item. + SimpleName _NewItemName( SimpleName & startName ) const; + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbModel ) +OBVIOUS_PRIVATE_COPY( MbModel ) +}; // MbModel + +IMPL_PERSISTENT_OPS( MbModel ) + +//---------------------------------------------------------------------------------------- +// \ru Дать все объекты. \en Get all the objects. +// --- +template +void MbModel::GetItems( Items & items ) const +{ + items.reserve( modelItems.size() ); + for ( NameItemArray::const_iterator iter = modelItems.begin(); iter != modelItems.end(); ++iter ) { + if ( iter->second != NULL ) + items.push_back( iter->second ); + } +} + +//---------------------------------------------------------------------------------------- +// \ru Отцепить все объекты. \en Detach all the objects. +// --- +template +void MbModel::DetachItems( Items & items ) +{ + items.reserve( modelItems.size() ); + NameItemArray::iterator iter = modelItems.begin(); + NameItemArray::const_iterator endItem = modelItems.end(); + for ( ; iter != endItem; ++iter ) { + MbItem * item = iter->second; + if ( item != NULL ) { + item->DecRef(); + items.push_back( item ); + } + } + modelItems.clear(); + AttributesChange(); +} + + +//---------------------------------------------------------------------------------------- +/** \brief \ru Чтение модели MbModel из потока #reader. + \en Read MbModel model from #reader stream. \~ + \details \ru Чтение модели MbModel из потока #reader. \n + \en Read MbModel model from #reader stream. \n \~ + \ingroup Model +*/ +// --- +MATH_FUNC (bool) ReadModelItems( reader &, MbModel & ); + +//---------------------------------------------------------------------------------------- +/** \brief \ru Чтение из потока объектов, определенных в данном дереве модели. + \en Read items, defined in the given model tree, from a stream. + \details \ru Чтение из потока всех объектов, определенных в данном дереве модели, включая его корень (корни). + \en Read items, defined in the given model tree including its root(s), from a stream. + \param[in] in - \ru Поток для чтения. \en Stream to read from. \~ + \param[in] tree - \ru Дерево модели. \en Model tree. \~ + \param[out] items - \ru Прочитанные объекты. \en Read objects. \~ +*/ +// --- +MATH_FUNC (void) ReadModelItemsFromTree( reader & in, const c3d::IModelTree * tree, std::vector< SPtr > & items ); + +//---------------------------------------------------------------------------------------- +/** \brief \ru Чтение из потока объектов, определенных в поддереве с корнем в данном узле. + \en Read items, defined in a subtree with a root at the given node, from a stream. + \details \ru Чтение из потока объектов, определенных в поддереве с корнем в данном узле, исключая сам узел. + Если определено флагом (addAttr == true), то из объекта, определенного заданным узлом, читаются атрибуты и добавляются в модель. + \en Read items, defined in a subtree with a root at the given node, from a stream, excluding the node itself. + If defined by the flag (addAttr == true), attributes are read from an object defined by the given node and added to the model. + \param[in] in - \ru Поток для чтения. \en Stream to read from. \~ + \param[in] node - \ru Узел дерева модели. \en Node of Model tree. \~ + \param[out] model - \ru Модель, куда добавлять прочитанные объекты. \en Model where to add read objects. \~ + \param[in] addAttr - \ru Флаг чтения атрибутов. \en Attributes read flag. \~ +*/ +// --- +MATH_FUNC (void) ReadModelItemsFromTree( reader & in, const c3d::IModelTreeNode * node, MbModel & model, bool addAttr = false ); + +//---------------------------------------------------------------------------------------- +/** \brief \ru Запись модели MbModel в поток #writer. + \en Write MbModel model to #writer stream. \~ + \details \ru Запись модели MbModel в поток #writer. \n + \en Write MbModel model to #writer stream. \n \~ + \ingroup Model +*/ +// --- +MATH_FUNC (void) WriteModelItems( writer &, const MbModel & ); + + +#endif // __MODEL_H diff --git a/C3d/Include/model_item.h b/C3d/Include/model_item.h index 908bfae..5306ff7 100644 --- a/C3d/Include/model_item.h +++ b/C3d/Include/model_item.h @@ -1,545 +1,545 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Объект геометрической модели. - \en A model geometric object. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __MODEL_ITEM_H -#define __MODEL_ITEM_H - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbItem; -namespace c3d // namespace C3D -{ -typedef SPtr ItemSPtr; -typedef SPtr ConstItemSPtr; - -typedef std::vector ItemsVector; -typedef std::vector ConstItemsVector; - -typedef std::vector ItemsSPtrVector; -typedef std::vector ConstItemsSPtrVector; - -typedef std::set ItemsSet; -typedef ItemsSet::iterator ItemsSetIt; -typedef ItemsSet::const_iterator ItemsSetConstIt; -typedef std::pair ItemsSetRet; - -typedef std::set ConstItemsSet; -typedef ConstItemsSet::iterator ConstItemsSetIt; -typedef ConstItemsSet::const_iterator ConstItemsSetConstIt; -typedef std::pair ConstItemsSetRet; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Объект геометрической модели. - \en A model geometric object. \~ - \details \ru Родительский класс объектов геометрической модели. \n - Наследниками являются: \n - локальная система координат MbAssistingItem,\n - точечный каркас MbPointFrame,\n - проволочный каркас MbWireFrame,\n - твёрдое тело MbSolid,\n - полигональный объект MbMesh,\n - вставка объекта в локальной системе координат MbInstance,\n - сборка объектов в локальной системе координат MbAssembly,\n - вставка трехмерного объекта MbSpaceInstance,\n - вставка двумерного объекта MbPlaneInstance в плоскости XY локальной системы координат.\n - Объект содержит последовательность и способы своего построения MbTransactions.\n - Объект содержит не геометрические свойства в виде контейнера атрибутов MbAttributeContainer.\n - Имя объекта геометрической модели представляет собой контейнер простых имён. - В начале контейнера содержится простое имя SimpleName, присвоенное объекту геометрической моделью MbModel. \n - Если объект не держит в себе других объектов, то контейнер содержит одно простое имя SimpleName. - Ели объект держит в себе другие объекты (MbAssembly или MbInstance), - то имя внутренних объектов представляет собой контейнер, содержащий как минимум два простых имени. - Количество элементов имени объекта отражают количество уровней вложенности объект относительно модели. - \en Parent class of model geometric objects. \n - Inheritors are: \n - local coordinate system of MbAssistingItem,\n - MbPointFrame point-frame,\n - MbWireFrame wireframe,\n - MbSolid solid,\n - MbMesh polygonal planar object,\n - MbInstance instance of object in the local coordinate system,\n - MbAssembly assembly of objects in the local coordinate system,\n - MbSpaceInstance instance of three-dimensional object,\n - MbPlaneInstance instance of a two-dimensional object in the XY-plane of a local coordinate system.\n - Object contains MbTransactions sequence and ways to construct itself.\n - Object contains non-geometric properties as MbAttributeContainer attribute container.\n - The name of an object of a geometric model is represented as a container of simple names. - In the beginning of the container there is a SimpleName simple name assigned to object by MbModel geometric model. \n - If the object doesn't contain other objects, then the container contains one SimpleName simple name. - If the object contains other objects (MbAssembly or MbInstance), - then the internal objects name is represented as a container with at least two simple names. - Number of the elements of an object's name corresponds to the number of levels of objects inclusion relative to the model. \~ - \ingroup Model_Items -*/ -// --- -class MATH_CLASS MbItem : public MbSpaceItem, - public MbTransactions, - public MbAttributeContainer, - public MbSyncItem { - -private: - SimpleName name; ///< \ru Имя объекта геометрической модели. \en Name of a geometric model object. - -protected: - /// \ru Конструктор копирования с регистратором дублирования. \en Copy-constructor with duplication registrator. - explicit MbItem( const MbItem &, MbRegDuplicate * ); -public: - /// \ru Конструктор. \en Constructor. - MbItem(); - /// \ru Деструктор. \en Destructor. - virtual ~MbItem(); - -public : - VISITING_CLASS( MbItem ); - - /** \ru \name Общие функции геометрического объекта. - \en \name Common functions of a geometric object. - \{ */ - virtual MbeSpaceType IsA() const = 0; // \ru Тип объекта. \en A type of an object. - virtual MbeSpaceType Type() const; // \ru Групповой тип объекта. \en Group type of object. - virtual MbeSpaceType Family() const; // \ru Семейство объекта. \en Family of object. - virtual MbeImplicationType ImplicationType() const; // \ru Тип контейнера атрибутов - классификатор наследников. \en Type of an attribute container is a classifier of inheritors. - virtual MbSpaceItem & Duplicate ( MbRegDuplicate * = NULL ) const = 0; // \ru Создать копию. \en Create a copy. - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ) = 0; // \ru Преобразовать согласно матрице. \en Transform according to the matrix. - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ) = 0; // \ru Сдвинуть вдоль вектора. \en Translate along a vector. - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ) = 0; // \ru Повернуть вокруг оси. \en Rotate about an axis. - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const = 0; // \ru Являются ли объекты равными? \en Are the objects equal? - virtual bool SetEqual ( const MbSpaceItem & init ) = 0; // \ru Сделать объекты равными. \en Make the objects equal. - virtual double DistanceToPoint ( const MbCartPoint3D & ) const = 0; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. - virtual void AddYourGabaritTo( MbCube & r ) const = 0; // \ru Добавь свой габарит в куб. \en Add bounding box into a cube. - virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const = 0; // \ru Рассчитать габарит в локальной системы координат. \en Calculate bounding box in the local coordinate system. - - virtual MbProperty & CreateProperty( MbePrompt n ) const; // \ru Создать собственное свойство. \en Create a custom property. - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & properties ); // \ru Установить свойства объекта. \en Set properties of the object. - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - virtual void GetBasisPoints( MbControlData & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - /** - \brief \ru Получить систему координат объекта, если она есть. - \en Get the coordinate system of an item if it is exist. - \return \ru Функция вернет true, если объект имеет собственную подсистему координат, - иначе считается, что ЛСК объекта всегда "стандартная" (MbPlacement3D::global). - \en The function returns true, if the object have its own local coordinate system, - otherwise it is considered that the object LCS is always "standard" (MbPlacement3D :: global). - */ - virtual bool GetPlacement( MbPlacement3D & p ) const { p = MbPlacement3D::global; return false; } - /// \ru Установить систему координат объекта, если возможно. \en Set the coordinate system of an item if it is possible. - virtual bool SetPlacement( const MbPlacement3D & ) { return false; } - - /** \brief \ru Построить полигональную копию mesh. - \en Build polygonal copy mesh. \~ - \details \ru Построить полигональную копию данного объекта, представленную полигонами, или/и плоскими пластинами. - \en Build a polygonal copy of the object that is represented by polygons or/and fasets. \~ - \param[in] stepData - \ru Данные для вычисления шага при построении полигонального. - \en Data for еру step calculation for polygonal object. \~ - \param[in] note - \ru Способ построения полигонального объекта. - \en Way for polygonal object constructing. \~ - \param[in, out] mesh - \ru Построенный полигональный объект. - \en The builded polygonal object. - */ - virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const = 0; - /** \} */ - - /** \ru \name Общие функции объекта геометрической модели - \en \name Common functions of object of geometric model. - \{ */ - - /** \brief \ru Перестроить объект по журналу построения. - \en Reconstruct object according to the history tree. \~ - \details \ru Создать заново объект по журналу построения. - \en Create object by the history tree. \~ - \param[in] sameShell - \ru Полнота копирования элементов. - \en Whether to perform complete copying of elements while constructing. \~ - \param[out] items - \ru Контейнер для складывания элементов невыполненных построений (может быть NULL). - \en Container for the elements of not performed constructions (can be NULL). \~ - \return \ru Перестроен ли объект. - \en Whether an object is constructed. \~ - \ingroup Model_Items - */ - virtual bool RebuildItem( MbeCopyMode sameShell, RPArray * items, IProgressIndicator * progInd ); - - /** \brief \ru Создать полигональный объект. - \en Create polygonal object. \~ - \details \ru Создать полигональный объект - упрощенную копию данного объекта. - \en Create a polygonal object - a polygonal copy of the given object. \~ - \param[in] stepData - \ru Данные для вычисления шага при триангуляции. - \en Data for step calculation during triangulation. \~ - \param[in] note - \ru Способ построения полигонального объекта. - \en Way for polygonal object constructing. \~ - \return \ru Построенный полигональный объект. - \en Created polygonal object. \~ - \ingroup Model_Items - */ - virtual MbItem * CreateMesh( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * iReg ) const = 0; - - /** \brief \ru Добавить полигональный объект. - \en Add polygonal object. \~ - \details \ru Добавить свою полигональную копию в присланный полигональный объект. - \en Add your own polygonal copy to the given polygonal object. \~ - \param[in] stepData - \ru Данные для вычисления шага при триангуляции. - \en Data for step calculation during triangulation. \~ - \param[in] note - \ru Способ построения полигонального объекта. - \en Way for polygonal object constructing. \~ - \param[out] mesh - \ru Присланный полигональный объект. - \en Given polygonal object. \~ - \return \ru Добавлен ли объект. - \en Whether the object is added. \~ - \ingroup Model_Items - */ - virtual bool AddYourMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; - - /** \brief \ru Разрезать полигональный объект одной или двумя параллельными плоскостями. - \en Cut the polygonal object by one or two parallel planes. \~ - \details \ru Построить полигональный объект из части исходного полигонального объекта, - лежащей под плоскостью XY локальной системы координат на заданном расстоянии.\n - Функция "режет" только полигональный объект MbMesh. - Функция "режет" объект двумя плоскостями: - плоскостью XY локальной системы координат place и плоскостью, параллельной ей и - расположенной на расстоянии distance ниже неё. - Если distance<=0, то функция "режет" объект только одной плоскостью XY локальной системы.\n - Содержимое объекта, необходимое для построения разрезанного объекта и не затронутое режущими плоскостями, - добавляется в возвращаемый разрезанный объект без копирования.\n - \en Create polygonal object from a part of source polygonal object - which located under XY-plane of local coordinate system at given distance.\n - Function 'cuts' only MbMesh polygonal object. - Function 'cuts' the object by two planes: - XY plane of 'place' local coordinate system and plane parallel to it and - located at 'distance' distance below it. - If 'distance' is less than or equal to zero, then the function "cuts" an object only by one XY plane of local coordinate system.\n - Contents of an object that are necessary for creation of cut object and not affected by cutting planes - are added to returned cut object without copying.\n \~ - \param[in] place - \ru Локальная система координат, плоскость XY которой задаёт режущую плоскость. - \en A local coordinate system which XY plane defines a cutting plane. \~ - \param[in] distance - \ru Расстояние до параллельной режущей плоскости откладывается в отрицательную сторону оси Z локальной системы. - \en Distance to a parallel cutting plane is measured in negative direction of Z-axis of local coordinate system. \~ - \result \ru Возвращает новый полигональный объект, лежащий под плоскость XY локальной системы координат на заданном расстоянии. - \en Returns new polygonal object that located under XY-plane of local coordinate system at given distance. \~ - \ingroup Model_Items - */ - virtual MbItem * CutMesh( const MbPlacement3D & cutPlace, double distance ) const; - - /** \brief \ru Найти ближайший объект или имя ближайшего объекта. - \en Find the nearest object or name of the nearest object. \~ - \details \ru Найти ближайший трехмерный объект или его имя по типу объекта и - составляющий элемент искомого объекта или его имя по топологическому или двумерному типу элемента (по требованию) - на расстоянии от прямой, не превышающем заданной величины. - Функция предназначена для идентификации геометрического объекта, породившего полигональный объект. - Реальный поиск выполняется для элементов MbPrimitive полигонального объекта MbMesh, - у которых берётся информация о породившем примитив геометрическом объекте. - \en Find the nearest three-dimensional object or its name by type of object and - component of the required object or its name by topological or two-dimensional type of the element (on demand) - at distance from line less than or equal to the given value. - Function is intended for identification of a geometric object which is begetter of a polygonal object. - The real search is performed for MbMesh polygonal object's MbPrimitive elements - from which the information is taken about geometric object which is begetter of the primitive. \~ - \param[in] sType - \ru Тип искомого объекта. - \en Type of required object. \~ - \param[in] tType - \ru Топологический тип составляющего элемента искомого объекта. - \en Topological type of the required object's component. \~ - \param[in] pType - \ru Двумерный тип составляющего элемента искомого объекта. - \en Two-dimensional type of the required object's component. \~ - \param[in] axis - \ru Прямая поиска. - \en Line of search. \~ - \param[in] maxDistance - \ru Расстояние от прямой, на котором ищется объект. - \en Distance from the line on which the object is looked for. \~ - \param[in] gridPriority - \ru Повышенный приоритет триангуляционной сетки при поиске. - \en Increased priority triangulation grid when searching. \~ - \param[out] t - \ru Параметр прямой для найденной точки. - \en Parameter of found point on line. \~ - \param[out] dMin - \ru Найденное расстояние объекта от прямой. - \en Found distance from line to an object. \~ - \param[out] find - \ru Найденный объект. - \en Found object. \~ - \param[out] findName - \ru Имя найденного объекта. - \en Name of the found object. \~ - \param[out] element - \ru Найденный составляющий элемент объекта. - \en Found component of the object. \~ - \param[out] elementName - \ru Имя найденного составляющего элемента объекта. - \en Name of found component of the object. \~ - \param[out] path - \ru Путь положения объекта в модели. - \en Object's path in the model. \~ - \param[out] from - \ru Матрица преобразования найденного объекта в глобальную систему координат. - \en Transformation matrix of the found object to the global coordinate system. \~ - \return \ru Найден ли объект или его имя. - \en Whether the object or its name is found. \~ - \ingroup Model_Items - */ - virtual bool NearestMesh( MbeSpaceType sType, MbeTopologyType tType, MbePlaneType pType, - const MbAxis3D & axis, double maxDistance, bool gridPriority, double & t, double & dMin, - MbItem *& find, SimpleName & findName, - MbRefItem *& element, SimpleName & elementName, - MbPath & path, MbMatrix3D & from ) const; - - /** \brief \ru Дать все объекты указанного типа. - \en Get all objects by type. \~ - \details \ru Дать все объекты указанного типа, - а также матрицы преобразования их в глобальную систему координат. \n - \en Get all objects by type - and get transformation matrix to the global coordinate system. \n \~ - \param[in] type - \ru Тип объекта. - \en Object's type. \~ - \param[in] from - \ru Исходная матрица преобразования в глобальную систему координат. - \en Initial transformation matrix to the global coordinate system. \~ - \param[out] items - \ru Множество найденных объектов. - \en Found objects. \~ - \param[out] matrs - \ru Матрицы преобразования найденных объектов в глобальную систему координат. - \en Transformation matrix of found objects to the global coordinate system. \~ - \return \ru Добавлен ли данный объект. - \en Whether add this object. \~ - \ingroup Model_Items - */ - virtual bool GetItems( MbeSpaceType type, const MbMatrix3D & from, - RPArray & items, SArray & matrs ); - /** \brief \ru Дать все уникальные объекты указанного типа. - \en Get all unique objects by type. \~ - \details \ru Дать все уникальные объекты указанного типа. \n - \en Get all unique objects by type. \n \~ - \param[in] type - \ru Тип объекта. - \en Object's type. \~ - \param[out] items - \ru Множество найденных объектов. - \en Found objects. \~ - \return \ru Добавлен ли данный объект. - \en Whether add this object. \~ - \ingroup Model_Items - */ - virtual bool GetUniqItems( MbeSpaceType type, CSSArray & items ) const; - - /** \brief \ru Дать объект по его пути. - \en Get the object by its path. \~ - \details \ru Дать объект по его пути положения в модели и - дать матрицу преобразования объекта в глобальную систему координат. - Объект может содержаться в другом объекте (в сборке или вставке). - \en Get the object by path of its position in the model and - get transformation matrix of the object to the global coordinate system. - Object can be contained in other object (in assembly or in instance). \~ - \param[in] path - \ru Путь объекта. - \en Path of object. \~ - \param[in] ind - \ru Индекс требуемого объекта в path. - \en Index of the desired object in 'path'. \~ - \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. - \en Transformation matrix of object to the global coordinate system. \~ - \param[in] currInd - \ru Индекс текущего объекта в path. - \en Index of current object in path. \~ - \return \ru Найден ли путь и матрица объекта. - \en Whether the path and the matrix of object are found. \~ - \ingroup Model_Items - */ - virtual const MbItem * GetItemByPath( const MbPath & path, size_t ind, MbMatrix3D & from, size_t currInd = 0 ) const; - - /** \brief \ru Найти объект по геометрическому объекту. - \en Find object by geometric object. \~ - \details \ru Найти объект по геометрическому объекту, - а также получить путь к объекту в модели - и матрицу преобразования в глобальную систему координат. \n - \en Find object by geometric object - and also get the path to the object in model - and get transformation matrix to the global coordinate system. \n \~ - \param[in] s - \ru Геометрический объект. - \en Geometric object. \~ - \param[out] path - \ru Путь к объекту в модели. - \en Path to object in the model. \~ - \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. - \en Transformation matrix of object to the global coordinate system. \~ - \return \ru Найден ли путь и матрица объекта. - \en Whether the path and the matrix of object are found. \~ - \ingroup Model_Items - */ - virtual const MbItem * FindItem( const MbSpaceItem * s, MbPath & path, MbMatrix3D & from ) const; - - /** \brief \ru Найти объект по геометрическому объекту. - \en Find object by geometric object. \~ - \details \ru Найти объект по геометрическому объекту, - а также получить путь к объекту в модели - и матрицу преобразования в глобальную систему координат. \n - \en Find object by geometric object - and also get the path to the object in model - and get transformation matrix to the global coordinate system. \n \~ - \param[in] s - \ru Геометрический объект. - \en Geometric object. \~ - \param[out] path - \ru Путь к объекту в модели. - \en Path to object in the model. \~ - \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. - \en Transformation matrix of object to the global coordinate system. \~ - \return \ru Найден ли путь и матрица объекта. - \en Whether the path and the matrix of object are found. \~ - \ingroup Model_Items - */ - virtual const MbItem * FindItem( const MbPlaneItem * s, MbPath & path, MbMatrix3D & from ) const; - - /** \brief \ru Найти объект по объекту геометрической модели. - \en Find object by object of geometric model \~ - \details \ru Найти объект по объекту геометрической модели. - а также получить путь к объекту в модели - и матрицу преобразования в глобальную систему координат. \n - \en Find object by object of geometric model - and also get the path to the object in model - and get transformation matrix to the global coordinate system. \n \~ - \param[in] s - \ru Геометрический объект. - \en Geometric object. \~ - \param[out] path - \ru Путь к объекту в модели. - \en Path to object in the model. \~ - \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. - \en Transformation matrix of object to the global coordinate system. \~ - \return \ru Найден ли путь и матрица объекта. - \en Whether the path and the matrix of object are found. \~ - \ingroup Model_Items - */ - virtual const MbItem * FindItem( const MbItem * s, MbPath & path, MbMatrix3D & from ) const; - - /** \brief \ru Найти объект по имени. - \en Find object by name. \~ - \details \ru Найти объект по имени, а также получить путь к объекту в модели - и матрицу преобразования в глобальную систему координат. \n - \en Find object by name and also get path to object in model - and get transformation matrix to the global coordinate system. \n \~ - \param[in] n - \ru Имя объекта. - \en A name of an object. \~ - \param[out] path - \ru Путь к объекту в модели. - \en Path to object in the model. \~ - \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. - \en Transformation matrix of object to the global coordinate system. \~ - \return \ru Найден ли путь и матрица объекта. - \en Whether the path and the matrix of object are found. \~ - \ingroup Model_Items - */ - virtual const MbItem * GetItemByName( SimpleName n, MbPath & path, MbMatrix3D & from ) const; - - /** \brief \ru Преобразовать выбранный объект согласно матрице. - \en Transform selected object according to the matrix. \~ - \details \ru Преобразовать выбранный простой объект согласно матрице c использованием регистратора. - Если объект содержит другие объекты геометрической модели, то преобразуется выбранное содержимое. - \en Transform selected simple object according to the matrix using the registrator. - If object contains other objects of geometric model then selected contents will be transformed. \~ - \param[in] matr - \ru Матрица преобразования. - \en A transformation matrix. \~ - \param[in] iReg - \ru Регистратор. - \en Registrator. \~ - \ingroup Model_Items - */ - virtual void TransformSelected( const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); - - /** \brief \ru Сдвинуть выбранный объект вдоль вектора. - \en Move selected object along a vector. \~ - \details \ru Сдвинуть вдоль вектора с использованием регистратора выбранный простой объект. - Если объект содержит другие объекты геометрической модели, то преобразуется выбранное содержимое. - \en Move selected simple object along the vector using the registrator. - If object contains other objects of geometric model then selected contents will be transformed. \~ - \param[in] to - \ru Вектор сдвига. - \en Translation vector. \~ - \param[in] iReg - \ru Регистратор. - \en Registrator. \~ - \ingroup Model_Items - */ - virtual void MoveSelected( const MbVector3D & to, MbRegTransform * iReg = NULL ); - - /** \brief \ru Повернуть выбранный объект вокруг оси на заданный угол. - \en Rotate selected object by a given angle about an axis. \~ - \details \ru Повернуть вокруг оси на заданный угол с использованием регистратора выбранный простой объект. - Если объект содержит другие объекты геометрической модели, то преобразуется выбранное содержимое. - \en Rotate selected simple object about the axis by the given angle using the registrator. - If object contains other objects of geometric model then selected contents will be transformed. \~ - \param[in] axis - \ru Ось поворота. - \en The rotation axis. \~ - \param[in] angle - \ru Угол поворота. - \en The rotation angle. \~ - \param[in] iReg - \ru Регистратор. - \en Registrator. \~ - \ingroup Model_Items - */ - virtual void RotateSelected( const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); - - /// \ru Дать матрицу преобразования из локальной системы объекта. \en Get transform matrix from local coordinate system of object. - virtual bool GetMatrixFrom( MbMatrix3D & from ) const; - /// \ru Дать матрицу преобразования в локальную систему объекта. \en Get transform matrix into local coordinate system of object. - virtual bool GetMatrixInto( MbMatrix3D & into ) const; - - /// \ru Копировать строители и атрибуты. \en Copy creators and attributes. - void Assign( const MbItem & other ); - /// \ru Копировать имя объекта. \en Copy the name of an object. - void CopyItemName( const MbItem & other ) { name = other.GetItemName(); } - /// \ru Выдать имя объекта. \en Get name of object. - SimpleName GetItemName() const { return name; } - /// \ru Установить имя объекта. \en Set name of the object. - void SetItemName( SimpleName n ) { name = n; } - /// \ru Соответствует ли знаковый атрибут объекту? \en Whether a sign attribute matches an object? - bool IsAttributeEqual( int attribute ); - - /** \} */ - -protected: - /// \ru Захватить объект, если ядро работает в многопоточном режиме. \en Catch object if multithreading mode is on. - void LockItem() const; - /// \ru Освободить объект, если ядро работает в многопоточном режиме. \en Release object if multithreading mode is on. - void UnlockItem() const; - -private: - /** \brief \ru Построить путь положения объекта. - \en Create path of object's position. \~ - \details \ru Построить путь положения объекта в модели и - дать матрицу преобразования объекта в глобальную систему координат. - Объект может содержаться в другом объекте (в сборке или вставке). - \en Create path of object's position in the model and - get transformation matrix of the object to the global coordinate system. - Object can be contained in other object (in assembly or in instance). \~ - \param[in] obj - \ru Объект. - \en Object. \~ - \param[out] path - \ru Путь объекта. - \en Path of object. \~ - \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. - \en Transformation matrix of object to the global coordinate system. \~ - \return \ru Найден ли путь и матрица объекта. - \en Whether the path and the matrix of object are found. \~ - \ingroup Model_Items - */ - virtual bool MakePath( const MbItem & obj, MbPath & path, MbMatrix3D & from ) const; - -public: - DECLARE_PERSISTENT_CLASS( MbItem ); - OBVIOUS_PRIVATE_COPY( MbItem ); -}; // MbItem - -IMPL_PERSISTENT_OPS( MbItem ) - - -//---------------------------------------------------------------------------------------- -// The functor implementing less operator of two model objects. -//--- -struct LessName -{ - bool operator()( const MbItem * _Left, const MbItem * _Right ) const - { - return (_Left->GetItemName() < _Right->GetItemName()); - } - bool operator()( const MbItem * _Left, SimpleName _Right ) const - { - return _Left->GetItemName() < _Right; - } - bool operator()( SimpleName _Left, const MbItem * _Right ) const - { - return _Left < _Right->GetItemName(); - } -}; - - -#endif // __MODEL_ITEM_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Объект геометрической модели. + \en A model geometric object. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __MODEL_ITEM_H +#define __MODEL_ITEM_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class MATH_CLASS MbItem; +namespace c3d // namespace C3D +{ +typedef SPtr ItemSPtr; +typedef SPtr ConstItemSPtr; + +typedef std::vector ItemsVector; +typedef std::vector ConstItemsVector; + +typedef std::vector ItemsSPtrVector; +typedef std::vector ConstItemsSPtrVector; + +typedef std::set ItemsSet; +typedef ItemsSet::iterator ItemsSetIt; +typedef ItemsSet::const_iterator ItemsSetConstIt; +typedef std::pair ItemsSetRet; + +typedef std::set ConstItemsSet; +typedef ConstItemsSet::iterator ConstItemsSetIt; +typedef ConstItemsSet::const_iterator ConstItemsSetConstIt; +typedef std::pair ConstItemsSetRet; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Объект геометрической модели. + \en A model geometric object. \~ + \details \ru Родительский класс объектов геометрической модели. \n + Наследниками являются: \n + локальная система координат MbAssistingItem,\n + точечный каркас MbPointFrame,\n + проволочный каркас MbWireFrame,\n + твёрдое тело MbSolid,\n + полигональный объект MbMesh,\n + вставка объекта в локальной системе координат MbInstance,\n + сборка объектов в локальной системе координат MbAssembly,\n + вставка трехмерного объекта MbSpaceInstance,\n + вставка двумерного объекта MbPlaneInstance в плоскости XY локальной системы координат.\n + Объект содержит последовательность и способы своего построения MbTransactions.\n + Объект содержит не геометрические свойства в виде контейнера атрибутов MbAttributeContainer.\n + Имя объекта геометрической модели представляет собой контейнер простых имён. + В начале контейнера содержится простое имя SimpleName, присвоенное объекту геометрической моделью MbModel. \n + Если объект не держит в себе других объектов, то контейнер содержит одно простое имя SimpleName. + Ели объект держит в себе другие объекты (MbAssembly или MbInstance), + то имя внутренних объектов представляет собой контейнер, содержащий как минимум два простых имени. + Количество элементов имени объекта отражают количество уровней вложенности объект относительно модели. + \en Parent class of model geometric objects. \n + Inheritors are: \n + local coordinate system of MbAssistingItem,\n + MbPointFrame point-frame,\n + MbWireFrame wireframe,\n + MbSolid solid,\n + MbMesh polygonal planar object,\n + MbInstance instance of object in the local coordinate system,\n + MbAssembly assembly of objects in the local coordinate system,\n + MbSpaceInstance instance of three-dimensional object,\n + MbPlaneInstance instance of a two-dimensional object in the XY-plane of a local coordinate system.\n + Object contains MbTransactions sequence and ways to construct itself.\n + Object contains non-geometric properties as MbAttributeContainer attribute container.\n + The name of an object of a geometric model is represented as a container of simple names. + In the beginning of the container there is a SimpleName simple name assigned to object by MbModel geometric model. \n + If the object doesn't contain other objects, then the container contains one SimpleName simple name. + If the object contains other objects (MbAssembly or MbInstance), + then the internal objects name is represented as a container with at least two simple names. + Number of the elements of an object's name corresponds to the number of levels of objects inclusion relative to the model. \~ + \ingroup Model_Items +*/ +// --- +class MATH_CLASS MbItem : public MbSpaceItem, + public MbTransactions, + public MbAttributeContainer, + public MbSyncItem { + +private: + SimpleName name; ///< \ru Имя объекта геометрической модели. \en Name of a geometric model object. + +protected: + /// \ru Конструктор копирования с регистратором дублирования. \en Copy-constructor with duplication registrator. + explicit MbItem( const MbItem &, MbRegDuplicate * ); +public: + /// \ru Конструктор. \en Constructor. + MbItem(); + /// \ru Деструктор. \en Destructor. + virtual ~MbItem(); + +public : + VISITING_CLASS( MbItem ); + + /** \ru \name Общие функции геометрического объекта. + \en \name Common functions of a geometric object. + \{ */ + virtual MbeSpaceType IsA() const = 0; // \ru Тип объекта. \en A type of an object. + virtual MbeSpaceType Type() const; // \ru Групповой тип объекта. \en Group type of object. + virtual MbeSpaceType Family() const; // \ru Семейство объекта. \en Family of object. + virtual MbeImplicationType ImplicationType() const; // \ru Тип контейнера атрибутов - классификатор наследников. \en Type of an attribute container is a classifier of inheritors. + virtual MbSpaceItem & Duplicate ( MbRegDuplicate * = NULL ) const = 0; // \ru Создать копию. \en Create a copy. + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ) = 0; // \ru Преобразовать согласно матрице. \en Transform according to the matrix. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ) = 0; // \ru Сдвинуть вдоль вектора. \en Translate along a vector. + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ) = 0; // \ru Повернуть вокруг оси. \en Rotate about an axis. + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const = 0; // \ru Являются ли объекты равными? \en Are the objects equal? + virtual bool SetEqual ( const MbSpaceItem & init ) = 0; // \ru Сделать объекты равными. \en Make the objects equal. + virtual double DistanceToPoint ( const MbCartPoint3D & ) const = 0; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. + virtual void AddYourGabaritTo( MbCube & r ) const = 0; // \ru Добавь свой габарит в куб. \en Add bounding box into a cube. + virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const = 0; // \ru Рассчитать габарит в локальной системы координат. \en Calculate bounding box in the local coordinate system. + + virtual MbProperty & CreateProperty( MbePrompt n ) const; // \ru Создать собственное свойство. \en Create a custom property. + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & properties ); // \ru Установить свойства объекта. \en Set properties of the object. + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + virtual void GetBasisPoints( MbControlData & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + /** + \brief \ru Получить систему координат объекта, если она есть. + \en Get the coordinate system of an item if it is exist. + \return \ru Функция вернет true, если объект имеет собственную подсистему координат, + иначе считается, что ЛСК объекта всегда "стандартная" (MbPlacement3D::global). + \en The function returns true, if the object have its own local coordinate system, + otherwise it is considered that the object LCS is always "standard" (MbPlacement3D :: global). + */ + virtual bool GetPlacement( MbPlacement3D & p ) const { p = MbPlacement3D::global; return false; } + /// \ru Установить систему координат объекта, если возможно. \en Set the coordinate system of an item if it is possible. + virtual bool SetPlacement( const MbPlacement3D & ) { return false; } + + /** \brief \ru Построить полигональную копию mesh. + \en Build polygonal copy mesh. \~ + \details \ru Построить полигональную копию данного объекта, представленную полигонами, или/и плоскими пластинами. + \en Build a polygonal copy of the object that is represented by polygons or/and fasets. \~ + \param[in] stepData - \ru Данные для вычисления шага при построении полигонального. + \en Data for еру step calculation for polygonal object. \~ + \param[in] note - \ru Способ построения полигонального объекта. + \en Way for polygonal object constructing. \~ + \param[in, out] mesh - \ru Построенный полигональный объект. + \en The builded polygonal object. + */ + virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const = 0; + /** \} */ + + /** \ru \name Общие функции объекта геометрической модели + \en \name Common functions of object of geometric model. + \{ */ + + /** \brief \ru Перестроить объект по журналу построения. + \en Reconstruct object according to the history tree. \~ + \details \ru Создать заново объект по журналу построения. + \en Create object by the history tree. \~ + \param[in] sameShell - \ru Полнота копирования элементов. + \en Whether to perform complete copying of elements while constructing. \~ + \param[out] items - \ru Контейнер для складывания элементов невыполненных построений (может быть NULL). + \en Container for the elements of not performed constructions (can be NULL). \~ + \return \ru Перестроен ли объект. + \en Whether an object is constructed. \~ + \ingroup Model_Items + */ + virtual bool RebuildItem( MbeCopyMode sameShell, RPArray * items, IProgressIndicator * progInd ); + + /** \brief \ru Создать полигональный объект. + \en Create polygonal object. \~ + \details \ru Создать полигональный объект - упрощенную копию данного объекта. + \en Create a polygonal object - a polygonal copy of the given object. \~ + \param[in] stepData - \ru Данные для вычисления шага при триангуляции. + \en Data for step calculation during triangulation. \~ + \param[in] note - \ru Способ построения полигонального объекта. + \en Way for polygonal object constructing. \~ + \return \ru Построенный полигональный объект. + \en Created polygonal object. \~ + \ingroup Model_Items + */ + virtual MbItem * CreateMesh( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * iReg ) const = 0; + + /** \brief \ru Добавить полигональный объект. + \en Add polygonal object. \~ + \details \ru Добавить свою полигональную копию в присланный полигональный объект. + \en Add your own polygonal copy to the given polygonal object. \~ + \param[in] stepData - \ru Данные для вычисления шага при триангуляции. + \en Data for step calculation during triangulation. \~ + \param[in] note - \ru Способ построения полигонального объекта. + \en Way for polygonal object constructing. \~ + \param[out] mesh - \ru Присланный полигональный объект. + \en Given polygonal object. \~ + \return \ru Добавлен ли объект. + \en Whether the object is added. \~ + \ingroup Model_Items + */ + virtual bool AddYourMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; + + /** \brief \ru Разрезать полигональный объект одной или двумя параллельными плоскостями. + \en Cut the polygonal object by one or two parallel planes. \~ + \details \ru Построить полигональный объект из части исходного полигонального объекта, + лежащей под плоскостью XY локальной системы координат на заданном расстоянии.\n + Функция "режет" только полигональный объект MbMesh. + Функция "режет" объект двумя плоскостями: + плоскостью XY локальной системы координат place и плоскостью, параллельной ей и + расположенной на расстоянии distance ниже неё. + Если distance<=0, то функция "режет" объект только одной плоскостью XY локальной системы.\n + Содержимое объекта, необходимое для построения разрезанного объекта и не затронутое режущими плоскостями, + добавляется в возвращаемый разрезанный объект без копирования.\n + \en Create polygonal object from a part of source polygonal object + which located under XY-plane of local coordinate system at given distance.\n + Function 'cuts' only MbMesh polygonal object. + Function 'cuts' the object by two planes: + XY plane of 'place' local coordinate system and plane parallel to it and + located at 'distance' distance below it. + If 'distance' is less than or equal to zero, then the function "cuts" an object only by one XY plane of local coordinate system.\n + Contents of an object that are necessary for creation of cut object and not affected by cutting planes + are added to returned cut object without copying.\n \~ + \param[in] place - \ru Локальная система координат, плоскость XY которой задаёт режущую плоскость. + \en A local coordinate system which XY plane defines a cutting plane. \~ + \param[in] distance - \ru Расстояние до параллельной режущей плоскости откладывается в отрицательную сторону оси Z локальной системы. + \en Distance to a parallel cutting plane is measured in negative direction of Z-axis of local coordinate system. \~ + \result \ru Возвращает новый полигональный объект, лежащий под плоскость XY локальной системы координат на заданном расстоянии. + \en Returns new polygonal object that located under XY-plane of local coordinate system at given distance. \~ + \ingroup Model_Items + */ + virtual MbItem * CutMesh( const MbPlacement3D & cutPlace, double distance ) const; + + /** \brief \ru Найти ближайший объект или имя ближайшего объекта. + \en Find the nearest object or name of the nearest object. \~ + \details \ru Найти ближайший трехмерный объект или его имя по типу объекта и + составляющий элемент искомого объекта или его имя по топологическому или двумерному типу элемента (по требованию) + на расстоянии от прямой, не превышающем заданной величины. + Функция предназначена для идентификации геометрического объекта, породившего полигональный объект. + Реальный поиск выполняется для элементов MbPrimitive полигонального объекта MbMesh, + у которых берётся информация о породившем примитив геометрическом объекте. + \en Find the nearest three-dimensional object or its name by type of object and + component of the required object or its name by topological or two-dimensional type of the element (on demand) + at distance from line less than or equal to the given value. + Function is intended for identification of a geometric object which is begetter of a polygonal object. + The real search is performed for MbMesh polygonal object's MbPrimitive elements + from which the information is taken about geometric object which is begetter of the primitive. \~ + \param[in] sType - \ru Тип искомого объекта. + \en Type of required object. \~ + \param[in] tType - \ru Топологический тип составляющего элемента искомого объекта. + \en Topological type of the required object's component. \~ + \param[in] pType - \ru Двумерный тип составляющего элемента искомого объекта. + \en Two-dimensional type of the required object's component. \~ + \param[in] axis - \ru Прямая поиска. + \en Line of search. \~ + \param[in] maxDistance - \ru Расстояние от прямой, на котором ищется объект. + \en Distance from the line on which the object is looked for. \~ + \param[in] gridPriority - \ru Повышенный приоритет триангуляционной сетки при поиске. + \en Increased priority triangulation grid when searching. \~ + \param[out] t - \ru Параметр прямой для найденной точки. + \en Parameter of found point on line. \~ + \param[out] dMin - \ru Найденное расстояние объекта от прямой. + \en Found distance from line to an object. \~ + \param[out] find - \ru Найденный объект. + \en Found object. \~ + \param[out] findName - \ru Имя найденного объекта. + \en Name of the found object. \~ + \param[out] element - \ru Найденный составляющий элемент объекта. + \en Found component of the object. \~ + \param[out] elementName - \ru Имя найденного составляющего элемента объекта. + \en Name of found component of the object. \~ + \param[out] path - \ru Путь положения объекта в модели. + \en Object's path in the model. \~ + \param[out] from - \ru Матрица преобразования найденного объекта в глобальную систему координат. + \en Transformation matrix of the found object to the global coordinate system. \~ + \return \ru Найден ли объект или его имя. + \en Whether the object or its name is found. \~ + \ingroup Model_Items + */ + virtual bool NearestMesh( MbeSpaceType sType, MbeTopologyType tType, MbePlaneType pType, + const MbAxis3D & axis, double maxDistance, bool gridPriority, double & t, double & dMin, + MbItem *& find, SimpleName & findName, + MbRefItem *& element, SimpleName & elementName, + MbPath & path, MbMatrix3D & from ) const; + + /** \brief \ru Дать все объекты указанного типа. + \en Get all objects by type. \~ + \details \ru Дать все объекты указанного типа, + а также матрицы преобразования их в глобальную систему координат. \n + \en Get all objects by type + and get transformation matrix to the global coordinate system. \n \~ + \param[in] type - \ru Тип объекта. + \en Object's type. \~ + \param[in] from - \ru Исходная матрица преобразования в глобальную систему координат. + \en Initial transformation matrix to the global coordinate system. \~ + \param[out] items - \ru Множество найденных объектов. + \en Found objects. \~ + \param[out] matrs - \ru Матрицы преобразования найденных объектов в глобальную систему координат. + \en Transformation matrix of found objects to the global coordinate system. \~ + \return \ru Добавлен ли данный объект. + \en Whether add this object. \~ + \ingroup Model_Items + */ + virtual bool GetItems( MbeSpaceType type, const MbMatrix3D & from, + RPArray & items, SArray & matrs ); + /** \brief \ru Дать все уникальные объекты указанного типа. + \en Get all unique objects by type. \~ + \details \ru Дать все уникальные объекты указанного типа. \n + \en Get all unique objects by type. \n \~ + \param[in] type - \ru Тип объекта. + \en Object's type. \~ + \param[out] items - \ru Множество найденных объектов. + \en Found objects. \~ + \return \ru Добавлен ли данный объект. + \en Whether add this object. \~ + \ingroup Model_Items + */ + virtual bool GetUniqItems( MbeSpaceType type, CSSArray & items ) const; + + /** \brief \ru Дать объект по его пути. + \en Get the object by its path. \~ + \details \ru Дать объект по его пути положения в модели и + дать матрицу преобразования объекта в глобальную систему координат. + Объект может содержаться в другом объекте (в сборке или вставке). + \en Get the object by path of its position in the model and + get transformation matrix of the object to the global coordinate system. + Object can be contained in other object (in assembly or in instance). \~ + \param[in] path - \ru Путь объекта. + \en Path of object. \~ + \param[in] ind - \ru Индекс требуемого объекта в path. + \en Index of the desired object in 'path'. \~ + \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. + \en Transformation matrix of object to the global coordinate system. \~ + \param[in] currInd - \ru Индекс текущего объекта в path. + \en Index of current object in path. \~ + \return \ru Найден ли путь и матрица объекта. + \en Whether the path and the matrix of object are found. \~ + \ingroup Model_Items + */ + virtual const MbItem * GetItemByPath( const MbPath & path, size_t ind, MbMatrix3D & from, size_t currInd = 0 ) const; + + /** \brief \ru Найти объект по геометрическому объекту. + \en Find object by geometric object. \~ + \details \ru Найти объект по геометрическому объекту, + а также получить путь к объекту в модели + и матрицу преобразования в глобальную систему координат. \n + \en Find object by geometric object + and also get the path to the object in model + and get transformation matrix to the global coordinate system. \n \~ + \param[in] s - \ru Геометрический объект. + \en Geometric object. \~ + \param[out] path - \ru Путь к объекту в модели. + \en Path to object in the model. \~ + \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. + \en Transformation matrix of object to the global coordinate system. \~ + \return \ru Найден ли путь и матрица объекта. + \en Whether the path and the matrix of object are found. \~ + \ingroup Model_Items + */ + virtual const MbItem * FindItem( const MbSpaceItem * s, MbPath & path, MbMatrix3D & from ) const; + + /** \brief \ru Найти объект по геометрическому объекту. + \en Find object by geometric object. \~ + \details \ru Найти объект по геометрическому объекту, + а также получить путь к объекту в модели + и матрицу преобразования в глобальную систему координат. \n + \en Find object by geometric object + and also get the path to the object in model + and get transformation matrix to the global coordinate system. \n \~ + \param[in] s - \ru Геометрический объект. + \en Geometric object. \~ + \param[out] path - \ru Путь к объекту в модели. + \en Path to object in the model. \~ + \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. + \en Transformation matrix of object to the global coordinate system. \~ + \return \ru Найден ли путь и матрица объекта. + \en Whether the path and the matrix of object are found. \~ + \ingroup Model_Items + */ + virtual const MbItem * FindItem( const MbPlaneItem * s, MbPath & path, MbMatrix3D & from ) const; + + /** \brief \ru Найти объект по объекту геометрической модели. + \en Find object by object of geometric model \~ + \details \ru Найти объект по объекту геометрической модели. + а также получить путь к объекту в модели + и матрицу преобразования в глобальную систему координат. \n + \en Find object by object of geometric model + and also get the path to the object in model + and get transformation matrix to the global coordinate system. \n \~ + \param[in] s - \ru Геометрический объект. + \en Geometric object. \~ + \param[out] path - \ru Путь к объекту в модели. + \en Path to object in the model. \~ + \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. + \en Transformation matrix of object to the global coordinate system. \~ + \return \ru Найден ли путь и матрица объекта. + \en Whether the path and the matrix of object are found. \~ + \ingroup Model_Items + */ + virtual const MbItem * FindItem( const MbItem * s, MbPath & path, MbMatrix3D & from ) const; + + /** \brief \ru Найти объект по имени. + \en Find object by name. \~ + \details \ru Найти объект по имени, а также получить путь к объекту в модели + и матрицу преобразования в глобальную систему координат. \n + \en Find object by name and also get path to object in model + and get transformation matrix to the global coordinate system. \n \~ + \param[in] n - \ru Имя объекта. + \en A name of an object. \~ + \param[out] path - \ru Путь к объекту в модели. + \en Path to object in the model. \~ + \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. + \en Transformation matrix of object to the global coordinate system. \~ + \return \ru Найден ли путь и матрица объекта. + \en Whether the path and the matrix of object are found. \~ + \ingroup Model_Items + */ + virtual const MbItem * GetItemByName( SimpleName n, MbPath & path, MbMatrix3D & from ) const; + + /** \brief \ru Преобразовать выбранный объект согласно матрице. + \en Transform selected object according to the matrix. \~ + \details \ru Преобразовать выбранный простой объект согласно матрице c использованием регистратора. + Если объект содержит другие объекты геометрической модели, то преобразуется выбранное содержимое. + \en Transform selected simple object according to the matrix using the registrator. + If object contains other objects of geometric model then selected contents will be transformed. \~ + \param[in] matr - \ru Матрица преобразования. + \en A transformation matrix. \~ + \param[in] iReg - \ru Регистратор. + \en Registrator. \~ + \ingroup Model_Items + */ + virtual void TransformSelected( const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); + + /** \brief \ru Сдвинуть выбранный объект вдоль вектора. + \en Move selected object along a vector. \~ + \details \ru Сдвинуть вдоль вектора с использованием регистратора выбранный простой объект. + Если объект содержит другие объекты геометрической модели, то преобразуется выбранное содержимое. + \en Move selected simple object along the vector using the registrator. + If object contains other objects of geometric model then selected contents will be transformed. \~ + \param[in] to - \ru Вектор сдвига. + \en Translation vector. \~ + \param[in] iReg - \ru Регистратор. + \en Registrator. \~ + \ingroup Model_Items + */ + virtual void MoveSelected( const MbVector3D & to, MbRegTransform * iReg = NULL ); + + /** \brief \ru Повернуть выбранный объект вокруг оси на заданный угол. + \en Rotate selected object by a given angle about an axis. \~ + \details \ru Повернуть вокруг оси на заданный угол с использованием регистратора выбранный простой объект. + Если объект содержит другие объекты геометрической модели, то преобразуется выбранное содержимое. + \en Rotate selected simple object about the axis by the given angle using the registrator. + If object contains other objects of geometric model then selected contents will be transformed. \~ + \param[in] axis - \ru Ось поворота. + \en The rotation axis. \~ + \param[in] angle - \ru Угол поворота. + \en The rotation angle. \~ + \param[in] iReg - \ru Регистратор. + \en Registrator. \~ + \ingroup Model_Items + */ + virtual void RotateSelected( const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); + + /// \ru Дать матрицу преобразования из локальной системы объекта. \en Get transform matrix from local coordinate system of object. + virtual bool GetMatrixFrom( MbMatrix3D & from ) const; + /// \ru Дать матрицу преобразования в локальную систему объекта. \en Get transform matrix into local coordinate system of object. + virtual bool GetMatrixInto( MbMatrix3D & into ) const; + + /// \ru Копировать строители и атрибуты. \en Copy creators and attributes. + void Assign( const MbItem & other ); + /// \ru Копировать имя объекта. \en Copy the name of an object. + void CopyItemName( const MbItem & other ) { name = other.GetItemName(); } + /// \ru Выдать имя объекта. \en Get name of object. + SimpleName GetItemName() const { return name; } + /// \ru Установить имя объекта. \en Set name of the object. + void SetItemName( SimpleName n ) { name = n; } + /// \ru Соответствует ли знаковый атрибут объекту? \en Whether a sign attribute matches an object? + bool IsAttributeEqual( int attribute ); + + /** \} */ + +protected: + /// \ru Захватить объект, если ядро работает в многопоточном режиме. \en Catch object if multithreading mode is on. + void LockItem() const; + /// \ru Освободить объект, если ядро работает в многопоточном режиме. \en Release object if multithreading mode is on. + void UnlockItem() const; + +private: + /** \brief \ru Построить путь положения объекта. + \en Create path of object's position. \~ + \details \ru Построить путь положения объекта в модели и + дать матрицу преобразования объекта в глобальную систему координат. + Объект может содержаться в другом объекте (в сборке или вставке). + \en Create path of object's position in the model and + get transformation matrix of the object to the global coordinate system. + Object can be contained in other object (in assembly or in instance). \~ + \param[in] obj - \ru Объект. + \en Object. \~ + \param[out] path - \ru Путь объекта. + \en Path of object. \~ + \param[out] from - \ru Матрица преобразования объекта в глобальную систему координат. + \en Transformation matrix of object to the global coordinate system. \~ + \return \ru Найден ли путь и матрица объекта. + \en Whether the path and the matrix of object are found. \~ + \ingroup Model_Items + */ + virtual bool MakePath( const MbItem & obj, MbPath & path, MbMatrix3D & from ) const; + +public: + DECLARE_PERSISTENT_CLASS( MbItem ); + OBVIOUS_PRIVATE_COPY( MbItem ); +}; // MbItem + +IMPL_PERSISTENT_OPS( MbItem ) + + +//---------------------------------------------------------------------------------------- +// The functor implementing less operator of two model objects. +//--- +struct LessName +{ + bool operator()( const MbItem * _Left, const MbItem * _Right ) const + { + return (_Left->GetItemName() < _Right->GetItemName()); + } + bool operator()( const MbItem * _Left, SimpleName _Right ) const + { + return _Left->GetItemName() < _Right; + } + bool operator()( SimpleName _Left, const MbItem * _Right ) const + { + return _Left < _Right->GetItemName(); + } +}; + + +#endif // __MODEL_ITEM_H diff --git a/C3d/Include/model_tree.h b/C3d/Include/model_tree.h index eadb623..51430ba 100644 --- a/C3d/Include/model_tree.h +++ b/C3d/Include/model_tree.h @@ -1,309 +1,309 @@ -////////////////////////////////////////////////////////////////////////////////////////// -/** \file - \brief \ru Реализация дерева модели - \en Implementation of Model Tree classes \~ -*/ -////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef __MODEL_TREE_H -#define __MODEL_TREE_H - - -#include -#include -#include -#include -#include -#include -#include -#include - -//---------------------------------------------------------------------------------------- -// \ru Реализация интерфейсов дерева модели. \en Implementation of Model Tree interfaces. -//---------------------------------------------------------------------------------------- - -namespace c3d // namespace C3D -{ -//---------------------------------------------------------------------------------------- -/** \brief \ru Узел дерева. - \en Tree node. \~ - \details \ru Узел дерева (может иметь несколько потомков). \n - \en Tree node (can have several children). \n \~ - \ingroup Base_Tools_IO -*/ -// --- -class MATH_CLASS MbTreeNode : public IModelTreeNode -{ - // \ru Временное хранилище для индексов потомков узла (используется при чтении узла). - // \en Temporary storage for indices of child nodes (used while reading the node). - std::vector m_childrenIndices; - // \ru Данные узла \en The node data. - MbItemData m_data; - // \ru Флаг, указывающий, открыт ли узел при проходе вглубь по дереву. - // \en Flag which indicates whether the node is entered during traversing into depth over the tree. - mutable bool m_open; - // \ru Флаг, указывающий, читать ли только часть узла. - // \en Flag which indicates whether to read only a part of the node. - mutable bool m_partial; -public: - MbTreeNode() : m_open(false), m_partial(false) {} - MbTreeNode ( const MbItemData& data ) : m_data(data), m_open(false), m_partial(false) {} - MbTreeNode ( const MbTreeNode& node ) : m_data(node.m_data), m_childrenIndices(node.m_childrenIndices), - m_open(node.m_open), m_partial(node.m_partial) { GetChildren() = node.GetChildren(); GetParents() = node.GetParents(); } - - ///--------- - /// \ru Методы IModelTreeNode. \en IModelTreeNode methods. - - /// \ru Доступ к данным узла. \en Access to the node data. - virtual MbItemData& GetData() { return m_data; } - virtual const MbItemData& GetData() const { return m_data; } - - /// \ru Доступ к позиции чтения/записм узла. \en Access to the node read/write position. - virtual ClusterReference& GetPosition() { return m_data.position; } - virtual const ClusterReference& GetPosition() const { return m_data.position; } - - /// \ru Узнать, читать ли только часть узла. - /// \en Check whether to read the node partially. - virtual bool PartialRead() const { return m_partial; }; - - /// \ru Установить признак частичного или полного чтения узла. - /// \en Set indication of full or partial node reading. - virtual void SetPartialRead ( bool partial ) const { m_partial = partial; }; - - /// \ru Записать узел. \en Write the node. - virtual writer & operator >> ( writer & ); - - /// \ru Прочитать узел. \en Read the node. - virtual reader & operator << ( reader & ); - - /// \ru Доступ ко все потомкам узла. \en Access to the all descendants of the node. - void GetAllDescendants ( std::set& nodes ) const; - - /// \ru Создать узел с данными текущего узла и добавить его в дерево. - /// Рекурсивно скопировать в дерево всех предков текущего узла с сохранением иерархии. - /// \en Create a node with data from the current node. - /// Copy recursively all parents of the node to the tree preserving the hierarchy. - MbTreeNode* CopyToTreeWithParents ( c3d::IModelTree* tree, bool partial ) const; - - /// \ru Создать узел с данными текущего узла и добавить его в дерево. - /// Рекурсивно скопировать в дерево всех потомков текущего узла с сохранением иерархии. - /// \en Create a node with data from the current node. - /// Copy recursively all children of the node to the tree preserving the hierarchy. - MbTreeNode* CopyToTreeWithChildren ( c3d::IModelTree* tree, bool partial ) const; - - /// \ru Доступ к флагу, который указывет, открыт ли узел при проходе вглубь по дереву - /// (false означает, что узел и его потомки уже пройдены или еще не обнаружены). - /// \en Access to the flag which indicates whether the node is entered during traversing into depth over the tree - /// (false - means that the node and its children are already leaved or are not met yet). - bool IsOpen() const { return m_open; } - void SetOpen ( bool open ) { m_open = open; } - void SetOpen ( bool open ) const { m_open = open; } - - // \ru Равенство определяется по id объекта MbItemData. - // \en Equality is defined by id field of MbItemData object. - bool operator == ( const MbTreeNode& node2 ) const - { - if ( !( GetData() == node2.GetData() ) || - GetData().id != node2.GetData().id ) - return false; - return true; - } - - // \ru Сравнение по полям объектов MbItemData. - // \en Comparison of filtering fields of MbItemData objects. - bool operator < ( const MbTreeNode& node2 ) const - { - return m_data < node2.m_data; - } - - friend class MbModelTree; - -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Узел дерева исполнений. - \en Embodiments tree node. \~ - \details \ru Узел дерева исполнений (может иметь несколько потомков). - \en Embodiments tree node (can have several children). - \ingroup Base_Tools_IO -*/ -// --- -class MATH_CLASS MbEmbodimentNode : public IEmbodimentNode -{ - const MbTreeNode * m_subtree; -public: - MbEmbodimentNode( const MbTreeNode* node ) : IEmbodimentNode(), m_subtree( node ) {} - ~MbEmbodimentNode() {} - - // \ru Выдать узел дерева модели, соответствующий данному исполнению. - // \en Get a model tree node which corresponds to a given embodiment. - virtual const IModelTreeNode * GetModelTreeNode() const { return m_subtree; } - - // \ru Доступ к информации об исполнении. \en Access to the embodiment info. - virtual const MbItemData& GetEmbodimentData() const { C3D_ASSERT( m_subtree != NULL ); return m_subtree->GetData(); } - - // \ru Построить дерево модели, которое содержится в данном исполнении. - // \en Build a tree of a model which is contained in a given embodiment. - virtual std_unique_ptr GetEmbodiment() const; - -private: - MbEmbodimentNode(); -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Дерево геометрической модели. - \en Tree of geometric model. \~ - \details \ru Дерево геометрической модели. (может иметь несколько корней). - \en Tree of geometric model (can have several roots). \n \~ - \ingroup Base_Tools_IO -*/ -// --- -class MATH_CLASS MbModelTree : public IModelTree -{ -private: - // \ru Временное хранилище для индексов корней дерева (используется при чтении дерева). - // \en Temporary storage for indices of the tree roots (used while reading the tree). - std::vector m_rootsIndices; - // \ru Все узлы дерева, упорядоченные по данным. - // \en All nodes of the tree, ordered by data. - std::set m_filteredNodes; - // \ru Стек узлов, открытых при чтении/записи дерева. - // \en Stack of nodes opened during reading/writing the tree. - std::stack m_nestedNodesStack; - // \ru Все узлы дерева, упорядоченные по ID. - // \en All nodes of the tree, ordered by ID. - std::map m_indexToNode; // \ru Вспомогательный массив. \en Auxiliary map. - VERSION m_currentVersion; - IEmbodimentTree m_embTree; // \ru Дерево исполнений. \en Embodiment tree. -public: - - // \ru Конструктор. \en Constructor. - MbModelTree(); - - // \ru Деструктор. \en Destructor. - virtual ~MbModelTree(); - - ///--------- - /// \ru Методы IModelTreeNode. \en IModelTreeNode methods. - - /// \ru Создать узел по данными и добавить в дерево. \en Create a node by data and add to the tree. - virtual void AddNode ( const TapeBase* mem, const ClusterReference& ref ); - - /// \ru Закрыть узел (удалить узел из стека, так что родительский узел станет текущим). - /// \en Close the node (remove it from the stack so that its parent becomes the current node). - virtual void CloseNode ( const TapeBase* mem ); - - // \ru Построить дерево из узлов, выбранных по фильтрам. В случае дерева исполнений, функция работает с первым исполнением. - // \en Build a tree with nodes, selected by filters. In case of embodiment tree, the function works with the first embodiment. - virtual std_unique_ptr GetFilteredTree ( const std::vector& filters ) const; - - // \ru Построить дерево по заданным узлам. Не применимо для дерева исполнений (в этом случае возвращает NULL). - // \en Build a tree for given nodes. Not applicable to embodiment tree (in this case, returns NULL). - virtual std_unique_ptr GetFilteredTree ( std::vector& nodes ) const; - - // \ru Выдать указатель на дерево исполнений. Выдает NULL, если не применимо (нет исполнений). - // \en Get pointer to embodiments tree. Return NULL if not applicable (no embodiments). - virtual const IEmbodimentTree* GetEmbodimentsTree() const { return GetType() == mtt_Embodiment ? &m_embTree : NULL; } - - /// \ru Версия дерева. \en Tree version. - virtual VERSION GetVersion() { return m_currentVersion; } - virtual void SetVersion( VERSION version ) { m_currentVersion = version; } - - /// \ru Записать дерево. \en Write the tree. - virtual writer & operator >> ( writer & ); - - /// \ru Прочитать дерево. \en Read the tree. - virtual reader & operator << ( reader & ); - - ///--------- - - /// \ru Добавить узел с данными из указанного узла, если узел с такими данными не существует. - /// \param node - узел с данными. - /// \param added - заполняется, если ненулевой (true - узел добавлен, false - узел уже существует). - /// \return - возвращает указатель на узел дерева. - /// \en Add a node with the data from the given node if a node with such data does not exist. - /// \param node - a node with data. - /// \param added - filled if non-null (true - if a node added, false - a node already exists). - /// \return - a pointer to the tree node. - MbTreeNode* AddNode ( const MbTreeNode& node, bool* added = NULL ); - - /// \ru Добавить узел с указанными данными, если узел с такими данными не существует. - /// \param node - данные. - /// \param added - заполняется, если ненулевой (true - узел добавлен, false - узел уже существует). - /// \return - возвращает указатель на узел дерева. - /// \en Add a node with the given if a node with such data does not exist. - /// \param node - a data. - /// \param added - filled if non-null (true - if a node added, false - a node already exists). - /// \return - a pointer to the tree node. - MbTreeNode* AddNode ( const MbItemData& data, bool* added = NULL ); - - /// \ru Доступ к узлам дерева, упорядоченным по данным. - /// \en Access to nodes of the tree, ordered by data. - std::set& GetFilteredNodes() { return m_filteredNodes; } - const std::set& GetFilteredNodes() const { return m_filteredNodes; } - - // \ru Добавить в корень текущего дерева указанное поддерево. \en Add a given subtree to the current tree root. - const void AddSubtree( IModelTree* tree, const IModelTreeNode* node ) const; - - /// \ru Заполнить массив корней дерева. - /// \en Fill the tree roots. - void FillRoots(); - -protected: - /// \ru Добавить ветвь в дерево: - /// \param branch - листовой узел с ветвью дерева, ведущей к нему, начиная с корневого узла дерева; - /// \param partial - определяет тип чтения листового узла (частичное или полное). - /// \en Add a branch to the tree: - /// \param branch - a leaf node with the tree branch, leading to it; - /// \param partial - defines partial or full read of the leaf node. - void AddBranch( const NodeBranch& branch, bool partial ); - - /// \ru Получить уникальные узлы для данного набора узлов. Проходятся все заданные узлы и исключаются те, - /// которые являются потомками заданных узлов (и будут прочитаны, как их часть). - /// Таким образом, результат будет содержать узлы поддеревьев, содержащих все заданные узлы. - /// \en Get unique nodes for given set of nodes. Walk through the given nodes and exclude nodes, - /// which are children of other given nodes (and will be read as a part of them). - /// Thus, the result set of nodes will represent the roots of subtrees containing all given nodes. - std::vector GetUniqueNodes ( std::vector& nodes ) const; - - - // \ru Построить дерево по заданным узлам без проверки типа. - // \en Build a tree for given nodes without type check. - const IModelTree* GetFilteredTreeEx( std::vector& nodes ) const; - - /// \ru Построить дерево по индексам (используется при чтении дерева). - /// \en Build the tree using indices (used during reading the tree). - void BuildTree(); - -private: - OBVIOUS_PRIVATE_COPY(MbModelTree) -}; - -//---------------------------------------------------------------------------------------- -/// \ru Операторы для записи дерева в xml формате. -/// \en Operators for outputing a tree to xml. -// --- - -//---------------------------------------------------------------------------------------- -/// \ru Запись узла дерева в xml формате. \en Tree node writing to xml. -// --- -MATH_FUNC( c3d::t_ofstream& ) operator << ( c3d::t_ofstream& file, const IModelTreeNode& node ); - -//---------------------------------------------------------------------------------------- -/// \ru Запись узла дерева в xml формате. \en Tree node writing to xml. -// --- -MATH_FUNC( c3d::t_ofstream& ) operator << ( c3d::t_ofstream& file, IModelTreeNode& node ); - -//---------------------------------------------------------------------------------------- -/// \ru Запись дерева в xml формате. \en Tree writing to xml. -// --- -MATH_FUNC( c3d::t_ofstream& ) operator << ( c3d::t_ofstream& file, const IModelTree& tree ); - -//---------------------------------------------------------------------------------------- -/// \ru Запись дерева в xml формате. \en Tree writing to xml. -// --- -MATH_FUNC( c3d::t_ofstream& ) operator << ( c3d::t_ofstream& file, IModelTree& tree ); - -} //namespace c3d - -#endif // __MODEL_TREE_H +////////////////////////////////////////////////////////////////////////////////////////// +/** \file + \brief \ru Реализация дерева модели + \en Implementation of Model Tree classes \~ +*/ +////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef __MODEL_TREE_H +#define __MODEL_TREE_H + + +#include +#include +#include +#include +#include +#include +#include +#include + +//---------------------------------------------------------------------------------------- +// \ru Реализация интерфейсов дерева модели. \en Implementation of Model Tree interfaces. +//---------------------------------------------------------------------------------------- + +namespace c3d // namespace C3D +{ +//---------------------------------------------------------------------------------------- +/** \brief \ru Узел дерева. + \en Tree node. \~ + \details \ru Узел дерева (может иметь несколько потомков). \n + \en Tree node (can have several children). \n \~ + \ingroup Base_Tools_IO +*/ +// --- +class MATH_CLASS MbTreeNode : public IModelTreeNode +{ + // \ru Временное хранилище для индексов потомков узла (используется при чтении узла). + // \en Temporary storage for indices of child nodes (used while reading the node). + std::vector m_childrenIndices; + // \ru Данные узла \en The node data. + MbItemData m_data; + // \ru Флаг, указывающий, открыт ли узел при проходе вглубь по дереву. + // \en Flag which indicates whether the node is entered during traversing into depth over the tree. + mutable bool m_open; + // \ru Флаг, указывающий, читать ли только часть узла. + // \en Flag which indicates whether to read only a part of the node. + mutable bool m_partial; +public: + MbTreeNode() : m_open(false), m_partial(false) {} + MbTreeNode ( const MbItemData& data ) : m_data(data), m_open(false), m_partial(false) {} + MbTreeNode ( const MbTreeNode& node ) : m_data(node.m_data), m_childrenIndices(node.m_childrenIndices), + m_open(node.m_open), m_partial(node.m_partial) { GetChildren() = node.GetChildren(); GetParents() = node.GetParents(); } + + ///--------- + /// \ru Методы IModelTreeNode. \en IModelTreeNode methods. + + /// \ru Доступ к данным узла. \en Access to the node data. + virtual MbItemData& GetData() { return m_data; } + virtual const MbItemData& GetData() const { return m_data; } + + /// \ru Доступ к позиции чтения/записм узла. \en Access to the node read/write position. + virtual ClusterReference& GetPosition() { return m_data.position; } + virtual const ClusterReference& GetPosition() const { return m_data.position; } + + /// \ru Узнать, читать ли только часть узла. + /// \en Check whether to read the node partially. + virtual bool PartialRead() const { return m_partial; }; + + /// \ru Установить признак частичного или полного чтения узла. + /// \en Set indication of full or partial node reading. + virtual void SetPartialRead ( bool partial ) const { m_partial = partial; }; + + /// \ru Записать узел. \en Write the node. + virtual writer & operator >> ( writer & ); + + /// \ru Прочитать узел. \en Read the node. + virtual reader & operator << ( reader & ); + + /// \ru Доступ ко все потомкам узла. \en Access to the all descendants of the node. + void GetAllDescendants ( std::set& nodes ) const; + + /// \ru Создать узел с данными текущего узла и добавить его в дерево. + /// Рекурсивно скопировать в дерево всех предков текущего узла с сохранением иерархии. + /// \en Create a node with data from the current node. + /// Copy recursively all parents of the node to the tree preserving the hierarchy. + MbTreeNode* CopyToTreeWithParents ( c3d::IModelTree* tree, bool partial ) const; + + /// \ru Создать узел с данными текущего узла и добавить его в дерево. + /// Рекурсивно скопировать в дерево всех потомков текущего узла с сохранением иерархии. + /// \en Create a node with data from the current node. + /// Copy recursively all children of the node to the tree preserving the hierarchy. + MbTreeNode* CopyToTreeWithChildren ( c3d::IModelTree* tree, bool partial ) const; + + /// \ru Доступ к флагу, который указывет, открыт ли узел при проходе вглубь по дереву + /// (false означает, что узел и его потомки уже пройдены или еще не обнаружены). + /// \en Access to the flag which indicates whether the node is entered during traversing into depth over the tree + /// (false - means that the node and its children are already leaved or are not met yet). + bool IsOpen() const { return m_open; } + void SetOpen ( bool open ) { m_open = open; } + void SetOpen ( bool open ) const { m_open = open; } + + // \ru Равенство определяется по id объекта MbItemData. + // \en Equality is defined by id field of MbItemData object. + bool operator == ( const MbTreeNode& node2 ) const + { + if ( !( GetData() == node2.GetData() ) || + GetData().id != node2.GetData().id ) + return false; + return true; + } + + // \ru Сравнение по полям объектов MbItemData. + // \en Comparison of filtering fields of MbItemData objects. + bool operator < ( const MbTreeNode& node2 ) const + { + return m_data < node2.m_data; + } + + friend class MbModelTree; + +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Узел дерева исполнений. + \en Embodiments tree node. \~ + \details \ru Узел дерева исполнений (может иметь несколько потомков). + \en Embodiments tree node (can have several children). + \ingroup Base_Tools_IO +*/ +// --- +class MATH_CLASS MbEmbodimentNode : public IEmbodimentNode +{ + const MbTreeNode * m_subtree; +public: + MbEmbodimentNode( const MbTreeNode* node ) : IEmbodimentNode(), m_subtree( node ) {} + ~MbEmbodimentNode() {} + + // \ru Выдать узел дерева модели, соответствующий данному исполнению. + // \en Get a model tree node which corresponds to a given embodiment. + virtual const IModelTreeNode * GetModelTreeNode() const { return m_subtree; } + + // \ru Доступ к информации об исполнении. \en Access to the embodiment info. + virtual const MbItemData& GetEmbodimentData() const { C3D_ASSERT( m_subtree != NULL ); return m_subtree->GetData(); } + + // \ru Построить дерево модели, которое содержится в данном исполнении. + // \en Build a tree of a model which is contained in a given embodiment. + virtual std_unique_ptr GetEmbodiment() const; + +private: + MbEmbodimentNode(); +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Дерево геометрической модели. + \en Tree of geometric model. \~ + \details \ru Дерево геометрической модели. (может иметь несколько корней). + \en Tree of geometric model (can have several roots). \n \~ + \ingroup Base_Tools_IO +*/ +// --- +class MATH_CLASS MbModelTree : public IModelTree +{ +private: + // \ru Временное хранилище для индексов корней дерева (используется при чтении дерева). + // \en Temporary storage for indices of the tree roots (used while reading the tree). + std::vector m_rootsIndices; + // \ru Все узлы дерева, упорядоченные по данным. + // \en All nodes of the tree, ordered by data. + std::set m_filteredNodes; + // \ru Стек узлов, открытых при чтении/записи дерева. + // \en Stack of nodes opened during reading/writing the tree. + std::stack m_nestedNodesStack; + // \ru Все узлы дерева, упорядоченные по ID. + // \en All nodes of the tree, ordered by ID. + std::map m_indexToNode; // \ru Вспомогательный массив. \en Auxiliary map. + VERSION m_currentVersion; + IEmbodimentTree m_embTree; // \ru Дерево исполнений. \en Embodiment tree. +public: + + // \ru Конструктор. \en Constructor. + MbModelTree(); + + // \ru Деструктор. \en Destructor. + virtual ~MbModelTree(); + + ///--------- + /// \ru Методы IModelTreeNode. \en IModelTreeNode methods. + + /// \ru Создать узел по данными и добавить в дерево. \en Create a node by data and add to the tree. + virtual void AddNode ( const TapeBase* mem, const ClusterReference& ref ); + + /// \ru Закрыть узел (удалить узел из стека, так что родительский узел станет текущим). + /// \en Close the node (remove it from the stack so that its parent becomes the current node). + virtual void CloseNode ( const TapeBase* mem ); + + // \ru Построить дерево из узлов, выбранных по фильтрам. В случае дерева исполнений, функция работает с первым исполнением. + // \en Build a tree with nodes, selected by filters. In case of embodiment tree, the function works with the first embodiment. + virtual std_unique_ptr GetFilteredTree ( const std::vector& filters ) const; + + // \ru Построить дерево по заданным узлам. Не применимо для дерева исполнений (в этом случае возвращает NULL). + // \en Build a tree for given nodes. Not applicable to embodiment tree (in this case, returns NULL). + virtual std_unique_ptr GetFilteredTree ( std::vector& nodes ) const; + + // \ru Выдать указатель на дерево исполнений. Выдает NULL, если не применимо (нет исполнений). + // \en Get pointer to embodiments tree. Return NULL if not applicable (no embodiments). + virtual const IEmbodimentTree* GetEmbodimentsTree() const { return GetType() == mtt_Embodiment ? &m_embTree : NULL; } + + /// \ru Версия дерева. \en Tree version. + virtual VERSION GetVersion() { return m_currentVersion; } + virtual void SetVersion( VERSION version ) { m_currentVersion = version; } + + /// \ru Записать дерево. \en Write the tree. + virtual writer & operator >> ( writer & ); + + /// \ru Прочитать дерево. \en Read the tree. + virtual reader & operator << ( reader & ); + + ///--------- + + /// \ru Добавить узел с данными из указанного узла, если узел с такими данными не существует. + /// \param node - узел с данными. + /// \param added - заполняется, если ненулевой (true - узел добавлен, false - узел уже существует). + /// \return - возвращает указатель на узел дерева. + /// \en Add a node with the data from the given node if a node with such data does not exist. + /// \param node - a node with data. + /// \param added - filled if non-null (true - if a node added, false - a node already exists). + /// \return - a pointer to the tree node. + MbTreeNode* AddNode ( const MbTreeNode& node, bool* added = NULL ); + + /// \ru Добавить узел с указанными данными, если узел с такими данными не существует. + /// \param node - данные. + /// \param added - заполняется, если ненулевой (true - узел добавлен, false - узел уже существует). + /// \return - возвращает указатель на узел дерева. + /// \en Add a node with the given if a node with such data does not exist. + /// \param node - a data. + /// \param added - filled if non-null (true - if a node added, false - a node already exists). + /// \return - a pointer to the tree node. + MbTreeNode* AddNode ( const MbItemData& data, bool* added = NULL ); + + /// \ru Доступ к узлам дерева, упорядоченным по данным. + /// \en Access to nodes of the tree, ordered by data. + std::set& GetFilteredNodes() { return m_filteredNodes; } + const std::set& GetFilteredNodes() const { return m_filteredNodes; } + + // \ru Добавить в корень текущего дерева указанное поддерево. \en Add a given subtree to the current tree root. + const void AddSubtree( IModelTree* tree, const IModelTreeNode* node ) const; + + /// \ru Заполнить массив корней дерева. + /// \en Fill the tree roots. + void FillRoots(); + +protected: + /// \ru Добавить ветвь в дерево: + /// \param branch - листовой узел с ветвью дерева, ведущей к нему, начиная с корневого узла дерева; + /// \param partial - определяет тип чтения листового узла (частичное или полное). + /// \en Add a branch to the tree: + /// \param branch - a leaf node with the tree branch, leading to it; + /// \param partial - defines partial or full read of the leaf node. + void AddBranch( const NodeBranch& branch, bool partial ); + + /// \ru Получить уникальные узлы для данного набора узлов. Проходятся все заданные узлы и исключаются те, + /// которые являются потомками заданных узлов (и будут прочитаны, как их часть). + /// Таким образом, результат будет содержать узлы поддеревьев, содержащих все заданные узлы. + /// \en Get unique nodes for given set of nodes. Walk through the given nodes and exclude nodes, + /// which are children of other given nodes (and will be read as a part of them). + /// Thus, the result set of nodes will represent the roots of subtrees containing all given nodes. + std::vector GetUniqueNodes ( std::vector& nodes ) const; + + + // \ru Построить дерево по заданным узлам без проверки типа. + // \en Build a tree for given nodes without type check. + const IModelTree* GetFilteredTreeEx( std::vector& nodes ) const; + + /// \ru Построить дерево по индексам (используется при чтении дерева). + /// \en Build the tree using indices (used during reading the tree). + void BuildTree(); + +private: + OBVIOUS_PRIVATE_COPY(MbModelTree) +}; + +//---------------------------------------------------------------------------------------- +/// \ru Операторы для записи дерева в xml формате. +/// \en Operators for outputing a tree to xml. +// --- + +//---------------------------------------------------------------------------------------- +/// \ru Запись узла дерева в xml формате. \en Tree node writing to xml. +// --- +MATH_FUNC( c3d::t_ofstream& ) operator << ( c3d::t_ofstream& file, const IModelTreeNode& node ); + +//---------------------------------------------------------------------------------------- +/// \ru Запись узла дерева в xml формате. \en Tree node writing to xml. +// --- +MATH_FUNC( c3d::t_ofstream& ) operator << ( c3d::t_ofstream& file, IModelTreeNode& node ); + +//---------------------------------------------------------------------------------------- +/// \ru Запись дерева в xml формате. \en Tree writing to xml. +// --- +MATH_FUNC( c3d::t_ofstream& ) operator << ( c3d::t_ofstream& file, const IModelTree& tree ); + +//---------------------------------------------------------------------------------------- +/// \ru Запись дерева в xml формате. \en Tree writing to xml. +// --- +MATH_FUNC( c3d::t_ofstream& ) operator << ( c3d::t_ofstream& file, IModelTree& tree ); + +} //namespace c3d + +#endif // __MODEL_TREE_H diff --git a/C3d/Include/model_tree_data.h b/C3d/Include/model_tree_data.h index cfbd831..5907391 100644 --- a/C3d/Include/model_tree_data.h +++ b/C3d/Include/model_tree_data.h @@ -1,809 +1,822 @@ -////////////////////////////////////////////////////////////////////////////////////////// -/** \file - \brief \ru Реализация данных узла дерева модели - \en Implementation of data of Model Tree node \~ -*/ -////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef __MODEL_TREE_DATA_H -#define __MODEL_TREE_DATA_H - - -#include -#include -#include -#include -#include -#include -#include -#include - -//---------------------------------------------------------------------------------------- -// \ru Реализация пользовательских данных узла дерева модели. -// \en Implementation of user data of the model tree node. -//---------------------------------------------------------------------------------------- - -namespace c3d // namespace C3D -{ -//---------------------------------------------------------------------------------------- -/** \brief \ru Тип пользовательских данных узла дерева модели. - \en A type of user data of the model tree node. \~ - \details \ru \ru Тип пользовательских данных узла дерева модели. - \en A type of user data of the model tree node. \~ - \ingroup Base_Tools_IO -*/ -// --- -enum MbeItemDataType -{ - idtBool, // bool - idtInteger, // int - idtDouble, // double - idtString, // c3d::string_t - - // \ru Данные атрибутов, для которых хранится тип и значение атрибута. - // \en Attributes data which keeps attribute type and value. - idtAttrBool, // MbBoolAttribute (bool) - idtAttrInt, // MbIntAttribute (int) - idtAttrDouble, // MbDoubleAttribute (double) - idtAttrString, // MbStringAttribute (c3d::string_t) - idtAttrInt64, // MbInt64Attribute (int64) - idtAttrIdentifier, // MbIdentifier (int32) - idtAttrColor, // MbColor (uint32) - idtAttrWidth, // MbWidth (int) - idtAttrStyle, // MbStyle (int) - idtAttrSelected, // MbSelected (bool) - idtAttrVisible, // MbVisible (bool) - idtAttrChanged, // MbChanged (bool) - idtAttrDencity, // MbDencity (double) - idtAttrUpdateStamp, // MbUpdateStamp (uint32) - idtAttrAnchor, // MbAnchorAttribute (uint8) - - // \ru Данные сложных атрибутов, для которых хранится только тип. - // \en Complex attributes data which keeps attribute type only. - idtAttrVisual, // MbVisual - idtAttrWireCount, // MbWireCount - idtAttrName, // MbNameAttribute - idtAttrGeom, // MbGeomAttribute - idtAttrStampRib, // MbStampRibAttribute - idtAttrModelInfo, // MbModelInfo - idtAttrPersonOrganizationInfo, // MbPersonOrganizationInfo - - // \ru Специальная обработка атрибута - хранится свойство Идентификатор (Обозначение). - // \en Special processing of attribute - keep property Identifier. - idtAttrProductInfo, // MbProductInfo - - // \ru Данные сложных атрибутов, для которых хранится только тип (продолжение). - // \en Complex attributes data which keeps attribute type only (continuation). - idtAttrSTEPTextDescription, // MbSTEPTextDescription - idtAttrSTEPReferenceHolder, // MbSTEPReferenceHolder - idtAttrBinary, // MbBinaryAttribute - - // \ru Атрибут исполнения (хранится тип и пара значений). - // \en Attribute of embodiment (keeps type and values pair). - idtAttrEmbodiment, // MbEmbodimentAttribute - - // \ru Новый тип должен добавляться непосредственно перед idtCount (после всех определенных ранее типов). - // \en New type should be added just before idtCount (after all types defined before). - idtCount // \ru Число поддерживаемых типов данных. \en Number of supported data types -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Базовый класс для пользовательских данных узла дерева. - \en A base class for user data of a tree node. \~ - \details \ru Базовый класс для пользовательских данных узла дерева. - \en A base class for user data of a tree node. \~ - \ingroup Base_Tools_IO -*/ -// --- -class MATH_CLASS ItemDataBase -{ -// \ru Приведение объекта 'item' к типу данных 'Type' (тип проверен заранее). -// \en Cast object 'item' to the data type 'Type' (type already verified). -#define CAST(Type, item) dynamic_cast(const_cast(item)) - -protected: - bool m_filterByType; // \ru Фильтр только по типу (значение данных игнорируется). \en Filter by type only (ignore data value). - -public: - - ItemDataBase() : m_filterByType(false) {} - virtual ~ItemDataBase() {} - - // \ru Тип данных. \en The data type. - virtual MbeItemDataType Type() const = 0; - - // \ru Размер записи данных в поток. \en The data size in the stream. - virtual size_t Size( writer& ) const = 0; - - // \ru Создать данные заданного типа. \en Create data of the given type. - static ItemDataBase* Create( MbeItemDataType type ); - - // \ru Создать копию данных. \en Create data copy. - static ItemDataBase* Create( ItemDataBase* item ); - - // \ru Прочитать данные. \en Read data. - reader& operator << ( reader& in ); - - // \ru Записать данные. \en Write data. - writer& operator >> ( writer& out ) const; - - // \ru Сравнить данные. \en Compare data. - bool operator == ( ItemDataBase* item2 ) const; - - /// \ru Сравнить данные. \en Compare data. - bool operator < ( ItemDataBase* item2 ) const; - - // \ru Выдать/установить флаг сравнения только по типу (значение игнорируется). - // \en Get/set flag for comparing by type only (value ignored). - bool IgnoreValue() const { return m_filterByType; } - void SetIgnoreValue( bool ignore ) { m_filterByType = ignore; } -}; - - -//---------------------------------------------------------------------------------------- -/// \ru Создать объект пользовательских данных для атрибута. Возвращает NULL, если данный атрибут не поддерживается деревом модели. -/// \en Create user data object for the attribute. Return NULL if this attribute is not supported in the model tree. -//--- -MATH_FUNC( ItemDataBase* ) CreateAttributeData( MbAttribute* attr ); - -//---------------------------------------------------------------------------------------- -/// \ru Прочитать атрибуты для узла дерева. -/// \en Read attributes for the model tree node. -//--- -MATH_FUNC( std_unique_ptr ) GetTreeNodeAttributes( const IModelTreeNode * node, reader& in ); - - -//---------------------------------------------------------------------------------------- -// \ru Функции чтения и записи для пользовательских данных атрибута. -// \en Functions of reading and writing for attribute user data. -//--- -#define MTREE_PERSISTENT_DATA_OBJ(Class) \ - public: \ - static void Read(reader& in, Class* item) { in >> item->m_value; } \ - static void Write(writer& out, const Class* item) { out << item->m_value; } - -//---------------------------------------------------------------------------------------- -// \ru Определение типа для пользовательских данных атрибута. -// \en Type definition for attribute user data. -//--- -#define MTREE_DEFINE_DATA_TYPE(type) \ - public: \ - virtual MbeItemDataType Type() const { return type; } - -//---------------------------------------------------------------------------------------- -/// \ru Определение размера записи пользовательских данных атрибута в поток, как sizeof. -/// \en Definition of data size of attribute user data in the stream as sizeof. -//--- -#define MTREE_DEFINE_DATA_SIZE_STD(data) \ - public: \ - virtual size_t Size(writer&) const { return sizeof(data); } - -//---------------------------------------------------------------------------------------- -// \ru Макрос для объявления класса без данных (dataless) для атрибута. -// \en Macro for defining attribute dataless class. -//--- -#define MTREE_ATTR_DATALESS_CLASS(Class,ClassType) \ -class Class : public ItemDataBase { \ -public: \ - virtual ~Class() {} \ - virtual size_t Size( writer& ) const { return 0; } \ - virtual MbeItemDataType Type() const { return ClassType; } \ - static void Read( reader&, Class* ) {} \ - static void Write( writer&, const Class* ) {} \ -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Свойство типа bool. - \en Bool property. \~ - \details \ru Свойство типа bool. - \en Bool property. \~ - \ingroup Base_Tools_IO -*/ -// --- -class ItemDataBool : public ItemDataBase -{ -public: - bool m_value; - - ItemDataBool() : m_value( false ) {} - ItemDataBool( bool value ) : m_value( value ) {} - virtual ~ItemDataBool() {} - - MTREE_DEFINE_DATA_SIZE_STD(m_value) - MTREE_DEFINE_DATA_TYPE(idtBool) - MTREE_PERSISTENT_DATA_OBJ(ItemDataBool) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Свойство типа integer. - \en Integer property. \~ - \details \ru Свойство типа integer. - \en Integer property. \~ - \ingroup Base_Tools_IO -*/ -// --- -class ItemDataInteger : public ItemDataBase -{ -public: - int m_value; - - ItemDataInteger() : m_value( 0 ) {} - ItemDataInteger( int value ) : m_value( value ) {} - virtual ~ItemDataInteger() {} - - MTREE_DEFINE_DATA_SIZE_STD(int32) - MTREE_DEFINE_DATA_TYPE(idtInteger) - MTREE_PERSISTENT_DATA_OBJ(ItemDataInteger) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Свойство типа double. - \en Double property. \~ - \details \ru Свойство типа double. - \en Double property. \~ - \ingroup Base_Tools_IO -*/ -// --- -class ItemDataDouble : public ItemDataBase -{ -public: - double m_value; - - ItemDataDouble() : m_value( 0 ) {} - ItemDataDouble( double value ) : m_value( value ) {} - virtual ~ItemDataDouble() {} - - MTREE_DEFINE_DATA_SIZE_STD(m_value) - MTREE_DEFINE_DATA_TYPE(idtDouble) - MTREE_PERSISTENT_DATA_OBJ(ItemDataDouble) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Свойство типа string. - \en String property. \~ - \details \ru Свойство типа string. - \en String property. \~ - \ingroup Base_Tools_IO -*/ -// --- -class ItemDataString : public ItemDataBase -{ -public: - c3d::string_t m_value; - - ItemDataString() {} - ItemDataString( c3d::string_t value ) : m_value( value ) {} - virtual ~ItemDataString() {} - - /// \ru Размер записи данных в поток. \en The data size in the stream. - virtual size_t Size( writer& out ) const { return out.__lenWchar( m_value.c_str() ); } - - MTREE_DEFINE_DATA_TYPE(idtString) - MTREE_PERSISTENT_DATA_OBJ(ItemDataString) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута bool. - \en Data of Bool attribute. \~ - \details \ru Данные атрибута bool. - \en Data of Bool attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrBool : public ItemDataBool -{ -public: - ItemAttrBool() : ItemDataBool() {} - ItemAttrBool( bool value ) : ItemDataBool( value ) {} - - MTREE_DEFINE_DATA_TYPE(idtAttrBool) - MTREE_PERSISTENT_DATA_OBJ(ItemAttrBool) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута integer. - \en Data of Integer attribute. \~ - \details \ru Данные атрибута integer. - \en Data of Integer attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrInteger : public ItemDataInteger -{ -public: - ItemAttrInteger() : ItemDataInteger() {} - ItemAttrInteger( int value ) : ItemDataInteger( value ) {} - - MTREE_DEFINE_DATA_TYPE(idtAttrInt) - MTREE_PERSISTENT_DATA_OBJ(ItemAttrInteger) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута double. - \en Data of Double attribute. \~ - \details \ru Данные атрибута double. - \en Data of Double attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrDouble : public ItemDataDouble -{ -public: - ItemAttrDouble() : ItemDataDouble() {} - ItemAttrDouble( double value ) : ItemDataDouble( value ) {} - - MTREE_DEFINE_DATA_TYPE(idtAttrDouble) - MTREE_PERSISTENT_DATA_OBJ(ItemAttrDouble) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута string. - \en Data of String attribute. \~ - \details \ru Данные атрибута string. - \en Data of String attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrString : public ItemDataString -{ -public: - ItemAttrString() : ItemDataString() {} - ItemAttrString( c3d::string_t value ) : ItemDataString( value ) {} - - MTREE_DEFINE_DATA_TYPE(idtAttrString) - MTREE_PERSISTENT_DATA_OBJ(ItemAttrString) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута MbProductInfo. - \en Data of MbProductInfo attribute. \~ - \details \ru Данные атрибута MbProductInfo. - \en Data of MbProductInfo attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrProductInfo : public ItemDataString -{ -public: - ItemAttrProductInfo() : ItemDataString() {} - ItemAttrProductInfo( c3d::string_t value ) : ItemDataString( value ) {} - - MTREE_DEFINE_DATA_TYPE( idtAttrProductInfo ) - - static void Read( reader &, ItemAttrProductInfo * ) {} - static void Write( writer &, const ItemAttrProductInfo * ) {} -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута int64. - \en Data of int64 attribute. \~ - \details \ru Данные атрибута int64. - \en Data of int64 attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrInt64 : public ItemDataBase -{ -public: - int64 m_value; - - ItemAttrInt64() : m_value( 0 ) {} - ItemAttrInt64( int64 value ) : m_value( value ) {} - - MTREE_DEFINE_DATA_SIZE_STD(m_value) - MTREE_DEFINE_DATA_TYPE(idtAttrInt64) - MTREE_PERSISTENT_DATA_OBJ(ItemAttrInt64) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута Идентификатор. - \en Data of Identifier attribute. \~ - \details \ru Данные атрибута Идентификатор. - \en Data of Identifier attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrIdentifier : public ItemDataBase -{ -public: - int32 m_value; - - ItemAttrIdentifier() : m_value( 0 ) {} - ItemAttrIdentifier( int32 value ) : m_value( value ) {} - - MTREE_DEFINE_DATA_SIZE_STD(m_value) - MTREE_DEFINE_DATA_TYPE(idtAttrIdentifier) - MTREE_PERSISTENT_DATA_OBJ(ItemAttrIdentifier) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута Цвет. - \en Data of Color attribute. \~ - \details \ru Данные атрибута Цвет. - \en Data of Color attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrColor : public ItemDataBase -{ -public: - uint32 m_value; - - ItemAttrColor() : m_value( 0 ) {} - ItemAttrColor( uint32 value ) : m_value( value ) {} - - MTREE_DEFINE_DATA_SIZE_STD(m_value) - MTREE_DEFINE_DATA_TYPE(idtAttrColor) - MTREE_PERSISTENT_DATA_OBJ(ItemAttrColor) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута Толщина. - \en Data of Width attribute. \~ - \details \ru Данные атрибута Толщина. - \en Data of Width attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrWidth : public ItemDataInteger -{ -public: - ItemAttrWidth() : ItemDataInteger() {} - ItemAttrWidth( int value ) : ItemDataInteger( value ) {} - - MTREE_DEFINE_DATA_TYPE(idtAttrWidth) - MTREE_PERSISTENT_DATA_OBJ(ItemAttrWidth) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута Стиль. - \en Data of Style attribute. \~ - \details \ru Данные атрибута Стиль. - \en Data of Style attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrStyle : public ItemDataInteger -{ -public: - ItemAttrStyle() : ItemDataInteger() {} - ItemAttrStyle( int value ) : ItemDataInteger( value ) {} - - MTREE_DEFINE_DATA_TYPE(idtAttrStyle) - MTREE_PERSISTENT_DATA_OBJ(ItemAttrStyle) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута Селектированность. - \en Data of Selection attribute. \~ - \details \ru Данные атрибута Селектированность. - \en Data of Selection attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrSelected : public ItemDataBool -{ -public: - ItemAttrSelected() : ItemDataBool() {} - ItemAttrSelected( bool value ) : ItemDataBool( value ) {} - - MTREE_DEFINE_DATA_TYPE(idtAttrSelected) - MTREE_PERSISTENT_DATA_OBJ(ItemAttrSelected) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута Видимость. - \en Data of Visibility attribute. \~ - \details \ru Данные атрибута Видимость. - \en Data of Visibility attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrVisible : public ItemDataBool -{ -public: - ItemAttrVisible() : ItemDataBool() {} - ItemAttrVisible( bool value ) : ItemDataBool( value ) {} - - MTREE_DEFINE_DATA_TYPE(idtAttrVisible) - MTREE_PERSISTENT_DATA_OBJ(ItemAttrVisible) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута Изменённость. - \en Data of Modification attribute. \~ - \details \ru Данные атрибута Изменённость. - \en Data of Modification attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrChanged : public ItemDataBool -{ -public: - ItemAttrChanged() : ItemDataBool() {} - ItemAttrChanged( bool value ) : ItemDataBool( value ) {} - - MTREE_DEFINE_DATA_TYPE(idtAttrChanged) - MTREE_PERSISTENT_DATA_OBJ(ItemAttrChanged) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута Плотность. - \en Data of Dencity attribute. \~ - \details \ru Данные атрибута Плотность. - \en Data of Dencity attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrDencity : public ItemDataDouble -{ -public: - ItemAttrDencity() : ItemDataDouble( 0 ) {} - ItemAttrDencity( double value ) : ItemDataDouble( value ) {} - - MTREE_DEFINE_DATA_TYPE(idtAttrDencity) - MTREE_PERSISTENT_DATA_OBJ(ItemAttrDencity) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута Метка времени обновления. - \en Data of Update timestamp attribute. \~ - \details \ru Данные атрибута Метка времени обновления. - \en Data of Update timestamp attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrUpdateStamp : public ItemDataBase -{ -public: - uint32 m_value; - - ItemAttrUpdateStamp() : m_value( 0 ) {} - ItemAttrUpdateStamp( uint32 value ) : m_value( value ) {} - - MTREE_DEFINE_DATA_SIZE_STD(m_value) - MTREE_DEFINE_DATA_TYPE(idtAttrUpdateStamp) - MTREE_PERSISTENT_DATA_OBJ(ItemAttrUpdateStamp) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута Якорь. - \en Data of Anchor attribute. \~ - \details \ru Данные атрибута Якорь. - \en Data of Anchor attribute. \~ -\ingroup Base_Tools_IO -*/ -// --- -class ItemAttrAnchor : public ItemDataBase -{ -public: - uint8 m_value; - - ItemAttrAnchor() : m_value( 0 ) {} - ItemAttrAnchor( uint8 value ) : m_value( value ) {} - - MTREE_DEFINE_DATA_SIZE_STD(m_value) - MTREE_DEFINE_DATA_TYPE(idtAttrAnchor) - MTREE_PERSISTENT_DATA_OBJ(ItemAttrAnchor) -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные атрибута исполнения. - \en Data of embodiment attribute. \~ - \details \ru Данные атрибута исполнения. - \en Data of embodiment attribute. \~ - \ingroup Base_Tools_IO -*/ -// --- -class ItemAttrEmbodiment : public ItemDataBase -{ -public: - typedef std::pair, bool> EmbData; - EmbData m_value; - - ItemAttrEmbodiment() : m_value( std::pair(0, 0),false ) {} - ItemAttrEmbodiment( const EmbData& value ) : m_value( value ) {} - - MTREE_DEFINE_DATA_SIZE_STD( m_value ) - MTREE_DEFINE_DATA_TYPE( idtAttrEmbodiment ) - static void Read( reader& in, ItemAttrEmbodiment* item ) { - in >> item->m_value.first.first; - in >> item->m_value.first.second; - if ( in.MathVersion() >= 0x13000010L ) - in >> item->m_value.second; - } - static void Write( writer& out, const ItemAttrEmbodiment* item ) { - out << item->m_value.first.first; - out << item->m_value.first.second; - if ( out.MathVersion() >= 0x13000010L ) - out << item->m_value.second; - } -}; - -//---------------------------------------------------------------------------------------- -/// \ru Объявление классов без данных для атрибутов. -/// \en Definition of attribute dataless classes. -//--- -MTREE_ATTR_DATALESS_CLASS(ItemAttrVisual, idtAttrVisual); -MTREE_ATTR_DATALESS_CLASS(ItemAttrWireCount, idtAttrWireCount); -MTREE_ATTR_DATALESS_CLASS(ItemAttrName, idtAttrName); -MTREE_ATTR_DATALESS_CLASS(ItemAttrGeom, idtAttrGeom); -MTREE_ATTR_DATALESS_CLASS(ItemAttrStampRib, idtAttrStampRib); -MTREE_ATTR_DATALESS_CLASS(ItemAttrModelInfo, idtAttrModelInfo); -MTREE_ATTR_DATALESS_CLASS(ItemAttrPersonOrganizationInfo, idtAttrPersonOrganizationInfo); -MTREE_ATTR_DATALESS_CLASS(ItemAttrSTEPTextDescription, idtAttrSTEPTextDescription); -MTREE_ATTR_DATALESS_CLASS(ItemAttrSTEPReferenceHolder, idtAttrSTEPReferenceHolder); -MTREE_ATTR_DATALESS_CLASS(ItemAttrBinary, idtAttrBinary); - -//---------------------------------------------------------------------------------------- -/** \brief \ru Контейнер для пользовательских данных узла дерева. - \en A container for user data of a tree node. \~ - \details \ru Контейнер для пользовательских данных узла дерева (владеет данными). - \en A container for user data of a tree node (owns the data). \~ - \ingroup Base_Tools_IO -*/ -// --- -class MATH_CLASS UserDataMap : public MultiMap -{ -public: - UserDataMap() {} - UserDataMap( const UserDataMap& other ); - - ~UserDataMap(); // \ru Владеет данными. \en Owns the data. - - /// \ru Оператор ==. \en Operator ==. - bool operator == ( const UserDataMap& other ) const; - - /// \ru Оператор <. \en Operator <. - bool operator < ( const UserDataMap& other ) const; - - /// \ru Оператор =. \en Operator =. - UserDataMap& operator = ( const UserDataMap& other ); -}; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Данные узла дерева. -\en Tree node data. \~ -\details \ru Данные узла дерева. \n -\en Tree node data. \n \~ -\ingroup Base_Tools_IO -*/ -// --- -struct MATH_CLASS MbItemData -{ - // \ru Признак наличия локальной системы координат. - // \en Token of local coordinate system presence. - enum PlacementPresenceToken - { - ppt_No = 0x01, - ppt_Yes = 0x02 - }; - - // \ru Уникальный ID узла в дереве модели. \en Unique id in the model tree. - // \ru Не учитывается при сравнении. \en Not considered during comparison. - mutable size_t id; - // \ru Тип объекта. \en Object type. - // \ru type==st_Undefined означает, что фильтр по типу не определен. - // \en type==st_Undefined means that filter is undefined. - MbeSpaceType type; - // \ru иИмя объекта. \en Object name. - // \ru name==SYS_MAX_UINT32 означает, что фильтр по имени не определен. - // \en name==SYS_MAX_UINT32 means that filter is undefined. - SimpleName name; - // \ru gabarit.IsEmpty()==true означает, что фильтр по габариту не определен. - // \en gabarit.IsEmpty()==true means that filter is undefined. - MbCube gabarit; - // \ru Позиция записи/чтения узла. \en Position for the node writing/reading. - // \ru position.IsValid()==false означает, что это поле не учитывается при сравнении. - // \en position.IsValid()==false means that this field is not considered during comparison. - ClusterReference position; - // \ru Локальная система координат объекта. \en Local coordinate system for the object. - // \ru Не учитывается при сравнении. \en Not considered during comparison. - MbPlacement3D placement; - - // \ru Кроме обязательных данных узла, описанных выше, можно задать пользовательские данные, - // которые содержатся в контейнере 'properties'. - // \en In addition to the mandatory node data, described above, it is possible to define user data, - // which is kept in the 'properties' container. - // \ru Контейнер для пользовательских данных узла. - // \en Container for user data of a node. - UserDataMap properties; - - - // \ru Конструкторы. \en Constructors. - MbItemData() : id( SYS_MAX_T ), type( st_Undefined ), name( SYS_MAX_UINT32 ) {} - MbItemData( MbeSpaceType t, SimpleName n, const MbCube& c, ClusterReference& pos ) : id( SYS_MAX_T ), type( t ), name( n ), gabarit( c ), position( pos ) {} - MbItemData( MbeSpaceType t, SimpleName n, const MbCube& c, ClusterReference& pos, const UserDataMap& prop ) : - id( SYS_MAX_T ), type( t ), name( n ), gabarit( c ), position( pos ), properties( prop ) {} - MbItemData( const MbItemData& data ) : id( data.id ), type( data.type ), - name( data.name ), gabarit( data.gabarit ), position( data.position ), - placement( data.placement ), properties( data.properties ) {} - - // \ru Признак пустых (неинициализированных) данных. \en Indicator of empty (uninitialized) data. - bool IsEmpty() const; - - // \ru Идентичность полей-фильтров (id не важен). - // \en Equality of filtering fields (id is irrelevant). - bool operator == ( const MbItemData& rt ) const; - - // \ru Сравнение полей-фильтров (id не важен). - // \en Comparison of filtering fields (id is irrelevant). - bool operator < ( const MbItemData& rt ) const; - - // \ru Специальное сравнение габаритов для сортировки объектов. - // \en Special comparison of bounding boxes for object sorting. - static bool CompareGabarits( const MbCube& a, const MbCube& b ); - - // \ru Запись и чтение. - // \en Writing and reading. - writer& operator >> ( writer & out ); - reader& operator << ( reader & out ); -}; - -//---------------------------------------------------------------------------------------- -/// \ru Чтение UserDataMap. \en UserDataMap reading. -// --- -inline reader& operator >> ( reader & in, UserDataMap& itemmap ) -{ - size_t typeCount = ::ReadCOUNT( in ); // \ru Количество типов данных в наборе. \en A number of types in the map. - - if ( in.good() ) { - for ( size_t i = 0; i < typeCount; i++ ) { - int t; - in >> t; - size_t typeSize = ::ReadCOUNT( in ); - C3D_ASSERT( t < (int)idtCount ); - if ( t < (int)idtCount ) { - MbeItemDataType type = (MbeItemDataType)t; // \ru Тип данных. \en A data type. - ItemDataBase* item = ItemDataBase::Create( type ); - *item << in; - itemmap.Associate( type, item ); - } - else { // skip unknown data - char* buff = new char[typeSize]; - in.readBytes( buff, typeSize ); - delete[] buff; - } - - if ( !in.good() ) { - in.setState( io::fail ); - break; - } - } - } - return in; -} - -//---------------------------------------------------------------------------------------- -/// \ru Запись UserDataMap. \en UserDataMap writing. -// --- -inline writer& operator << ( writer & out, const UserDataMap& itemmap ) -{ - size_t mapCount = itemmap.Count(); - ::WriteCOUNT( out, mapCount ); // \ru Количество типов данных в наборе. \en A number of types in the map. - - if ( out.good() && mapCount ) { - UserDataMap::Iterator curIter = itemmap.First(); - while ( !curIter.Empty() ) { - MbeItemDataType type = curIter.Key(); - ItemDataBase* item = curIter.Value(); - C3D_ASSERT( type < idtCount && item != NULL ); - if ( type < idtCount && item != NULL ) { - out << (int)type; // \ru Тип данных. \en A data type. - size_t dataSize = item->Size( out ); - ::WriteCOUNT( out, dataSize );// \ru Размер данных. \en Data size. - *item >> out; - } - if ( !out.good() ) { - out.setState( io::fail ); - break; - } - curIter++; - } - } - return out; -} - -} //namespace c3d - -#endif // __MODEL_TREE_DATA_H +////////////////////////////////////////////////////////////////////////////////////////// +/** \file + \brief \ru Реализация данных узла дерева модели + \en Implementation of data of Model Tree node \~ +*/ +////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef __MODEL_TREE_DATA_H +#define __MODEL_TREE_DATA_H + + +#include +#include +#include +#include +#include +#include +#include +#include + +//---------------------------------------------------------------------------------------- +// \ru Реализация пользовательских данных узла дерева модели. +// \en Implementation of user data of the model tree node. +//---------------------------------------------------------------------------------------- + +namespace c3d // namespace C3D +{ +//---------------------------------------------------------------------------------------- +/** \brief \ru Тип пользовательских данных узла дерева модели. + \en A type of user data of the model tree node. \~ + \details \ru \ru Тип пользовательских данных узла дерева модели. + \en A type of user data of the model tree node. \~ + \ingroup Base_Tools_IO +*/ +// --- +enum MbeItemDataType +{ + // \ru Простые пользовательские данные. \en Simple user data. + idtBool, // bool + idtInteger, // int + idtDouble, // double + idtString, // c3d::string_t + + // \ru Данные атрибутов. \en Attributes data. + + // \ru Данные атрибутов, для которых хранится тип и значение атрибута. + // \en Attributes data which keeps attribute type and value. + idtAttrBool, // MbBoolAttribute (bool) at_BoolAttribute + idtAttrInt, // MbIntAttribute (int) at_IntAttribute + idtAttrDouble, // MbDoubleAttribute (double) at_DoubleAttribute + idtAttrString, // MbStringAttribute (c3d::string_t) at_StringAttribute + idtAttrInt64, // MbInt64Attribute (int64) at_Int64Attribute + idtAttrIdentifier, // MbIdentifier (int32) at_Identifier + idtAttrColor, // MbColor (uint32) at_Color + idtAttrWidth, // MbWidth (int) at_Width + idtAttrStyle, // MbStyle (int) at_Style + idtAttrSelected, // MbSelected (bool) at_Selected + idtAttrVisible, // MbVisible (bool) at_Visible + idtAttrChanged, // MbChanged (bool) at_Changed + idtAttrDencity, // MbDencity (double) at_Dencity + idtAttrUpdateStamp, // MbUpdateStamp (uint32) at_UpdateStamp + idtAttrAnchor, // MbAnchorAttribute (uint8) at_AnchorAttribute + + // \ru Данные сложных атрибутов, для которых хранится только тип. + // \en Complex attributes data which keeps attribute type only. + idtAttrVisual, // MbVisual at_Visual + idtAttrWireCount, // MbWireCount at_WireCount + idtAttrName, // MbNameAttribute at_NameAttribute + idtAttrGeom, // MbGeomAttribute at_GeomAttribute + idtAttrStampRib, // MbStampRibAttribute at_StampRibAttribute + idtAttrModelInfo, // MbModelInfo at_ModelInfo + idtAttrPersonOrganizationInfo, // MbPersonOrganizationInfo at_PersonOrganizationInfo + + // \ru Специальная обработка атрибута - хранится свойство Идентификатор (Обозначение). + // \en Special processing of attribute - keep property Identifier. + idtAttrProductInfo, // MbProductInfo at_ProductInfo + + // \ru Данные сложных атрибутов, для которых хранится только тип (продолжение 1). + // \en Complex attributes data which keeps attribute type only (continuation 1). + idtAttrSTEPTextDescription, // MbSTEPTextDescription at_STEPTextDescription + idtAttrSTEPReferenceHolder, // MbSTEPReferenceHolder at_STEPReferenceHolder + idtAttrBinary, // MbBinaryAttribute at_BinaryAttribute + + // \ru Специальный атрибут исполнения (хранится тип и пара значений). + // \en Special attribute of embodiment (keeps type and values pair). + idtAttrEmbodiment, // MbEmbodimentAttribute at_Embodiment + + // \ru Данные сложных атрибутов, для которых хранится только тип (продолжение 2). + // \en Complex attributes data which keeps attribute type only (continuation 2). + idtAttrStrains, // MbStrains at_Strains + idtAttrElasticity, // MbElasticity at_Elasticity + idtAttrSheetFlanging, // MbSheetFlangingAttribute at_SweptFlangeAttribute + + + // \ru Новый тип должен добавляться непосредственно перед idtCount (после всех определенных ранее типов). + // \en New type should be added just before idtCount (after all types defined before). + idtCount // \ru Число поддерживаемых типов данных. \en Number of supported data types +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Базовый класс для пользовательских данных узла дерева. + \en A base class for user data of a tree node. \~ + \details \ru Базовый класс для пользовательских данных узла дерева. + \en A base class for user data of a tree node. \~ + \ingroup Base_Tools_IO +*/ +// --- +class MATH_CLASS ItemDataBase +{ +// \ru Приведение объекта 'item' к типу данных 'Type' (тип проверен заранее). +// \en Cast object 'item' to the data type 'Type' (type already verified). +#define CAST(Type, item) dynamic_cast(const_cast(item)) + +protected: + bool m_filterByType; // \ru Фильтр только по типу (значение данных игнорируется). \en Filter by type only (ignore data value). + +public: + + ItemDataBase() : m_filterByType(false) {} + virtual ~ItemDataBase() {} + + // \ru Тип данных. \en The data type. + virtual MbeItemDataType Type() const = 0; + + // \ru Размер записи данных в поток. \en The data size in the stream. + virtual size_t Size( writer& ) const = 0; + + // \ru Создать данные заданного типа. \en Create data of the given type. + static ItemDataBase* Create( MbeItemDataType type ); + + // \ru Создать копию данных. \en Create data copy. + static ItemDataBase* Create( ItemDataBase* item ); + + // \ru Прочитать данные. \en Read data. + reader& operator << ( reader& in ); + + // \ru Записать данные. \en Write data. + writer& operator >> ( writer& out ) const; + + // \ru Сравнить данные. \en Compare data. + bool operator == ( ItemDataBase* item2 ) const; + + /// \ru Сравнить данные. \en Compare data. + bool operator < ( ItemDataBase* item2 ) const; + + // \ru Выдать/установить флаг сравнения только по типу (значение игнорируется). + // \en Get/set flag for comparing by type only (value ignored). + bool IgnoreValue() const { return m_filterByType; } + void SetIgnoreValue( bool ignore ) { m_filterByType = ignore; } +}; + + +//---------------------------------------------------------------------------------------- +/// \ru Создать объект пользовательских данных для атрибута. Возвращает NULL, если данный атрибут не поддерживается деревом модели. +/// \en Create user data object for the attribute. Return NULL if this attribute is not supported in the model tree. +//--- +MATH_FUNC( ItemDataBase* ) CreateAttributeData( MbAttribute* attr ); + +//---------------------------------------------------------------------------------------- +/// \ru Прочитать атрибуты для узла дерева. +/// \en Read attributes for the model tree node. +//--- +MATH_FUNC( std_unique_ptr ) GetTreeNodeAttributes( const IModelTreeNode * node, reader& in ); + + +//---------------------------------------------------------------------------------------- +// \ru Функции чтения и записи для пользовательских данных атрибута. +// \en Functions of reading and writing for attribute user data. +//--- +#define MTREE_PERSISTENT_DATA_OBJ(Class) \ + public: \ + static void Read(reader& in, Class* item) { in >> item->m_value; } \ + static void Write(writer& out, const Class* item) { out << item->m_value; } + +//---------------------------------------------------------------------------------------- +// \ru Определение типа для пользовательских данных атрибута. +// \en Type definition for attribute user data. +//--- +#define MTREE_DEFINE_DATA_TYPE(type) \ + public: \ + virtual MbeItemDataType Type() const { return type; } + +//---------------------------------------------------------------------------------------- +/// \ru Определение размера записи пользовательских данных атрибута в поток, как sizeof. +/// \en Definition of data size of attribute user data in the stream as sizeof. +//--- +#define MTREE_DEFINE_DATA_SIZE_STD(data) \ + public: \ + virtual size_t Size(writer&) const { return sizeof(data); } + +//---------------------------------------------------------------------------------------- +// \ru Макрос для объявления класса без данных (dataless) для атрибута. +// \en Macro for defining attribute dataless class. +//--- +#define MTREE_ATTR_DATALESS_CLASS(Class,ClassType) \ +class Class : public ItemDataBase { \ +public: \ + virtual ~Class() {} \ + virtual size_t Size( writer& ) const { return 0; } \ + virtual MbeItemDataType Type() const { return ClassType; } \ + static void Read( reader&, Class* ) {} \ + static void Write( writer&, const Class* ) {} \ +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Свойство типа bool. + \en Bool property. \~ + \details \ru Свойство типа bool. + \en Bool property. \~ + \ingroup Base_Tools_IO +*/ +// --- +class ItemDataBool : public ItemDataBase +{ +public: + bool m_value; + + ItemDataBool() : m_value( false ) {} + ItemDataBool( bool value ) : m_value( value ) {} + virtual ~ItemDataBool() {} + + MTREE_DEFINE_DATA_SIZE_STD(m_value) + MTREE_DEFINE_DATA_TYPE(idtBool) + MTREE_PERSISTENT_DATA_OBJ(ItemDataBool) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Свойство типа integer. + \en Integer property. \~ + \details \ru Свойство типа integer. + \en Integer property. \~ + \ingroup Base_Tools_IO +*/ +// --- +class ItemDataInteger : public ItemDataBase +{ +public: + int m_value; + + ItemDataInteger() : m_value( 0 ) {} + ItemDataInteger( int value ) : m_value( value ) {} + virtual ~ItemDataInteger() {} + + MTREE_DEFINE_DATA_SIZE_STD(int32) + MTREE_DEFINE_DATA_TYPE(idtInteger) + MTREE_PERSISTENT_DATA_OBJ(ItemDataInteger) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Свойство типа double. + \en Double property. \~ + \details \ru Свойство типа double. + \en Double property. \~ + \ingroup Base_Tools_IO +*/ +// --- +class ItemDataDouble : public ItemDataBase +{ +public: + double m_value; + + ItemDataDouble() : m_value( 0 ) {} + ItemDataDouble( double value ) : m_value( value ) {} + virtual ~ItemDataDouble() {} + + MTREE_DEFINE_DATA_SIZE_STD(m_value) + MTREE_DEFINE_DATA_TYPE(idtDouble) + MTREE_PERSISTENT_DATA_OBJ(ItemDataDouble) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Свойство типа string. + \en String property. \~ + \details \ru Свойство типа string. + \en String property. \~ + \ingroup Base_Tools_IO +*/ +// --- +class ItemDataString : public ItemDataBase +{ +public: + c3d::string_t m_value; + + ItemDataString() {} + ItemDataString( c3d::string_t value ) : m_value( value ) {} + virtual ~ItemDataString() {} + + /// \ru Размер записи данных в поток. \en The data size in the stream. + virtual size_t Size( writer& out ) const { return out.__lenWchar( m_value.c_str() ); } + + MTREE_DEFINE_DATA_TYPE(idtString) + MTREE_PERSISTENT_DATA_OBJ(ItemDataString) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута bool. + \en Data of Bool attribute. \~ + \details \ru Данные атрибута bool. + \en Data of Bool attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrBool : public ItemDataBool +{ +public: + ItemAttrBool() : ItemDataBool() {} + ItemAttrBool( bool value ) : ItemDataBool( value ) {} + + MTREE_DEFINE_DATA_TYPE(idtAttrBool) + MTREE_PERSISTENT_DATA_OBJ(ItemAttrBool) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута integer. + \en Data of Integer attribute. \~ + \details \ru Данные атрибута integer. + \en Data of Integer attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrInteger : public ItemDataInteger +{ +public: + ItemAttrInteger() : ItemDataInteger() {} + ItemAttrInteger( int value ) : ItemDataInteger( value ) {} + + MTREE_DEFINE_DATA_TYPE(idtAttrInt) + MTREE_PERSISTENT_DATA_OBJ(ItemAttrInteger) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута double. + \en Data of Double attribute. \~ + \details \ru Данные атрибута double. + \en Data of Double attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrDouble : public ItemDataDouble +{ +public: + ItemAttrDouble() : ItemDataDouble() {} + ItemAttrDouble( double value ) : ItemDataDouble( value ) {} + + MTREE_DEFINE_DATA_TYPE(idtAttrDouble) + MTREE_PERSISTENT_DATA_OBJ(ItemAttrDouble) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута string. + \en Data of String attribute. \~ + \details \ru Данные атрибута string. + \en Data of String attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrString : public ItemDataString +{ +public: + ItemAttrString() : ItemDataString() {} + ItemAttrString( c3d::string_t value ) : ItemDataString( value ) {} + + MTREE_DEFINE_DATA_TYPE(idtAttrString) + MTREE_PERSISTENT_DATA_OBJ(ItemAttrString) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута MbProductInfo. + \en Data of MbProductInfo attribute. \~ + \details \ru Данные атрибута MbProductInfo. + \en Data of MbProductInfo attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrProductInfo : public ItemDataString +{ +public: + ItemAttrProductInfo() : ItemDataString() {} + ItemAttrProductInfo( c3d::string_t value ) : ItemDataString( value ) {} + + MTREE_DEFINE_DATA_TYPE( idtAttrProductInfo ) + + static void Read( reader &, ItemAttrProductInfo * ) {} + static void Write( writer &, const ItemAttrProductInfo * ) {} +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута int64. + \en Data of int64 attribute. \~ + \details \ru Данные атрибута int64. + \en Data of int64 attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrInt64 : public ItemDataBase +{ +public: + int64 m_value; + + ItemAttrInt64() : m_value( 0 ) {} + ItemAttrInt64( int64 value ) : m_value( value ) {} + + MTREE_DEFINE_DATA_SIZE_STD(m_value) + MTREE_DEFINE_DATA_TYPE(idtAttrInt64) + MTREE_PERSISTENT_DATA_OBJ(ItemAttrInt64) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута Идентификатор. + \en Data of Identifier attribute. \~ + \details \ru Данные атрибута Идентификатор. + \en Data of Identifier attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrIdentifier : public ItemDataBase +{ +public: + int32 m_value; + + ItemAttrIdentifier() : m_value( 0 ) {} + ItemAttrIdentifier( int32 value ) : m_value( value ) {} + + MTREE_DEFINE_DATA_SIZE_STD(m_value) + MTREE_DEFINE_DATA_TYPE(idtAttrIdentifier) + MTREE_PERSISTENT_DATA_OBJ(ItemAttrIdentifier) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута Цвет. + \en Data of Color attribute. \~ + \details \ru Данные атрибута Цвет. + \en Data of Color attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrColor : public ItemDataBase +{ +public: + uint32 m_value; + + ItemAttrColor() : m_value( 0 ) {} + ItemAttrColor( uint32 value ) : m_value( value ) {} + + MTREE_DEFINE_DATA_SIZE_STD(m_value) + MTREE_DEFINE_DATA_TYPE(idtAttrColor) + MTREE_PERSISTENT_DATA_OBJ(ItemAttrColor) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута Толщина. + \en Data of Width attribute. \~ + \details \ru Данные атрибута Толщина. + \en Data of Width attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrWidth : public ItemDataInteger +{ +public: + ItemAttrWidth() : ItemDataInteger() {} + ItemAttrWidth( int value ) : ItemDataInteger( value ) {} + + MTREE_DEFINE_DATA_TYPE(idtAttrWidth) + MTREE_PERSISTENT_DATA_OBJ(ItemAttrWidth) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута Стиль. + \en Data of Style attribute. \~ + \details \ru Данные атрибута Стиль. + \en Data of Style attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrStyle : public ItemDataInteger +{ +public: + ItemAttrStyle() : ItemDataInteger() {} + ItemAttrStyle( int value ) : ItemDataInteger( value ) {} + + MTREE_DEFINE_DATA_TYPE(idtAttrStyle) + MTREE_PERSISTENT_DATA_OBJ(ItemAttrStyle) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута Селектированность. + \en Data of Selection attribute. \~ + \details \ru Данные атрибута Селектированность. + \en Data of Selection attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrSelected : public ItemDataBool +{ +public: + ItemAttrSelected() : ItemDataBool() {} + ItemAttrSelected( bool value ) : ItemDataBool( value ) {} + + MTREE_DEFINE_DATA_TYPE(idtAttrSelected) + MTREE_PERSISTENT_DATA_OBJ(ItemAttrSelected) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута Видимость. + \en Data of Visibility attribute. \~ + \details \ru Данные атрибута Видимость. + \en Data of Visibility attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrVisible : public ItemDataBool +{ +public: + ItemAttrVisible() : ItemDataBool() {} + ItemAttrVisible( bool value ) : ItemDataBool( value ) {} + + MTREE_DEFINE_DATA_TYPE(idtAttrVisible) + MTREE_PERSISTENT_DATA_OBJ(ItemAttrVisible) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута Изменённость. + \en Data of Modification attribute. \~ + \details \ru Данные атрибута Изменённость. + \en Data of Modification attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrChanged : public ItemDataBool +{ +public: + ItemAttrChanged() : ItemDataBool() {} + ItemAttrChanged( bool value ) : ItemDataBool( value ) {} + + MTREE_DEFINE_DATA_TYPE(idtAttrChanged) + MTREE_PERSISTENT_DATA_OBJ(ItemAttrChanged) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута Плотность. + \en Data of Dencity attribute. \~ + \details \ru Данные атрибута Плотность. + \en Data of Dencity attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrDencity : public ItemDataDouble +{ +public: + ItemAttrDencity() : ItemDataDouble( 0 ) {} + ItemAttrDencity( double value ) : ItemDataDouble( value ) {} + + MTREE_DEFINE_DATA_TYPE(idtAttrDencity) + MTREE_PERSISTENT_DATA_OBJ(ItemAttrDencity) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута Метка времени обновления. + \en Data of Update timestamp attribute. \~ + \details \ru Данные атрибута Метка времени обновления. + \en Data of Update timestamp attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrUpdateStamp : public ItemDataBase +{ +public: + uint32 m_value; + + ItemAttrUpdateStamp() : m_value( 0 ) {} + ItemAttrUpdateStamp( uint32 value ) : m_value( value ) {} + + MTREE_DEFINE_DATA_SIZE_STD(m_value) + MTREE_DEFINE_DATA_TYPE(idtAttrUpdateStamp) + MTREE_PERSISTENT_DATA_OBJ(ItemAttrUpdateStamp) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута Якорь. + \en Data of Anchor attribute. \~ + \details \ru Данные атрибута Якорь. + \en Data of Anchor attribute. \~ +\ingroup Base_Tools_IO +*/ +// --- +class ItemAttrAnchor : public ItemDataBase +{ +public: + uint8 m_value; + + ItemAttrAnchor() : m_value( 0 ) {} + ItemAttrAnchor( uint8 value ) : m_value( value ) {} + + MTREE_DEFINE_DATA_SIZE_STD(m_value) + MTREE_DEFINE_DATA_TYPE(idtAttrAnchor) + MTREE_PERSISTENT_DATA_OBJ(ItemAttrAnchor) +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные атрибута исполнения. + \en Data of embodiment attribute. \~ + \details \ru Данные атрибута исполнения. + \en Data of embodiment attribute. \~ + \ingroup Base_Tools_IO +*/ +// --- +class ItemAttrEmbodiment : public ItemDataBase +{ +public: + typedef std::pair, bool> EmbData; + EmbData m_value; + + ItemAttrEmbodiment() : m_value( std::pair(0, 0),false ) {} + ItemAttrEmbodiment( const EmbData& value ) : m_value( value ) {} + + MTREE_DEFINE_DATA_SIZE_STD( m_value ) + MTREE_DEFINE_DATA_TYPE( idtAttrEmbodiment ) + static void Read( reader& in, ItemAttrEmbodiment* item ) { + item->m_value.first.first = ::ReadSimpleName( in ); // in >> item->m_value.first.first; + item->m_value.first.second = ::ReadSimpleName( in ); // in >> item->m_value.first.second; + if ( in.MathVersion() >= 0x13000010L ) + in >> item->m_value.second; + } + static void Write( writer& out, const ItemAttrEmbodiment* item ) { + ::WriteSimpleName( out, item->m_value.first.first ); // out << item->m_value.first.first; + ::WriteSimpleName( out, item->m_value.first.second ); // out << item->m_value.first.second; + if ( out.MathVersion() >= 0x13000010L ) + out << item->m_value.second; + } +}; + +//---------------------------------------------------------------------------------------- +/// \ru Объявление классов без данных для атрибутов. +/// \en Definition of attribute dataless classes. +//--- +MTREE_ATTR_DATALESS_CLASS(ItemAttrVisual, idtAttrVisual); +MTREE_ATTR_DATALESS_CLASS(ItemAttrWireCount, idtAttrWireCount); +MTREE_ATTR_DATALESS_CLASS(ItemAttrName, idtAttrName); +MTREE_ATTR_DATALESS_CLASS(ItemAttrGeom, idtAttrGeom); +MTREE_ATTR_DATALESS_CLASS(ItemAttrStampRib, idtAttrStampRib); +MTREE_ATTR_DATALESS_CLASS(ItemAttrModelInfo, idtAttrModelInfo); +MTREE_ATTR_DATALESS_CLASS(ItemAttrPersonOrganizationInfo, idtAttrPersonOrganizationInfo); +MTREE_ATTR_DATALESS_CLASS(ItemAttrSTEPTextDescription, idtAttrSTEPTextDescription); +MTREE_ATTR_DATALESS_CLASS(ItemAttrSTEPReferenceHolder, idtAttrSTEPReferenceHolder); +MTREE_ATTR_DATALESS_CLASS(ItemAttrBinary, idtAttrBinary); +MTREE_ATTR_DATALESS_CLASS(ItemAttrStrains, idtAttrStrains); +MTREE_ATTR_DATALESS_CLASS(ItemAttrElasticity, idtAttrElasticity); +MTREE_ATTR_DATALESS_CLASS(ItemAttrSheetFlanging, idtAttrSheetFlanging); + +//---------------------------------------------------------------------------------------- +/** \brief \ru Контейнер для пользовательских данных узла дерева. + \en A container for user data of a tree node. \~ + \details \ru Контейнер для пользовательских данных узла дерева (владеет данными). + \en A container for user data of a tree node (owns the data). \~ + \ingroup Base_Tools_IO +*/ +// --- +class MATH_CLASS UserDataMap : public MultiMap +{ +public: + UserDataMap() {} + UserDataMap( const UserDataMap& other ); + + ~UserDataMap(); // \ru Владеет данными. \en Owns the data. + + /// \ru Оператор ==. \en Operator ==. + bool operator == ( const UserDataMap& other ) const; + + /// \ru Оператор <. \en Operator <. + bool operator < ( const UserDataMap& other ) const; + + /// \ru Оператор =. \en Operator =. + UserDataMap& operator = ( const UserDataMap& other ); +}; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Данные узла дерева. +\en Tree node data. \~ +\details \ru Данные узла дерева. \n +\en Tree node data. \n \~ +\ingroup Base_Tools_IO +*/ +// --- +struct MATH_CLASS MbItemData +{ + // \ru Признак наличия локальной системы координат. + // \en Token of local coordinate system presence. + enum PlacementPresenceToken + { + ppt_No = 0x01, + ppt_Yes = 0x02 + }; + + // \ru Уникальный ID узла в дереве модели. \en Unique id in the model tree. + // \ru Не учитывается при сравнении. \en Not considered during comparison. + mutable size_t id; + // \ru Тип объекта. \en Object type. + // \ru type==st_Undefined означает, что фильтр по типу не определен. + // \en type==st_Undefined means that filter is undefined. + MbeSpaceType type; + // \ru иИмя объекта. \en Object name. + // \ru name==SYS_MAX_UINT32 означает, что фильтр по имени не определен. + // \en name==SYS_MAX_UINT32 means that filter is undefined. + SimpleName name; + // \ru gabarit.IsEmpty()==true означает, что фильтр по габариту не определен. + // \en gabarit.IsEmpty()==true means that filter is undefined. + MbCube gabarit; + // \ru Позиция записи/чтения узла. \en Position for the node writing/reading. + // \ru position.IsValid()==false означает, что это поле не учитывается при сравнении. + // \en position.IsValid()==false means that this field is not considered during comparison. + ClusterReference position; + // \ru Локальная система координат объекта. \en Local coordinate system for the object. + // \ru Не учитывается при сравнении. \en Not considered during comparison. + MbPlacement3D placement; + + // \ru Кроме обязательных данных узла, описанных выше, можно задать пользовательские данные, + // которые содержатся в контейнере 'properties'. + // \en In addition to the mandatory node data, described above, it is possible to define user data, + // which is kept in the 'properties' container. + // \ru Контейнер для пользовательских данных узла. + // \en Container for user data of a node. + UserDataMap properties; + + + // \ru Конструкторы. \en Constructors. + MbItemData() : id( SYS_MAX_T ), type( st_Undefined ), name( SYS_MAX_UINT32 ) {} + MbItemData( MbeSpaceType t, SimpleName n, const MbCube& c, ClusterReference& pos ) : id( SYS_MAX_T ), type( t ), name( n ), gabarit( c ), position( pos ) {} + MbItemData( MbeSpaceType t, SimpleName n, const MbCube& c, ClusterReference& pos, const UserDataMap& prop ) : + id( SYS_MAX_T ), type( t ), name( n ), gabarit( c ), position( pos ), properties( prop ) {} + MbItemData( const MbItemData& data ) : id( data.id ), type( data.type ), + name( data.name ), gabarit( data.gabarit ), position( data.position ), + placement( data.placement ), properties( data.properties ) {} + + // \ru Признак пустых (неинициализированных) данных. \en Indicator of empty (uninitialized) data. + bool IsEmpty() const; + + // \ru Идентичность полей-фильтров (id не важен). + // \en Equality of filtering fields (id is irrelevant). + bool operator == ( const MbItemData& rt ) const; + + // \ru Сравнение полей-фильтров (id не важен). + // \en Comparison of filtering fields (id is irrelevant). + bool operator < ( const MbItemData& rt ) const; + + // \ru Специальное сравнение габаритов для сортировки объектов. + // \en Special comparison of bounding boxes for object sorting. + static bool CompareGabarits( const MbCube& a, const MbCube& b ); + + // \ru Запись и чтение. + // \en Writing and reading. + writer& operator >> ( writer & out ); + reader& operator << ( reader & out ); +}; + +//---------------------------------------------------------------------------------------- +/// \ru Чтение UserDataMap. \en UserDataMap reading. +// --- +inline reader& operator >> ( reader & in, UserDataMap& itemmap ) +{ + size_t typeCount = ::ReadCOUNT( in ); // \ru Количество типов данных в наборе. \en A number of types in the map. + + if ( in.good() ) { + for ( size_t i = 0; i < typeCount; i++ ) { + int t; + in >> t; + size_t typeSize = ::ReadCOUNT( in ); + C3D_ASSERT( t < (int)idtCount ); + if ( t < (int)idtCount ) { + MbeItemDataType type = (MbeItemDataType)t; // \ru Тип данных. \en A data type. + ItemDataBase* item = ItemDataBase::Create( type ); + *item << in; + itemmap.Associate( type, item ); + } + else { // skip unknown data + char* buff = new char[typeSize]; + in.readBytes( buff, typeSize ); + delete[] buff; + } + + if ( !in.good() ) { + in.setState( io::fail ); + break; + } + } + } + return in; +} + +//---------------------------------------------------------------------------------------- +/// \ru Запись UserDataMap. \en UserDataMap writing. +// --- +inline writer& operator << ( writer & out, const UserDataMap& itemmap ) +{ + size_t mapCount = itemmap.Count(); + ::WriteCOUNT( out, mapCount ); // \ru Количество типов данных в наборе. \en A number of types in the map. + + if ( out.good() && mapCount ) { + UserDataMap::Iterator curIter = itemmap.First(); + while ( !curIter.Empty() ) { + MbeItemDataType type = curIter.Key(); + ItemDataBase* item = curIter.Value(); + C3D_ASSERT( type < idtCount && item != NULL ); + if ( type < idtCount && item != NULL ) { + out << (int)type; // \ru Тип данных. \en A data type. + size_t dataSize = item->Size( out ); + ::WriteCOUNT( out, dataSize );// \ru Размер данных. \en Data size. + *item >> out; + } + if ( !out.good() ) { + out.setState( io::fail ); + break; + } + curIter++; + } + } + return out; +} + +} //namespace c3d + +#endif // __MODEL_TREE_DATA_H diff --git a/C3d/Include/multiline.h b/C3d/Include/multiline.h index f2b55f5..cb8e0b8 100644 --- a/C3d/Include/multiline.h +++ b/C3d/Include/multiline.h @@ -1,1816 +1,1816 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Мультилиния. - \en Multiline. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -// \ru Заголовочный файл мультилинии cодержит следующие разделы. \en Header file of multiline contains the following sections. -// \ru _1_ EnMLVertexTracingType - тип обхода углов в вершине мультилинии \en _1_ EnMLVertexTracingType - type of traverse of corners at a vertex of multiline -// \ru _2_ EnMLInnerTipType - тип внутренней законцовки мультилинии \en _2_ EnMLInnerTipType - type of inner tip of multiline -// \ru _3_ EnMLTipType - тип законцовки мультилинии \en _3_ EnMLTipType - type of tip of multiline -// \ru _4_ StMLTipParams - структура параметров законцовки мультилинии \en _4_ StMLTipParams - tip of multiline parameters structure -// \ru _5_ StVertexOfMultilineInfo - информация о вершине мультилинии \en _5_ StVertexOfMultilineInfo - information about vertex of multiline -// \ru _6_ MbMultiline - класс мультилиния \en _6_ MbMultiline - multiline class -// \ru _7_ Функция построения скругления базовой кривой мультилинии \en _7_ Function for multiline base curve fillet construction -// \ru _8_ Функция построения фаски базовой кривой мультилинии \en _8_ Function for multiline base curve chamfer construction -// \ru _9_ Внеклассные функции расчета/учета радиусов кривизны \en _9_ Out-of-class functions for curvature radii calculation/consideration -// \ru _10_ Внеклассная функция гладкого стыка двух последовательных кривых \en _10_ Out-of-class function for smooth joint of two consecutive curves -// \ru _11_ Разбить мультилинию на две части \en _11_ Split multiline into two pieces -// \ru _12_ Разбить мультилинию на N равных частей \en _12_ Split multiline into N equal pieces -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __MULTILINE_H -#define __MULTILINE_H - - -#include -#include -#include -#include -#include - - -//------------------------------------------------------------------------------ -// _1_ -/** \brief \ru Тип обхода углов. - \en Type of traverse of corners. \~ - \details \ru Тип обхода углов в вершине мультилинии.\n - \en Type of traverse of corners at a vertex of multiline.\n \~ - \ingroup Algorithms_2D -*/ // --- -enum EnMLVertexTracingType { - mvt_ShearType, ///< \ru Обход срезом. \en Traverse by shear. - mvt_FilletType, ///< \ru Обход со скруглением. \en Traverse by fillet. - mvt_SpecFilletType, ///< \ru Обход со скруглением заданным радиусом. \en Traverse by fillet with a given radius. - // \ru ДОБАВЛЕНИЕ ТОЛЬКО В КОНЕЦ!!! \en ADDITION ONLY TO THE END!!! -}; - - -//------------------------------------------------------------------------------ -// _2_ -/** \brief \ru Тип внутренней законцовки. - \en Type of inner tip. \~ - \details \ru Тип внутренней законцовки мультилинии.\n - \en Type of inner tip of multiline.\n \~ - \ingroup Algorithms_2D -*/ // --- -enum EnMLInnerTipType { - mit_UndefTip, ///< \ru Законцовки нет. \en No tip. - mit_VerticesTip, ///< \ru Законцовка между соответствующими вершинами. \en Tip between corresponding vertices. - mit_LinearTip, ///< \ru Линейная законцовка. \en Linear tip. - mit_ArcTip, ///< \ru Дуговая законцовка. \en Arc tip. - // \ru ДОБАВЛЕНИЕ ТОЛЬКО В КОНЕЦ!!! \en ADDITION ONLY TO THE END!!! -}; - - -//------------------------------------------------------------------------------ -// _3_ -/** \brief \ru Тип законцовки. - \en Type of tip. \~ - \details \ru Тип законцовки мультилинии.\n - \en Type of tip of multiline.\n \~ - \ingroup Algorithms_2D -*/ // --- -enum EnMLTipType { - mtt_UndefTip, ///< \ru Законцовки нет. \en No tip. - mtt_LinearTip, ///< \ru Линейная законцовка. \en Linear tip. - mtt_ArcTip, ///< \ru Дуговая законцовка. \en Arc tip. - mtt_PolylineTip, ///< \ru Ломаная законцовка. \en Polyline tip. - mtt_ObliqueTip, ///< \ru Наклонная законцовка. \en Inclined tip. - // \ru ДОБАВЛЕНИЕ ТОЛЬКО В КОНЕЦ!!! \en ADDITION ONLY TO THE END!!! -}; - - -//------------------------------------------------------------------------------ -// _4_ -/** \brief \ru Cтруктура параметров законцовки. - \en Tip parameters structure. \~ - \details \ru Cтруктура параметров законцовки мультилинии.\n - Изменять данные объекта можно только из MbMultiline. - \en Multiline tip parameters structure.\n - Object data can be changed only from MbMultiline. \~ - \ingroup Algorithms_2D -*/ -struct MATH_CLASS StMLTipParams { -friend class MbMultiline; -private: - EnMLTipType tipType; ///< \ru Тип законцовки. \en Type of tip. - - /** \brief \ru Параметр законцовки. - \en Parameter of tip. \~ - \details \ru Для mtt_UndefTip неопределен,\n - для mtt_LinearTip - расстояние от конца ЛМ до законцовки,\n - для mtt_ArcTip - расстояние от конца ЛМ до вершины дуги законцовки,\n - для mtt_PolylineTip - расстояние от конца ЛМ до вершины угла законцовки,\n - для mtt_ObliqueTip - угол поворота нормали от конца мультилинии (в радианах). - \en Undefined for mtt_UndefTip,\n - for mtt_LinearTip - distance from the end of multiline to the tip,\n - for mtt_ArcTip - distance from the end of multiline to the vertex of arc of tip,\n - for mtt_PolylineTip - distance from the end of multiline to the vertex of corner of tip,\n - for mtt_ObliqueTip - angle of rotation of normal from the end of multiline (in radians).\~ - */ - double tipParam; - -public: - StMLTipParams(); ///< \ru Умолчательный конструктор. \en Default constructor. - StMLTipParams( const StMLTipParams & other ); ///< \ru Копирующий конструктор. \en Copy-constructor. - - /** \brief \ru Конструктор по типу законцовки и параметру законцовки. - \en Constructor by type of tip and parameter of tip. \~ - \details \ru Конструктор по типу законцовки и параметру законцовки.\n - \en Constructor by type of tip and parameter of tip.\n \~ - \param[in] _tipType - \ru Тип законцовки. - \en Type of tip. \~ - \param[in] _tipParam - \ru Параметр законцовки, зависит от типа законцовки:\n - для mtt_UndefTip неопределен,\n - для mtt_LinearTip - расстояние от конца ЛМ до законцовки,\n - для mtt_ArcTip - расстояние от конца ЛМ до вершины дуги законцовки,\n - для mtt_PolylineTip - расстояние от конца ЛМ до вершины угла законцовки,\n - для mtt_ObliqueTip - угол поворота нормали от конца мультилинии (в радианах). - \en Parameter of tip, depends on type of tip:\n - for mtt_UndefTip is undefined,\n - for mtt_LinearTip - distance from the end of multiline to the tip,\n - for mtt_ArcTip - distance from the end of multiline to the vertex of arc of tip,\n - for mtt_PolylineTip - distance from the end of multiline to the vertex of corner of tip,\n - for mtt_ObliqueTip - angle of rotation of normal from the end of multiline (in radians). \~ - */ - StMLTipParams( EnMLTipType _tipType, double _tipParam ); - -public: - - /**\ru \name Функции доступа к данным - \en \name Functions for access to data - \{ */ - /// \ru Тип законцовки. \en Type of tip. - EnMLTipType GetTipType () const { return tipType; } - /// \ru Параметр законцовки. \en Parameter of tip. - double GetTipParam() const { return tipParam; } - /** \} */ - /**\ru \name Операторы сравнения - \en \name Comparison operators - \{ */ - /// \ru Оператор сравнения. \en Comparison operator. - bool operator ==( const StMLTipParams & ) const; - /// \ru Оператор сравнения. \en Comparison operator. - bool operator !=( const StMLTipParams & ) const; - /** \} */ - -protected: - - /**\ru \name Функции инициализации - \en \name Initialization functions - \{ */ - - /// \ru Инициализация по структуре параметров законцовки. \en Initialization by tip parameters structure. - void Init ( const StMLTipParams & other ); - - /** \brief \ru Инициализация по типу законцовки и параметру законцовки. - \en Initialization by type of tip and parameter of tip. \~ - \details \ru Инициализация по типу законцовки и параметру законцовки.\n - \en Initialization by type of tip and parameter of tip.\n \~ - \param[in] _tipType - \ru Тип законцовки. - \en Type of tip. \~ - \param[in] _tipParam - \ru Параметр законцовки, зависит от типа законцовки:\n - для mtt_UndefTip неопределен,\n - для mtt_LinearTip - расстояние от конца ЛМ до законцовки,\n - для mtt_ArcTip - расстояние от конца ЛМ до вершины дуги законцовки,\n - для mtt_PolylineTip - расстояние от конца ЛМ до вершины угла законцовки,\n - для mtt_ObliqueTip - угол поворота нормали от конца мультилинии (в радианах). - \en Parameter of tip, depends on type of tip:\n - for mtt_UndefTip is undefined,\n - for mtt_LinearTip - distance from the end of multiline to the tip,\n - for mtt_ArcTip - distance from the end of multiline to the vertex of arc of tip,\n - for mtt_PolylineTip - distance from the end of multiline to the vertex of corner of tip,\n - for mtt_ObliqueTip - angle of rotation of normal from the end of multiline (in radians). \~ - */ - void Init ( EnMLTipType _tipType, double _tipParam ); - - /** \} */ - /**\ru \name Функции изменения данных - \en \name Functions for changing data - \{ */ - - /** \brief \ru Изменить тип законцовки. - \en Change type of tip. \~ - \details \ru Изменить тип законцовки мультилинии.\ n - \en Change type of tip of multiline.\n \~ - \param[in] othTipType - \ru Новый тип законцовки. - \en New type of tip. \~ - \return \ru false, если старое значение типа совпадает со значением othTipType. - \en False if the old value of type coincides with the value of othTipType. \~ - */ - bool ChangeTipType ( EnMLTipType othTipType ); - - /** \brief \ru Изменить параметр законцовки. - \en Change parameter of tip. \~ - \details \ru Изменить параметр законцовки мультилинии.\n - \en Change parameter of tip of multiline.\n \~ - \param[in] othTipParam - \ru Новый параметр законцовки. - \en New parameter of tip. \~ - \return \ru false, если старое значение параметра совпадает со значением othTipParam. - \en False if the old value of parameter coincides with the value of othTipParam. \~ - */ - bool ChangeTipParam( double othTipParam ); - - /** \brief \ru Трансформация. - \en Transformation. \~ - \details \ru Преобразование объекта согласно матрице.\n - \en Transform object according to the matrix.\n \~ - \param[in] matr - \ru Матрица трансформации. - \en Transformation matrix. \~ - */ - void Transform ( const MbMatrix & matr ); - /** \} */ - -private: - /// \ru Оператор присваивания. \en Assignment operator. - void operator = ( const StMLTipParams & ); - - KNOWN_OBJECTS_RW_REF_OPERATORS_EX( StMLTipParams, MATH_FUNC_EX ) -}; // StMLTipParams - - -//------------------------------------------------------------------------------ -// _5_ -/** \brief \ru Информация о вершине. - \en Information about a vertex. \~ - \details \ru Информация о вершине мультилинии.\n - Изменять данные объекта можно только из MbMultiline. - \en Information about a vertex of multiline.\n - Object data can be changed only from MbMultiline. \~ - \ingroup Algorithms_2D -*/ -struct MATH_CLASS StVertexOfMultilineInfo { -friend class MbMultiline; -private: - bool smoothJoint; ///< \ru Флаг гладкого стыка в вершине сегментов базовой линии мультилинии \en Flag of multiline base line segments smooth joint at a vertex - ///< \ru (только для сплайнов). \en (only for splines). - EnMLVertexTracingType tracingType; ///< \ru Тип обхода углов в вершине мультилинии. \en Type of traverse of corners at a vertex of multiline. - double specFilletRad; ///< \ru Радиус особого скругления на линии мультилинии (если tracingType == mvt_SpecFilletType). \en Radius of a special fillet on a line of multiline (if tracingType == mvt_SpecFilletType). - // \ru Параметры законцовки в вершине (внутренней) \en Parameters of (inner) tip at a vertex - EnMLInnerTipType tipType; ///< \ru Тип внутренней законцовки. \en Type of inner tip. - bool firstSegTip; ///< \ru Законцовка для первого сегмента вершины. \en Tip for the first segment of a vertex. - -public: - StVertexOfMultilineInfo(); ///< \ru Умолчательный конструктор. \en Default constructor. - StVertexOfMultilineInfo( const StVertexOfMultilineInfo & other ); ///< \ru Копирующий конструктор. \en Copy-constructor. - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор.\n - \en Constructor.\n \~ - \param[in] _smoothJoint - \ru Флаг гладкого стыка в вершине сегментов базовой линии мультилинии,\n - используется только для сплайнов. - \en Flag of multiline base line segments smooth joint at a vertex,\n - used only for splines. \~ - \param[in] _tracingType - \ru Тип обхода углов в вершине мультилинии. - \en Type of traverse of corners at a vertex of multiline. \~ - \param[in] _specFilletRad - \ru Радиус особого скругления на линии мультилинии,\n - если tracingType == mvt_SpecFilletType. - \en Radius of a special fillet on line of multiline,\n - if tracingType == mvt_SpecFilletType. \~ - \param[in] _tipType - \ru Тип внутренней законцовки. - \en Type of inner tip. \~ - \param[in] _firstSegTip - \ru Законцовка для первого сегмента вершины. - \en Tip for the first segment of a vertex. \~ - */ - StVertexOfMultilineInfo( bool _smoothJoint, EnMLVertexTracingType _tracingType, - double _specFilletRad, EnMLInnerTipType _tipType, bool _firstSegTip ); - -public: - - /**\ru \name Функции доступа к данным - \en \name Functions for access to data - \{ */ - /// \ru Тип обхода углов в вершине мультилинии. \en Type of traverse of corners at a vertex of multiline. - EnMLVertexTracingType GetTracingType () const { return tracingType; } - /// \ru Флаг гладкого стыка в вершине сегментов базовой линии мультилинии. \en Flag of multiline base line segments smooth joint at a vertex. - bool IsSmoothJoint () const { return smoothJoint; } - /// \ru Радиус особого скругления на линии мультилинии. \en Radius of special fillet on base line of multiline. - double GetSpecFilletRad() const { return specFilletRad; } - /// \ru Тип внутренней законцовки. \en Type of inner tip. - EnMLInnerTipType GetTipType () const { return tipType; } - /// \ru Законцовка для первого сегмента вершины. \en Tip for the first segment of a vertex. - bool IsFirstSegTip () const { return firstSegTip; } - /// \ru Обход скруглением. \en Traverse by fillet. - bool IsFilletTracing () const; - -public: - - /** \} */ - /**\ru \name Операторы сравнения и присваивания - \en \name Comparison operators and assignment - \{ */ - /// \ru Оператор сравнения. \en Comparison operator. - bool operator == ( const StVertexOfMultilineInfo & ) const; - /// \ru Оператор сравнения. \en Comparison operator. - bool operator != ( const StVertexOfMultilineInfo & ) const; - /// \ru Оператор присваивания. \en Assignment operator. - StVertexOfMultilineInfo & operator = ( const StVertexOfMultilineInfo & other ) - { - Init( other ); - return *this; - } - - /** \} */ - - -protected: - - /**\ru \name Функции инициализации - \en \name Initialization functions - \{ */ - - /// \ru Инициализация по информации о вершине мультилинии. \en Initialization by an information about a vertex of multiline. - void Init( const StVertexOfMultilineInfo & other ); - - /** \brief \ru Инициализация. - \en Initialization. \~ - \details \ru Инициализация.\n - \en Initialization.\n \~ - \param[in] _smoothJoint - \ru Флаг гладкого стыка в вершине сегментов базовой линии мультилинии,\n - используется только для сплайнов. - \en Flag of multiline base line segments smooth joint at a vertex,\n - used only for splines. \~ - \param[in] _tracingType - \ru Тип обхода углов в вершине мультилинии. - \en Type of traverse of corners at a vertex of multiline. \~ - \param[in] _specFilletRad - \ru Радиус особого скругления на линии мультилинии,\n - если tracingType == mvt_SpecFilletType. - \en Radius of a special fillet on line of multiline,\n - if tracingType == mvt_SpecFilletType. \~ - \param[in] _tipType - \ru Тип внутренней законцовки. - \en Type of inner tip. \~ - \param[in] _firstSegTip - \ru Законцовка для первого сегмента вершины. - \en Tip for the first segment of a vertex. \~ - */ - void Init ( bool _smoothJoint, EnMLVertexTracingType _tracingType, - double _specFilletRad, EnMLInnerTipType _tipType, - bool _firstSegTip ); - /** \} */ - /**\ru \name Функции изменения данных - \en \name Functions for changing data - \{ */ - /// \ru Изменить флаг гладкого стыка в вершине сегментов базовой линии мультилинии. \en Change flag of multiline base line segments smooth joint at a vertex. - bool ChangeSmoothJoint ( bool othSmoothJoint ); - /// \ru Изменить тип обхода углов в вершине мультилинии. \en Change type of traverse of corners at a vertex of multiline. - bool ChangeTracingType ( EnMLVertexTracingType othTracingType ); - /// \ru Изменить радиус особого скругления на линии мультилинии. \en Change radius of special fillet on base line of multiline. - bool ChangeSpecFilletRad( double othSpecFilletRad ); - /// \ru Изменить тип внутренней законцовки. \en Change type of inner tip. - bool ChangeTipType ( EnMLInnerTipType othTipType ); - /// \ru Изменить флаг законцовки для первого сегмента вершины. \en Change flag of tip for the first segment of a vertex. - bool ChangeFirstSegTip ( bool othFirstSegTip ); - - /** \brief \ru Трансформация. - \en Transformation. \~ - \details \ru Преобразование объекта согласно матрице.\n - \en Transform object according to the matrix.\n \~ - \param[in] matr - \ru Матрица трансформации. - \en Transformation matrix. \~ - */ - void Transform ( const MbMatrix & matr ); - /** \} */ - -private: - KNOWN_OBJECTS_RW_REF_OPERATORS_EX( StVertexOfMultilineInfo, MATH_FUNC_EX ) -}; // StVertexOfMultilineInfo - - -//------------------------------------------------------------------------------ -/** \brief \ru Класс для перестроения разрывов. - \en Class for breaks rebuilding. \~ - \details \ru Класс для перестроения разрывов. Содержит номера сегментов базовой кривой.\n - \en Class for breaks rebuilding. Contains indices of the base curve segments.\n \~ - \ingroup Algorithms_2D -*/ -// --- -class MATH_CLASS MbBreaksRebuild { - -public: - SArray baseNumbers; ///< \ru Номера сегментов базовой кривой для сегментов линии мультилинии. \en Indices of the base curve segments for line segments of multiline. - -public: - /// \ru Конструктор по массиву номеров. \en Constructor by array of indices. - MbBreaksRebuild( const SArray & bNumbers ) - :baseNumbers( bNumbers ) - { - } - ~MbBreaksRebuild() {} -}; - - -//------------------------------------------------------------------------------ -// _6_ -/** \brief \ru Мультилиния. - \en Multiline. \~ - \details \ru Мультилиния - составной геометрический объект, состоящий из\n - 1) кривых curves, построенных эквидистантно к базовой кривой мультилинии basisCurve - с радиусами equidRadii. При этом способ обхода углов для каждой вершины - basisCurve определяется отдельно (с помощью vertices.tracingType и vertices.specFilletRad).\n - 2) массива законцовок tipCurves в вершинах мультилинии, тип которых определяется - соответствующим vertices[i].tipParams.tipType.\n - 3) законцовок begTipCurve и endTipCurve на концах мультилинии, тип которых определяется - с помощью begTipParams и endTipParams.\n - При создании линии мультилинии (добавлении радиуса эквидистанты) создается контур, - который будет жить (меняться только внутри) до того момента, пока в массиве equidRadii - есть соответствующий ему в начале элемент.\n - Контур законцовки живет до тех пор, пока соответствующий тип законцовки не равен m_t_UndefTip.\n - За существованием и удалением законцовок должны следить функции SetTipType.\n - При построении кривых мультилинии вырожденные участки исключаются только в вершинах (перехлесты). - \en Multiline is a composite geometric object consisting of\n - 1) 'curves' curves constructed to be equidistant to base line 'basisCurve' of the multiline - with 'equidRadii' radii. The method of traverse of corners for each vertex of - 'basisCurve' is defined separately (with the help of vertices.tracingType and vertices.specFilletRad).\n - 2) 'tipCurves' array of tips at vertices of multiline, which type is defined - by corresponding vertices[i].tipParams.tipType.\n - 3) 'begTipCurve' and 'endTipCurve' tips at the ends of multiline, which type is defined - with the help of 'begTipParams' and 'endTipParams'.\n - While creating line of multiline (adding equidistance radius) the contour is created - which will be alive (can be changed only internally) while the 'equidRadii' array - contains element at the beginning corresponding to it.\n - The contour of a tip will be alive while the corresponding type of the tip isn't equal to m_t_UndefTip.\n - SetTipType functions care about the existence and removal of tips.\n - Degenerated regions are excluded only at vertices (overlaps) while constructing the curves of a multiline. \~ - \ingroup Region_2D -*/ -// --- -class MATH_CLASS MbMultiline : public MbPlaneItem { -private: - MbContour * basisCurve; ///< \ru Базовая кривая (БК) (всегда не NULL). \en Base curve (BC) (always not NULL). - SArray vertices; ///< \ru Массив вершин мультилинии (согласован с вершинами БК). \en Array of vertices of a multiline (agreed with the vertices of the base curve). - CSSArray equidRadii; ///< \ru Сортированный массив радиусов эквидистантных кривых. \en Sorted array of radii of equidistant curves. - StMLTipParams begTipParams; ///< \ru Параметры законцовки в начале мультилинии (начале БК). \en Parameters of a tip at the beginning of a multiline ( the beginning of base curve). - StMLTipParams endTipParams; ///< \ru Параметры законцовки в конце мультилинии (конце БК). \en Parameters of a tip at the end of a multiline (end of the base curve). - bool processClosed; ///< \ru Обрабатывать ли замкнутость БК (доп. вершина). \en Whether to process the closedness of the base curve (additional vertex). - bool isTransparent; ///< \ru "Прозрачная" ли мультилиния. \en Whether the multiline is "transparent". - // \ru Объекты, которые составляют мультилинию (рекомендовали не делать их mutable, а писать и читать) \en Objects which constitute a multiline (recommended to read and write and not to make them mutable) - // \ru ЭТИ ОБЪЕКТЫ НЕЛЬЗЯ МЕНЯТЬ СНАРУЖИ!!! \en THESE OBJECTS CAN'T BE CHANGED OUTSIDE!!! - PArray curves; ///< \ru Кривые мультилинии (согласован с equidRadii) (всегда не NULL). \en Curves of a multiline (agreed with the 'equidRadii') (always not NULL). - PArray tipCurves; ///< \ru Законцовки в вершинах мультилинии (согласован с vertices). \en Tips at vertices of a multiline (agreed with 'vertices'). - MbContour * begTipCurve; ///< \ru Законцовка в начале мультилинии (начале БК). \en Tip at the beginning of a multiline (beginning of the base curve). - MbContour * endTipCurve; ///< \ru Законцовка в конце мультилинии (конце БК). \en Tip at the end of a multiline (end of the base curve). - - mutable double maxPosRadius; ///< \ru Максимально возможный для невывернутого построения положительный радиус. \en Maximum possible positive radius for non-everted construction. - mutable double minNegRadius; ///< \ru Минимально возможный для невывернутого построения отрицательный радиус. \en Minimum possible negative radius for non-everted construction. - mutable size_t minNotDegInd; ///< \ru Индекс невырожденного элемента curves с минимальным радиусом. \en Index of non-everted element of 'curves' with minimal radius. - mutable size_t maxNotDegInd; ///< \ru Индекс невырожденного элемента curves с максимальным радиусом. \en Index of non-everted element of 'curves' with maximal radius. - -public: - MbMultiline(); ///< \ru Конструктор пустой мультилинии. \en Constructor of an empty multiline. - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор.\n - \en Constructor.\n \~ - \param[in] _basisCurve - \ru Базовая кривая. - \en Base curve. \~ - \param[in] vertInfo - \ru Информация о вершине мультилинии (применяется ко всем вершинам). - \en Information about a vertex of the multiline (applied to all vertices). \~ - \param[in] _equidRadii - \ru Сортированный массив радиусов эквидистантных кривых. - \en Sorted array of radii of equidistant curves. \~ - \param[in] _begTipParams - \ru Параметры законцовки в начале мультилинии. - \en Parameters of tip at the beginning of multiline. \~ - \param[in] _endTipParams - \ru Параметры законцовки в конце мультилинии. - \en Parameters of a tip at the end of a multiline. \~ - \param[in] _processClosed - \ru Обрабатывать ли замкнутость базовой кривой. - \en Whether to process the closedness of the base curve. \~ - \param[in] _isTransparent - \ru "Прозрачная" ли мультилиния\n - параметр не используется, мультилиния считается прозрачной. - \en Whether the multiline is "transparent"\n - parameter isn't used, multiline is considered to be transparent. \~ - */ - MbMultiline( const MbContour & _basisCurve, const StVertexOfMultilineInfo & vertInfo, - const SArray & _equidRadii, - const StMLTipParams & _begTipParams, const StMLTipParams & _endTipParams, - bool _processClosed, bool _isTransparent ); - -protected: - /// \ru Копирующий конструктор. \en Copy-constructor. - MbMultiline( const MbMultiline & ); - -private: - MbMultiline( const MbContour & _basisCurve, const SArray & _vertices, - const SArray & _equidRadii, - const StMLTipParams & _begTipParams, const StMLTipParams & _endTipParams, - bool _processClosed, bool _isTransparent, bool & error ); - -public: - virtual ~MbMultiline(); - -public: - - /**\ru \name Общие функции геометрического объекта - \en \name Common functions of a geometric object - \{ */ - virtual MbePlaneType IsA () const; // \ru Тип объекта. \en A type of an object. - virtual MbePlaneType Type () const; // \ru Групповой тип объекта. \en Group type of an object. - virtual MbePlaneType Family () const; // \ru Семейство объекта. \en Family of an object. - virtual bool IsSame ( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными. \en Determine whether objects are equal. - virtual bool IsSimilar ( const MbPlaneItem & item ) const; // \ru Являются ли объекты подобными. \en Determine whether the objects are similar. - virtual bool SetEqual ( const MbPlaneItem & item ); // \ru Сделать объекты равными. \en Make the objects equal. - virtual void Transform ( const MbMatrix & matr, MbRegTransform * = NULL, const MbSurface * newSurface = NULL );// \ru Преобразовать объект согласно матрице. \en Transform an object according to the matrix. - virtual void Move ( const MbVector & to, MbRegTransform * = NULL, const MbSurface * newSurface = NULL );// \ru Сдвинуть объект вдоль вектора. \en Move an object along a vector. - virtual void Rotate ( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Повернуть вокруг точки на угол. \en Rotate at angle around a point. - virtual MbPlaneItem & Duplicate ( MbRegDuplicate * = NULL ) const; // \ru Сделать копию объекта. \en Create a copy of the object. - virtual void AddYourGabaritTo( MbRect & r ) const; // \ru Добавить свой габарит в присланный габарит. \en Add your own bounding box into the given bounding box. - - virtual bool IsVisibleInRect ( const MbRect & r, bool exact = false ) const; // \ru Виден ли объект в заданном прям-ке. \en Whether the object is visible in the given rectangle. - virtual double DistanceToPoint ( const MbCartPoint & toP ) const; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. - virtual bool DistanceToPointIfLess( const MbCartPoint & to, double &distance) const; // \ru Вычислить расстояние до точки, если оно меньше d. \en Calculate the distance to the point if it is less than d. - - virtual MbProperty& CreateProperty( MbePrompt name ) const; // \ru Создать собственное свойство. \en Create a custom property. - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта. \en Set properties of the object. - virtual void GetBasisPoints( MbControlData & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - /** \} */ - /**\ru \name Функции доступа к данным - \en \name Functions for access to data - \{ */ - - /// \ru Базовая кривая. \en Base curve. - const MbContour & GetBasisCurve () const { return *basisCurve; } - /// \ru Дать базовую кривую для изменения. \en Get base curve for editing. - MbContour & SetBasisCurve () { return *basisCurve; } - /// \ru Количество вершин. \en Count of vertices. - size_t GetVerticesCount () const { return vertices.Count(); } - /// \ru Вершина по номеру. Номер должен быть меньше количества вершин. \en Vertex by an index. Index must be less than count of vertices. - const StVertexOfMultilineInfo & GetVertex ( size_t i ) const { return vertices[i]; } - /// \ru Количество радиусов эквидистант. \en Count of radii of equidistant curves. - size_t GetEquidRadiiCount() const { return equidRadii.Count(); } - /// \ru Радиус эквидистанты по номеру. Номер должен быть меньше количества радиусов. \en Radius of equidistant curve by an index. Index must be less than count of radii. - double GetEquidRadius ( size_t i ) const { return equidRadii[i]; } - /// \ru Параметры законцовки в начале мультилинии. \en Parameters of tip at the beginning of multiline. - const StMLTipParams & GetBegTipParams () const { return begTipParams; } - /// \ru Параметры законцовки в конце мультилинии. \en Parameters of a tip at the end of a multiline. - const StMLTipParams & GetEndTipParams () const { return endTipParams; } - /// \ru Признак обрабатывания замкнутости базовой кривой. \en An attribute of the base curve closedness processing. - bool IsProcessClosed () const { return processClosed; } - /// \ru Прозрачность мультилинии. \en Transparency of multiline. - bool IsTransparent () const { return isTransparent; } - // \ru Функции доступа к объектам мультилинии \en Functions for access to objects of multiline - // \ru ЭТИ ОБЪЕКТЫ НЕЛЬЗЯ МЕНЯТЬ СНАРУЖИ!!! \en THESE OBJECTS CAN'T BE CHANGED OUTSIDE!!! - - /// \ru Количество кривых мультилинии. \en Count of curves of a multiline. - size_t GetCurvesCount () const { return curves.Count(); } - /// \ru Кривая мультилинии по номеру. Номер должен быть меньше количества кривых. \en Curve of multiline by an index. Index must be less than count of curves. - const MbContourWithBreaks* GetCurve ( size_t i ) const { return curves[i]; } - - /// \ru Количество кривых - законцовок в вершинах мультилинии. \en Count of tip-curves at vertices of multiline. - size_t GetTipCurvesCount () const { return tipCurves.Count(); } - /// \ru Кривая - законцовка по номеру. Номер должен быть меньше количества законцовок. \en Tip-curve by an index. Index must be less than the count of tips. - const MbContour * GetTipCurve ( size_t i ) const { return tipCurves[i]; } - - /// \ru Законцовка в начале мультилинии. \en Tip at the beginning of a multiline. - const MbContour * GetBegTipCurve () const { return begTipCurve; } - /// \ru Законцовка в конце мультилинии. \en Tip at the end of a multiline. - const MbContour * GetEndTipCurve () const { return endTipCurve; } - - /// \ru Максимально возможный для невывернутого построения положительный радиус. \en Maximum possible positive radius for non-everted construction. - double GetMaxPosRadius () const { return maxPosRadius; } - /// \ru Минимально возможный для невывернутого построения отрицательный радиус. \en Minimum possible negative radius for non-everted construction. - double GetMinNegRadius () const { return minNegRadius; } - - /// \ru Индекс невырожденной кривой мультилинии с минимальным радиусом \en Index of non-degenerated curve of a multiline with minimal radius - size_t GetMinNotDegInd () const { return minNotDegInd; } - /// \ru Индекс невырожденной кривой мультилинии с максимальным радиусом \en Index of non-degenerated curve of a multiline with maximal radius - size_t GetMaxNotDegInd () const { return maxNotDegInd; } - - /** \} */ - /**\ru \name Функции изменения данных: изменение базовой кривой мультилинии - \en \name Functions for changing data: changing the base curve of a multiline - \{ */ - - /// \ru Очистить базовую кривую. \en Clear the base curve. - void ClearBasisCurve (); - - /** \brief \ru Заменить базовую кривую. - \en Replace the base curve. \~ - \details \ru Заменить базовую кривую.\n - При замене базовой кривой мультилинии в ней удаляются все разрывы. - \en Replace the base curve.\n - When replacing the base curve of multiline all of its breaks are removed. \~ - \param[in] _basisCurve - \ru Новая базовая кривая. - \en New base curve. \~ - \param[in] vertInfo - \ru Новая информация о вершинах мультилинии. - \en New information about vertices of multiline. \~ - */ - void ReplaceBasisCurve ( const MbContour & _basisCurve, - const StVertexOfMultilineInfo & vertInfo ); - - /** \brief \ru Добавить сегмент в базовую кривую. - \en Add a segment to a base curve. \~ - \details \ru Добавить сегмент в базовую кривую.\n - \en Add a segment to a base curve.\n \~ - \param[in] segment - \ru Добавляемый сегмент,\n - если является ломаной или контуром, то в мультилинию добавляются только составляющие сегменты. - \en Segment to add,\n - if it is polyline or contour, then only component segments are added to the multiline. \~ - \param[in] vertInfo - \ru Информация присваивается всем добавленным или измененным вершинам. - \en Information is assigned to all added or changed vertices. \~ - \return \ru true, если сегмент был добавлен. - \en True if a segment has been added. \~ - */ - bool AddBasisSegment ( MbCurve * segment, - const StVertexOfMultilineInfo & vertInfo ); - - /// \ru Удалить последний сегмент базовой кривой. \en Delete the last segment of the base curve. - void DeleteLastBasisSegment (); - - /** \brief \ru Вставить вершину. - \en Insert a vertex. \~ - \details \ru Вставить вершину. Сегмент разбивается на два.\n - \en Insert a vertex. Segment is split into two segments.\n \~ - \param[in] t - \ru Параметр на базовой кривой. - \en Parameter on the base curve. \~ - \param[in] vertInfo - \ru Информация о новой вершине мультилинии. - \en Information about new vertex of a multiline. \~ - \return \ru Индекс разбитого сегмента. - \en Index of split segment. \~ - */ - size_t InsertVertex ( double t, - const StVertexOfMultilineInfo & vertInfo ); - - /** \brief \ru Удалить вершину мультилинии. - \en Delete a vertex of multiline. \~ - \details \ru Удалить вершину мультилинии.\n - Пара сегментов, примыкающих к вершине, заменяется отрезком.\n - Разрывы на прилегающих сегментах исчезают. - Разрывы, лежащие на прямолинейных прилегающих сегментах, - остаются, привязываясь к вершинам нового отрезка. - \en Delete a vertex of multiline.\n - Pair of segments adjoining the vertex is replaced by a segment.\n - Breaks on adjacent segments are disappeared. - Breaks on straight adjacent segments - are remained by binding to the vertices of a new segment. \~ - \param[in] i - \ru Индекс вершины. - \en An index of a vertex. \~ - \return \ru true, если вершина была удалена. - \en True if a vertex is deleted. \~ - */ - bool RemoveVertex ( size_t i ); - - /** \brief \ru Сместить геометрическую hot-точку. - \en Shift a geometric hot-point. \~ - \details \ru Сместить геометрическую hot-точку базовой кривой.\n - При перемещении точки разрывы контуров, соответствующих прилегающим прямолинейным - сегментам привязываются к соседним неподвижным вершинам базовой кривой. - \en Shift a geometric hot-point of the base curve.\n - While shifting point, the contour breaks corresponding to adjacent straight - segments are bound to the neighboring fixed vertices of the base curve. \~ - \param[in] segInd - \ru Номер сегмента. - \en An index of a segment. \~ - \param[in] subInd - \ru Номер точки на сегменте:\n - если subInd = 0 - общая точка между двумя сегментами (может быть задана для любого сегмента),\n - если subInd = 1 - средняя точкой отрезка, средняя точкой дуги, базовая точка сплайнов pt_Nurbs и pt_Bezier,\n - если subInd > 1 - базовая точка сплайнов pt_Nurbs и pt_Bezier. - \en An index of a point on the segment:\n - if subInd = 0 - common point between two segments (can be specified for any segment),\n - if subInd = 1 - middle point of a segment, middle point of an arc, the base point of pt_Nurbs and pt_Bezier splines,\n - if subInd > 1 - base point of pt_Nurbs and pt_Bezier splines. \~ - \param[in] newPoint - \ru Новое положение точки. - \en New position of the point. \~ - \return \ru Точка была смещена. - \en Point has been shifted. \~ - */ - bool SetBasisCurvesGeoHotPoint( size_t segInd, size_t subInd, - const MbCartPoint & newPoint ); - - /** \brief \ru Удалить геометрическую hot-точку. - \en Delete a geometric hot-point. \~ - \details \ru Удалить геометрическую hot-точку базовой кривой.\n - \en Delete a geometric hot-point of the base curve.\n \~ - \param[in] segInd - \ru Номер сегмента. - \en An index of a segment. \~ - \param[in] subInd - \ru Номер точки на сегменте:\n - если subInd = 0 - результат аналогичен удалению вершины мультилинии RemoveVertex,\n - если subInd = 1 - удаление средней точки дуги (дуга заменяется отрезком), - удаление базовой точки сплайнов pt_Nurbs и pt_Bezier (изменение формы),\n - если subInd > 1 - удаление базовой точки сплайнов pt_Nurbs и pt_Bezier (изменение формы). - \en An index of a point on the segment:\n - if subInd = 0 - result is similar to deletion of the 'RemoveVertex' vertex of a multiline,\n - if subInd = 1 - deletion of the middle point of an arc (the arc is replaced by segment), - deletion of the base point of pt_Nurbs and pt_Bezier splines (changing of shape),\n - if subInd > 1 - deletion of the base point of pt_Nurbs and pt_Bezier splines (changing of shape). \~ - \return \ru true, если hot-точка была удалена. - \en True if hot-point has been deleted. \~ - */ - bool DelBasisCurvesGeoHotPoint( size_t segInd, size_t subInd ); - - /** \} */ - /**\ru \name Функции изменения данных: скругления и фаски базовой кривой - \en \name Functions for changing data: fillet and chamfer of the base curve - \{ */ - - /** \brief \ru Скруглить два соседних сегмента. - \en Fillet two neighboring segments. \~ - \details \ru Скруглить два соседних сегмента базовой кривой.\n - \en Fillet two neighboring segments of base curve.\n \~ - \param[in] index - \ru Индекс первого скругляемого сегмента. - \en Index of the first segment to fillet. \~ - \param[in] rad - \ru Радиус скругления. - \en The radius of fillet. \~ - \param[in] vertInfo - \ru Информация для новых вершин мультилинии. - \en Information for new vertices of a multiline. \~ - \return \ru true в случае успеха операции. - \en Returns true if the operation succeeded. \~ - */ - bool FilletTwoBasisSegments ( ptrdiff_t & index, double rad, - const StVertexOfMultilineInfo & vertInfo ); - - /** \brief \ru Скруглить базовую кривую. - \en Fillet the base curve. \~ - \details \ru Скруглить все углы базовой кривой.\n - \en Fillet all corners of the base curve.\n \~ - \param[in] rad - \ru Радиус скругления. - \en The radius of fillet. \~ - \param[in] vertInfo - \ru Информация для новых вершин мультилинии. - \en Information for new vertices of a multiline. \~ - \return \ru true, если добавилось хотя бы одно скругление. - \en True if at least one fillet is added. \~ - */ - bool FilletBasisCurve ( double rad, - const StVertexOfMultilineInfo & vertInfo ); - - /** \brief \ru Вставить фаску между двумя соседними сегментами базовой кривой мультилинии. - \en Insert a chamfer between two neighboring segments of the base curve of a multiline. \~ - \details \ru Вставить фаску между двумя соседними сегментами базовой кривой мультилинии.\n - \en Insert a chamfer between two neighboring segments of the base curve of a multiline.\n \~ - \param[in] index - \ru Индекс первого скругляемого сегмента. - \en Index of the first segment to fillet. \~ - \param[in] len - \ru Длина фаски. - \en Length of chamfer. \~ - \param[in] par - \ru Параметр в зависимости от типа type:\n - если type = true, par - угол\n - если type = false, par - размер. - \en Parameter depending on 'type' type:\n - if type = true, par is a corner\n - if type = false, par is a size. \~ - \param[in] type - \ru Тип задания фаски:\n - true - фаска задана как размер + угол,\n - false - фаска задана как размер + размер. - \en The type of a chamfer specification:\n - true - chamfer specified as size + angle,\n - false - chamfer specified as size + size. \~ - \param[in] firstSeg - \ru true, если параметр par относится к первому сегменту. - \en True if 'par' parameter is related to the first segment. \~ - \param[in] vertInfo - \ru Информация для новых вершин мультилинии. - \en Information for new vertices of a multiline. \~ - \return \ru true в случае успеха операции. - \en Returns true if the operation succeeded. \~ - */ - bool ChamferTwoBasisSegments ( ptrdiff_t & index, double len, double par, - bool type, bool firstSeg, - const StVertexOfMultilineInfo & vertInfo ); - - /** \brief \ru Вставить фаску между каждыми двумя соседними сегментами базовой кривой мультилинии. - \en Insert a chamfer between each two neighboring segments of the base curve of a multiline. \~ - \details \ru Вставить фаску между каждыми двумя соседними сегментами базовой кривой мультилинии.\n - Параметр par относится к первому сегменту из каждой пары. - \en Insert a chamfer between each two neighboring segments of the base curve of a multiline.\n - 'par' parameter is related to the first segment of each pair. \~ - \param[in] len - \ru Длина фаски. - \en Length of chamfer. \~ - \param[in] par - \ru Параметр в зависимости от типа type:\n - если type = true, par - угол,\n - если type = false, par - размер. - \en Parameter depending on 'type' type:\n - if type = true, par is a corner,\n - if type = false, par is a size. \~ - \param[in] type - \ru Тип задания фаски:\n - true - фаска задана как размер + угол,\n - false - фаска задана как размер + размер. - \en The type of a chamfer specification:\n - true - chamfer specified as size + angle,\n - false - chamfer specified as size + size. \~ - \param[in] vertInfo - \ru Информация для новых вершин мультилинии. - \en Information for new vertices of a multiline. \~ - \return \ru true, если добавилась хотя бы одна фаска. - \en True if at least one chamfer is added. \~ - */ - bool ChamferBasisCurve ( double len, double par, bool type, - const StVertexOfMultilineInfo & vertInfo ); ///< \ru Вставить фаску БК \en Insert chamfer of the base curve - - /** \} */ - /**\ru \name Функции изменения данных: изменение параметров вершины мультилинии - \en \name Functions for changing data: changing the parameters of a vertex of a multiline - \{ */ - - /** \brief \ru Установить флаг гладкого стыка. - \en Set the flag of smooth joint. \~ - \details \ru Установить флаг гладкого стыка в вершине.\n - \en Set the flag of smooth joint at a vertex.\n \~ - \param[in] i - \ru Индекс вершины мультилинии. - \en An index of a vertex of a multiline. \~ - \param[in] othSmoothJoint - \ru Флаг гладкого стыка. - \en Flag of smooth joint. \~ - */ - void SetSmoothJoint ( size_t i, bool othSmoothJoint ); - - /** \brief \ru Установить тип обхода. - \en Set the type of traverse. \~ - \details \ru Установить тип обхода вершины.\n - \en Set the type of traverse of the vertex.\n \~ - \param[in] i - \ru Индекс вершины мультилинии. - \en An index of a vertex of a multiline. \~ - \param[in] othTracingType - \ru Тип обхода. - \en Type of traverse. \~ - */ - void SetTracingType ( size_t i, EnMLVertexTracingType othTracingType ); - - /** \brief \ru Установить радиус специального скругления. - \en Set the radius of a special fillet. \~ - \details \ru Установить радиус специального скругления вершины мультилинии.\n - \en Set the radius of special fillet of a vertex of the multiline.\n \~ - \param[in] i - \ru Индекс вершины мультилинии. - \en An index of a vertex of a multiline. \~ - \param[in] othSpecFilletRad - \ru Радиус. - \en Radius. \~ - */ - void SetSpecFilletRad ( size_t i, double othSpecFilletRad ); - - /** \brief \ru Установить тип законцовки. - \en Set the type of a tip. \~ - \details \ru Установить тип законцовки (разделителя) в вершине.\n - \en Set the type of tip (splitter) at the vertex.\n \~ - \param[in] i - \ru Индекс вершины мультилинии. - \en An index of a vertex of a multiline. \~ - \param[in] othTipType - \ru Тип законцовки. - \en Type of tip. \~ - */ - void SetTipType ( size_t i, EnMLInnerTipType othTipType ); - - /** \brief \ru Установить направление законцовки. - \en Set the direction of a tip. \~ - \details \ru Установить направление законцовки в вершине мультилинии.\n - \en Set the direction of a tip at a vertex of multiline.\n \~ - \param[in] i - \ru Индекс вершины мультилинии. - \en An index of a vertex of a multiline. \~ - \param[in] othFirstSegTip - \ru Направление законцовки, если true - от первого сегмента. - \en Direction of tip, if true - from the first segment. \~ - */ - void SetTipDirection ( size_t i, bool othFirstSegTip ); - - /** \brief \ru Установить информацию о вершине. - \en Set the information about a vertex. \~ - \details \ru Установить информацию о вершине мультилинии.\n - \en Set the information about a vertex of multiline.\n \~ - \param[in] i - \ru Индекс вершины мультилинии. - \en An index of a vertex of a multiline. \~ - \param[in] vertInfo - \ru Информация о вершине. - \en Information about a vertex. \~ - */ - void SetVertexOfMultilineInfo ( size_t i, - const StVertexOfMultilineInfo & vertInfo ); - - /** \} */ - /**\ru \name Функции изменения данных: изменение радиусов эквидистант - \en \name Functions for changing data: changing the radii of equidistant curves - \{ */ - - /** \brief \ru Установить значение радиуса кривой. - \en Set the value of radius of curve. \~ - \details \ru Установить значение радиуса кривой.\n - \en Set the value of radius of curve.\n \~ - \param[in] i - \ru Индекс кривой мультилинии. - \en An index of curve of multiline. \~ - \param[in] radius - \ru Новое значение радиуса. - \en New value of the radius. \~ - \param[out] newIndex - \ru Новый индекс кривой. - \en New index of curve. \~ - \return \ru true, если радиус был изменен. - \en True if a radius has been changed. \~ - */ - bool SetRadius ( size_t i, double radius, size_t & newIndex ); - - /** \brief \ru Изменение всех радиусов кривых. - \en Changing all radii of curves. \~ - \details \ru Изменение одновременно всех радиусов кривых.\n - Значения радиусов будут изменены, если их количество в newRadii совпадает с количеством кривых. - \en Simultaneous changing all radii of curves.\n - Values of radii will be changed if their count in 'newRadii' is coincident to the count of curves. \~ - \param[in] newRadii - \ru Новые значения радиусов кривых. - \en New values of radii of curves. \~ - \return \ru true, если радиусы были изменены. - \en True if radii has been changed. \~ - */ - bool SetRadii ( const CSSArray & newRadii ); - - /** \brief \ru Изменение радиуса. - \en Change the radius. \~ - \details \ru Изменение радиуса кривой мультилинии.\n - \en Change the radius of a curve of multiline.\n \~ - \param[in] oldRadius - \ru Старое значение радиуса. - \en Old value of the radius. \~ - \param[in] radius - \ru Новое значение радиуса. - \en New value of the radius. \~ - */ - void ChangeRadius ( double oldRadius, double radius ); - - /** \brief \ru Добавление радиуса кривой мультилинии. - \en Add the radius of a curve of multiline. \~ - \details \ru Добавление радиуса кривой мультилинии.\n - Фактически, добавление кривой мультилинии. - \en Add the radius of a curve of multiline.\n - Actually, addition of a curve of multiline. \~ - \param[in] radius - \ru Значение радиуса. - \en Value of radius. \~ - \return \ru Значение индекса новой кривой\n - если кривая не была добавлена, индекс равен SYS_MAX_T. - \en Value of an index of the new curve.\n - if curve was not added, then the index is equal to SYS_MAX_T. \~ - */ - size_t AddRadius ( double radius ); - - /** \brief \ru Удаление кривой. - \en Delete a curve. \~ - \details \ru Удаление кривой мультилинии.\n - \en Delete a curve of multiline.\n \~ - \param[in] i - \ru Индекс кривой. - \en A curve index. \~ - \return \ru true, если кривая была удалена. - \en True if curve has been deleted. \~ - */ - bool RemoveRadius ( size_t i ); - - /** \brief \ru Удаление кривой. - \en Delete a curve. \~ - \details \ru Удаление кривой мультилинии.\n - \en Delete a curve of multiline.\n \~ - \param[in] oldRadius - \ru Радиус кривой. - \en Radius of a curve. \~ - \return \ru true, если кривая была удалена. - \en True if curve has been deleted. \~ - */ - bool RemoveRadius ( double oldRadius ); - - /** \} */ - /**\ru \name Функции изменения данных: изменение параметров законцовок - \en \name Functions for changing data: change the parameters of tips - \{ */ - - /** \brief \ru Изменение типа законцовки в начале. - \en Change the type of a tip at the beginning. \~ - \details \ru Изменение типа законцовки мультилинии в начале.\n - \en Change the type of a tip of multiline at the beginning.\n \~ - \param[in] othTipType - \ru Новый тип законцовки. - \en New type of tip. \~ - */ - void SetBegTipType ( EnMLTipType othTipType ); - - /** \brief \ru Изменение параметра законцовки в начале. - \en Change the parameter of a tip at the beginning. \~ - \details \ru Изменение параметра законцовки мультилинии в начале.\n - \en Change the parameter of a tip of multiline at the beginning.\n \~ - \param[in] othTipParam - \ru Новый параметр законцовки. - \en New parameter of tip. \~ - */ - void SetBegTipParam ( double othTipParam ); - - /** \brief \ru Изменение типа законцовки в конце. - \en Change the type of a tip at the end. \~ - \details \ru Изменение типа законцовки мультилинии в конце.\n - \en Change the type of a tip of multiline at the end.\n \~ - \param[in] othTipType - \ru Новый тип законцовки. - \en New type of tip. \~ - */ - void SetEndTipType ( EnMLTipType othTipType ); - - /** \brief \ru Изменение параметра законцовки в конце. - \en Change the parameter of a tip at the end. \~ - \details \ru Изменение параметра законцовки мультилинии в конце.\n - \en Change the parameter of a tip of multiline at the end.\n \~ - \param[in] othTipParam - \ru Новый параметр законцовки. - \en New parameter of tip. \~ - */ - void SetEndTipParam ( double othTipParam ); - - /** \} */ - /**\ru \name Функции изменения данных - \en \name Functions for changing data - \{ */ - - /** \brief \ru Изменение обработки замкнутости. - \en Change the closedness processing. \~ - \details \ru Изменение флага обработки замкнутости.\n - \en Change the flag of the closedness processing.\n \~ - \param[in] othProcessClosed - \ru Флаг обработки замкнутости. - \en Flag of the closedness processing. \~ - */ - void SetProcessClosed ( bool othProcessClosed ); - - /** \brief \ru Изменение прозрачности мультилинии. - \en Change the transparency of multiline. \~ - \details \ru Изменение флага прозрачности мультилинии.\n - \en Change the flag of transparency of multiline.\n \~ - \param[in] othTransparent - \ru Флаг прозрачности. - \en Transparency flag. \~ - */ - void SetTransparent ( bool othTransparent ); - - /** \} */ - /**\ru \name Работа с разрывами: добавление разрывов - \en \name Working with breaks: addition of breaks - \{ */ - - /** \brief \ru Усечение части кривой мультилинии между точками. - \en Trimming of a piece of a curve of multiline between points. \~ - \details \ru Усечение части кривой мультилинии между точками.\n - Добавление разрыва. - \en Trimming of a piece of a curve of multiline between points.\n - Add a break. \~ - \param[in] contour - \ru Кривая мультилинии для добавления разрыва. - \en Curve of multiline for addition of a break. \~ - \param[in] point1 - \ru Первая граница разрыва. - \en The first boundary of the break. \~ - \param[in] point2 - \ru Вторая граница разрыва. - \en The second boundary of the break. \~ - \param[in] point3 - \ru Точка, которая показывает удаляемую часть замкнутого контура,\n - в случае разомкнутого контура она игнорируется. - \en The point indicating the piece of a closed contour to be deleted,\n - ignored in case of the opened contour. \~ - \param[in] invertBreak - \ru Если true, то разрыв накладывается на противоположную часть контура. - \en If 'true', then the break is applied to the opposite piece of the contour. \~ - \return \ru true, если разрыв был добавлен. - \en True if a break has been added. \~ - */ - bool DeletePartP1P2 ( MbContourWithBreaks * contour, - const MbCartPoint & point1, - const MbCartPoint & point2, - const MbCartPoint & point3, - bool invertBreak = false ); - - /** \brief \ru Усечение части кривой мультилинии между точками. - \en Trimming of a piece of a curve of multiline between points. \~ - \details \ru Усечение части кривой мультилинии между точками.\n - Добавление разрыва. - \en Trimming of a piece of a curve of multiline between points.\n - Add a break. \~ - \param[in] cNumber - \ru Номер кривой мультилинии для добавления разрыва. - \en Index of curve of multiline for addition of a break. \~ - \param[in] point1 - \ru Первая граница разрыва. - \en The first boundary of the break. \~ - \param[in] point2 - \ru Вторая граница разрыва. - \en The second boundary of the break. \~ - \param[in] point3 - \ru Точка, которая показывает удаляемую часть замкнутого контура,\n - в случае разомкнутого контура она игнорируется. - \en The point indicating the piece of a closed contour to be deleted,\n - ignored in case of the opened contour. \~ - \param[in] invertBreak - \ru Если true, то разрыв накладывается на противоположную часть контура. - \en If 'true', then the break is applied to the opposite piece of the contour. \~ - \return \ru true, если разрыв был добавлен. - \en True if a break has been added. \~ - */ - bool DeletePartP1P2 ( size_t cNumber, - const MbCartPoint & point1, - const MbCartPoint & point2, - const MbCartPoint & point3, - bool invertBreak = false ); - - /** \brief \ru Усечение части кривой мультилинии между параметрами контура. - \en Trimming of a piece of a curve of multiline between parameters of contour. \~ - \details \ru Усечение части кривой мультилинии между параметрами контура.\n - Добавление разрыва. - \en Trimming of a piece of a curve of multiline between parameters of contour.\n - Add a break. \~ - \param[in] cNumber - \ru Номер кривой мультилинии для добавления разрыва. - \en Index of curve of multiline for addition of a break. \~ - \param[in] t1 - \ru Первая граница разрыва. - \en The first boundary of the break. \~ - \param[in] t2 - \ru Вторая граница разрыва. - \en The second boundary of the break. \~ - \param[in] t3 - \ru Параметр, который показывает удаляемую часть замкнутого контура,\n - в случае разомкнутого контура он игнорируется. - \en A parameter which indicates a removable part of the closed contour, \n - ignored in case of opened contour. \~ - \param[in] invertBreak - \ru Если true, то разрыв накладывается на противоположную часть контура. - \en If 'true', then the break is applied to the opposite piece of the contour. \~ - \return \ru true, если разрыв был добавлен. - \en True if a break has been added. \~ - */ - bool DeletePartP1P2 ( size_t cNumber, - double t1, double t2, double t3, - bool invertBreak = false ); - - /** \} */ - /**\ru \name Работа с разрывами: удаление разрывов - \en \name Working with breaks: deletion of breaks - \{ */ - - /** \brief \ru Удалить разрывы. - \en Remove breaks. \~ - \details \ru Удалить все разрывы мультилинии.\n - \en Delete all breaks of multiline.\n \~ - \return \ru true, если хотя бы один разрыв был удален. - \en true, if at least one break has been deleted. \~ - */ - bool DeleteBreaks ( ); - - /** \brief \ru Удалить разрывы кривой. - \en Remove breaks of curve. \~ - \details \ru Удалить все разрывы кривой мультилинии.\n - \en Delete all breaks of curve of multiline.\n \~ - \param[in] cNumber - \ru Номер кривой. - \en Index of curve. \~ - \return \ru true, если хотя бы один разрыв был удален. - \en true, if at least one break has been deleted. \~ - */ - bool DeleteBreaks ( size_t cNumber ); - - /** \brief \ru Удалить разрыв кривой. - \en Delete a break of curve. \~ - \details \ru Удалить разрыв по параметру на кривой.\n - \en Delete a break by a parameter on the curve.\n \~ - \param[in] cNumber - \ru Номер кривой. - \en Index of curve. \~ - \param[in] t - \ru Параметр на кривой. - \en A parameter on the curve. \~ - \return \ru true, если разрыв был удален. - \en True if a break has been deleted. \~ - */ - bool DeleteBreak ( size_t cNumber, double t ); - - /** \brief \ru Удалить разрыв. - \en Delete a break. \~ - \details \ru Удалить разрыв по номеру.\n - \en Delete a break by an index.\n \~ - \param[in] cNumber - \ru Номер кривой. - \en Index of curve. \~ - \param[in] brNumber - \ru Номер разрыва на кривой. - \en Index of a break on the curve. \~ - \return \ru true, если разрыв был удален. - \en True if a break has been deleted. \~ - */ - bool DeleteBreakAtNumber ( size_t cNumber, size_t brNumber ); - - /** \brief \ru Удалить разрывы малой длины. - \en Delete breaks of small length. \~ - \details \ru Удалить разрывы малой метрической длины у кривой мультилинии.\n - В случае успеха линия мультилинии перестраивается соответственно разрывам. - \en Delete breaks of small length of a curve of multiline.\n - In case of success the line of multiline is rebuilt according to breaks. \~ - \param[in] cNumber - \ru Номер кривой мультилинии. - \en Index of a curve of multiline. \~ - \param[in] length - \ru Минимальная длина невидимой части. - \en Minimal length of invisible piece. \~ - \return \ru true, если хотя бы один разрыв кривой был удален. - \en True if at least one break of the curve has been deleted. \~ - */ - bool DeleteSmallBreaks ( size_t cNumber, double length ); - - /** \brief \ru Удалить малые видимые части. - \en Delete small visible pieces. \~ - \details \ru Удалить видимые части малой метрической длины у кривой мультилинии.\n - Соответствует объединению близких разрывов в один. - В случае успеха видимые контуры кривой перестраиваются соответственно разрывам. - \en Delete small visible pieces of a curve of multiline with small metric length.\n - Corresponds to union of close breaks into one. - In case of success the visible contours of the line is rebuilt according to breaks. \~ - \param[in] cNumber - \ru Номер кривой мультилинии. - \en Index of a curve of multiline. \~ - \param[in] length - \ru Минимальная длина видимой части. - \en Minimal length of visible piece. \~ - \return \ru true, если разрывы кривой были изменены. - \en True if breaks has been changed. \~ - */ - bool DeleteSmallVisContours ( size_t cNumber, double length ); - - /** \} */ - /**\ru \name Работа с разрывами - \en \name Working with breaks - \{ */ - - /** \brief \ru Находится ли интервал на разрыве. - \en Whether the interval is on a break. \~ - \details \ru Находится ли интервал параметров на разрыве кривой.\n - \en Whether the interval of parameters is on a break of a curve.\n \~ - \param[in] cNumber - \ru Номер кривой мультилинии. - \en Index of a curve of multiline. \~ - \param[in] rect - \ru Интервал для проверки. - \en Interval to check. \~ - \return \ru true, если интервал полностью находится на разрыве или совпадает с ним. - \en True if interval entirely is on the break or coincides with it. \~ - */ - bool IsRectInBreak ( size_t cNumber, const MbRect1D & rect ); - - /** \brief \ru Запомнить разрывы. - \en Memorize the breaks. \~ - \details \ru Запомнить разрывы невидимыми контурами.\n - Для использования в паре с AddBreaksByInvisContours.\n - Удаляет все разрывы мультилинии.\n - Все контуры invisContours имеют счетчик ссылок = 1 (вызван AddRef()). - \en Memorize the breaks as invisible contours.\n - For using together with AddBreaksByInvisContours.\n - Deletes all breaks of multiline.\n - All 'invisContours' contours has a reference counter = 1 (called AddRef()). \~ - \param[out] invisContours - \ru Набор невидимых контуров всех кривых мультилинии. - \en Set of invisible contours of all curves of multiline. \~ - */ - void GetBreaksInInvisContours( RPArray & invisContours ); - - /** \brief \ru Добавить разрывы. - \en Add breaks. \~ - \details \ru Добавить разрывы невидимыми контурами.\n - Для использования в паре с GetBreaksInInvisContours.\n - Контуры invisContours удаляются (вызывается Release()).\n - Данной функцией можно наложить разрывы на мультилинию, - если они были получены методом GetBreaksInInvisContours у этой мультилинии - и форма мультилинии не была изменена. - \en Add the breaks as invisible contours.\n - For using together with GetBreaksInInvisContours.\n - 'invisContours' contours are deleted (called Release()).\n - It is possible to apply breaks to multiline by this function, - if they were obtained by GetBreaksInInvisContours method from this multiline - and shape of multiline wasn't changed. \~ - \param[in] invisContours - \ru Набор невидимых контуров всех кривых мультилинии. - \en Set of invisible contours of all curves of multiline. \~ - */ - void AddBreaksByInvisContours( RPArray & invisContours ); - - /** \brief \ru Номера контуров, пересекаемых отрезком. - \en Indices of contours intersected with a segment. \~ - \details \ru Номера контуров, которые пересекаются с отрезком по двум точкам.\n - \en Indices of contours which are intersected with segment by two points.\n \~ - \param[in] p1 - \ru Первая точка отрезка. - \en The first point of a segment. \~ - \param[in] p2 - \ru Вторая точка отрезка. - \en The second point of a segment. \~ - \param[out] cNumbers - \ru Номера контуров. - \en Indices of contours. \~ - */ - void CurvesIntersectNumbers ( const MbCartPoint & p1, const MbCartPoint & p2, SArray & cNumbers ) const; - - /** \} */ - /**\ru \name Информация о мультилинии - \en \name Information about multiline. - \{ */ - - /// \ru Вырождена ли мультилиния. \en Whether the multiline is degenerate. - bool IsDegenerate( double lenEps = Math::LengthEps ) const; - /// \ru Замкнутая ли мультилиния. \en Whether the multiline is closed. - bool IsClosed () const; - /// \ru Получить ширину мультилинии. \en Get width of multiline. - double GetWidth () const; - /// \ru Лежит ли данная точка на мультилинии. \en Whether the given point is on multiline. - bool IsPointOn ( const MbCartPoint & point ) const; - - /** \brief \ru Найти индекс кривой мультилинии. - \en Find an index of a curve of multiline. \~ - \details \ru Найти индекс кривой мультилинии.\n - \en Find an index of a curve of multiline.\n \~ - \param[in] radius - \ru Радиус нужной кривой. - \en Radius of the required curve. \~ - \return \ru Индекс кривой. - \en A curve index. \~ - */ - size_t FindRadius ( double radius ); - - /** \} */ - /**\ru \name Операции с мультилинией - \en \name Operations with multiline - \{ */ - - /** \brief \ru Усечь мультилинию. - \en Truncate multiline. \~ - \details \ru Усечь мультилинию.\n - Вернуть мультилинию, базовая кривая которой - - копия участка между параметрами t1 и t2 базовой данной мультилинии. - \en Truncate multiline.\n - Return multiline which base curve is - a copy of piece between parameters t1 and t2 of the base curve of a given multiline. \~ - \param[in] t1 - \ru Начальный параметр усечения. - \en Start parameter of trimming. \~ - \param[in] t2 - \ru Конечный параметр усечения. - \en End parameter of trimming. \~ - \param[in] sense - \ru Направление усеченной базовой кривой. - \en Direction of the trimmed base curve. \~ - \return \ru Новую мультилинию. - \en New multiline. \~ - */ - MbMultiline * Trimmed( double t1, double t2, int sense ) const; - - /** \} */ -private: - /**\ru \name Внутренние функции мультилинии - \en \name Internal functions of multiline - \{ */ - - // \ru Все внутренние функции реализованы в MltLine_.cpp (кроме тех, для которых указан другой файл) \en All internal functions implemented in MltLine_.cpp (except ones for which the other file is specified) - // \ru Насчет объектов \en Calculation of objects - /// \ru Насчитать кривую мультилинии c радиусом эквидистанты rad. \en Calculate curve of multiline with 'rad' equidistant radius. - void CalculateCurve ( double rad, MbContourWithBreaks & contour ); - /// \ru Насчитать кривую мультилинии c радиусом эквидистанты rad c заполнением информации. \en Calculate curve of multiline with equidistant radius 'rad' and filling the information. - void CalculateCurveWithInfo ( double rad, MbContourWithBreaks & contour, - SArray & baseIndexes ); - /// \ru Насчитать все кривые (с определением minNotDegInd и maxNotDegInd). \en Calculate all the curves (with determination of minNotDegInd and maxNotDegInd). - void CalculateCurves (); - /// \ru Насчитать все кривые с информацией для граничных невырожденных кривых. \en Calculate all the curves with information for non-degenerate boundary curves. - bool CalculateCurvesWithInfo ( SArray & minInfo, SArray & maxInfo ); - /// \ru Насчитать все законцовки в вершинах. \en Calculate all tips at vertices. - void CalculateTipCurves ( const SArray & minInfo, const SArray & maxInfo ); - /// \ru Насчитать все законцовки в вершинах. \en Calculate all tips at vertices. - void CalculateTipCurves (); - /// \ru Насчитать все кривые и все законцовки в вершинах. \en Calculate all curves and all tips at vertices. - void CalculateCurvesAndTipCurves(); - /// \ru Насчитать законцовку в начале. \en Calculate tip at the beginning. - void CalculateBegTipCurve ( SArray * changeCurvesNumbers = NULL ); - /// \ru Насчитать законцовку в конце. \en Calculate tip at the end. - void CalculateEndTipCurve (); - - // \ru Отцепление объектов \en Detach the objects - - /// \ru Отцепить все кривые. \en Detach all curves. - void DeleteCurves (); - /// \ru Отцепить кривую с индексом i. \en Detach i-th curve. - void DeleteCurve ( size_t i ); - /// \ru Отцепить все законцовки в вершинах. \en Detach all tips at vertices. - void DeleteTipCurves (); - /// \ru Отцепить законцовку в вершине с индексом i. \en Detach i-th tip at vertex. - void DeleteTipCurve ( size_t i ); - /// \ru Отцепить законцовку в начале. \en Detach tip at the beginning. - void DeleteBegTipCurve (); - /// \ru Отцепить законцовку в конце. \en Detach tip at the end. - void DeleteEndTipCurve (); - /// \ru Отцепить все объекты. \en Detach all the objects. - void DeleteAllObjects (); - - // \ru Пересчет объектов \en Recalculation of objects - - /// \ru Перестроить все объекты. \en Rebuild all objects. - void RebuildAllObjects (); - - // \ru Обработки изменения vertices (без перестроения) \en Processing of 'vertices' changing (without rebuilding) - void AddVert ( const StVertexOfMultilineInfo & vertInfo ); - void InsertVert ( size_t i, const StVertexOfMultilineInfo & vertInfo ); - void ChangeVert ( size_t i, const StVertexOfMultilineInfo & vertInfo ); - void RemoveVert ( size_t i ); - - // \ru Вспомогательные функции \en Auxiliary functions - - /// \ru Обработать изменение i-ого сегмента БК. \en Process change of i-th segment of base curve. - void CalculateChangeOfSegment ( size_t i ); - /// \ru Продлить кривую до законцовок. \en Extend curve to the tips. - void ProlongCurveToTips ( size_t i ); - /// \ru Рассчитать удаление i-ой кривой (обработка). \en Calculate removal of i-th curve (processing). - void CalculateRemovalOfCurve ( size_t i ); - - // \ru Скругление/фаска двух соседних сегментов БК (реализация в MLOper.cpp) \en Fillet/chamfer of two neighboring segments of the base curve (implementation in MLOper.cpp) - bool FilletOrChamferTwoBasisSegs( ptrdiff_t & index, const StVertexOfMultilineInfo & vertInfo, - bool fillet, bool recalcCrvRadii, - double param, - double par = 0.0, bool type = false, bool firstSeg = true ); - - // \ru Работа с разрывами \en Working with breaks - // \ru Работа с разрывами при редактировании (сохранение фиксированной точки и длины) \en Working with breaks while editing (preserving of fixed point and length) - void SetBreaksFixedVars ( size_t segInd, size_t subInd, const MbCartPoint & newPoint ); - void TransformBreaks ( const MbMatrix & matr ); - - // \ru Удалить разрывы на сегментах контура, с соотв. базовым номером \en Delete breaks on segments of contour with corresponding base index - void DeleteBreaksAtBaseNumber ( size_t baseNumber, bool delTracingBreaks, bool delEquidBreaks, - bool delInLineSeg = true/*\ru Удалять ли разрывы с прямолинейных сегментов - \en Whether to remove breaks from straight segments \~*/ ); - - // \ru Функции восстановления разрывов по невидимым контурам \en Functions for recovering breaks by invisible contours - void GetBreaksInInvisContours( bool allBreaks, // \ru Удалять все разырвы \en Delete all breaks - size_t i, // \ru Номер вершины, специально для изменения типа обхода \en Index of vertex, specially for changing the type of traverse \~ - bool addContWbr, // \ru Набирать контуры с разрывами \en Collect contours with breaks - RPArray & breaksContours, - RPArray & invisContours ); - void AddBreaksByInvisContours( RPArray & breaksContours, - RPArray & invisContours ); - // \ru Для перестроения разрывов \en For breaks rebuilding - void GetBreaks ( RPArray & baseNumbers ); - void RebuildBreaks ( RPArray & baseNumbers ); - - /** \} */ -private: - void operator =( const MbMultiline & ); ///< \ru Не реализован \en Not implemented - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbMultiline ) -}; // MbMultiline - -IMPL_PERSISTENT_OPS( MbMultiline ) - -//------------------------------------------------------------------------------ -// _7_ -/** \brief \ru Построить скругления мультилинии. - \en Construct a fillet of multiline. \~ - \details \ru Построить скругления базовой кривой мультилинии.\n - \en Construct a fillet of the base curve of multiline.\n \~ - \param[in] multiline - \ru Изменяемая мультилиния. - \en A multiline to be modified. \~ - \param[in] rad - \ru Радиус скругления. - \en The radius of fillet. \~ - \param[in] nodeFlag - \ru Флаг выбора скругляемых вершин:\n - true - скругление всех вершин мультилинии,\n - false - скругление ближайшей вершины к точке pnt. - \en Flag of selection of vertices to fillet:\n - true - fillet of all vertices of multiline,\n - false - fillet of the vertex nearest to 'pnt' point. \~ - \param[in] pnt - \ru Точка для указания нужной вершины. - \en Point for indication the required vertex. \~ - \param[in] vertInfo - \ru Информация для новых вершин мультилинии. - \en Information for new vertices of a multiline. \~ - \ingroup Algorithms_2D -*/ // --- -MATH_FUNC (bool) FilletMultiline ( MbMultiline & multiline, double rad, - bool nodeFlag, MbCartPoint & pnt, - const StVertexOfMultilineInfo & vertInfo ); - - -//------------------------------------------------------------------------------ -// _8_ -/** \brief \ru Построить фаски мультилинии. - \en Construct a chamfer of multiline. \~ - \details \ru Построить фаски базовой кривой мультилинии.\n - \en Construct a chamfer of the base curve of multiline.\n \~ - \param[in] multiline - \ru Изменяема мультилиния. - \en A multiline to be modified. \~ - \param[in] len - \ru Длина фаски. - \en Length of chamfer. \~ - \param[in] par - \ru Параметр в зависимости от типа type:\n - если type = true, par - угол,\n - если type = false, par - размер. - \en Parameter depending on 'type' type:\n - if type = true, par is a corner,\n - if type = false, par is a size. \~ - \param[in] type - \ru Тип задания фаски:\n - true - фаска задана как размер + угол,\n - false - фаска задана как размер + размер. - \en The type of a chamfer specification:\n - true - chamfer specified as size + angle,\n - false - chamfer specified as size + size. \~ - \param[in] nodeFlag - \ru Флаг выбора обрабатываемых вершин:\n - true - фаска между каждыми двумя соседними сегментами мультилинии,\n - false - фаска между двумя соседними сегментами мультилинии, примыкающими к ближайшей к точке pnt вершине. - \en Flag of selection of vertices to process:\n - true - chamfer between each two neighboring segments of multiline,\n - false - chamfer between two neighboring multiline segments joining at the vertex nearest to 'pnt' point. \~ - \param[in] pnt - \ru Точка для указания нужной пары сегментов. - \en Point for indication the required pair of segments. \~ - \param[in] vertInfo - \ru Информация для новых вершин мультилинии. - \en Information for new vertices of a multiline. \~ - \ingroup Algorithms_2D -*/ // --- -MATH_FUNC (bool) ChamferMultiline( MbMultiline & multiline, double len, double par, bool type, - bool nodeFlag, MbCartPoint & pnt, - const StVertexOfMultilineInfo & vertInfo ); - - -//////////////////////////////////////////////////////////////////////////////// -// -// _9_ -/// \ru Внеклассные функции расчета/учета радиусов кривизны (реализация в MltLine.cpp) \en Out-of-class functions for curvature radii calculation/consideration (implementation in MltLine.cpp) -// -//////////////////////////////////////////////////////////////////////////////// - - -//------------------------------------------------------------------------------ -/** \brief \ru Учесть радиусы кривизны кривой. - \en Consider the curve curvature radii. \~ - \details \ru Учесть минимальный положительный и максимальный отрицательный радиусы кривизны кривой.\n - Для внутреннего использования. - \en Consider the curve curvature minimum positive and maximum negative radii.\n - For internal use only. \~ - \param[in] curve - \ru Кривая мультилинии. - \en A curve of multiline. \~ - \param[in] angle - \ru Угловая толерантность. - \en An angular tolerance. \~ - \param[in, out] minPos - \ru Радиус кривой, если он меньше текущего значения переменной minPos. - \en Curve radius if it is less than current value of 'minPos' variable. \~ - \param[in, out] maxNeg - \ru Радиус кривой, если он больше текущего значения переменной minPos. - \en Curve radius if it is greater than current value of 'minPos' variable. \~ - \ingroup Algorithms_2D -*/ // --- -void ToTakeIntoCurvesCrvRadii( MbCurve & curve, double angle, double & minPos, double & maxNeg ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Получить радиусы кривизны контура. - \en Get the contour curvatures radii. \~ - \details \ru Получить минимальный положительный и максимальный отрицательный радиусы кривизны контура.\n - Для внутреннего использования. - \en Get the contour curvature minimum positive and maximum negative radii.\n - For internal use only. \~ - \param[in] contour - \ru Контур. - \en A contour. \~ - \param[in] angle - \ru Угловая толерантность. - \en An angular tolerance. \~ - \param[out] minPos - \ru Минимальный радиус сегмента контура. - \en Minimal contour segment radius. \~ - \param[out] maxNeg - \ru Максимальный радиус сегмента контура. - \en Maximal contour segment radius. \~ - \ingroup Algorithms_2D -*/ // --- -void GetContoursCrvRadii( MbContour & contour, double angle, double & minPos, double & maxNeg ); - - -//------------------------------------------------------------------------------ -// _10_ -/** \brief \ru Состыковать две кривые. - \en Join two curves. \~ - \details \ru Гладко состыковать две последовательные кривые.\n - Для внутреннего использования. - \en Smoothly join two consecutive curves.\n - For internal use only. \~ - \param[in] curve1 - \ru Первая кривая. - \en The first curve. \~ - \param[in] curve2 - \ru Вторая кривая. - \en The second curve. \~ - \return \ru true, если хотя бы одна кривая была изменена. - \en True if at least one curve has been changed. \~ - \ingroup Algorithms_2D -*/ // --- -bool SmoothJointSuccessiveCurves( MbCurve & curve1, MbCurve & curve2 ); - - -//------------------------------------------------------------------------------ -// _11_ -/** \brief \ru Разбить мультилинию. - \en Split multiline. \~ - \details \ru Разбить мультилинию на две части. - \en Split multiline into two pieces. \~ - \param[in] multiline - \ru Разбиваемая мультилиния. - \en A multiline to be split. \~ - \param[in] p1 - \ru Точка разбиения или, если мультилиния замкнута, начальная точка для новой мультилинии. - \en Splitting point or start point of a new multiline if the multiline is closed. \~ - \param[in] p2 - \ru Если мультилиния замкнута, то конечная точка для новой мультилинии. - \en End point of a new multiline if the multiline is closed. \~ - \param[out] parts - \ru Массив полученных участков (2 элемента). - \en The array of obtained pieces (two elements). \~ - \ingroup Algorithms_2D -*/ // --- -MATH_FUNC (bool) BreakMultiline( const MbMultiline & multiline, - const MbCartPoint & p1, const MbCartPoint & p2, - PArray & parts ); - - -//------------------------------------------------------------------------------ -// _12_ -/** \brief \ru Разбить мультилинию. - \en Split multiline. \~ - \details \ru Разбить мультилинию на N равных частей. - \en Split multiline into N equal pieces. \~ - \param[in] multiline - \ru Разбиваемая мультилиния. - \en A multiline to be split. \~ - \param[in] partsCount - \ru Количество частей. - \en The count of pieces. \~ - \param[in] point - \ru Ограничивающая точка для замкнутой мультилинии. - \en Bounding point for closed multiline. \~ - \param[out] parts - \ru Массив полученных участков (partsCount элементов). - \en The array of obtained pieces (partsCount elements). \~ - \ingroup Algorithms_2D -*/ // --- -MATH_FUNC (bool) BreakMultilineNParts( const MbMultiline & multiline, size_t partsCount, - const MbCartPoint & point, PArray & parts ); - -// \ru LF-Linux: inline-функции следует помещать в заголовочный файл, а не cpp! \en LF-Linux: inline-functions should be placed in the header file instead of cpp! -//------------------------------------------------------------------------------- -/// \ru Вырождена ли мультилиния \en Whether the multiline is degenerate -// --- -inline bool MbMultiline::IsDegenerate( double lenEps ) const { - return ( (minNotDegInd == SYS_MAX_T) || // \ru Значит, и maxNotDegInd == SYS_MAX_T \en So maxNotDegInd == SYS_MAX_T - (basisCurve != NULL && basisCurve->IsDegenerate(lenEps)) ); -} - - -//------------------------------------------------------------------------------- -/// \ru Замкнутая ли мультилиния \en Whether the multiline is closed -// --- -inline bool MbMultiline::IsClosed() const { - return ( basisCurve->IsClosed() && processClosed && basisCurve->GetSegmentsCount() > 1 ); -} - - -//------------------------------------------------------------------------------- -/// \ru Получить ширину мультилинии \en Get width of multiline -// --- -inline double MbMultiline::GetWidth() const -{ - if ( minNotDegInd == maxNotDegInd ) - return 0.0; - else - return ( equidRadii[maxNotDegInd] - equidRadii[minNotDegInd] ); -} - -//------------------------------------------------------------------------------- -/// \ru Обход скруглением (одним из) \en Traverse by fillet (one of) -// --- -inline bool StVertexOfMultilineInfo::IsFilletTracing() const -{ - return ( tracingType == mvt_FilletType || tracingType == mvt_SpecFilletType ); -} - - -//------------------------------------------------------------------------------- -/// -// --- -inline bool StVertexOfMultilineInfo::operator ==( const StVertexOfMultilineInfo & with ) const -{ - return smoothJoint == with.smoothJoint && - tracingType == with.tracingType && - ::fabs(specFilletRad - with.specFilletRad) < EXTENT_REGION && - tipType == with.tipType && - firstSegTip == with.firstSegTip; -} - - -//------------------------------------------------------------------------------- -/// -// --- -inline bool StVertexOfMultilineInfo::operator !=( const StVertexOfMultilineInfo & with ) const -{ - return !(*this == with); -} - - -//------------------------------------------------------------------------------- -/// \ru Инициализация по объекту \en Initialization by object -// --- -inline void StVertexOfMultilineInfo::Init( const StVertexOfMultilineInfo & other ) -{ - smoothJoint = other.smoothJoint; - tracingType = other.tracingType; - specFilletRad = other.specFilletRad; - tipType = other.tipType; - firstSegTip = other.firstSegTip; -} - - -//------------------------------------------------------------------------------- -/// \ru Инициализация по параметрам \en Initialize by parameters -// --- -inline void StVertexOfMultilineInfo::Init( bool _smoothJoint, EnMLVertexTracingType _tracingType, - double _specFilletRad, EnMLInnerTipType _tipType, - bool _firstSegTip ) -{ - smoothJoint = _smoothJoint; - tracingType = _tracingType; - specFilletRad = _specFilletRad; - tipType = _tipType; - firstSegTip = _firstSegTip; -} - - -//------------------------------------------------------------------------------- -/// \ru Изменить флаг "гладкий стык" (smoothJoint) \en Change flag "smooth joint" (smoothJoint) -// --- -inline bool StVertexOfMultilineInfo::ChangeSmoothJoint( bool othSmoothJoint ) -{ - if ( smoothJoint == othSmoothJoint ) - return false; - else { - smoothJoint = othSmoothJoint; - return true; - } -} - - -//------------------------------------------------------------------------------- -/// \ru Изменить тип обхода углов в вершине (tracingType) \en Change type of traverse of corners at a vertex (tracingType) -// --- -inline bool StVertexOfMultilineInfo::ChangeTracingType( EnMLVertexTracingType othTracingType ) -{ - if ( tracingType == othTracingType ) - return false; - else { - tracingType = othTracingType; - return true; - } -} - - -//------------------------------------------------------------------------------- -/// \ru Изменить радиус особого скругления (specFilletRad) \en Change radius of special fillet (specFilletRad) -// --- -inline bool StVertexOfMultilineInfo::ChangeSpecFilletRad( double othSpecFilletRad ) -{ - if ( ::fabs(specFilletRad - othSpecFilletRad) < Math::LengthEps ) - return false; - else { - specFilletRad = othSpecFilletRad; - return true; - } -} - - -//------------------------------------------------------------------------------- -/// \ru Изменить тип законцовки (tipType) \en Change type of tip (tipType) -// --- -inline bool StVertexOfMultilineInfo::ChangeTipType( EnMLInnerTipType othTipType ) -{ - if ( tipType == othTipType ) - return false; - else { - tipType = othTipType; - return true; - } -} - - -//------------------------------------------------------------------------------- -/// \ru Изменить флаг сегмента законцовки (isFirstSegTip) \en Change flag of segment of tip (isFirstSegTip) -// --- -inline bool StVertexOfMultilineInfo::ChangeFirstSegTip( bool othFirstSegTip ) -{ - if ( firstSegTip == othFirstSegTip ) - return false; - else { - firstSegTip = othFirstSegTip; - return true; - } -} - - -//------------------------------------------------------------------------------- -/// \ru Преобразовать объект согласно матрице \en Transform an object according to the matrix -// --- -inline void StVertexOfMultilineInfo::Transform( const MbMatrix & matr ) -{ - matr.TransformScalarX( specFilletRad ); // \ru Преобразовать радиус \en Transform radius -} - - -//------------------------------------------------------------------------------- -/// -// --- -inline bool StMLTipParams::operator ==( const StMLTipParams & with ) const -{ - return (tipType == with.tipType ) && - ::fabs(tipParam - with.tipParam) < EXTENT_REGION; -} - - -//------------------------------------------------------------------------------- -/// -// --- -inline bool StMLTipParams::operator !=( const StMLTipParams & with ) const -{ - return !(*this == with); -} - - -//------------------------------------------------------------------------------- -/// \ru Инициализация \en Initialization -// --- -inline void StMLTipParams::Init( const StMLTipParams & other ) -{ - tipType = other.tipType; - tipParam = other.tipParam; -} - - -//------------------------------------------------------------------------------- -/// \ru Инициализация \en Initialization -// --- -inline void StMLTipParams::Init( EnMLTipType _tipType, double _tipParam ) -{ - tipType = _tipType; - tipParam = _tipParam; -} - - -//------------------------------------------------------------------------------- -/// \ru Изменить тип законцовки (tipType) \en Change type of tip (tipType) -// --- -inline bool StMLTipParams::ChangeTipType( EnMLTipType othTipType ) -{ - if ( tipType == othTipType ) - return false; - else { - tipType = othTipType; - return true; - } -} - - -//------------------------------------------------------------------------------- -/// \ru Изменить параметр законцовки (tipParam) \en Change parameter of tip (tipParam) -// --- -inline bool StMLTipParams::ChangeTipParam( double othTipParam ) -{ - if ( ::fabs(tipParam - othTipParam) < Math::LengthEps ) - return false; - else { - tipParam = othTipParam; - return true; - } -} - - -//------------------------------------------------------------------------------- -/// \ru Преобразовать объект согласно матрице \en Transform an object according to the matrix -// --- -inline void StMLTipParams::Transform( const MbMatrix & matr ) -{ - matr.TransformScalarX( tipParam ); // \ru Преобразовать расстояние \en Transform distance -} - - -#endif // __MULTILINE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Мультилиния. + \en Multiline. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// \ru Заголовочный файл мультилинии cодержит следующие разделы. \en Header file of multiline contains the following sections. +// \ru _1_ EnMLVertexTracingType - тип обхода углов в вершине мультилинии \en _1_ EnMLVertexTracingType - type of traverse of corners at a vertex of multiline +// \ru _2_ EnMLInnerTipType - тип внутренней законцовки мультилинии \en _2_ EnMLInnerTipType - type of inner tip of multiline +// \ru _3_ EnMLTipType - тип законцовки мультилинии \en _3_ EnMLTipType - type of tip of multiline +// \ru _4_ StMLTipParams - структура параметров законцовки мультилинии \en _4_ StMLTipParams - tip of multiline parameters structure +// \ru _5_ StVertexOfMultilineInfo - информация о вершине мультилинии \en _5_ StVertexOfMultilineInfo - information about vertex of multiline +// \ru _6_ MbMultiline - класс мультилиния \en _6_ MbMultiline - multiline class +// \ru _7_ Функция построения скругления базовой кривой мультилинии \en _7_ Function for multiline base curve fillet construction +// \ru _8_ Функция построения фаски базовой кривой мультилинии \en _8_ Function for multiline base curve chamfer construction +// \ru _9_ Внеклассные функции расчета/учета радиусов кривизны \en _9_ Out-of-class functions for curvature radii calculation/consideration +// \ru _10_ Внеклассная функция гладкого стыка двух последовательных кривых \en _10_ Out-of-class function for smooth joint of two consecutive curves +// \ru _11_ Разбить мультилинию на две части \en _11_ Split multiline into two pieces +// \ru _12_ Разбить мультилинию на N равных частей \en _12_ Split multiline into N equal pieces +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __MULTILINE_H +#define __MULTILINE_H + + +#include +#include +#include +#include +#include + + +//------------------------------------------------------------------------------ +// _1_ +/** \brief \ru Тип обхода углов. + \en Type of traverse of corners. \~ + \details \ru Тип обхода углов в вершине мультилинии.\n + \en Type of traverse of corners at a vertex of multiline.\n \~ + \ingroup Algorithms_2D +*/ // --- +enum EnMLVertexTracingType { + mvt_ShearType, ///< \ru Обход срезом. \en Traverse by shear. + mvt_FilletType, ///< \ru Обход со скруглением. \en Traverse by fillet. + mvt_SpecFilletType, ///< \ru Обход со скруглением заданным радиусом. \en Traverse by fillet with a given radius. + // \ru ДОБАВЛЕНИЕ ТОЛЬКО В КОНЕЦ!!! \en ADDITION ONLY TO THE END!!! +}; + + +//------------------------------------------------------------------------------ +// _2_ +/** \brief \ru Тип внутренней законцовки. + \en Type of inner tip. \~ + \details \ru Тип внутренней законцовки мультилинии.\n + \en Type of inner tip of multiline.\n \~ + \ingroup Algorithms_2D +*/ // --- +enum EnMLInnerTipType { + mit_UndefTip, ///< \ru Законцовки нет. \en No tip. + mit_VerticesTip, ///< \ru Законцовка между соответствующими вершинами. \en Tip between corresponding vertices. + mit_LinearTip, ///< \ru Линейная законцовка. \en Linear tip. + mit_ArcTip, ///< \ru Дуговая законцовка. \en Arc tip. + // \ru ДОБАВЛЕНИЕ ТОЛЬКО В КОНЕЦ!!! \en ADDITION ONLY TO THE END!!! +}; + + +//------------------------------------------------------------------------------ +// _3_ +/** \brief \ru Тип законцовки. + \en Type of tip. \~ + \details \ru Тип законцовки мультилинии.\n + \en Type of tip of multiline.\n \~ + \ingroup Algorithms_2D +*/ // --- +enum EnMLTipType { + mtt_UndefTip, ///< \ru Законцовки нет. \en No tip. + mtt_LinearTip, ///< \ru Линейная законцовка. \en Linear tip. + mtt_ArcTip, ///< \ru Дуговая законцовка. \en Arc tip. + mtt_PolylineTip, ///< \ru Ломаная законцовка. \en Polyline tip. + mtt_ObliqueTip, ///< \ru Наклонная законцовка. \en Inclined tip. + // \ru ДОБАВЛЕНИЕ ТОЛЬКО В КОНЕЦ!!! \en ADDITION ONLY TO THE END!!! +}; + + +//------------------------------------------------------------------------------ +// _4_ +/** \brief \ru Cтруктура параметров законцовки. + \en Tip parameters structure. \~ + \details \ru Cтруктура параметров законцовки мультилинии.\n + Изменять данные объекта можно только из MbMultiline. + \en Multiline tip parameters structure.\n + Object data can be changed only from MbMultiline. \~ + \ingroup Algorithms_2D +*/ +struct MATH_CLASS StMLTipParams { +friend class MbMultiline; +private: + EnMLTipType tipType; ///< \ru Тип законцовки. \en Type of tip. + + /** \brief \ru Параметр законцовки. + \en Parameter of tip. \~ + \details \ru Для mtt_UndefTip неопределен,\n + для mtt_LinearTip - расстояние от конца ЛМ до законцовки,\n + для mtt_ArcTip - расстояние от конца ЛМ до вершины дуги законцовки,\n + для mtt_PolylineTip - расстояние от конца ЛМ до вершины угла законцовки,\n + для mtt_ObliqueTip - угол поворота нормали от конца мультилинии (в радианах). + \en Undefined for mtt_UndefTip,\n + for mtt_LinearTip - distance from the end of multiline to the tip,\n + for mtt_ArcTip - distance from the end of multiline to the vertex of arc of tip,\n + for mtt_PolylineTip - distance from the end of multiline to the vertex of corner of tip,\n + for mtt_ObliqueTip - angle of rotation of normal from the end of multiline (in radians).\~ + */ + double tipParam; + +public: + StMLTipParams(); ///< \ru Умолчательный конструктор. \en Default constructor. + StMLTipParams( const StMLTipParams & other ); ///< \ru Копирующий конструктор. \en Copy-constructor. + + /** \brief \ru Конструктор по типу законцовки и параметру законцовки. + \en Constructor by type of tip and parameter of tip. \~ + \details \ru Конструктор по типу законцовки и параметру законцовки.\n + \en Constructor by type of tip and parameter of tip.\n \~ + \param[in] _tipType - \ru Тип законцовки. + \en Type of tip. \~ + \param[in] _tipParam - \ru Параметр законцовки, зависит от типа законцовки:\n + для mtt_UndefTip неопределен,\n + для mtt_LinearTip - расстояние от конца ЛМ до законцовки,\n + для mtt_ArcTip - расстояние от конца ЛМ до вершины дуги законцовки,\n + для mtt_PolylineTip - расстояние от конца ЛМ до вершины угла законцовки,\n + для mtt_ObliqueTip - угол поворота нормали от конца мультилинии (в радианах). + \en Parameter of tip, depends on type of tip:\n + for mtt_UndefTip is undefined,\n + for mtt_LinearTip - distance from the end of multiline to the tip,\n + for mtt_ArcTip - distance from the end of multiline to the vertex of arc of tip,\n + for mtt_PolylineTip - distance from the end of multiline to the vertex of corner of tip,\n + for mtt_ObliqueTip - angle of rotation of normal from the end of multiline (in radians). \~ + */ + StMLTipParams( EnMLTipType _tipType, double _tipParam ); + +public: + + /**\ru \name Функции доступа к данным + \en \name Functions for access to data + \{ */ + /// \ru Тип законцовки. \en Type of tip. + EnMLTipType GetTipType () const { return tipType; } + /// \ru Параметр законцовки. \en Parameter of tip. + double GetTipParam() const { return tipParam; } + /** \} */ + /**\ru \name Операторы сравнения + \en \name Comparison operators + \{ */ + /// \ru Оператор сравнения. \en Comparison operator. + bool operator ==( const StMLTipParams & ) const; + /// \ru Оператор сравнения. \en Comparison operator. + bool operator !=( const StMLTipParams & ) const; + /** \} */ + +protected: + + /**\ru \name Функции инициализации + \en \name Initialization functions + \{ */ + + /// \ru Инициализация по структуре параметров законцовки. \en Initialization by tip parameters structure. + void Init ( const StMLTipParams & other ); + + /** \brief \ru Инициализация по типу законцовки и параметру законцовки. + \en Initialization by type of tip and parameter of tip. \~ + \details \ru Инициализация по типу законцовки и параметру законцовки.\n + \en Initialization by type of tip and parameter of tip.\n \~ + \param[in] _tipType - \ru Тип законцовки. + \en Type of tip. \~ + \param[in] _tipParam - \ru Параметр законцовки, зависит от типа законцовки:\n + для mtt_UndefTip неопределен,\n + для mtt_LinearTip - расстояние от конца ЛМ до законцовки,\n + для mtt_ArcTip - расстояние от конца ЛМ до вершины дуги законцовки,\n + для mtt_PolylineTip - расстояние от конца ЛМ до вершины угла законцовки,\n + для mtt_ObliqueTip - угол поворота нормали от конца мультилинии (в радианах). + \en Parameter of tip, depends on type of tip:\n + for mtt_UndefTip is undefined,\n + for mtt_LinearTip - distance from the end of multiline to the tip,\n + for mtt_ArcTip - distance from the end of multiline to the vertex of arc of tip,\n + for mtt_PolylineTip - distance from the end of multiline to the vertex of corner of tip,\n + for mtt_ObliqueTip - angle of rotation of normal from the end of multiline (in radians). \~ + */ + void Init ( EnMLTipType _tipType, double _tipParam ); + + /** \} */ + /**\ru \name Функции изменения данных + \en \name Functions for changing data + \{ */ + + /** \brief \ru Изменить тип законцовки. + \en Change type of tip. \~ + \details \ru Изменить тип законцовки мультилинии.\ n + \en Change type of tip of multiline.\n \~ + \param[in] othTipType - \ru Новый тип законцовки. + \en New type of tip. \~ + \return \ru false, если старое значение типа совпадает со значением othTipType. + \en False if the old value of type coincides with the value of othTipType. \~ + */ + bool ChangeTipType ( EnMLTipType othTipType ); + + /** \brief \ru Изменить параметр законцовки. + \en Change parameter of tip. \~ + \details \ru Изменить параметр законцовки мультилинии.\n + \en Change parameter of tip of multiline.\n \~ + \param[in] othTipParam - \ru Новый параметр законцовки. + \en New parameter of tip. \~ + \return \ru false, если старое значение параметра совпадает со значением othTipParam. + \en False if the old value of parameter coincides with the value of othTipParam. \~ + */ + bool ChangeTipParam( double othTipParam ); + + /** \brief \ru Трансформация. + \en Transformation. \~ + \details \ru Преобразование объекта согласно матрице.\n + \en Transform object according to the matrix.\n \~ + \param[in] matr - \ru Матрица трансформации. + \en Transformation matrix. \~ + */ + void Transform ( const MbMatrix & matr ); + /** \} */ + +private: + /// \ru Оператор присваивания. \en Assignment operator. + void operator = ( const StMLTipParams & ); + + KNOWN_OBJECTS_RW_REF_OPERATORS_EX( StMLTipParams, MATH_FUNC_EX ) +}; // StMLTipParams + + +//------------------------------------------------------------------------------ +// _5_ +/** \brief \ru Информация о вершине. + \en Information about a vertex. \~ + \details \ru Информация о вершине мультилинии.\n + Изменять данные объекта можно только из MbMultiline. + \en Information about a vertex of multiline.\n + Object data can be changed only from MbMultiline. \~ + \ingroup Algorithms_2D +*/ +struct MATH_CLASS StVertexOfMultilineInfo { +friend class MbMultiline; +private: + bool smoothJoint; ///< \ru Флаг гладкого стыка в вершине сегментов базовой линии мультилинии \en Flag of multiline base line segments smooth joint at a vertex + ///< \ru (только для сплайнов). \en (only for splines). + EnMLVertexTracingType tracingType; ///< \ru Тип обхода углов в вершине мультилинии. \en Type of traverse of corners at a vertex of multiline. + double specFilletRad; ///< \ru Радиус особого скругления на линии мультилинии (если tracingType == mvt_SpecFilletType). \en Radius of a special fillet on a line of multiline (if tracingType == mvt_SpecFilletType). + // \ru Параметры законцовки в вершине (внутренней) \en Parameters of (inner) tip at a vertex + EnMLInnerTipType tipType; ///< \ru Тип внутренней законцовки. \en Type of inner tip. + bool firstSegTip; ///< \ru Законцовка для первого сегмента вершины. \en Tip for the first segment of a vertex. + +public: + StVertexOfMultilineInfo(); ///< \ru Умолчательный конструктор. \en Default constructor. + StVertexOfMultilineInfo( const StVertexOfMultilineInfo & other ); ///< \ru Копирующий конструктор. \en Copy-constructor. + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор.\n + \en Constructor.\n \~ + \param[in] _smoothJoint - \ru Флаг гладкого стыка в вершине сегментов базовой линии мультилинии,\n + используется только для сплайнов. + \en Flag of multiline base line segments smooth joint at a vertex,\n + used only for splines. \~ + \param[in] _tracingType - \ru Тип обхода углов в вершине мультилинии. + \en Type of traverse of corners at a vertex of multiline. \~ + \param[in] _specFilletRad - \ru Радиус особого скругления на линии мультилинии,\n + если tracingType == mvt_SpecFilletType. + \en Radius of a special fillet on line of multiline,\n + if tracingType == mvt_SpecFilletType. \~ + \param[in] _tipType - \ru Тип внутренней законцовки. + \en Type of inner tip. \~ + \param[in] _firstSegTip - \ru Законцовка для первого сегмента вершины. + \en Tip for the first segment of a vertex. \~ + */ + StVertexOfMultilineInfo( bool _smoothJoint, EnMLVertexTracingType _tracingType, + double _specFilletRad, EnMLInnerTipType _tipType, bool _firstSegTip ); + +public: + + /**\ru \name Функции доступа к данным + \en \name Functions for access to data + \{ */ + /// \ru Тип обхода углов в вершине мультилинии. \en Type of traverse of corners at a vertex of multiline. + EnMLVertexTracingType GetTracingType () const { return tracingType; } + /// \ru Флаг гладкого стыка в вершине сегментов базовой линии мультилинии. \en Flag of multiline base line segments smooth joint at a vertex. + bool IsSmoothJoint () const { return smoothJoint; } + /// \ru Радиус особого скругления на линии мультилинии. \en Radius of special fillet on base line of multiline. + double GetSpecFilletRad() const { return specFilletRad; } + /// \ru Тип внутренней законцовки. \en Type of inner tip. + EnMLInnerTipType GetTipType () const { return tipType; } + /// \ru Законцовка для первого сегмента вершины. \en Tip for the first segment of a vertex. + bool IsFirstSegTip () const { return firstSegTip; } + /// \ru Обход скруглением. \en Traverse by fillet. + bool IsFilletTracing () const; + +public: + + /** \} */ + /**\ru \name Операторы сравнения и присваивания + \en \name Comparison operators and assignment + \{ */ + /// \ru Оператор сравнения. \en Comparison operator. + bool operator == ( const StVertexOfMultilineInfo & ) const; + /// \ru Оператор сравнения. \en Comparison operator. + bool operator != ( const StVertexOfMultilineInfo & ) const; + /// \ru Оператор присваивания. \en Assignment operator. + StVertexOfMultilineInfo & operator = ( const StVertexOfMultilineInfo & other ) + { + Init( other ); + return *this; + } + + /** \} */ + + +protected: + + /**\ru \name Функции инициализации + \en \name Initialization functions + \{ */ + + /// \ru Инициализация по информации о вершине мультилинии. \en Initialization by an information about a vertex of multiline. + void Init( const StVertexOfMultilineInfo & other ); + + /** \brief \ru Инициализация. + \en Initialization. \~ + \details \ru Инициализация.\n + \en Initialization.\n \~ + \param[in] _smoothJoint - \ru Флаг гладкого стыка в вершине сегментов базовой линии мультилинии,\n + используется только для сплайнов. + \en Flag of multiline base line segments smooth joint at a vertex,\n + used only for splines. \~ + \param[in] _tracingType - \ru Тип обхода углов в вершине мультилинии. + \en Type of traverse of corners at a vertex of multiline. \~ + \param[in] _specFilletRad - \ru Радиус особого скругления на линии мультилинии,\n + если tracingType == mvt_SpecFilletType. + \en Radius of a special fillet on line of multiline,\n + if tracingType == mvt_SpecFilletType. \~ + \param[in] _tipType - \ru Тип внутренней законцовки. + \en Type of inner tip. \~ + \param[in] _firstSegTip - \ru Законцовка для первого сегмента вершины. + \en Tip for the first segment of a vertex. \~ + */ + void Init ( bool _smoothJoint, EnMLVertexTracingType _tracingType, + double _specFilletRad, EnMLInnerTipType _tipType, + bool _firstSegTip ); + /** \} */ + /**\ru \name Функции изменения данных + \en \name Functions for changing data + \{ */ + /// \ru Изменить флаг гладкого стыка в вершине сегментов базовой линии мультилинии. \en Change flag of multiline base line segments smooth joint at a vertex. + bool ChangeSmoothJoint ( bool othSmoothJoint ); + /// \ru Изменить тип обхода углов в вершине мультилинии. \en Change type of traverse of corners at a vertex of multiline. + bool ChangeTracingType ( EnMLVertexTracingType othTracingType ); + /// \ru Изменить радиус особого скругления на линии мультилинии. \en Change radius of special fillet on base line of multiline. + bool ChangeSpecFilletRad( double othSpecFilletRad ); + /// \ru Изменить тип внутренней законцовки. \en Change type of inner tip. + bool ChangeTipType ( EnMLInnerTipType othTipType ); + /// \ru Изменить флаг законцовки для первого сегмента вершины. \en Change flag of tip for the first segment of a vertex. + bool ChangeFirstSegTip ( bool othFirstSegTip ); + + /** \brief \ru Трансформация. + \en Transformation. \~ + \details \ru Преобразование объекта согласно матрице.\n + \en Transform object according to the matrix.\n \~ + \param[in] matr - \ru Матрица трансформации. + \en Transformation matrix. \~ + */ + void Transform ( const MbMatrix & matr ); + /** \} */ + +private: + KNOWN_OBJECTS_RW_REF_OPERATORS_EX( StVertexOfMultilineInfo, MATH_FUNC_EX ) +}; // StVertexOfMultilineInfo + + +//------------------------------------------------------------------------------ +/** \brief \ru Класс для перестроения разрывов. + \en Class for breaks rebuilding. \~ + \details \ru Класс для перестроения разрывов. Содержит номера сегментов базовой кривой.\n + \en Class for breaks rebuilding. Contains indices of the base curve segments.\n \~ + \ingroup Algorithms_2D +*/ +// --- +class MATH_CLASS MbBreaksRebuild { + +public: + SArray baseNumbers; ///< \ru Номера сегментов базовой кривой для сегментов линии мультилинии. \en Indices of the base curve segments for line segments of multiline. + +public: + /// \ru Конструктор по массиву номеров. \en Constructor by array of indices. + MbBreaksRebuild( const SArray & bNumbers ) + :baseNumbers( bNumbers ) + { + } + ~MbBreaksRebuild() {} +}; + + +//------------------------------------------------------------------------------ +// _6_ +/** \brief \ru Мультилиния. + \en Multiline. \~ + \details \ru Мультилиния - составной геометрический объект, состоящий из\n + 1) кривых curves, построенных эквидистантно к базовой кривой мультилинии basisCurve + с радиусами equidRadii. При этом способ обхода углов для каждой вершины + basisCurve определяется отдельно (с помощью vertices.tracingType и vertices.specFilletRad).\n + 2) массива законцовок tipCurves в вершинах мультилинии, тип которых определяется + соответствующим vertices[i].tipParams.tipType.\n + 3) законцовок begTipCurve и endTipCurve на концах мультилинии, тип которых определяется + с помощью begTipParams и endTipParams.\n + При создании линии мультилинии (добавлении радиуса эквидистанты) создается контур, + который будет жить (меняться только внутри) до того момента, пока в массиве equidRadii + есть соответствующий ему в начале элемент.\n + Контур законцовки живет до тех пор, пока соответствующий тип законцовки не равен m_t_UndefTip.\n + За существованием и удалением законцовок должны следить функции SetTipType.\n + При построении кривых мультилинии вырожденные участки исключаются только в вершинах (перехлесты). + \en Multiline is a composite geometric object consisting of\n + 1) 'curves' curves constructed to be equidistant to base line 'basisCurve' of the multiline + with 'equidRadii' radii. The method of traverse of corners for each vertex of + 'basisCurve' is defined separately (with the help of vertices.tracingType and vertices.specFilletRad).\n + 2) 'tipCurves' array of tips at vertices of multiline, which type is defined + by corresponding vertices[i].tipParams.tipType.\n + 3) 'begTipCurve' and 'endTipCurve' tips at the ends of multiline, which type is defined + with the help of 'begTipParams' and 'endTipParams'.\n + While creating line of multiline (adding equidistance radius) the contour is created + which will be alive (can be changed only internally) while the 'equidRadii' array + contains element at the beginning corresponding to it.\n + The contour of a tip will be alive while the corresponding type of the tip isn't equal to m_t_UndefTip.\n + SetTipType functions care about the existence and removal of tips.\n + Degenerated regions are excluded only at vertices (overlaps) while constructing the curves of a multiline. \~ + \ingroup Region_2D +*/ +// --- +class MATH_CLASS MbMultiline : public MbPlaneItem { +private: + MbContour * basisCurve; ///< \ru Базовая кривая (БК) (всегда не NULL). \en Base curve (BC) (always not NULL). + SArray vertices; ///< \ru Массив вершин мультилинии (согласован с вершинами БК). \en Array of vertices of a multiline (agreed with the vertices of the base curve). + CSSArray equidRadii; ///< \ru Сортированный массив радиусов эквидистантных кривых. \en Sorted array of radii of equidistant curves. + StMLTipParams begTipParams; ///< \ru Параметры законцовки в начале мультилинии (начале БК). \en Parameters of a tip at the beginning of a multiline ( the beginning of base curve). + StMLTipParams endTipParams; ///< \ru Параметры законцовки в конце мультилинии (конце БК). \en Parameters of a tip at the end of a multiline (end of the base curve). + bool processClosed; ///< \ru Обрабатывать ли замкнутость БК (доп. вершина). \en Whether to process the closedness of the base curve (additional vertex). + bool isTransparent; ///< \ru "Прозрачная" ли мультилиния. \en Whether the multiline is "transparent". + // \ru Объекты, которые составляют мультилинию (рекомендовали не делать их mutable, а писать и читать) \en Objects which constitute a multiline (recommended to read and write and not to make them mutable) + // \ru ЭТИ ОБЪЕКТЫ НЕЛЬЗЯ МЕНЯТЬ СНАРУЖИ!!! \en THESE OBJECTS CAN'T BE CHANGED OUTSIDE!!! + PArray curves; ///< \ru Кривые мультилинии (согласован с equidRadii) (всегда не NULL). \en Curves of a multiline (agreed with the 'equidRadii') (always not NULL). + PArray tipCurves; ///< \ru Законцовки в вершинах мультилинии (согласован с vertices). \en Tips at vertices of a multiline (agreed with 'vertices'). + MbContour * begTipCurve; ///< \ru Законцовка в начале мультилинии (начале БК). \en Tip at the beginning of a multiline (beginning of the base curve). + MbContour * endTipCurve; ///< \ru Законцовка в конце мультилинии (конце БК). \en Tip at the end of a multiline (end of the base curve). + + mutable double maxPosRadius; ///< \ru Максимально возможный для невывернутого построения положительный радиус. \en Maximum possible positive radius for non-everted construction. + mutable double minNegRadius; ///< \ru Минимально возможный для невывернутого построения отрицательный радиус. \en Minimum possible negative radius for non-everted construction. + mutable size_t minNotDegInd; ///< \ru Индекс невырожденного элемента curves с минимальным радиусом. \en Index of non-everted element of 'curves' with minimal radius. + mutable size_t maxNotDegInd; ///< \ru Индекс невырожденного элемента curves с максимальным радиусом. \en Index of non-everted element of 'curves' with maximal radius. + +public: + MbMultiline(); ///< \ru Конструктор пустой мультилинии. \en Constructor of an empty multiline. + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор.\n + \en Constructor.\n \~ + \param[in] _basisCurve - \ru Базовая кривая. + \en Base curve. \~ + \param[in] vertInfo - \ru Информация о вершине мультилинии (применяется ко всем вершинам). + \en Information about a vertex of the multiline (applied to all vertices). \~ + \param[in] _equidRadii - \ru Сортированный массив радиусов эквидистантных кривых. + \en Sorted array of radii of equidistant curves. \~ + \param[in] _begTipParams - \ru Параметры законцовки в начале мультилинии. + \en Parameters of tip at the beginning of multiline. \~ + \param[in] _endTipParams - \ru Параметры законцовки в конце мультилинии. + \en Parameters of a tip at the end of a multiline. \~ + \param[in] _processClosed - \ru Обрабатывать ли замкнутость базовой кривой. + \en Whether to process the closedness of the base curve. \~ + \param[in] _isTransparent - \ru "Прозрачная" ли мультилиния\n + параметр не используется, мультилиния считается прозрачной. + \en Whether the multiline is "transparent"\n + parameter isn't used, multiline is considered to be transparent. \~ + */ + MbMultiline( const MbContour & _basisCurve, const StVertexOfMultilineInfo & vertInfo, + const SArray & _equidRadii, + const StMLTipParams & _begTipParams, const StMLTipParams & _endTipParams, + bool _processClosed, bool _isTransparent ); + +protected: + /// \ru Копирующий конструктор. \en Copy-constructor. + MbMultiline( const MbMultiline & ); + +private: + MbMultiline( const MbContour & _basisCurve, const SArray & _vertices, + const SArray & _equidRadii, + const StMLTipParams & _begTipParams, const StMLTipParams & _endTipParams, + bool _processClosed, bool _isTransparent, bool & error ); + +public: + virtual ~MbMultiline(); + +public: + + /**\ru \name Общие функции геометрического объекта + \en \name Common functions of a geometric object + \{ */ + virtual MbePlaneType IsA () const; // \ru Тип объекта. \en A type of an object. + virtual MbePlaneType Type () const; // \ru Групповой тип объекта. \en Group type of an object. + virtual MbePlaneType Family () const; // \ru Семейство объекта. \en Family of an object. + virtual bool IsSame ( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными. \en Determine whether objects are equal. + virtual bool IsSimilar ( const MbPlaneItem & item ) const; // \ru Являются ли объекты подобными. \en Determine whether the objects are similar. + virtual bool SetEqual ( const MbPlaneItem & item ); // \ru Сделать объекты равными. \en Make the objects equal. + virtual void Transform ( const MbMatrix & matr, MbRegTransform * = NULL, const MbSurface * newSurface = NULL );// \ru Преобразовать объект согласно матрице. \en Transform an object according to the matrix. + virtual void Move ( const MbVector & to, MbRegTransform * = NULL, const MbSurface * newSurface = NULL );// \ru Сдвинуть объект вдоль вектора. \en Move an object along a vector. + virtual void Rotate ( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Повернуть вокруг точки на угол. \en Rotate at angle around a point. + virtual MbPlaneItem & Duplicate ( MbRegDuplicate * = NULL ) const; // \ru Сделать копию объекта. \en Create a copy of the object. + virtual void AddYourGabaritTo( MbRect & r ) const; // \ru Добавить свой габарит в присланный габарит. \en Add your own bounding box into the given bounding box. + + virtual bool IsVisibleInRect ( const MbRect & r, bool exact = false ) const; // \ru Виден ли объект в заданном прям-ке. \en Whether the object is visible in the given rectangle. + virtual double DistanceToPoint ( const MbCartPoint & toP ) const; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. + virtual bool DistanceToPointIfLess( const MbCartPoint & to, double &distance) const; // \ru Вычислить расстояние до точки, если оно меньше d. \en Calculate the distance to the point if it is less than d. + + virtual MbProperty& CreateProperty( MbePrompt name ) const; // \ru Создать собственное свойство. \en Create a custom property. + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта. \en Set properties of the object. + virtual void GetBasisPoints( MbControlData & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + /** \} */ + /**\ru \name Функции доступа к данным + \en \name Functions for access to data + \{ */ + + /// \ru Базовая кривая. \en Base curve. + const MbContour & GetBasisCurve () const { return *basisCurve; } + /// \ru Дать базовую кривую для изменения. \en Get base curve for editing. + MbContour & SetBasisCurve () { return *basisCurve; } + /// \ru Количество вершин. \en Count of vertices. + size_t GetVerticesCount () const { return vertices.Count(); } + /// \ru Вершина по номеру. Номер должен быть меньше количества вершин. \en Vertex by an index. Index must be less than count of vertices. + const StVertexOfMultilineInfo & GetVertex ( size_t i ) const { return vertices[i]; } + /// \ru Количество радиусов эквидистант. \en Count of radii of equidistant curves. + size_t GetEquidRadiiCount() const { return equidRadii.Count(); } + /// \ru Радиус эквидистанты по номеру. Номер должен быть меньше количества радиусов. \en Radius of equidistant curve by an index. Index must be less than count of radii. + double GetEquidRadius ( size_t i ) const { return equidRadii[i]; } + /// \ru Параметры законцовки в начале мультилинии. \en Parameters of tip at the beginning of multiline. + const StMLTipParams & GetBegTipParams () const { return begTipParams; } + /// \ru Параметры законцовки в конце мультилинии. \en Parameters of a tip at the end of a multiline. + const StMLTipParams & GetEndTipParams () const { return endTipParams; } + /// \ru Признак обрабатывания замкнутости базовой кривой. \en An attribute of the base curve closedness processing. + bool IsProcessClosed () const { return processClosed; } + /// \ru Прозрачность мультилинии. \en Transparency of multiline. + bool IsTransparent () const { return isTransparent; } + // \ru Функции доступа к объектам мультилинии \en Functions for access to objects of multiline + // \ru ЭТИ ОБЪЕКТЫ НЕЛЬЗЯ МЕНЯТЬ СНАРУЖИ!!! \en THESE OBJECTS CAN'T BE CHANGED OUTSIDE!!! + + /// \ru Количество кривых мультилинии. \en Count of curves of a multiline. + size_t GetCurvesCount () const { return curves.Count(); } + /// \ru Кривая мультилинии по номеру. Номер должен быть меньше количества кривых. \en Curve of multiline by an index. Index must be less than count of curves. + const MbContourWithBreaks* GetCurve ( size_t i ) const { return curves[i]; } + + /// \ru Количество кривых - законцовок в вершинах мультилинии. \en Count of tip-curves at vertices of multiline. + size_t GetTipCurvesCount () const { return tipCurves.Count(); } + /// \ru Кривая - законцовка по номеру. Номер должен быть меньше количества законцовок. \en Tip-curve by an index. Index must be less than the count of tips. + const MbContour * GetTipCurve ( size_t i ) const { return tipCurves[i]; } + + /// \ru Законцовка в начале мультилинии. \en Tip at the beginning of a multiline. + const MbContour * GetBegTipCurve () const { return begTipCurve; } + /// \ru Законцовка в конце мультилинии. \en Tip at the end of a multiline. + const MbContour * GetEndTipCurve () const { return endTipCurve; } + + /// \ru Максимально возможный для невывернутого построения положительный радиус. \en Maximum possible positive radius for non-everted construction. + double GetMaxPosRadius () const { return maxPosRadius; } + /// \ru Минимально возможный для невывернутого построения отрицательный радиус. \en Minimum possible negative radius for non-everted construction. + double GetMinNegRadius () const { return minNegRadius; } + + /// \ru Индекс невырожденной кривой мультилинии с минимальным радиусом \en Index of non-degenerated curve of a multiline with minimal radius + size_t GetMinNotDegInd () const { return minNotDegInd; } + /// \ru Индекс невырожденной кривой мультилинии с максимальным радиусом \en Index of non-degenerated curve of a multiline with maximal radius + size_t GetMaxNotDegInd () const { return maxNotDegInd; } + + /** \} */ + /**\ru \name Функции изменения данных: изменение базовой кривой мультилинии + \en \name Functions for changing data: changing the base curve of a multiline + \{ */ + + /// \ru Очистить базовую кривую. \en Clear the base curve. + void ClearBasisCurve (); + + /** \brief \ru Заменить базовую кривую. + \en Replace the base curve. \~ + \details \ru Заменить базовую кривую.\n + При замене базовой кривой мультилинии в ней удаляются все разрывы. + \en Replace the base curve.\n + When replacing the base curve of multiline all of its breaks are removed. \~ + \param[in] _basisCurve - \ru Новая базовая кривая. + \en New base curve. \~ + \param[in] vertInfo - \ru Новая информация о вершинах мультилинии. + \en New information about vertices of multiline. \~ + */ + void ReplaceBasisCurve ( const MbContour & _basisCurve, + const StVertexOfMultilineInfo & vertInfo ); + + /** \brief \ru Добавить сегмент в базовую кривую. + \en Add a segment to a base curve. \~ + \details \ru Добавить сегмент в базовую кривую.\n + \en Add a segment to a base curve.\n \~ + \param[in] segment - \ru Добавляемый сегмент,\n + если является ломаной или контуром, то в мультилинию добавляются только составляющие сегменты. + \en Segment to add,\n + if it is polyline or contour, then only component segments are added to the multiline. \~ + \param[in] vertInfo - \ru Информация присваивается всем добавленным или измененным вершинам. + \en Information is assigned to all added or changed vertices. \~ + \return \ru true, если сегмент был добавлен. + \en True if a segment has been added. \~ + */ + bool AddBasisSegment ( MbCurve * segment, + const StVertexOfMultilineInfo & vertInfo ); + + /// \ru Удалить последний сегмент базовой кривой. \en Delete the last segment of the base curve. + void DeleteLastBasisSegment (); + + /** \brief \ru Вставить вершину. + \en Insert a vertex. \~ + \details \ru Вставить вершину. Сегмент разбивается на два.\n + \en Insert a vertex. Segment is split into two segments.\n \~ + \param[in] t - \ru Параметр на базовой кривой. + \en Parameter on the base curve. \~ + \param[in] vertInfo - \ru Информация о новой вершине мультилинии. + \en Information about new vertex of a multiline. \~ + \return \ru Индекс разбитого сегмента. + \en Index of split segment. \~ + */ + size_t InsertVertex ( double t, + const StVertexOfMultilineInfo & vertInfo ); + + /** \brief \ru Удалить вершину мультилинии. + \en Delete a vertex of multiline. \~ + \details \ru Удалить вершину мультилинии.\n + Пара сегментов, примыкающих к вершине, заменяется отрезком.\n + Разрывы на прилегающих сегментах исчезают. + Разрывы, лежащие на прямолинейных прилегающих сегментах, + остаются, привязываясь к вершинам нового отрезка. + \en Delete a vertex of multiline.\n + Pair of segments adjoining the vertex is replaced by a segment.\n + Breaks on adjacent segments are disappeared. + Breaks on straight adjacent segments + are remained by binding to the vertices of a new segment. \~ + \param[in] i - \ru Индекс вершины. + \en An index of a vertex. \~ + \return \ru true, если вершина была удалена. + \en True if a vertex is deleted. \~ + */ + bool RemoveVertex ( size_t i ); + + /** \brief \ru Сместить геометрическую hot-точку. + \en Shift a geometric hot-point. \~ + \details \ru Сместить геометрическую hot-точку базовой кривой.\n + При перемещении точки разрывы контуров, соответствующих прилегающим прямолинейным + сегментам привязываются к соседним неподвижным вершинам базовой кривой. + \en Shift a geometric hot-point of the base curve.\n + While shifting point, the contour breaks corresponding to adjacent straight + segments are bound to the neighboring fixed vertices of the base curve. \~ + \param[in] segInd - \ru Номер сегмента. + \en An index of a segment. \~ + \param[in] subInd - \ru Номер точки на сегменте:\n + если subInd = 0 - общая точка между двумя сегментами (может быть задана для любого сегмента),\n + если subInd = 1 - средняя точкой отрезка, средняя точкой дуги, базовая точка сплайнов pt_Nurbs и pt_Bezier,\n + если subInd > 1 - базовая точка сплайнов pt_Nurbs и pt_Bezier. + \en An index of a point on the segment:\n + if subInd = 0 - common point between two segments (can be specified for any segment),\n + if subInd = 1 - middle point of a segment, middle point of an arc, the base point of pt_Nurbs and pt_Bezier splines,\n + if subInd > 1 - base point of pt_Nurbs and pt_Bezier splines. \~ + \param[in] newPoint - \ru Новое положение точки. + \en New position of the point. \~ + \return \ru Точка была смещена. + \en Point has been shifted. \~ + */ + bool SetBasisCurvesGeoHotPoint( size_t segInd, size_t subInd, + const MbCartPoint & newPoint ); + + /** \brief \ru Удалить геометрическую hot-точку. + \en Delete a geometric hot-point. \~ + \details \ru Удалить геометрическую hot-точку базовой кривой.\n + \en Delete a geometric hot-point of the base curve.\n \~ + \param[in] segInd - \ru Номер сегмента. + \en An index of a segment. \~ + \param[in] subInd - \ru Номер точки на сегменте:\n + если subInd = 0 - результат аналогичен удалению вершины мультилинии RemoveVertex,\n + если subInd = 1 - удаление средней точки дуги (дуга заменяется отрезком), + удаление базовой точки сплайнов pt_Nurbs и pt_Bezier (изменение формы),\n + если subInd > 1 - удаление базовой точки сплайнов pt_Nurbs и pt_Bezier (изменение формы). + \en An index of a point on the segment:\n + if subInd = 0 - result is similar to deletion of the 'RemoveVertex' vertex of a multiline,\n + if subInd = 1 - deletion of the middle point of an arc (the arc is replaced by segment), + deletion of the base point of pt_Nurbs and pt_Bezier splines (changing of shape),\n + if subInd > 1 - deletion of the base point of pt_Nurbs and pt_Bezier splines (changing of shape). \~ + \return \ru true, если hot-точка была удалена. + \en True if hot-point has been deleted. \~ + */ + bool DelBasisCurvesGeoHotPoint( size_t segInd, size_t subInd ); + + /** \} */ + /**\ru \name Функции изменения данных: скругления и фаски базовой кривой + \en \name Functions for changing data: fillet and chamfer of the base curve + \{ */ + + /** \brief \ru Скруглить два соседних сегмента. + \en Fillet two neighboring segments. \~ + \details \ru Скруглить два соседних сегмента базовой кривой.\n + \en Fillet two neighboring segments of base curve.\n \~ + \param[in] index - \ru Индекс первого скругляемого сегмента. + \en Index of the first segment to fillet. \~ + \param[in] rad - \ru Радиус скругления. + \en The radius of fillet. \~ + \param[in] vertInfo - \ru Информация для новых вершин мультилинии. + \en Information for new vertices of a multiline. \~ + \return \ru true в случае успеха операции. + \en Returns true if the operation succeeded. \~ + */ + bool FilletTwoBasisSegments ( ptrdiff_t & index, double rad, + const StVertexOfMultilineInfo & vertInfo ); + + /** \brief \ru Скруглить базовую кривую. + \en Fillet the base curve. \~ + \details \ru Скруглить все углы базовой кривой.\n + \en Fillet all corners of the base curve.\n \~ + \param[in] rad - \ru Радиус скругления. + \en The radius of fillet. \~ + \param[in] vertInfo - \ru Информация для новых вершин мультилинии. + \en Information for new vertices of a multiline. \~ + \return \ru true, если добавилось хотя бы одно скругление. + \en True if at least one fillet is added. \~ + */ + bool FilletBasisCurve ( double rad, + const StVertexOfMultilineInfo & vertInfo ); + + /** \brief \ru Вставить фаску между двумя соседними сегментами базовой кривой мультилинии. + \en Insert a chamfer between two neighboring segments of the base curve of a multiline. \~ + \details \ru Вставить фаску между двумя соседними сегментами базовой кривой мультилинии.\n + \en Insert a chamfer between two neighboring segments of the base curve of a multiline.\n \~ + \param[in] index - \ru Индекс первого скругляемого сегмента. + \en Index of the first segment to fillet. \~ + \param[in] len - \ru Длина фаски. + \en Length of chamfer. \~ + \param[in] par - \ru Параметр в зависимости от типа type:\n + если type = true, par - угол\n + если type = false, par - размер. + \en Parameter depending on 'type' type:\n + if type = true, par is a corner\n + if type = false, par is a size. \~ + \param[in] type - \ru Тип задания фаски:\n + true - фаска задана как размер + угол,\n + false - фаска задана как размер + размер. + \en The type of a chamfer specification:\n + true - chamfer specified as size + angle,\n + false - chamfer specified as size + size. \~ + \param[in] firstSeg - \ru true, если параметр par относится к первому сегменту. + \en True if 'par' parameter is related to the first segment. \~ + \param[in] vertInfo - \ru Информация для новых вершин мультилинии. + \en Information for new vertices of a multiline. \~ + \return \ru true в случае успеха операции. + \en Returns true if the operation succeeded. \~ + */ + bool ChamferTwoBasisSegments ( ptrdiff_t & index, double len, double par, + bool type, bool firstSeg, + const StVertexOfMultilineInfo & vertInfo ); + + /** \brief \ru Вставить фаску между каждыми двумя соседними сегментами базовой кривой мультилинии. + \en Insert a chamfer between each two neighboring segments of the base curve of a multiline. \~ + \details \ru Вставить фаску между каждыми двумя соседними сегментами базовой кривой мультилинии.\n + Параметр par относится к первому сегменту из каждой пары. + \en Insert a chamfer between each two neighboring segments of the base curve of a multiline.\n + 'par' parameter is related to the first segment of each pair. \~ + \param[in] len - \ru Длина фаски. + \en Length of chamfer. \~ + \param[in] par - \ru Параметр в зависимости от типа type:\n + если type = true, par - угол,\n + если type = false, par - размер. + \en Parameter depending on 'type' type:\n + if type = true, par is a corner,\n + if type = false, par is a size. \~ + \param[in] type - \ru Тип задания фаски:\n + true - фаска задана как размер + угол,\n + false - фаска задана как размер + размер. + \en The type of a chamfer specification:\n + true - chamfer specified as size + angle,\n + false - chamfer specified as size + size. \~ + \param[in] vertInfo - \ru Информация для новых вершин мультилинии. + \en Information for new vertices of a multiline. \~ + \return \ru true, если добавилась хотя бы одна фаска. + \en True if at least one chamfer is added. \~ + */ + bool ChamferBasisCurve ( double len, double par, bool type, + const StVertexOfMultilineInfo & vertInfo ); ///< \ru Вставить фаску БК \en Insert chamfer of the base curve + + /** \} */ + /**\ru \name Функции изменения данных: изменение параметров вершины мультилинии + \en \name Functions for changing data: changing the parameters of a vertex of a multiline + \{ */ + + /** \brief \ru Установить флаг гладкого стыка. + \en Set the flag of smooth joint. \~ + \details \ru Установить флаг гладкого стыка в вершине.\n + \en Set the flag of smooth joint at a vertex.\n \~ + \param[in] i - \ru Индекс вершины мультилинии. + \en An index of a vertex of a multiline. \~ + \param[in] othSmoothJoint - \ru Флаг гладкого стыка. + \en Flag of smooth joint. \~ + */ + void SetSmoothJoint ( size_t i, bool othSmoothJoint ); + + /** \brief \ru Установить тип обхода. + \en Set the type of traverse. \~ + \details \ru Установить тип обхода вершины.\n + \en Set the type of traverse of the vertex.\n \~ + \param[in] i - \ru Индекс вершины мультилинии. + \en An index of a vertex of a multiline. \~ + \param[in] othTracingType - \ru Тип обхода. + \en Type of traverse. \~ + */ + void SetTracingType ( size_t i, EnMLVertexTracingType othTracingType ); + + /** \brief \ru Установить радиус специального скругления. + \en Set the radius of a special fillet. \~ + \details \ru Установить радиус специального скругления вершины мультилинии.\n + \en Set the radius of special fillet of a vertex of the multiline.\n \~ + \param[in] i - \ru Индекс вершины мультилинии. + \en An index of a vertex of a multiline. \~ + \param[in] othSpecFilletRad - \ru Радиус. + \en Radius. \~ + */ + void SetSpecFilletRad ( size_t i, double othSpecFilletRad ); + + /** \brief \ru Установить тип законцовки. + \en Set the type of a tip. \~ + \details \ru Установить тип законцовки (разделителя) в вершине.\n + \en Set the type of tip (splitter) at the vertex.\n \~ + \param[in] i - \ru Индекс вершины мультилинии. + \en An index of a vertex of a multiline. \~ + \param[in] othTipType - \ru Тип законцовки. + \en Type of tip. \~ + */ + void SetTipType ( size_t i, EnMLInnerTipType othTipType ); + + /** \brief \ru Установить направление законцовки. + \en Set the direction of a tip. \~ + \details \ru Установить направление законцовки в вершине мультилинии.\n + \en Set the direction of a tip at a vertex of multiline.\n \~ + \param[in] i - \ru Индекс вершины мультилинии. + \en An index of a vertex of a multiline. \~ + \param[in] othFirstSegTip - \ru Направление законцовки, если true - от первого сегмента. + \en Direction of tip, if true - from the first segment. \~ + */ + void SetTipDirection ( size_t i, bool othFirstSegTip ); + + /** \brief \ru Установить информацию о вершине. + \en Set the information about a vertex. \~ + \details \ru Установить информацию о вершине мультилинии.\n + \en Set the information about a vertex of multiline.\n \~ + \param[in] i - \ru Индекс вершины мультилинии. + \en An index of a vertex of a multiline. \~ + \param[in] vertInfo - \ru Информация о вершине. + \en Information about a vertex. \~ + */ + void SetVertexOfMultilineInfo ( size_t i, + const StVertexOfMultilineInfo & vertInfo ); + + /** \} */ + /**\ru \name Функции изменения данных: изменение радиусов эквидистант + \en \name Functions for changing data: changing the radii of equidistant curves + \{ */ + + /** \brief \ru Установить значение радиуса кривой. + \en Set the value of radius of curve. \~ + \details \ru Установить значение радиуса кривой.\n + \en Set the value of radius of curve.\n \~ + \param[in] i - \ru Индекс кривой мультилинии. + \en An index of curve of multiline. \~ + \param[in] radius - \ru Новое значение радиуса. + \en New value of the radius. \~ + \param[out] newIndex - \ru Новый индекс кривой. + \en New index of curve. \~ + \return \ru true, если радиус был изменен. + \en True if a radius has been changed. \~ + */ + bool SetRadius ( size_t i, double radius, size_t & newIndex ); + + /** \brief \ru Изменение всех радиусов кривых. + \en Changing all radii of curves. \~ + \details \ru Изменение одновременно всех радиусов кривых.\n + Значения радиусов будут изменены, если их количество в newRadii совпадает с количеством кривых. + \en Simultaneous changing all radii of curves.\n + Values of radii will be changed if their count in 'newRadii' is coincident to the count of curves. \~ + \param[in] newRadii - \ru Новые значения радиусов кривых. + \en New values of radii of curves. \~ + \return \ru true, если радиусы были изменены. + \en True if radii has been changed. \~ + */ + bool SetRadii ( const CSSArray & newRadii ); + + /** \brief \ru Изменение радиуса. + \en Change the radius. \~ + \details \ru Изменение радиуса кривой мультилинии.\n + \en Change the radius of a curve of multiline.\n \~ + \param[in] oldRadius - \ru Старое значение радиуса. + \en Old value of the radius. \~ + \param[in] radius - \ru Новое значение радиуса. + \en New value of the radius. \~ + */ + void ChangeRadius ( double oldRadius, double radius ); + + /** \brief \ru Добавление радиуса кривой мультилинии. + \en Add the radius of a curve of multiline. \~ + \details \ru Добавление радиуса кривой мультилинии.\n + Фактически, добавление кривой мультилинии. + \en Add the radius of a curve of multiline.\n + Actually, addition of a curve of multiline. \~ + \param[in] radius - \ru Значение радиуса. + \en Value of radius. \~ + \return \ru Значение индекса новой кривой\n + если кривая не была добавлена, индекс равен SYS_MAX_T. + \en Value of an index of the new curve.\n + if curve was not added, then the index is equal to SYS_MAX_T. \~ + */ + size_t AddRadius ( double radius ); + + /** \brief \ru Удаление кривой. + \en Delete a curve. \~ + \details \ru Удаление кривой мультилинии.\n + \en Delete a curve of multiline.\n \~ + \param[in] i - \ru Индекс кривой. + \en A curve index. \~ + \return \ru true, если кривая была удалена. + \en True if curve has been deleted. \~ + */ + bool RemoveRadius ( size_t i ); + + /** \brief \ru Удаление кривой. + \en Delete a curve. \~ + \details \ru Удаление кривой мультилинии.\n + \en Delete a curve of multiline.\n \~ + \param[in] oldRadius - \ru Радиус кривой. + \en Radius of a curve. \~ + \return \ru true, если кривая была удалена. + \en True if curve has been deleted. \~ + */ + bool RemoveRadius ( double oldRadius ); + + /** \} */ + /**\ru \name Функции изменения данных: изменение параметров законцовок + \en \name Functions for changing data: change the parameters of tips + \{ */ + + /** \brief \ru Изменение типа законцовки в начале. + \en Change the type of a tip at the beginning. \~ + \details \ru Изменение типа законцовки мультилинии в начале.\n + \en Change the type of a tip of multiline at the beginning.\n \~ + \param[in] othTipType - \ru Новый тип законцовки. + \en New type of tip. \~ + */ + void SetBegTipType ( EnMLTipType othTipType ); + + /** \brief \ru Изменение параметра законцовки в начале. + \en Change the parameter of a tip at the beginning. \~ + \details \ru Изменение параметра законцовки мультилинии в начале.\n + \en Change the parameter of a tip of multiline at the beginning.\n \~ + \param[in] othTipParam - \ru Новый параметр законцовки. + \en New parameter of tip. \~ + */ + void SetBegTipParam ( double othTipParam ); + + /** \brief \ru Изменение типа законцовки в конце. + \en Change the type of a tip at the end. \~ + \details \ru Изменение типа законцовки мультилинии в конце.\n + \en Change the type of a tip of multiline at the end.\n \~ + \param[in] othTipType - \ru Новый тип законцовки. + \en New type of tip. \~ + */ + void SetEndTipType ( EnMLTipType othTipType ); + + /** \brief \ru Изменение параметра законцовки в конце. + \en Change the parameter of a tip at the end. \~ + \details \ru Изменение параметра законцовки мультилинии в конце.\n + \en Change the parameter of a tip of multiline at the end.\n \~ + \param[in] othTipParam - \ru Новый параметр законцовки. + \en New parameter of tip. \~ + */ + void SetEndTipParam ( double othTipParam ); + + /** \} */ + /**\ru \name Функции изменения данных + \en \name Functions for changing data + \{ */ + + /** \brief \ru Изменение обработки замкнутости. + \en Change the closedness processing. \~ + \details \ru Изменение флага обработки замкнутости.\n + \en Change the flag of the closedness processing.\n \~ + \param[in] othProcessClosed - \ru Флаг обработки замкнутости. + \en Flag of the closedness processing. \~ + */ + void SetProcessClosed ( bool othProcessClosed ); + + /** \brief \ru Изменение прозрачности мультилинии. + \en Change the transparency of multiline. \~ + \details \ru Изменение флага прозрачности мультилинии.\n + \en Change the flag of transparency of multiline.\n \~ + \param[in] othTransparent - \ru Флаг прозрачности. + \en Transparency flag. \~ + */ + void SetTransparent ( bool othTransparent ); + + /** \} */ + /**\ru \name Работа с разрывами: добавление разрывов + \en \name Working with breaks: addition of breaks + \{ */ + + /** \brief \ru Усечение части кривой мультилинии между точками. + \en Trimming of a piece of a curve of multiline between points. \~ + \details \ru Усечение части кривой мультилинии между точками.\n + Добавление разрыва. + \en Trimming of a piece of a curve of multiline between points.\n + Add a break. \~ + \param[in] contour - \ru Кривая мультилинии для добавления разрыва. + \en Curve of multiline for addition of a break. \~ + \param[in] point1 - \ru Первая граница разрыва. + \en The first boundary of the break. \~ + \param[in] point2 - \ru Вторая граница разрыва. + \en The second boundary of the break. \~ + \param[in] point3 - \ru Точка, которая показывает удаляемую часть замкнутого контура,\n + в случае разомкнутого контура она игнорируется. + \en The point indicating the piece of a closed contour to be deleted,\n + ignored in case of the opened contour. \~ + \param[in] invertBreak - \ru Если true, то разрыв накладывается на противоположную часть контура. + \en If 'true', then the break is applied to the opposite piece of the contour. \~ + \return \ru true, если разрыв был добавлен. + \en True if a break has been added. \~ + */ + bool DeletePartP1P2 ( MbContourWithBreaks * contour, + const MbCartPoint & point1, + const MbCartPoint & point2, + const MbCartPoint & point3, + bool invertBreak = false ); + + /** \brief \ru Усечение части кривой мультилинии между точками. + \en Trimming of a piece of a curve of multiline between points. \~ + \details \ru Усечение части кривой мультилинии между точками.\n + Добавление разрыва. + \en Trimming of a piece of a curve of multiline between points.\n + Add a break. \~ + \param[in] cNumber - \ru Номер кривой мультилинии для добавления разрыва. + \en Index of curve of multiline for addition of a break. \~ + \param[in] point1 - \ru Первая граница разрыва. + \en The first boundary of the break. \~ + \param[in] point2 - \ru Вторая граница разрыва. + \en The second boundary of the break. \~ + \param[in] point3 - \ru Точка, которая показывает удаляемую часть замкнутого контура,\n + в случае разомкнутого контура она игнорируется. + \en The point indicating the piece of a closed contour to be deleted,\n + ignored in case of the opened contour. \~ + \param[in] invertBreak - \ru Если true, то разрыв накладывается на противоположную часть контура. + \en If 'true', then the break is applied to the opposite piece of the contour. \~ + \return \ru true, если разрыв был добавлен. + \en True if a break has been added. \~ + */ + bool DeletePartP1P2 ( size_t cNumber, + const MbCartPoint & point1, + const MbCartPoint & point2, + const MbCartPoint & point3, + bool invertBreak = false ); + + /** \brief \ru Усечение части кривой мультилинии между параметрами контура. + \en Trimming of a piece of a curve of multiline between parameters of contour. \~ + \details \ru Усечение части кривой мультилинии между параметрами контура.\n + Добавление разрыва. + \en Trimming of a piece of a curve of multiline between parameters of contour.\n + Add a break. \~ + \param[in] cNumber - \ru Номер кривой мультилинии для добавления разрыва. + \en Index of curve of multiline for addition of a break. \~ + \param[in] t1 - \ru Первая граница разрыва. + \en The first boundary of the break. \~ + \param[in] t2 - \ru Вторая граница разрыва. + \en The second boundary of the break. \~ + \param[in] t3 - \ru Параметр, который показывает удаляемую часть замкнутого контура,\n + в случае разомкнутого контура он игнорируется. + \en A parameter which indicates a removable part of the closed contour, \n + ignored in case of opened contour. \~ + \param[in] invertBreak - \ru Если true, то разрыв накладывается на противоположную часть контура. + \en If 'true', then the break is applied to the opposite piece of the contour. \~ + \return \ru true, если разрыв был добавлен. + \en True if a break has been added. \~ + */ + bool DeletePartP1P2 ( size_t cNumber, + double t1, double t2, double t3, + bool invertBreak = false ); + + /** \} */ + /**\ru \name Работа с разрывами: удаление разрывов + \en \name Working with breaks: deletion of breaks + \{ */ + + /** \brief \ru Удалить разрывы. + \en Remove breaks. \~ + \details \ru Удалить все разрывы мультилинии.\n + \en Delete all breaks of multiline.\n \~ + \return \ru true, если хотя бы один разрыв был удален. + \en true, if at least one break has been deleted. \~ + */ + bool DeleteBreaks ( ); + + /** \brief \ru Удалить разрывы кривой. + \en Remove breaks of curve. \~ + \details \ru Удалить все разрывы кривой мультилинии.\n + \en Delete all breaks of curve of multiline.\n \~ + \param[in] cNumber - \ru Номер кривой. + \en Index of curve. \~ + \return \ru true, если хотя бы один разрыв был удален. + \en true, if at least one break has been deleted. \~ + */ + bool DeleteBreaks ( size_t cNumber ); + + /** \brief \ru Удалить разрыв кривой. + \en Delete a break of curve. \~ + \details \ru Удалить разрыв по параметру на кривой.\n + \en Delete a break by a parameter on the curve.\n \~ + \param[in] cNumber - \ru Номер кривой. + \en Index of curve. \~ + \param[in] t - \ru Параметр на кривой. + \en A parameter on the curve. \~ + \return \ru true, если разрыв был удален. + \en True if a break has been deleted. \~ + */ + bool DeleteBreak ( size_t cNumber, double t ); + + /** \brief \ru Удалить разрыв. + \en Delete a break. \~ + \details \ru Удалить разрыв по номеру.\n + \en Delete a break by an index.\n \~ + \param[in] cNumber - \ru Номер кривой. + \en Index of curve. \~ + \param[in] brNumber - \ru Номер разрыва на кривой. + \en Index of a break on the curve. \~ + \return \ru true, если разрыв был удален. + \en True if a break has been deleted. \~ + */ + bool DeleteBreakAtNumber ( size_t cNumber, size_t brNumber ); + + /** \brief \ru Удалить разрывы малой длины. + \en Delete breaks of small length. \~ + \details \ru Удалить разрывы малой метрической длины у кривой мультилинии.\n + В случае успеха линия мультилинии перестраивается соответственно разрывам. + \en Delete breaks of small length of a curve of multiline.\n + In case of success the line of multiline is rebuilt according to breaks. \~ + \param[in] cNumber - \ru Номер кривой мультилинии. + \en Index of a curve of multiline. \~ + \param[in] length - \ru Минимальная длина невидимой части. + \en Minimal length of invisible piece. \~ + \return \ru true, если хотя бы один разрыв кривой был удален. + \en True if at least one break of the curve has been deleted. \~ + */ + bool DeleteSmallBreaks ( size_t cNumber, double length ); + + /** \brief \ru Удалить малые видимые части. + \en Delete small visible pieces. \~ + \details \ru Удалить видимые части малой метрической длины у кривой мультилинии.\n + Соответствует объединению близких разрывов в один. + В случае успеха видимые контуры кривой перестраиваются соответственно разрывам. + \en Delete small visible pieces of a curve of multiline with small metric length.\n + Corresponds to union of close breaks into one. + In case of success the visible contours of the line is rebuilt according to breaks. \~ + \param[in] cNumber - \ru Номер кривой мультилинии. + \en Index of a curve of multiline. \~ + \param[in] length - \ru Минимальная длина видимой части. + \en Minimal length of visible piece. \~ + \return \ru true, если разрывы кривой были изменены. + \en True if breaks has been changed. \~ + */ + bool DeleteSmallVisContours ( size_t cNumber, double length ); + + /** \} */ + /**\ru \name Работа с разрывами + \en \name Working with breaks + \{ */ + + /** \brief \ru Находится ли интервал на разрыве. + \en Whether the interval is on a break. \~ + \details \ru Находится ли интервал параметров на разрыве кривой.\n + \en Whether the interval of parameters is on a break of a curve.\n \~ + \param[in] cNumber - \ru Номер кривой мультилинии. + \en Index of a curve of multiline. \~ + \param[in] rect - \ru Интервал для проверки. + \en Interval to check. \~ + \return \ru true, если интервал полностью находится на разрыве или совпадает с ним. + \en True if interval entirely is on the break or coincides with it. \~ + */ + bool IsRectInBreak ( size_t cNumber, const MbRect1D & rect ); + + /** \brief \ru Запомнить разрывы. + \en Memorize the breaks. \~ + \details \ru Запомнить разрывы невидимыми контурами.\n + Для использования в паре с AddBreaksByInvisContours.\n + Удаляет все разрывы мультилинии.\n + Все контуры invisContours имеют счетчик ссылок = 1 (вызван AddRef()). + \en Memorize the breaks as invisible contours.\n + For using together with AddBreaksByInvisContours.\n + Deletes all breaks of multiline.\n + All 'invisContours' contours has a reference counter = 1 (called AddRef()). \~ + \param[out] invisContours - \ru Набор невидимых контуров всех кривых мультилинии. + \en Set of invisible contours of all curves of multiline. \~ + */ + void GetBreaksInInvisContours( RPArray & invisContours ); + + /** \brief \ru Добавить разрывы. + \en Add breaks. \~ + \details \ru Добавить разрывы невидимыми контурами.\n + Для использования в паре с GetBreaksInInvisContours.\n + Контуры invisContours удаляются (вызывается Release()).\n + Данной функцией можно наложить разрывы на мультилинию, + если они были получены методом GetBreaksInInvisContours у этой мультилинии + и форма мультилинии не была изменена. + \en Add the breaks as invisible contours.\n + For using together with GetBreaksInInvisContours.\n + 'invisContours' contours are deleted (called Release()).\n + It is possible to apply breaks to multiline by this function, + if they were obtained by GetBreaksInInvisContours method from this multiline + and shape of multiline wasn't changed. \~ + \param[in] invisContours - \ru Набор невидимых контуров всех кривых мультилинии. + \en Set of invisible contours of all curves of multiline. \~ + */ + void AddBreaksByInvisContours( RPArray & invisContours ); + + /** \brief \ru Номера контуров, пересекаемых отрезком. + \en Indices of contours intersected with a segment. \~ + \details \ru Номера контуров, которые пересекаются с отрезком по двум точкам.\n + \en Indices of contours which are intersected with segment by two points.\n \~ + \param[in] p1 - \ru Первая точка отрезка. + \en The first point of a segment. \~ + \param[in] p2 - \ru Вторая точка отрезка. + \en The second point of a segment. \~ + \param[out] cNumbers - \ru Номера контуров. + \en Indices of contours. \~ + */ + void CurvesIntersectNumbers ( const MbCartPoint & p1, const MbCartPoint & p2, SArray & cNumbers ) const; + + /** \} */ + /**\ru \name Информация о мультилинии + \en \name Information about multiline. + \{ */ + + /// \ru Вырождена ли мультилиния. \en Whether the multiline is degenerate. + bool IsDegenerate( double lenEps = Math::LengthEps ) const; + /// \ru Замкнутая ли мультилиния. \en Whether the multiline is closed. + bool IsClosed () const; + /// \ru Получить ширину мультилинии. \en Get width of multiline. + double GetWidth () const; + /// \ru Лежит ли данная точка на мультилинии. \en Whether the given point is on multiline. + bool IsPointOn ( const MbCartPoint & point ) const; + + /** \brief \ru Найти индекс кривой мультилинии. + \en Find an index of a curve of multiline. \~ + \details \ru Найти индекс кривой мультилинии.\n + \en Find an index of a curve of multiline.\n \~ + \param[in] radius - \ru Радиус нужной кривой. + \en Radius of the required curve. \~ + \return \ru Индекс кривой. + \en A curve index. \~ + */ + size_t FindRadius ( double radius ); + + /** \} */ + /**\ru \name Операции с мультилинией + \en \name Operations with multiline + \{ */ + + /** \brief \ru Усечь мультилинию. + \en Truncate multiline. \~ + \details \ru Усечь мультилинию.\n + Вернуть мультилинию, базовая кривая которой - + копия участка между параметрами t1 и t2 базовой данной мультилинии. + \en Truncate multiline.\n + Return multiline which base curve is + a copy of piece between parameters t1 and t2 of the base curve of a given multiline. \~ + \param[in] t1 - \ru Начальный параметр усечения. + \en Start parameter of trimming. \~ + \param[in] t2 - \ru Конечный параметр усечения. + \en End parameter of trimming. \~ + \param[in] sense - \ru Направление усеченной базовой кривой. + \en Direction of the trimmed base curve. \~ + \return \ru Новую мультилинию. + \en New multiline. \~ + */ + MbMultiline * Trimmed( double t1, double t2, int sense ) const; + + /** \} */ +private: + /**\ru \name Внутренние функции мультилинии + \en \name Internal functions of multiline + \{ */ + + // \ru Все внутренние функции реализованы в MltLine_.cpp (кроме тех, для которых указан другой файл) \en All internal functions implemented in MltLine_.cpp (except ones for which the other file is specified) + // \ru Насчет объектов \en Calculation of objects + /// \ru Насчитать кривую мультилинии c радиусом эквидистанты rad. \en Calculate curve of multiline with 'rad' equidistant radius. + void CalculateCurve ( double rad, MbContourWithBreaks & contour ); + /// \ru Насчитать кривую мультилинии c радиусом эквидистанты rad c заполнением информации. \en Calculate curve of multiline with equidistant radius 'rad' and filling the information. + void CalculateCurveWithInfo ( double rad, MbContourWithBreaks & contour, + SArray & baseIndexes ); + /// \ru Насчитать все кривые (с определением minNotDegInd и maxNotDegInd). \en Calculate all the curves (with determination of minNotDegInd and maxNotDegInd). + void CalculateCurves (); + /// \ru Насчитать все кривые с информацией для граничных невырожденных кривых. \en Calculate all the curves with information for non-degenerate boundary curves. + bool CalculateCurvesWithInfo ( SArray & minInfo, SArray & maxInfo ); + /// \ru Насчитать все законцовки в вершинах. \en Calculate all tips at vertices. + void CalculateTipCurves ( const SArray & minInfo, const SArray & maxInfo ); + /// \ru Насчитать все законцовки в вершинах. \en Calculate all tips at vertices. + void CalculateTipCurves (); + /// \ru Насчитать все кривые и все законцовки в вершинах. \en Calculate all curves and all tips at vertices. + void CalculateCurvesAndTipCurves(); + /// \ru Насчитать законцовку в начале. \en Calculate tip at the beginning. + void CalculateBegTipCurve ( SArray * changeCurvesNumbers = NULL ); + /// \ru Насчитать законцовку в конце. \en Calculate tip at the end. + void CalculateEndTipCurve (); + + // \ru Отцепление объектов \en Detach the objects + + /// \ru Отцепить все кривые. \en Detach all curves. + void DeleteCurves (); + /// \ru Отцепить кривую с индексом i. \en Detach i-th curve. + void DeleteCurve ( size_t i ); + /// \ru Отцепить все законцовки в вершинах. \en Detach all tips at vertices. + void DeleteTipCurves (); + /// \ru Отцепить законцовку в вершине с индексом i. \en Detach i-th tip at vertex. + void DeleteTipCurve ( size_t i ); + /// \ru Отцепить законцовку в начале. \en Detach tip at the beginning. + void DeleteBegTipCurve (); + /// \ru Отцепить законцовку в конце. \en Detach tip at the end. + void DeleteEndTipCurve (); + /// \ru Отцепить все объекты. \en Detach all the objects. + void DeleteAllObjects (); + + // \ru Пересчет объектов \en Recalculation of objects + + /// \ru Перестроить все объекты. \en Rebuild all objects. + void RebuildAllObjects (); + + // \ru Обработки изменения vertices (без перестроения) \en Processing of 'vertices' changing (without rebuilding) + void AddVert ( const StVertexOfMultilineInfo & vertInfo ); + void InsertVert ( size_t i, const StVertexOfMultilineInfo & vertInfo ); + void ChangeVert ( size_t i, const StVertexOfMultilineInfo & vertInfo ); + void RemoveVert ( size_t i ); + + // \ru Вспомогательные функции \en Auxiliary functions + + /// \ru Обработать изменение i-ого сегмента БК. \en Process change of i-th segment of base curve. + void CalculateChangeOfSegment ( size_t i ); + /// \ru Продлить кривую до законцовок. \en Extend curve to the tips. + void ProlongCurveToTips ( size_t i ); + /// \ru Рассчитать удаление i-ой кривой (обработка). \en Calculate removal of i-th curve (processing). + void CalculateRemovalOfCurve ( size_t i ); + + // \ru Скругление/фаска двух соседних сегментов БК (реализация в MLOper.cpp) \en Fillet/chamfer of two neighboring segments of the base curve (implementation in MLOper.cpp) + bool FilletOrChamferTwoBasisSegs( ptrdiff_t & index, const StVertexOfMultilineInfo & vertInfo, + bool fillet, bool recalcCrvRadii, + double param, + double par = 0.0, bool type = false, bool firstSeg = true ); + + // \ru Работа с разрывами \en Working with breaks + // \ru Работа с разрывами при редактировании (сохранение фиксированной точки и длины) \en Working with breaks while editing (preserving of fixed point and length) + void SetBreaksFixedVars ( size_t segInd, size_t subInd, const MbCartPoint & newPoint ); + void TransformBreaks ( const MbMatrix & matr ); + + // \ru Удалить разрывы на сегментах контура, с соотв. базовым номером \en Delete breaks on segments of contour with corresponding base index + void DeleteBreaksAtBaseNumber ( size_t baseNumber, bool delTracingBreaks, bool delEquidBreaks, + bool delInLineSeg = true/*\ru Удалять ли разрывы с прямолинейных сегментов + \en Whether to remove breaks from straight segments \~*/ ); + + // \ru Функции восстановления разрывов по невидимым контурам \en Functions for recovering breaks by invisible contours + void GetBreaksInInvisContours( bool allBreaks, // \ru Удалять все разырвы \en Delete all breaks + size_t i, // \ru Номер вершины, специально для изменения типа обхода \en Index of vertex, specially for changing the type of traverse \~ + bool addContWbr, // \ru Набирать контуры с разрывами \en Collect contours with breaks + RPArray & breaksContours, + RPArray & invisContours ); + void AddBreaksByInvisContours( RPArray & breaksContours, + RPArray & invisContours ); + // \ru Для перестроения разрывов \en For breaks rebuilding + void GetBreaks ( RPArray & baseNumbers ); + void RebuildBreaks ( RPArray & baseNumbers ); + + /** \} */ +private: + void operator =( const MbMultiline & ); ///< \ru Не реализован \en Not implemented + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbMultiline ) +}; // MbMultiline + +IMPL_PERSISTENT_OPS( MbMultiline ) + +//------------------------------------------------------------------------------ +// _7_ +/** \brief \ru Построить скругления мультилинии. + \en Construct a fillet of multiline. \~ + \details \ru Построить скругления базовой кривой мультилинии.\n + \en Construct a fillet of the base curve of multiline.\n \~ + \param[in] multiline - \ru Изменяемая мультилиния. + \en A multiline to be modified. \~ + \param[in] rad - \ru Радиус скругления. + \en The radius of fillet. \~ + \param[in] nodeFlag - \ru Флаг выбора скругляемых вершин:\n + true - скругление всех вершин мультилинии,\n + false - скругление ближайшей вершины к точке pnt. + \en Flag of selection of vertices to fillet:\n + true - fillet of all vertices of multiline,\n + false - fillet of the vertex nearest to 'pnt' point. \~ + \param[in] pnt - \ru Точка для указания нужной вершины. + \en Point for indication the required vertex. \~ + \param[in] vertInfo - \ru Информация для новых вершин мультилинии. + \en Information for new vertices of a multiline. \~ + \ingroup Algorithms_2D +*/ // --- +MATH_FUNC (bool) FilletMultiline ( MbMultiline & multiline, double rad, + bool nodeFlag, MbCartPoint & pnt, + const StVertexOfMultilineInfo & vertInfo ); + + +//------------------------------------------------------------------------------ +// _8_ +/** \brief \ru Построить фаски мультилинии. + \en Construct a chamfer of multiline. \~ + \details \ru Построить фаски базовой кривой мультилинии.\n + \en Construct a chamfer of the base curve of multiline.\n \~ + \param[in] multiline - \ru Изменяема мультилиния. + \en A multiline to be modified. \~ + \param[in] len - \ru Длина фаски. + \en Length of chamfer. \~ + \param[in] par - \ru Параметр в зависимости от типа type:\n + если type = true, par - угол,\n + если type = false, par - размер. + \en Parameter depending on 'type' type:\n + if type = true, par is a corner,\n + if type = false, par is a size. \~ + \param[in] type - \ru Тип задания фаски:\n + true - фаска задана как размер + угол,\n + false - фаска задана как размер + размер. + \en The type of a chamfer specification:\n + true - chamfer specified as size + angle,\n + false - chamfer specified as size + size. \~ + \param[in] nodeFlag - \ru Флаг выбора обрабатываемых вершин:\n + true - фаска между каждыми двумя соседними сегментами мультилинии,\n + false - фаска между двумя соседними сегментами мультилинии, примыкающими к ближайшей к точке pnt вершине. + \en Flag of selection of vertices to process:\n + true - chamfer between each two neighboring segments of multiline,\n + false - chamfer between two neighboring multiline segments joining at the vertex nearest to 'pnt' point. \~ + \param[in] pnt - \ru Точка для указания нужной пары сегментов. + \en Point for indication the required pair of segments. \~ + \param[in] vertInfo - \ru Информация для новых вершин мультилинии. + \en Information for new vertices of a multiline. \~ + \ingroup Algorithms_2D +*/ // --- +MATH_FUNC (bool) ChamferMultiline( MbMultiline & multiline, double len, double par, bool type, + bool nodeFlag, MbCartPoint & pnt, + const StVertexOfMultilineInfo & vertInfo ); + + +//////////////////////////////////////////////////////////////////////////////// +// +// _9_ +/// \ru Внеклассные функции расчета/учета радиусов кривизны (реализация в MltLine.cpp) \en Out-of-class functions for curvature radii calculation/consideration (implementation in MltLine.cpp) +// +//////////////////////////////////////////////////////////////////////////////// + + +//------------------------------------------------------------------------------ +/** \brief \ru Учесть радиусы кривизны кривой. + \en Consider the curve curvature radii. \~ + \details \ru Учесть минимальный положительный и максимальный отрицательный радиусы кривизны кривой.\n + Для внутреннего использования. + \en Consider the curve curvature minimum positive and maximum negative radii.\n + For internal use only. \~ + \param[in] curve - \ru Кривая мультилинии. + \en A curve of multiline. \~ + \param[in] angle - \ru Угловая толерантность. + \en An angular tolerance. \~ + \param[in, out] minPos - \ru Радиус кривой, если он меньше текущего значения переменной minPos. + \en Curve radius if it is less than current value of 'minPos' variable. \~ + \param[in, out] maxNeg - \ru Радиус кривой, если он больше текущего значения переменной minPos. + \en Curve radius if it is greater than current value of 'minPos' variable. \~ + \ingroup Algorithms_2D +*/ // --- +void ToTakeIntoCurvesCrvRadii( MbCurve & curve, double angle, double & minPos, double & maxNeg ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Получить радиусы кривизны контура. + \en Get the contour curvatures radii. \~ + \details \ru Получить минимальный положительный и максимальный отрицательный радиусы кривизны контура.\n + Для внутреннего использования. + \en Get the contour curvature minimum positive and maximum negative radii.\n + For internal use only. \~ + \param[in] contour - \ru Контур. + \en A contour. \~ + \param[in] angle - \ru Угловая толерантность. + \en An angular tolerance. \~ + \param[out] minPos - \ru Минимальный радиус сегмента контура. + \en Minimal contour segment radius. \~ + \param[out] maxNeg - \ru Максимальный радиус сегмента контура. + \en Maximal contour segment radius. \~ + \ingroup Algorithms_2D +*/ // --- +void GetContoursCrvRadii( MbContour & contour, double angle, double & minPos, double & maxNeg ); + + +//------------------------------------------------------------------------------ +// _10_ +/** \brief \ru Состыковать две кривые. + \en Join two curves. \~ + \details \ru Гладко состыковать две последовательные кривые.\n + Для внутреннего использования. + \en Smoothly join two consecutive curves.\n + For internal use only. \~ + \param[in] curve1 - \ru Первая кривая. + \en The first curve. \~ + \param[in] curve2 - \ru Вторая кривая. + \en The second curve. \~ + \return \ru true, если хотя бы одна кривая была изменена. + \en True if at least one curve has been changed. \~ + \ingroup Algorithms_2D +*/ // --- +bool SmoothJointSuccessiveCurves( MbCurve & curve1, MbCurve & curve2 ); + + +//------------------------------------------------------------------------------ +// _11_ +/** \brief \ru Разбить мультилинию. + \en Split multiline. \~ + \details \ru Разбить мультилинию на две части. + \en Split multiline into two pieces. \~ + \param[in] multiline - \ru Разбиваемая мультилиния. + \en A multiline to be split. \~ + \param[in] p1 - \ru Точка разбиения или, если мультилиния замкнута, начальная точка для новой мультилинии. + \en Splitting point or start point of a new multiline if the multiline is closed. \~ + \param[in] p2 - \ru Если мультилиния замкнута, то конечная точка для новой мультилинии. + \en End point of a new multiline if the multiline is closed. \~ + \param[out] parts - \ru Массив полученных участков (2 элемента). + \en The array of obtained pieces (two elements). \~ + \ingroup Algorithms_2D +*/ // --- +MATH_FUNC (bool) BreakMultiline( const MbMultiline & multiline, + const MbCartPoint & p1, const MbCartPoint & p2, + PArray & parts ); + + +//------------------------------------------------------------------------------ +// _12_ +/** \brief \ru Разбить мультилинию. + \en Split multiline. \~ + \details \ru Разбить мультилинию на N равных частей. + \en Split multiline into N equal pieces. \~ + \param[in] multiline - \ru Разбиваемая мультилиния. + \en A multiline to be split. \~ + \param[in] partsCount - \ru Количество частей. + \en The count of pieces. \~ + \param[in] point - \ru Ограничивающая точка для замкнутой мультилинии. + \en Bounding point for closed multiline. \~ + \param[out] parts - \ru Массив полученных участков (partsCount элементов). + \en The array of obtained pieces (partsCount elements). \~ + \ingroup Algorithms_2D +*/ // --- +MATH_FUNC (bool) BreakMultilineNParts( const MbMultiline & multiline, size_t partsCount, + const MbCartPoint & point, PArray & parts ); + +// \ru LF-Linux: inline-функции следует помещать в заголовочный файл, а не cpp! \en LF-Linux: inline-functions should be placed in the header file instead of cpp! +//------------------------------------------------------------------------------- +/// \ru Вырождена ли мультилиния \en Whether the multiline is degenerate +// --- +inline bool MbMultiline::IsDegenerate( double lenEps ) const { + return ( (minNotDegInd == SYS_MAX_T) || // \ru Значит, и maxNotDegInd == SYS_MAX_T \en So maxNotDegInd == SYS_MAX_T + (basisCurve != NULL && basisCurve->IsDegenerate(lenEps)) ); +} + + +//------------------------------------------------------------------------------- +/// \ru Замкнутая ли мультилиния \en Whether the multiline is closed +// --- +inline bool MbMultiline::IsClosed() const { + return ( basisCurve->IsClosed() && processClosed && basisCurve->GetSegmentsCount() > 1 ); +} + + +//------------------------------------------------------------------------------- +/// \ru Получить ширину мультилинии \en Get width of multiline +// --- +inline double MbMultiline::GetWidth() const +{ + if ( minNotDegInd == maxNotDegInd ) + return 0.0; + else + return ( equidRadii[maxNotDegInd] - equidRadii[minNotDegInd] ); +} + +//------------------------------------------------------------------------------- +/// \ru Обход скруглением (одним из) \en Traverse by fillet (one of) +// --- +inline bool StVertexOfMultilineInfo::IsFilletTracing() const +{ + return ( tracingType == mvt_FilletType || tracingType == mvt_SpecFilletType ); +} + + +//------------------------------------------------------------------------------- +/// +// --- +inline bool StVertexOfMultilineInfo::operator ==( const StVertexOfMultilineInfo & with ) const +{ + return smoothJoint == with.smoothJoint && + tracingType == with.tracingType && + ::fabs(specFilletRad - with.specFilletRad) < EXTENT_REGION && + tipType == with.tipType && + firstSegTip == with.firstSegTip; +} + + +//------------------------------------------------------------------------------- +/// +// --- +inline bool StVertexOfMultilineInfo::operator !=( const StVertexOfMultilineInfo & with ) const +{ + return !(*this == with); +} + + +//------------------------------------------------------------------------------- +/// \ru Инициализация по объекту \en Initialization by object +// --- +inline void StVertexOfMultilineInfo::Init( const StVertexOfMultilineInfo & other ) +{ + smoothJoint = other.smoothJoint; + tracingType = other.tracingType; + specFilletRad = other.specFilletRad; + tipType = other.tipType; + firstSegTip = other.firstSegTip; +} + + +//------------------------------------------------------------------------------- +/// \ru Инициализация по параметрам \en Initialize by parameters +// --- +inline void StVertexOfMultilineInfo::Init( bool _smoothJoint, EnMLVertexTracingType _tracingType, + double _specFilletRad, EnMLInnerTipType _tipType, + bool _firstSegTip ) +{ + smoothJoint = _smoothJoint; + tracingType = _tracingType; + specFilletRad = _specFilletRad; + tipType = _tipType; + firstSegTip = _firstSegTip; +} + + +//------------------------------------------------------------------------------- +/// \ru Изменить флаг "гладкий стык" (smoothJoint) \en Change flag "smooth joint" (smoothJoint) +// --- +inline bool StVertexOfMultilineInfo::ChangeSmoothJoint( bool othSmoothJoint ) +{ + if ( smoothJoint == othSmoothJoint ) + return false; + else { + smoothJoint = othSmoothJoint; + return true; + } +} + + +//------------------------------------------------------------------------------- +/// \ru Изменить тип обхода углов в вершине (tracingType) \en Change type of traverse of corners at a vertex (tracingType) +// --- +inline bool StVertexOfMultilineInfo::ChangeTracingType( EnMLVertexTracingType othTracingType ) +{ + if ( tracingType == othTracingType ) + return false; + else { + tracingType = othTracingType; + return true; + } +} + + +//------------------------------------------------------------------------------- +/// \ru Изменить радиус особого скругления (specFilletRad) \en Change radius of special fillet (specFilletRad) +// --- +inline bool StVertexOfMultilineInfo::ChangeSpecFilletRad( double othSpecFilletRad ) +{ + if ( ::fabs(specFilletRad - othSpecFilletRad) < Math::LengthEps ) + return false; + else { + specFilletRad = othSpecFilletRad; + return true; + } +} + + +//------------------------------------------------------------------------------- +/// \ru Изменить тип законцовки (tipType) \en Change type of tip (tipType) +// --- +inline bool StVertexOfMultilineInfo::ChangeTipType( EnMLInnerTipType othTipType ) +{ + if ( tipType == othTipType ) + return false; + else { + tipType = othTipType; + return true; + } +} + + +//------------------------------------------------------------------------------- +/// \ru Изменить флаг сегмента законцовки (isFirstSegTip) \en Change flag of segment of tip (isFirstSegTip) +// --- +inline bool StVertexOfMultilineInfo::ChangeFirstSegTip( bool othFirstSegTip ) +{ + if ( firstSegTip == othFirstSegTip ) + return false; + else { + firstSegTip = othFirstSegTip; + return true; + } +} + + +//------------------------------------------------------------------------------- +/// \ru Преобразовать объект согласно матрице \en Transform an object according to the matrix +// --- +inline void StVertexOfMultilineInfo::Transform( const MbMatrix & matr ) +{ + matr.TransformScalarX( specFilletRad ); // \ru Преобразовать радиус \en Transform radius +} + + +//------------------------------------------------------------------------------- +/// +// --- +inline bool StMLTipParams::operator ==( const StMLTipParams & with ) const +{ + return (tipType == with.tipType ) && + ::fabs(tipParam - with.tipParam) < EXTENT_REGION; +} + + +//------------------------------------------------------------------------------- +/// +// --- +inline bool StMLTipParams::operator !=( const StMLTipParams & with ) const +{ + return !(*this == with); +} + + +//------------------------------------------------------------------------------- +/// \ru Инициализация \en Initialization +// --- +inline void StMLTipParams::Init( const StMLTipParams & other ) +{ + tipType = other.tipType; + tipParam = other.tipParam; +} + + +//------------------------------------------------------------------------------- +/// \ru Инициализация \en Initialization +// --- +inline void StMLTipParams::Init( EnMLTipType _tipType, double _tipParam ) +{ + tipType = _tipType; + tipParam = _tipParam; +} + + +//------------------------------------------------------------------------------- +/// \ru Изменить тип законцовки (tipType) \en Change type of tip (tipType) +// --- +inline bool StMLTipParams::ChangeTipType( EnMLTipType othTipType ) +{ + if ( tipType == othTipType ) + return false; + else { + tipType = othTipType; + return true; + } +} + + +//------------------------------------------------------------------------------- +/// \ru Изменить параметр законцовки (tipParam) \en Change parameter of tip (tipParam) +// --- +inline bool StMLTipParams::ChangeTipParam( double othTipParam ) +{ + if ( ::fabs(tipParam - othTipParam) < Math::LengthEps ) + return false; + else { + tipParam = othTipParam; + return true; + } +} + + +//------------------------------------------------------------------------------- +/// \ru Преобразовать объект согласно матрице \en Transform an object according to the matrix +// --- +inline void StMLTipParams::Transform( const MbMatrix & matr ) +{ + matr.TransformScalarX( tipParam ); // \ru Преобразовать расстояние \en Transform distance +} + + +#endif // __MULTILINE_H diff --git a/C3d/Include/name_check.h b/C3d/Include/name_check.h index ce45618..28212ab 100644 --- a/C3d/Include/name_check.h +++ b/C3d/Include/name_check.h @@ -1,22 +1,26 @@ //////////////////////////////////////////////////////////////////////////////// /** \file - \brief \ru Работа с топологическими именами объекта. - \en Treatment of object's topological names. \~ + \brief \ru Работа с именами топологических объектов. + \en The treatment of names of topological objects. \~ */ //////////////////////////////////////////////////////////////////////////////// -#ifndef __NAME_CEHCK_H -#define __NAME_CEHCK_H +#ifndef __NAME_CHECK_H +#define __NAME_CHECK_H #include -#include -#include +#include +#include +#include -class MATH_CLASS MbSNameMaker; +class MATH_CLASS MbFace; +class MATH_CLASS MbCurveEdge; +class MATH_CLASS MbFaceShell; +class MATH_CLASS MbItem; //------------------------------------------------------------------------------ @@ -30,26 +34,29 @@ class MATH_CLASS MbSNameMaker; \en An first point. \~ \param[in] p2 - \ru Множество граней. \en Вторая точка. \~ + \param[in] precision - \ru Точность сравнения. + \en The precision of comparison. \~ \return \ru Возвращает: -1, если p1 < p2; +1, если p1 > p2; 0, если p1 == p2. \en Returns: -1 -if p1 < p2; +1 -if p1 > p2; 0 -if p1 == p2. \~ \ingroup Names */ // --- -inline int PointCompare3D ( const MbCartPoint3D & p1, const MbCartPoint3D & p2 ) +inline +int PointCompare3D ( const MbCartPoint3D & p1, const MbCartPoint3D & p2, double precision = Math::region ) { - if ( p2.x + Math::region < p1.x ) // по X + if ( p2.x + precision < p1.x ) // по X return 1; - else if ( p1.x + Math::region < p2.x ) + else if ( p1.x + precision < p2.x ) return -1; else { - if ( p2.y + Math::region < p1.y ) // по Y + if ( p2.y + precision < p1.y ) // по Y return 1; - else if ( p1.y + Math::region < p2.y ) + else if ( p1.y + precision < p2.y ) return -1; else { - if ( p2.z + Math::region < p1.z ) // по Z + if ( p2.z + precision < p1.z ) // по Z return 1; - else if ( p1.z + Math::region < p2.z ) + else if ( p1.z + precision < p2.z ) return -1; } } @@ -74,10 +81,10 @@ inline int PointCompare3D ( const MbCartPoint3D & p1, const MbCartPoint3D & p2 ) \ingroup Names */ // --- -MATH_FUNC (void) SetShellNames( RPArray & edges, - const RPArray & faces, - const MbSNameMaker & nameMaker, - bool processVertexes ); +MATH_FUNC (void) SetShellNames( RPArray & edges, + const RPArray & faces, + const MbSNameMaker & nameMaker, + bool processVertexes ); //------------------------------------------------------------------------------ @@ -95,8 +102,8 @@ MATH_FUNC (void) SetShellNames( RPArray & edges, */ // --- MATH_FUNC (void) SetFacesNames( const RPArray & faces, - const MbSNameMaker & nameMaker, - bool processVertexes ); + const MbSNameMaker & nameMaker, + bool processVertexes ); //------------------------------------------------------------------------------ @@ -111,18 +118,29 @@ MATH_FUNC (void) SetFacesNames( const RPArray & faces, \ingroup Names */ // --- -inline -void SetShellNames( MbFaceShell & shell, - const MbSNameMaker & nameMaker ) -{ - RPArray edges( 0, 1 ); - RPArray faces( 0, 1 ); +MATH_FUNC (void) SetShellNames( MbFaceShell & shell, + const MbSNameMaker & nameMaker ); - shell.GetEdges( edges ); // \ru Получение массива ребер \en Get an array of edges - shell.GetFaces( faces ); - ::SetShellNames( edges, faces, nameMaker, true ); -} +//------------------------------------------------------------------------------ +/** \brief \ru Установить имена элементам объекта. + \en Set names for elements of object. \~ + \details \ru Установить имена элементам объекта: граням, ребрам и вершинам тела, ребрам и вершинам проволочного каркаса, вершинам точечного каркаса. + \en Set names for elements of object: faces, edges and vertices of an solid, edges and vertices of a wireframe, vertices of a point frame. \~ + \param[in] item - \ru Тело, вставка, проволочный каркас, точечный каркас. + \en A solid, wire frame, point frame, instance. \~ + \param[in] nameMaker - \ru Именователь. + \en An object for naming the new objects. \~ + \param[in] clearNames - \ru Очистить предварительно все имена у элементов объекта. + \en Clear all names of object elements beforehand. \~ + \return \ru Возвращает true, если было выполнено именование. + \en Returns true if naming operation was performed. \~ + \ingroup Names +*/ +// --- +MATH_FUNC (bool) SetItemNames( MbItem & item, + const MbSNameMaker & nameMaker, + bool clearNames ); //------------------------------------------------------------------------------ @@ -155,14 +173,15 @@ struct NameIntersectionInfo { \ingroup Names */ // --- -MATH_FUNC (bool) CheckShellNames( const RPArray & shells, SArray & infos ); +MATH_FUNC (bool) CheckShellNames( const RPArray & shells, + SArray & infos ); //----------------------------------------------------------------------------- /** \brief \ru Выбрать имя объединяемых ребер. \en Select name for united edges. \~ \details \ru Выбрать наиболее подходящее имя при объединении двух ребер, \n - новое имя будет установленно первому ребру. \n + новое имя будет установлено первому ребру. \n \en Select the most suitable name while uniting two edges, \n the new name will be set to the first edge. \n \~ \param[in,out] edge1 - \ru Первое ребро. @@ -174,7 +193,9 @@ MATH_FUNC (bool) CheckShellNames( const RPArray & shells, SAr \ingroup Names */ //--- -MATH_FUNC (void) CombineNames( MbCurveEdge & edge1, const MbCurveEdge & edge2, VERSION version ); +MATH_FUNC (void) CombineNames( MbCurveEdge & edge1, + const MbCurveEdge & edge2, + VERSION version ); -#endif // __NAME_CEHCK_H +#endif // __NAME_CHECK_H diff --git a/C3d/Include/name_item.h b/C3d/Include/name_item.h index 49becf6..a252aaf 100644 --- a/C3d/Include/name_item.h +++ b/C3d/Include/name_item.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -22,11 +23,23 @@ #include #include #include +#include +#include class MATH_CLASS MbProperties; class MATH_CLASS MbName; +namespace c3d // namespace C3D +{ + typedef std::vector NamesVector; + typedef std::vector ConstNamesVector; + + typedef std::set NamesSet; + typedef std::set ConstNamesSet; +} // namespace C3D + + //---------------------------------------------------------------------------------------- /** \brief \ru Поменять местами. \en Swap. \~ @@ -47,7 +60,7 @@ inline void SwapIT( IntegralType & a, IntegralType & b ) { a^=b; b^=a; a^=b; } \ingroup Names */ // --- -const size_t sizeofSimpleName = sizeof( SimpleName ); +const_expr size_t sizeofSimpleName = sizeof( SimpleName ); ////////////////////////////////////////////////////////////////////////////////////////// @@ -141,9 +154,10 @@ reader & operator >> ( reader & in, SimpleNameArray *& nArr ); //---------------------------------------------------------------------------------------- /// \ru Чтение. \en Reading. // --- -inline reader & operator >> ( reader & in, SimpleNameArray & ref ) +inline +reader & operator >> ( reader & in, SimpleNameArray & ref ) { - size_t count = ReadCOUNT( in, true/*uint_val*/ ); + size_t count = ::ReadCOUNT( in, true/*uint_val*/ ); if ( in.good() && count ) { ref.m_array.SetSize( count, true/*clear*/ ); for ( size_t i = 0; i < count && in.good(); ++i ) { @@ -157,7 +171,8 @@ inline reader & operator >> ( reader & in, SimpleNameArray & ref ) //---------------------------------------------------------------------------------------- /// \ru Запись. \en Writing. // --- -inline writer & operator << ( writer & out, const SimpleNameArray & ref ) +inline +writer & operator << ( writer & out, const SimpleNameArray & ref ) { size_t count = ref.Count(); ::WriteCOUNT( out, count ); @@ -169,14 +184,16 @@ inline writer & operator << ( writer & out, const SimpleNameArray & ref ) //---------------------------------------------------------------------------------------- /// Hash32 // --- -inline SimpleName Hash32( const SimpleNameArray & snArr ) { - return (SimpleName)::Hash32( (uint8 *)snArr.GetAddr(), snArr.Count() * sizeofSimpleName ); +inline +SimpleName Hash32( const SimpleNameArray & snArr ) { + return (SimpleName)c3d::Hash32( (uint8 *)snArr.GetAddr(), snArr.Count() * sizeofSimpleName ); } //---------------------------------------------------------------------------------------- /// \ru Оператор конкатенации. \en Concatenation operator. // --- -inline SArray & operator += ( SArray & array, const SimpleNameArray & other ) +inline +SArray & operator += ( SArray & array, const SimpleNameArray & other ) { array += other.m_array; return array; @@ -198,35 +215,34 @@ inline SArray & operator += ( SArray & array, const Simp \ingroup Names */ // --- -struct MATH_CLASS MbIdArr : private LiSArray -{ +struct MATH_CLASS MbIdArr : private LiSArray { friend class MbName; private: uint16 countBase; ///< \ru Количество элементов в базовой части. \en The count of elements in base part. mutable SimpleName hash; ///< \ru Хэш множества простых имен. \en The hash of simple names` set. -protected://public: - MbFlags flags; ///< \ru Флаги. \en Flags. +protected: + MbFlags flags; ///< \ru Флаги. \en Flags. public: /// \ru Конструктор по умолчанию. \en Default constructor. - MbIdArr() : LiSArray(), countBase(0), hash(SIMPLENAME_MAX), flags() {} + MbIdArr() : LiSArray(), countBase(0), hash(c3d::SIMPLENAME_MAX), flags() {} /// \ru Конструктор копирования. \en Copy-constructor. MbIdArr( const MbIdArr & o ) : LiSArray(o), countBase(o.countBase), hash( o.hash ), flags(o.flags) {} public: SimpleName Hash() const; ///< \ru Вычислить хэш себя. \en Calculate hash of itself. - void FlashHash() { hash = SIMPLENAME_MAX; } ///< \ru Сбросить свой хэш. \en Reset hash value. + void FlashHash() { hash = c3d::SIMPLENAME_MAX; } ///< \ru Сбросить свой хэш. \en Reset hash value. size_t CountAll() const { return Count(); } ///< \ru Дать количество элементов массива. \en Get the number of elements in the array. size_t CountBase() const { return countBase; } ///< \ru Дать количество элементов массива в базовой части. \en Get the number of elements in base part. /// \ru Обнулить количество элементов \en Set the number of elements to null - void Flush () { LiSArray::Flush(); countBase = 0; hash = SIMPLENAME_MAX; } + void Flush () { LiSArray::Flush(); countBase = 0; hash = c3d::SIMPLENAME_MAX; } - bool IsEmpty () const; ///< \ru Множество пуст? \en Is the array empty? - bool GetCut ( SimpleName & cutIndex ) const; ///< \ru Получение индекса разрезки. \en Get an index of a cutaway. - void SetCopyIndex( SimpleName ci ); ///< \ru Установка индекса копирования. \en Set an index of copying. - bool GetCopyIndex( SimpleName & ci ) const; ///< \ru Получение индекса копирования. \en Get an index of copying. + bool IsEmpty () const; ///< \ru Множество пустое? \en Is the array empty? + bool GetCut ( SimpleName & ) const; ///< \ru Получение индекса резки. \en Get an index of a cutaway. + void SetCopyIndex( SimpleName ); ///< \ru Установка индекса копирования. \en Set an index of copying. + bool GetCopyIndex( SimpleName & ) const; ///< \ru Получение индекса копирования. \en Get an index of copying. size_t SizeOf() const; ///< \ru Размер в памяти. \en Size in memory. /// \ru Добавить основной элемент в конец массива. \en Add main element to the end of the array. @@ -262,17 +278,19 @@ KNOWN_OBJECTS_RW_REF_OPERATORS( MbIdArr ) //---------------------------------------------------------------------------------------- // \ru Вычислить хэш себя. \en Calculate hash of itself. // --- -inline SimpleName MbIdArr::Hash() const +inline +SimpleName MbIdArr::Hash() const { - if ( hash == SIMPLENAME_MAX ) - hash = ::Hash32( (uint8*)parr, count * sizeofSimpleName/*4*/ ); + if ( hash == c3d::SIMPLENAME_MAX ) + hash = c3d::Hash32( (uint8*)parr, count * sizeofSimpleName/*4*/ ); return hash; } //---------------------------------------------------------------------------------------- -// \ru Размер в памяти \en Size in memory +// \ru Размер в памяти. \en Size in memory. // --- -inline size_t MbIdArr::SizeOf() const +inline +size_t MbIdArr::SizeOf() const { size_t size = sizeof(MbFlags); size += sizeof(LiSArray) + Count() * sizeofSimpleName; //-V119 @@ -281,9 +299,10 @@ inline size_t MbIdArr::SizeOf() const } //---------------------------------------------------------------------------------------- -// +// \ru Оператор добавления. \en Operator for adding. // --- -inline void MbIdArr::operator += ( const MbIdArr & other ) +inline +void MbIdArr::operator += ( const MbIdArr & other ) { C3D_ASSERT( CountAll() == CountBase() ); if ( CountAll() == CountBase() ) { @@ -315,27 +334,27 @@ inline void MbIdArr::operator += ( const MbIdArr & other ) */ // --- class MATH_CLASS MbName { public: - typedef std_unique_ptr UniqueNamePtr; + typedef std_unique_ptr UniqueNamePtr; public : /// \ru Индекс имени. \en A name index. enum EIndexes - { // v e f + { // Index // v e f (vertices|edges|faces, (*) - mandatory, (+) - used, (-) - none ) // i_Main, // * * * // i_First, // * * * // i_Cut, // - + + // i_Copy, // - - + // i_Extra, // - - + - i_Main, ///< \ru Индекс главного имени. \en Main name index. - i_First, ///< \ru Индекс уникального имени, содержащего в себе hash, построенный по жестким правилам. \en Index of unique name which includes hash constructed by strict rules. - i_Cut, ///< \ru Индекс индекса разрезанности. \en Index of index of cutaway. - i_Copy, ///< \ru Индекс индекса копирования. \en Index of index of copying. - i_Extra, ///< \ru Индекс предыдущего hash'a копирования. \en Index of previous hash of copying. + i_Main, ///< \ru Индекс главного имени. \en Main name index. + i_First, ///< \ru Индекс уникального имени, содержащего в себе hash, построенный по жестким правилам. \en Index of unique name which includes hash constructed by strict rules. + i_Cut, ///< \ru Индекс индекса разрезанности. \en Index of index of cutaway. + i_Copy, ///< \ru Индекс индекса копирования. \en Index of index of copying. + i_Extra, ///< \ru Индекс предыдущего hash'a копирования. \en Index of previous hash of copying. i_PseudoCopy = -1, ///< \ru Индекс псевдо копирования. \en Index of pseudo copying. }; /// \ru Основной индекс имени. \en Main index of name. enum BaseNameIndex - { // v e f + { // Index // v e f // bni_Main, // * * * // bni_First, // * * * // bni_Cut, // - + + @@ -346,7 +365,7 @@ public : }; /// \ru Дополнительный индекс имени. \en Additional index of name. enum ExtraNameIndex - { // v e f + { // Index // v e f // eni_Copy, // - - + // eni_Extra, // - - + eni_Copy, ///< \ru Индекс индекса копирования. \en Index of index of copying. @@ -360,42 +379,65 @@ public : rmn_DummyFaceName = -4, ///< \ru Системное имя фиктивной грани. \en Default name of dummy face. rmn_ReservedName = -3, ///< \ru Системное имя резервное. \en Reserved name. rmn_SectionItemName = -2, ///< \ru Системное имя секущего объекта. \en Section item name. - rmn_DefaultName = -1, ///< \ru Системное имя по умолчанию (= SIMPLENAME_MAX). \en Default name (= SIMPLENAME_MAX). + rmn_DefaultName = -1, ///< \ru Системное имя по умолчанию (= c3d::SIMPLENAME_MAX). \en Default name (= c3d::SIMPLENAME_MAX). }; // The name -2 is also reserved for system names in the Kompas CAD. protected: /// \ru Флаги. \en Flags. enum EFlags { - f_Cut = 0x01, ///< \ru Примитив разрезан. \en Primitive is cut. - f_Sheet = 0x02, ///< \ru Примитив является листовым. (Действителен только для граней) \en Primitive is sheet. (Valid only for faces) - f_InnerBend = 0x04, ///< \ru Примитив является внутренней гранью сгиба. (Действителен только для граней) //-V112 \en Primitive is an internal face of bend. (Valid only for faces) //-V112 - f_OuterBend = 0x08, ///< \ru Примитив является внешней гранью сгиба. (Действителен только для граней) \en Primitive is an external face of bend. (Valid only for faces) - f_SideBend = 0x10, ///< \ru Примитив является боковой гранью сгиба. (Действителен только для граней) \en Primitive is a side face of bend. (Valid only for faces) - f_RibBend = 0x20, ///< \ru Примитив является гранью ребра жесткости листового тела. (Действителен только для граней) \en Primitive is a face of reinforcement rib of sheet solid. (Valid only for faces) + f_Cut = 0x01, ///< \ru Примитив разрезан. \en Primitive is cut. + f_Sheet = 0x02, ///< \ru Примитив является листовым. (Действителен только для граней) \en Primitive is sheet. (Valid only for faces) + f_InnerBend = 0x04, ///< \ru Примитив является внутренней гранью сгиба. (Действителен только для граней) //-V112 \en Primitive is an internal face of bend. (Valid only for faces) //-V112 + f_OuterBend = 0x08, ///< \ru Примитив является внешней гранью сгиба. (Действителен только для граней) \en Primitive is an external face of bend. (Valid only for faces) + f_SideBend = 0x10, ///< \ru Примитив является боковой гранью сгиба. (Действителен только для граней) \en Primitive is a side face of bend. (Valid only for faces) + f_RibBend = 0x20, ///< \ru Примитив является гранью ребра жесткости листового тела. (Действителен только для граней) \en Primitive is a face of reinforcement rib of sheet solid. (Valid only for faces) + f_SweptFlange = 0x40, ///< \ru Примитив является гранью отбортовки. (Действителен только для граней) \en Primitive is a face of a swept flange. (Valid only for faces) }; protected: MbIdArr defNames; ///< \ru Множество идентификаторов. \en An array of identifiers. - public: - static const UniqueNamePtr uniqueFaceName; ///< \ru Уникальное имя фиктивной грани. \en Unique name of dummy face. + static const UniqueNamePtr uniqueFaceName; ///< \ru Уникальное имя фиктивной грани. \en Unique name of dummy face. public : - /// \ru Конструктор по умолчанию \en Default constructor + /// \ru Конструктор по умолчанию. \en Default constructor. MbName() : defNames() {} /// \ru Конструктор копирования. \en Copy-constructor. + /** \brief \ru Конструктор копирования. + \en Copy-constructor. \~ + \details \ru Конструктор копирования. \n + \en Copy-constructor. \n \~ + \param[in] other - \ru Другое имя. + \en Other name. \~ + */ MbName( const MbName & other ) : defNames( other.defNames ) {} /// \ru Деструктор. \en Destructor. virtual ~MbName(); -public: +public: /// \ru Получить главное имя. \en Get main name. SimpleName GetMainName() const; - /// \ru Установить главное имя. \en Set main name. - bool SetMainName( SimpleName n ); - /// \ru Установить имя. \en Set name. - void SetName ( const MbName &, bool setFlags = true ); + /** \brief \ru Установить главное имя. + \en Set main name. \~ + \details \ru Установить главное имя. \n + \en Set main name. \n \~ + \param[in] mn - \ru Главное имя. + \en Main name. \~ + \return \ru Возвращает 'true' в случае успешного выполнения. + \en Returns 'true' in case of successful execution. \~ + */ + bool SetMainName( SimpleName mn ); + /** \brief \ru Установить имя. + \en Set name. \~ + \details \ru Установить другое имя. \n + \en Set other name. \n \~ + \param[in] n - \ru Имя. + \en Name. \~ + \param[in] setFlags - \ru Копировать флаги имени. + \en Copy name flags. \~ + */ + void SetName ( const MbName & n, bool setFlags = true ); /// \ru Пуст ли массив идентификаторов имени. \en Whether the array of name identifiers is empty. bool IsEmpty () const { return defNames.IsEmpty(); } @@ -415,10 +457,25 @@ public: /// \ru Дать или сгенерировать дополнительное имя. \en Get or generate the additional name. SimpleName GetNameExtra( size_t i ) const; - ///< \ru Получение индекса разрезки. \en Get an index of a cutaway. + /// \ru Получение индекса резки. \en Get an index of a cutaway. bool IsCutIndex() const; - /// \ru Получение индекса разрезки. \en Get an index of a cutaway. + /** \brief \ru Получить индекс резки. + \en Get an cut index. \~ + \details \ru Получить индекс резки. \n + \en Get an index of a cutaway. \n \~ + \param[out] cutIndex - \ru Индекс резки. + \en Cut index. \~ + \return \ru Возвращает 'true' в случае успешного выполнения. + \en Returns 'true' in case of successful execution. \~ + */ bool GetCutIndex( SimpleName & cutIndex ) const { return defNames.GetCut( cutIndex ); } + /** \brief \ru Установить индекс резки. + \en Set an cut index. \~ + \details \ru Установить индекс резки. \n + \en Set an index of a cutaway. \n \~ + \param[in] cutIndex - \ru Индекс резки. + \en Cut index. \~ + */ /// \ru Установка индекса разрезки. \en Set an index of cutaway. void SetCutIndex( SimpleName cutIndex ); /// \ru Удаление индекса разрезки. \en Delete an index of cutaway. @@ -431,35 +488,39 @@ public: /// \ru Прямой доступ к первому имени. \en Direct access to the first name. void SetFirstNameDirect( SimpleName fi ) { C3D_ASSERT(defNames.CountBase() > (size_t)i_First); defNames.SetValueBase(fi, (size_t)i_First); } - /// \ru Получение значения флага порезанности. \en Get flag of cutaway. - bool IsCutFlag() const; - /// \ru Установление значения флага порезанности. \en Set flag of cutaway. - void SetCutFlag( bool s = true ); + /// \ru Получить значения флага порезанности. \en Get flag of cutaway. + bool IsCutFlag() const { return !!defNames.flags.GetFlagValue( f_Cut ); } + /// \ru Установить значение флага порезанности. \en Set flag of cutaway. + void SetCutFlag( bool s = true ) { defNames.flags.SetFlagValue( f_Cut, s ); } - /// \ru Установление значения флага листового примитива. \en Set flag of sheet primitive. - void SetSheet( bool s ); - /// \ru Установка значения флага внутренней части сгиба. \en Set flag of internal part of bend. - void SetInnerBend( bool s ); - /// \ru Установка значения флага внешней части сгиба. \en Set flag of external part of bend. - void SetOuterBend( bool s ); - /// \ru Установка значения флага боковой грани сгиба. \en Set flag of side part of bend. - void SetSideBend( bool s ); - /// \ru Установка значения флага грани ребра жесткости листового тела. \en Set flag of reinforcement rib part of sheet solid. - void SetStampRibBend( bool s ); - /// \ru Получение значения флага листового примитива. \en Get flag of sheet primitive. - bool IsSheet() const; - /// \ru Получение значения флага внутренней части сгиба. \en Get flag of internal part of bend. - bool IsInnerBend() const; - /// \ru Получение значения флага внешней части сгиба. \en Get flag of external part of bend. - bool IsOuterBend() const; - /// \ru Получение значения флага боковой грани сгиба. \en Get flag of side part of bend. - bool IsSideBend() const; - /// \ru Получение значения флага грани ребра жесткости листового тела. \en Get flag of reinforcement rib part of sheet solid. - bool IsStampRibBend() const; + /// \ru Установить значение флага листового примитива. \en Set flag of sheet primitive. + void SetSheet( bool s ) { defNames.flags.SetFlagValue( f_Sheet, s ); } + /// \ru Установить значение флага внутренней части сгиба. \en Set flag of internal part of bend. + void SetInnerBend( bool s ) { defNames.flags.SetFlagValue( f_InnerBend, s ); } + /// \ru Установить значение флага внешней части сгиба. \en Set flag of external part of bend. + void SetOuterBend( bool s ) { defNames.flags.SetFlagValue( f_OuterBend, s ); } + /// \ru Установить значение флага боковой грани сгиба. \en Set flag of side part of bend. + void SetSideBend( bool s ) { defNames.flags.SetFlagValue( f_SideBend, s ); } + /// \ru Установить значение флага грани ребра жесткости листового тела. \en Set flag of reinforcement rib part of sheet solid. + void SetStampRibBend( bool s ) { defNames.flags.SetFlagValue( f_RibBend, s ); } + /// \ru Установить значение флага грани отбортовки. \en Set flag of a swept flange. + void SetSweptFlange( bool s ) { defNames.flags.SetFlagValue( f_SweptFlange, s ); } + /// \ru Получить значение флага листового примитива. \en Get flag of sheet primitive. + bool IsSheet() const { return !!defNames.flags.GetFlagValue( f_Sheet ); } + /// \ru Получить значение флага внутренней части сгиба. \en Get flag of internal part of bend. + bool IsInnerBend() const { return !!defNames.flags.GetFlagValue( f_InnerBend ); } + /// \ru Получить значение флага внешней части сгиба. \en Get flag of external part of bend. + bool IsOuterBend() const { return !!defNames.flags.GetFlagValue( f_OuterBend ); } + /// \ru Получить значение флага боковой грани сгиба. \en Get flag of side part of bend. + bool IsSideBend() const { return !!defNames.flags.GetFlagValue( f_SideBend ); } + /// \ru Получить значение флага грани ребра жесткости листового тела. \en Get flag of reinforcement rib part of sheet solid. + bool IsStampRibBend() const { return !!defNames.flags.GetFlagValue( f_RibBend ); } + /// \ru Получить значение флага грани отбортовки. \en Get flag of a swept flange. + bool IsSweptFlange() const { return !!defNames.flags.GetFlagValue( f_SweptFlange ); } - /// \ru Установка индекса копирования. \en Set an index of copying. + /// \ru Установить индекс копирования. \en Set an index of copying. void SetCopyIndex( SimpleName ci ) { defNames.SetCopyIndex( ci ); } - /// \ru Получение индекса копирования. \en Get an index of copying. + /// \ru Получить индекс копирования. \en Get an index of copying. bool GetCopyIndex( SimpleName & ci ) const { return defNames.GetCopyIndex( ci ); } /** \brief \ru Установить положение в сетке копирования. @@ -533,7 +594,8 @@ public: void Assign( const MbName & other ) { defNames.Assign( other.defNames ); } /// \ru Сделать из имени шаблон. \en Create template from name. - void MakeTemplate() { + void MakeTemplate() + { C3D_ASSERT( defNames.CountBase() ); if ( defNames.CountBase() > (size_t)i_First ) { SetFirstNameDirect( 0 ); @@ -563,9 +625,10 @@ KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbName, MATH_FUNC_EX ) // \ru Для рабо //---------------------------------------------------------------------------------------- -// +// \ru Множество пустое? \en Is the array empty? // --- -inline bool MbIdArr::IsEmpty() const { +inline +bool MbIdArr::IsEmpty() const { // \ru IsEmpty проверять parr[MbName::i_Main] только для count == 1 \en IsEmpty check parr[MbName::i_Main] only for count == 1 // \ru Если count > 1 - считать не пустым \en If count > 1, then it is considered as non-empty return ( Count() > (size_t)MbName::i_Main ) ? ( (Count() - 1) == (size_t)MbName::i_Main ? !parr[(size_t)MbName::i_Main] : false ) : true; @@ -575,7 +638,8 @@ inline bool MbIdArr::IsEmpty() const { //---------------------------------------------------------------------------------------- // \ru Получение индекса разрезки \en Get an index of a cutaway // --- -inline bool MbIdArr::GetCut( SimpleName & cutIndex ) const +inline +bool MbIdArr::GetCut( SimpleName & cutIndex ) const { if ( Count() > (size_t)MbName::i_Cut ) { cutIndex = parr[(size_t)MbName::i_Cut]; @@ -588,7 +652,8 @@ inline bool MbIdArr::GetCut( SimpleName & cutIndex ) const //---------------------------------------------------------------------------------------- // \ru Получение индекса копирования \en Get an index of copying // --- -inline bool MbIdArr::GetCopyIndex( SimpleName & ci ) const +inline +bool MbIdArr::GetCopyIndex( SimpleName & ci ) const { if ( Count() > (size_t)MbName::i_Copy ) { ci = parr[(size_t)MbName::i_Copy]; @@ -601,7 +666,8 @@ inline bool MbIdArr::GetCopyIndex( SimpleName & ci ) const //------------------------------------------------------------------------------ // \ru Получить главное имя. \en Get main name. // --- -inline SimpleName MbName::GetMainName() const +inline +SimpleName MbName::GetMainName() const { if ( defNames.CountBase() > (size_t)i_Main ) return defNames.GetValueBase( (size_t)i_Main ); @@ -612,7 +678,8 @@ inline SimpleName MbName::GetMainName() const //------------------------------------------------------------------------------ // \ru Установить главное имя. \en Set main name. // --- -inline bool MbName::SetMainName( SimpleName n ) +inline +bool MbName::SetMainName( SimpleName n ) { bool res = true; if ( static_cast(rmn_DummyFaceName) == n ) { // Reserved for unique name of dummy face (-1 = SIMPLENAME_MAX, -2 is already used for Kompas CAD and It's the error on their side) @@ -620,7 +687,6 @@ inline bool MbName::SetMainName( SimpleName n ) C3D_ASSERT_UNCONDITIONAL( false ); res = false; } - if ( defNames.CountBase() > 0 ) defNames.SetValueBase( n, 0 ); else @@ -633,7 +699,8 @@ inline bool MbName::SetMainName( SimpleName n ) //---------------------------------------------------------------------------------------- // \ru Проверка на равенство. \en Check for equality. // --- -inline bool MbName::operator == ( const MbName & n ) const +inline +bool MbName::operator == ( const MbName & n ) const { if ( defNames.CountAll() == n.defNames.CountAll() ) { if ( defNames.CountAll() ) { @@ -649,7 +716,8 @@ inline bool MbName::operator == ( const MbName & n ) const //---------------------------------------------------------------------------------------- // \ru Проверка не неравенство. \en Check for inequality. // --- -inline bool MbName::operator < ( const MbName & n ) const +inline +bool MbName::operator < ( const MbName & n ) const { if ( defNames.CountAll() == n.defNames.CountAll() ) { if ( defNames.CountAll() ) { @@ -666,9 +734,10 @@ inline bool MbName::operator < ( const MbName & n ) const //---------------------------------------------------------------------------------------- -// \ru Получение индекса разрезки. \en Get an index of a cutaway. +// \ru Получение индекса резки. \en Get an index of a cutaway. // --- -inline bool MbName::IsCutIndex() const +inline +bool MbName::IsCutIndex() const { SimpleName cutIndex = 0; if ( GetCutIndex( cutIndex ) ) @@ -677,112 +746,14 @@ inline bool MbName::IsCutIndex() const } -//---------------------------------------------------------------------------------------- -// \ru Получение значения флага порезанности. \en Get flag of cutaway. -// --- -inline bool MbName::IsCutFlag() const { - return !!defNames.flags.GetFlagValue( f_Cut ); -} - - -//---------------------------------------------------------------------------------------- -// \ru Установление значения флага порезанности. \en Set flag of cutaway. -// --- -inline void MbName::SetCutFlag( bool s ) { - defNames.flags.SetFlagValue( f_Cut, s ); -} - - -//---------------------------------------------------------------------------------------- -// \ru Установление значения флага листового примитива. \en Set flag of sheet primitive. -// --- -inline void MbName::SetSheet( bool s ) { - defNames.flags.SetFlagValue( f_Sheet, s ); -} - - -//---------------------------------------------------------------------------------------- -// \ru Получение значения флага листового примитива. \en Get flag of sheet primitive. -// --- -inline bool MbName::IsSheet() const { - return !!defNames.flags.GetFlagValue( f_Sheet ); -} - - -//---------------------------------------------------------------------------------------- -// \ru Установление значения флага внутренней части сгиба. \en Set flag of internal part of bend. -// --- -inline void MbName::SetInnerBend( bool s ) { - defNames.flags.SetFlagValue( f_InnerBend, s ); -} - - -//---------------------------------------------------------------------------------------- -// \ru Получение значения флага внутренней части сгиба. \en Get flag of internal part of bend. -// --- -inline bool MbName::IsInnerBend() const { - return !!defNames.flags.GetFlagValue( f_InnerBend ); -} - - -//---------------------------------------------------------------------------------------- -// \ru Установление значения флага внешней части сгиба. \en Set flag of external part of bend. -// --- -inline void MbName::SetOuterBend( bool s ) { - defNames.flags.SetFlagValue( f_OuterBend, s ); -} - - -//---------------------------------------------------------------------------------------- -// \ru Получение значения флага внешней части сгиба. \en Get flag of external part of bend. -// --- -inline bool MbName::IsOuterBend() const { - return !!defNames.flags.GetFlagValue( f_OuterBend ); -} - - -//---------------------------------------------------------------------------------------- -// \ru Установление значения флага боковой грани сгиба. \en Set flag of side part of bend. -// --- -inline void MbName::SetSideBend( bool s ) { - defNames.flags.SetFlagValue( f_SideBend, s ); -} - - -//---------------------------------------------------------------------------------------- -// \ru Получение значения флага боковой грани сгиба. \en Get flag of side part of bend. -// --- -inline bool MbName::IsSideBend() const { - return !!defNames.flags.GetFlagValue( f_SideBend ); -} - - -//---------------------------------------------------------------------------------------- -// \ru Установление значения флага грани ребра жесткости листового тела. \en Set flag of reinforcement rib part of sheet solid. -// --- -inline void MbName::SetStampRibBend( bool s ) { - defNames.flags.SetFlagValue( f_RibBend, s ); -} - - -//---------------------------------------------------------------------------------------- -// \ru Получение значения флага грани ребра жесткости листового тела. \en Get flag of reinforcement rib part of sheet solid. -// --- -inline bool MbName::IsStampRibBend() const { - return !!defNames.flags.GetFlagValue( f_RibBend ); -} - - ////////////////////////////////////////////////////////////////////////////////////////// // // \ru Имя объекта и его копии. \en Name of object and its duplicate. // ////////////////////////////////////////////////////////////////////////////////////////// - #define ORIGINAL_MAIN_NAME // \ru Используется для работы с массивами. \en Used for arrays treatment. - #ifdef ORIGINAL_MAIN_NAME //---------------------------------------------------------------------------------------- /** \brief \ru Имя объекта и его копии. @@ -793,26 +764,47 @@ inline bool MbName::IsStampRibBend() const { */ // --- class MATH_CLASS MbNamePair { + friend class MbNamePairList; private: - MbName * gageName; // \ru Имя оригинала. \en A name of original. - MbName * copyName; // \ru Имя копии. \en A name of duplicate. - + const MbName * gageName; ///< \ru Имя оригинала. \en A name of original. + const MbName * copyName; ///< \ru Имя копии. \en A name of duplicate. // могут использоваться для самостоятельного поиска - mutable SimpleName copyHash; // \ru Кэш хеша имени оригинала. \en + mutable SimpleName copyHash; ///< \ru Кэш хеша имени оригинала. \en Hash cache of the original name. public: - /// \ru Конструктор. \en Constructor. - MbNamePair( MbName * orig, MbName * copy ) : gageName( orig ), copyName( copy ), copyHash( SIMPLENAME_MAX ) {} - /// \ru Конструктор для поиска по имени копии. \en Constructor for find by copy name. - MbNamePair( MbName * copy ) : gageName( NULL ), copyName( copy ), copyHash( SIMPLENAME_MAX ) {} - /// \ru Конструктор для поиска по хешу копии. \en Constructor for find by copy hash. - MbNamePair( SimpleName copy ) : gageName( NULL ), copyName( NULL ), copyHash( copy ) {} + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор. \n + \en Constructor. \n \~ + \param[in] orig - \ru Имя оригинала. + \en The name of the original. \~ + \param[in] copy - \ru Имя копии. + \en The name of the copy. \~ + */ + MbNamePair( const MbName * orig, const MbName * copy ) : gageName( orig ), copyName( copy ), copyHash( c3d::SIMPLENAME_MAX ) {} + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор для поиска по имени копии. \n + \en Constructor for find by copy name. \n \~ + \param[in] copy - \ru Имя копии. + \en The name of the copy. \~ + */ + MbNamePair( const MbName * copy ) : gageName( C3D_NULL_PTR ), copyName( copy ), copyHash( c3d::SIMPLENAME_MAX ) {} + /// \ru . \en . + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор для поиска по хешу копии. \n + \en Constructor for find by copy hash. \n \~ + \param[in] copy - \ru Хэш имени копии. + \en The hash of the name of the copy. \~ + */ + MbNamePair( SimpleName copy ) : gageName( C3D_NULL_PTR ), copyName( C3D_NULL_PTR ), copyHash( copy ) {} /// \ru Деструктор. \en Destructor. ~MbNamePair() {} public: /// \ru Обнулить имя оригинала и имя копии. \en Set name of original and of its duplicate to null. - void SetNull() { gageName = NULL; copyName = NULL; copyHash = SIMPLENAME_MAX; } + void SetNull() { gageName = C3D_NULL_PTR; copyName = C3D_NULL_PTR; copyHash = c3d::SIMPLENAME_MAX; } /// \ru Оператор сравнения. \en Comparison operator. bool operator == ( const MbNamePair & other ) const; /// \ru Оператор меньше. \en "Less than" operator. @@ -824,10 +816,21 @@ public: copyHash = Prepare(*copyName).Hash(); return copyHash; } - /// \ru Правило приготовления имен. \en Names prepare rules. - static MbName Prepare( const MbName & name ); - - friend class MATH_CLASS MbNamePairList; + /** \brief \ru Правило приготовления имен. + \en Names prepare rules. \~ + \details \ru Правило приготовления имен. \n + \en Names prepare rules. \n \~ + \param[out] name - \ru Имя заготовки. + \en Workpiece name. \~ + \return \ru Возвращает имя. + \en Returns a name. \~ + */ + static MbName Prepare( const MbName & name ) + { + MbName ret( name ); + ret.SetMainName( c3d::SIMPLENAME_MAX ); + return ret; + } }; @@ -837,7 +840,6 @@ public: // ////////////////////////////////////////////////////////////////////////////////////////// - //---------------------------------------------------------------------------------------- /** \brief \ru Таблица соответствия имён. \en Table of names correspondence. \~ @@ -847,52 +849,69 @@ public: */ // --- class MATH_CLASS MbNamePairList { + friend class MATH_CLASS MbNameMaker; + friend class MATH_CLASS MbSNameMaker; private: - CSSArray checkList; ///< \ru Имена объектов и их копий. \en Names of objects and of its duplicates. - + mutable CSSArray checkList; ///< \ru Имена объектов и их копий. \en Names of objects and of its duplicates. public: /// \ru Конструктор. \en Constructor. - MbNamePairList( size_t count = 0 ) : checkList( count, 1 ) {} + MbNamePairList() : checkList() {} /// \ru Деструктор. \en Destructor. ~MbNamePairList() { DeleteNames(); } public: - /// \ru Добавить имя объекта и имя его копии, имя оригинала и копии должно быть создано по new. \en Store names of object and its duplicate, which are created by new. - void AddNameData( MbName * orig, MbName * copy ) + /** \brief \ru Добавить имя объекта и имя его копии. + \en Add names of object and its duplicate. \~ + \details \ru Добавить имя объекта и имя его копии, имя оригинала и копии должно быть создано по new. \n + \en Add names of object and its duplicate, which have to be created by new. \n \~ + \param[in] orig - \ru Имя оригинала. + \en The name of the original. \~ + \param[in] copy - \ru Имя копии. + \en The name of the copy. \~ + */ + void AddNameData( const MbName * orig, const MbName * copy ) { - C3D_ASSERT( (orig != NULL) && (copy != NULL) ); - if ( (orig != NULL) && (copy != NULL) ) { + C3D_ASSERT( (orig != C3D_NULL_PTR) && (copy != C3D_NULL_PTR) ); + if ( (orig != C3D_NULL_PTR) && (copy != C3D_NULL_PTR) ) { checkList.Add( MbNamePair( orig, copy ) ); } } /// \ru Выделить память под элементы. \en Allocate memory for elements. void Reserve( size_t count ) { checkList.Reserve( count ); } - /// \ru Очистить массив не удаляя память под элементы. \en Erase array without deleting memory for elements. + /// \ru Очистить массив, не удаляя память под элементы. \en Erase array without deleting memory for elements. void Erase() { DeleteNames(); checkList.Flush(); } /// \ru Удалить память под элементы. \en Delete memory for elements. void Free() { DeleteNames(); checkList.HardFlush(); } /// \ru Удалить ненужные элементы по именам копий. \en Clean up unnecessary pairs by name copies. - bool Clean( const std::vector & delNamesCopies ); + bool Clean( const c3d::ConstNamesVector & delNamesCopies ); /// \ru Заменить имена копий. \en Replace names copies. - bool Replace( const MbName & newNameCopies, const std::vector & oldNamesCopies ); + bool Replace( const MbName & newNameCopies, const c3d::ConstNamesVector & oldNamesCopies ); /// \ru Найти имя объекта по имени его копии. \en Find name of object by name of its duplicate. - const MbName * FindOriginalByCopy( const MbName * copy ); + const MbName * FindOriginalByCopy( const MbName * ) const; /// \ru Найти имя копии объекта по имени его оригинала. \en Find name of duplicate object by name of its original. - const MbName * FindCopyByOriginal( const MbName & original ) const; - /// \ru Найти имя объекта по хешу его копии. \en Find name of object by hash of its duplicate. - const MbName * FindOriginalByCopy( const SimpleName & originalHash ); + const MbName * FindCopyByOriginal( const MbName & ) const; + /// \ru Найти имя оригинала объекта по хешу имени копии объекта. \en Find the name of the original object by the hash of the name of the copy of the object. + const MbName * FindOriginalByCopy( const SimpleName & originalHash ) const; const CSSArray & GetCheckList() const { return checkList; } - - friend class MATH_CLASS MbNameMaker; - friend class MATH_CLASS MbSNameMaker; - private: /// \ru Удалить имена в каждом элементе. \en Delete names in each element. - void DeleteNames(); + void DeleteNames() + { + for ( size_t i = 0, cnt = checkList.size(); i < cnt; ++i ) { + MbNamePair & namePair = checkList[i]; + delete namePair.gageName; + delete namePair.copyName; + namePair.SetNull(); + } + } /// \ru Обнулить имя оригинала и имя копии в каждом элементе. \en Set name of original and of its duplicate to null in each element. - void SetNull(); + void SetNull() + { + for ( size_t i = 0, cnt = checkList.size(); i < cnt; ++i ) + checkList[i].SetNull(); + } }; #endif // ORIGINAL_MAIN_NAME @@ -903,6 +922,8 @@ private: // ////////////////////////////////////////////////////////////////////////////////////////// +class MATH_CLASS MbTopologyItem; +class MATH_CLASS MbTopologyProxy; //---------------------------------------------------------------------------------------- /** \brief \ru Генератор имен. @@ -912,13 +933,13 @@ private: \en Generator of names of topological objects by the given template. \n Do not use the main names from the range MbName::ReservedMainNames (exception - MbName::ReservedMainNames::rmn_DefaultName). \n \~ \internal -\ru Надо добиться того, чтобы имена не могли создаваться в обход определенных правил + \ru Надо добиться того, чтобы имена не могли создаваться в обход определенных правил и одновременно предусмотреть возможность создания новых правил!!! Задача противоречивая - попробуем вот так. Если надо создать новое правило генерации имени, то сделайте наследника от этого класса напишите новое правило создающее MbIdArr (LiSArray) (или просто SimpleName) и отдайте на вход MakeName -\en It is necessary to achieve that names can't be created passing over certain rules + \en It is necessary to achieve that names can't be created passing over certain rules and at the same time to provide possibility for creating new rules!!! Task is inconsistent - try so. If it is necessary to create a new rule for generating name, then inherit from this class, @@ -928,7 +949,7 @@ private: \ingroup Names */ //--- -class MATH_CLASS MbNameMaker { +class MATH_CLASS MbNameMaker : public MbRefItem { protected: MbName defName; ///< \ru Шаблон имени. \en Name template. @@ -939,64 +960,212 @@ protected: #endif // ORIGINAL_MAIN_NAME public: - /// \ru Конструктор по главному имени. \en Constructor by main name. - MbNameMaker( SimpleName mn ); - /// \ru Конструктор по имени. \en Constructor by name. - MbNameMaker( const MbName & _name ); - /// \ru Конструктор другому генератору имен. \en Constructor by another generator of names. - MbNameMaker ( const MbNameMaker & o ); + /** \brief \ru Конструктор по главному имени. + \en Constructor by main name. \~ + \details \ru Конструктор по главному имени. \n + \en Constructor by main name. \n \~ + \param[in] mn - \ru Главное имя. + \en Main name. \~ + */ + MbNameMaker( SimpleName mn ) + : MbRefItem() + , defName() + , version() +#ifdef ORIGINAL_MAIN_NAME + , original( c3d::SIMPLENAME_MAX ) + , nameList( C3D_NULL_PTR ) +#endif // ORIGINAL_MAIN_NAME + { + defName.SetMainName( mn ); + defName.MakeTemplate(); + } + /** \brief \ru Конструктор по имени. + \en Constructor by name. \~ + \details \ru Конструктор по имени. \n + \en Constructor by name. \n \~ + \param[in] n - \ru Имя. + \en Name. \~ + */ + MbNameMaker( const MbName & n ) + : MbRefItem() + , defName( n ) + , version() +#ifdef ORIGINAL_MAIN_NAME + , original( c3d::SIMPLENAME_MAX ) + , nameList( C3D_NULL_PTR ) +#endif // ORIGINAL_MAIN_NAME + { + defName.MakeTemplate(); + } + /** \brief \ru Конструктор другому генератору имен. + \en Constructor by another generator of names. \~ + \details \ru Конструктор другому генератору имен. \n + \en Constructor by another generator of names. \n \~ + \param[in] nm - \ru Генератор имен. + \en Names generator. \~ + */ + MbNameMaker ( const MbNameMaker & nm ) + : MbRefItem() + , defName( nm.defName ) + , version( nm.version ) +#ifdef ORIGINAL_MAIN_NAME + , original( nm.original ) + , nameList( nm.nameList ) +#endif // ORIGINAL_MAIN_NAME + {} /// \ru Деструктор. \en Destructor. - ~MbNameMaker() {} + virtual ~MbNameMaker() {} public: - /// \ru Доступ к главному имени. \en Access to main name. - SimpleName GetMainName () const { return defName.GetMainName(); } - /// \ru Установка главного имени. \en Set main name. - void SetMainName ( SimpleName n ) { defName.SetMainName( n ); } - /// \ru Версия изготовления. \en Version of manufacture. - const VersionContainer & GetVersionContainer() const { return version.GetVersionContainer(); } - /// \ru Версия изготовления. \en Version of manufacture. - const MbNameVersion & GetMbNameVersion() const { return version; } - /// \ru Версия изготовления. \en Version of manufacture. - void SetVersion( const MbNameVersion & v ) { version = v; } + /// \ru Получить главное имя. \en Get main name. + SimpleName GetMainName () const { return defName.GetMainName(); } + /** \brief \ru Установить главное имя. + \en Set main name. \~ + \details \ru Установить главное имя. \n + \en Set main name. \n \~ + \param[in] n - \ru Главное имя. + \en Main name. \~ + */ + void SetMainName ( SimpleName n ) { defName.SetMainName( n ); } - /// \ru Получить версию математического ядра. \en Get version of the mathematical kernel. - VERSION GetMathVersion() const { return version.GetVersionContainer().GetMathVersion(); } - /// \ru Установить версию математического ребра. \en Set version of the mathematical kernel. - void SetMathVersion( VERSION v ) { version.SetVersion( 0, v ); } + /// \ru Получить версию изготовления. \en Get a version of manufacture. + const VersionContainer & GetVersionContainer() const { return version.GetVersionContainer(); } + /// \ru Получить версию изготовления. \en Get a version of manufacture. + const MbNameVersion & GetMbNameVersion() const { return version; } + /** \brief \ru Установить версию изготовления. + \en Set version of manufacture. \~ + \details \ru Установить версию изготовления. \n + \en Set version of manufacture. \n \~ + \param[in] v - \ru Версия изготовления. + \en Version of manufacture. \~ + */ + void SetVersion( const MbNameVersion & v ) { version = v; } + /// \ru Получить версию математического ядра. \en Get version of the mathematical kernel. + VERSION GetMathVersion() const { return version.GetVersionContainer().GetMathVersion(); } + /** \brief \ru Установить версию математического ядра. + \en Set version of the mathematical kernel. \~ + \details \ru Установить версию математического ядра. \n + \en Set version of the mathematical kernel. \n \~ + \param[in] v - \ru Версия изготовления. + \en Version of manufacture. \~ + */ + void SetMathVersion( VERSION v ) { version.SetVersion( 0, v ); } + +public: + /** \brief \ru Получить имя топологического объекта. + \en Get topology item name. \~ + \details \ru Получить имя топологического объекта. \n + \en Get topology item name. \n \~ + \param[in] item - \ru Топологический объект. + \en Topology item. \~ + \param[in,out] name - \ru Копия имени топологического объекта. + \en Topology item name copy. \~ + */ + virtual void GetItemName( const MbTopologyItem & item, MbName & name ) const; + /** \brief \ru Установить имя топологическому объекту. + \en Set topology item name. \~ + \details \ru Установить имя топологическому объекту. \n + \en Set topology item name. \n \~ + \param[in] name - \ru Новое имя топологического объекта. + \en Topology item new name. \~ + \param[in,out] item - \ru Топологический объект. + \en Topology item. \~ + */ + virtual void SetItemName( const MbName & name, MbTopologyItem & item ) const; + + /// \ru Является ли именователь родительским для данного топологического элемента? \en Is the name maker a parent for a given topological element?. + virtual bool IsChild( const MbTopologyItem & ) const; + +public: + /** \brief \ru Генерация имени топологического объекта по двум простым именам. + \en Generate name of topological object by two simple names. \~ + \details \ru Генерация имени топологического объекта по двум простым именам. \n + \en Generate name of topological object by by two simple names. \n \~ + \param[in] sn1 - \ru Простое имя #1. + \en Simple name #2. \~ + \param[in] sn2 - \ru Простое имя #1. + \en Simple name #2. \~ + \param[in,out] dstItem - \ru Топологический объект. + \en Topology item. \~ + */ + virtual void MakeName( SimpleName sn1, SimpleName sn2, MbTopologyItem & dstItem ) const; + /** \brief \ru Генерация имени топологического объекта по простому имени. + \en Generate name of topological object by simple name. \~ + \details \ru Генерация имени топологического объекта по простому имени. \n + \en Generate name of topological object by simple name. \n \~ + \param[in] sn - \ru Простое имя. + \en Simple name. \~ + \param[in,out] dstItem - \ru Топологический объект. + \en Topology item. \~ + */ + virtual void MakeName( SimpleName sn, MbTopologyItem & dstItem ) const; + /** \brief \ru Генерация имени для грани скругления. + \en Generate name for fillet face. \~ + \details \ru Генерация имени для грани скругления. \n + \en Generate name for fillet face. \n \~ + \param[in] srcName - \ru Имя источника. + \en Source name. \~ + \param[in,out] dstItem - \ru Топологический объект. + \en Topology item. \~ + */ + virtual void MakeFilletFaceName( const MbName & srcName, MbTopologyItem & dstItem ) const; protected: - /// \ru Генерация имени name по шаблону и двум простым именам. \en Generate 'name' name by template and two simple names. - void MakeNameBy( SimpleName snFirst, SimpleName snCut, MbName & name ) const; - /// \ru Генерация имени name шаблону и источнику для грани скругления. \en Generate 'name' name by template and by source for fillet face. - void MakeNameBy( const MbName & source, MbName & name ) const; + /// \ru Генерация имени по двум простым именам SimpleName. \en Generate name by two simple names. + void MakeName( SimpleName sn1, SimpleName sn2, MbName & dstName ) const; + /// \ru Генерация имени по простому имени SimpleName. \en Generate name by simple name. + void MakeName( SimpleName sn, MbName & dstName ) const; + /// \ru Генерация имени для грани скругления. \en Generate name for fillet face. + void MakeFilletFaceName( const MbName & srcName, MbName & dstName ) const; + +public: + /** \brief \ru Генерация имени топологического объекта по шаблону и простому имени. + \en Generate name of topological object by template and by simple name. \~ + \details \ru Генерация имени топологического объекта по шаблону и простому имени. \n + \en Generate name of topological object by template and by simple name. \n \~ + \param[in] sn - \ru Простое имя. + \en Simple name. \~ + \param[in,out] dstItem - \ru Топологический объект. + \en Topology item. \~ + */ + virtual void MakeNameBy( SimpleName sn, MbTopologyItem & dstItem ) const; +protected: + /// \ru Генерация имени по шаблону и простому имени. \en Generate name by template and by simple name. + void MakeNameBy( SimpleName sn, MbName & dstName ) const; + /// \ru Генерация имени по шаблону и двум простым именам. \en Generate name by template and two simple names. + void MakeNameBy( SimpleName snFirst, SimpleName snCut, MbName & dstName ) const; + /// \ru Генерация имени по шаблону и источнику для грани скругления. \en Generate name by template and by source for fillet face. + void MakeNameBy( const MbName & srcName, MbName & dstName ) const; public: - /// \ru Генерация имени name по шаблону и простому имени. \en Generate 'name' name by template and by simple name. - void MakeNameBy( SimpleName sn, MbName & name ) const; - /// \ru Генерация имени name по двум простым именам SimpleName. \en Generate 'name' name by two SimpleName simple names. - void MakeName( SimpleName sn1, SimpleName sn2, MbName & name ) const; - /// \ru Генерация имени name по простому имени SimpleName. \en Generate 'name' name by SimpleName simple name. - void MakeName( SimpleName sn, MbName & name ) const; - /// \ru Генерация имени name для грани скругления. \en Generate 'name' name for fillet face. - void MakeFilletFaceName( const MbName &, MbName & name ) const; #ifdef ORIGINAL_MAIN_NAME - /// \ru Установить исходное главное имя и таблицу соответствия имён. \en Set original main name and table of name correspondence. - void SetOriginalMainName( SimpleName orig, MbNamePairList * list ) { original = orig; nameList = list; } - /// \ru Исходное главное имя. \en Source main name. - SimpleName GetOriginalMainName() const { return original; } - /// \ru Таблица соответствия имён оригиналов и их копий. \en Table of correspondence of names of originals and of its duplicates. - MbNamePairList * GetNameList() const { return nameList; } - /// \ru Получить генератор имен оригинала, считая, что это именователь копии. \en Get original name maker. - MbNameMaker GetOriginalNameMaker() const; - /// \ru Удалить ненужные элементы по именам копий. \en Clean up unnecessary pairs by name copies. - bool CleanNameList( std::vector & delNamesCopies ) const; - /// \ru Заменить имена копий. \en Replace names copies. - bool ReplaceNameList( const MbName & newNameCopies, const std::vector & oldNamesCopies ) const; + /// \ru Установить исходное главное имя и таблицу соответствия имён. \en Set original main name and table of name correspondence. + void SetOriginalMainName( SimpleName orig, MbNamePairList * list ) { original = orig; nameList = list; } + /// \ru Исходное главное имя. \en Source main name. + SimpleName GetOriginalMainName() const { return original; } + + /// \ru Таблица соответствия имён оригиналов и их копий. \en Table of correspondence of names of originals and of its duplicates. + const MbNamePairList * GetNameList() const { return nameList; } + /// \ru Таблица соответствия имён оригиналов и их копий. \en Table of correspondence of names of originals and of its duplicates. + MbNamePairList * SetNameList() const { return nameList; } + + /// \ru Получить генератор имен оригинала, считая, что это именователь копии. \en Get original name maker. + MbNameMaker GetOriginalNameMaker() const; + /// \ru Удалить ненужные элементы по именам копий. \en Clean up unnecessary pairs by name copies. + bool CleanNameList( c3d::ConstNamesVector & delNamesCopies ) const { + return ((nameList != C3D_NULL_PTR) ? nameList->Clean( delNamesCopies ) : false); + } + /// \ru Заменить имена копий. \en Replace names copies. + bool ReplaceNameList( const MbName & newNameCopies, const c3d::ConstNamesVector & oldNamesCopies ) const { + return ((nameList != C3D_NULL_PTR) ? nameList->Replace( newNameCopies, oldNamesCopies ) : false); + } #endif // ORIGINAL_MAIN_NAME - /// \ru Являются ли объекты равными? \en Determine whether an object is equal? - bool IsSame( const MbNameMaker & ) const; + /// \ru Являются ли объекты равными? \en Determine whether an object is equal? + bool IsSame( const MbNameMaker & ) const; +public: + /// \ru Создать заменитель топологического объекта. \en Create topology item proxy. + static MbTopologyProxy & CreateTopologyProxy(); protected: /// \ru Оператор чтения. \en Read operator. @@ -1046,15 +1215,37 @@ private: public: /// \ru Конструктор. \en Constructor. - explicit MbSNameMaker ( SimpleName _mainName = UNDEFINED_SNAME, ESides _sideAdd = MbSNameMaker::i_SideNone, SimpleName _buttAdd = 0 ); - /// \ru Конструктор копирования. \en Copy-constructor. - MbSNameMaker ( const MbSNameMaker & other ); + explicit MbSNameMaker ( SimpleName _mainName = c3d::UNDEFINED_SNAME, + ESides _sideAdd = MbSNameMaker::i_SideNone, + SimpleName _buttAdd = 0 ) + : SimpleNameArray( 0, 2 ) + , MbNameMaker ( _mainName ) + , sideAdd ( _sideAdd ) + , buttAdd ( _buttAdd ) + , cpyHist ( true ) + , addParentNamesAttributes( false ) + {} + /// \ru Конструктор по другому именователю (конструктор копирования). \en Constructor by other name-maker (copy-constructor). + MbSNameMaker ( const MbSNameMaker & other ) + : SimpleNameArray( other.Count(), 2 ) + , MbNameMaker ( other ) + , sideAdd ( i_SideNone ) + , buttAdd ( 0 ) + , cpyHist ( true ) + , addParentNamesAttributes( false ) + { + SetNameMaker( other, true ); + } + /// \ru Деструктор. \en Destructor. + virtual ~MbSNameMaker() {} +public: /// \ru Инициализировать по другому именователю. \en Initialize by another name-maker. - void SetName( const MbSNameMaker & other, bool setVersion = false ); - /// \ru Получить простое имя из массива с контролем выхода за границы \en Get simple name from array with control of overruning - SimpleName GetName( size_t i ) const; - /// \ru Инверсия. \en Inversion. + void SetNameMaker( const MbSNameMaker & other, bool setVersion = false ); + + /// \ru Получить простое имя из массива с контролем выхода за границы. \en Get simple name from array with control of overruning. + SimpleName GetSimpleName( size_t i ) const; + /// \ru Инверсия простых имен именователя. \en Inversion of simple names array. void Inverse(); /// \ru Выдать имя в виде условного положения в сетке копирования (для массивов). \en Get name in form of conditional position in grid of copying (for arrays). @@ -1075,32 +1266,34 @@ public: /// \ru Установить количество имен. \en Set count of names. void SetNamesCount( size_t newCount ); - /// \ru Генерация имени name торцевой грани: mainName, +/-defName, знак определяется направлением. \en Generate 'name' name for butt face: mainName, +/-defName, sign is defined by direction. - void SetButtFaceName( MbName & name, MbSNameMaker::ESides side ) const; - /// \ru Генерация имени name грани: mainName, hash( sideName, add ). \en Generate 'name' name for face: mainName, hash( sideName, add ). - void SetFaceName( MbName & name, size_t i, SimpleName add ) const; - /// \ru Генерация имени name каркаса. \en Generate 'name' name for frame. - void SetWireName( MbName & name, size_t i ) const; - /// \ru Генерация имени name ребра из имен граней (sense - направление ребра по отношению к подлежащей кривой). \en Generate 'name' name for edge from names of faces ('sense' is a direction of edge relative to underlined curve). - void CompileEdgeName( MbName & name, - const MbName * f1, - const MbName * f2, - size_t ind, - bool sameSense ) const; - /// \ru Генерация имени name по другому имени, полное совпадение. \en Generate 'name' name by other name, full coincidence. - void CompileEdgeName( MbName & name, const MbName & other ) const; +public: + /// \ru Генерация имени name торцевой грани: mainName, +/-defName, знак определяется направлением. \en Generate name for butt face: mainName, +/-defName, sign is defined by direction. + virtual void MakeButtFaceName( MbTopologyItem & item, MbSNameMaker::ESides side ) const; + /// \ru Генерация имени грани: mainName, hash( sideName, add ). \en Generate name for face: mainName, hash( sideName, add ). + virtual void MakeFaceName( MbTopologyItem & item, size_t i, SimpleName add ) const; + /// \ru Генерация имени на основе главного имени и простого имени из именователя. \en Generate name on the base of main name and simple name from this names generator. + virtual void MakeItemName( MbTopologyItem & item, size_t i ) const; + /// \ru Генерация имени ребра из имен граней (sense - направление ребра по отношению к подлежащей кривой). \en Generate name for edge from names of faces ('sense' is a direction of edge relative to underlined curve). + virtual void CompileEdgeName( MbTopologyItem & item, + const MbName * faceName1, + const MbName * faceName2, + size_t ind, + bool sameSense ) const; + /// \ru Генерация имени по другому имени, полное совпадение. \en Generate name by other name, full coincidence. + virtual void CompileEdgeName( MbTopologyItem & item, const MbName & other ) const; +public: /// \ru Добавить генератор имен. \en Add name generator. - void AddSNameMaker( const MbSNameMaker & other ); + void AddSNameMaker( const MbSNameMaker & other ) { *this += other; } /// \ru Создать с именем индекса iFrom. \en Create with name of iForm index. - MbSNameMaker * GetSNameMakerFrom( size_t iFrom, size_t iTo ); + MbSNameMaker * GetSNameMakerFrom( size_t iFrom, size_t iTo ); #ifdef ORIGINAL_MAIN_NAME /// \ru Получить генератор имен оригинала, считая, что это именователь копии. \en Get original name maker. - MbSNameMaker GetOriginalSNameMaker() const; + MbSNameMaker GetOriginalSNameMaker() const; #endif // ORIGINAL_MAIN_NAME /// \ru Являются ли объекты равными? \en Determine whether an object is equal? - bool IsSame( const MbSNameMaker & ) const; + bool IsSame( const MbSNameMaker & ) const; protected: /// \ru Оператор чтения. \en Read operator. @@ -1117,39 +1310,10 @@ private: //---------------------------------------------------------------------------------------- -// \ru Конструктор. \en Constructor. -//--- -inline MbSNameMaker::MbSNameMaker ( SimpleName _mainName, // =-1 - MbSNameMaker::ESides _sideAdd, // = 0 - SimpleName _buttAdd ) // = 0 - : SimpleNameArray( 0, 2 ) - , MbNameMaker ( _mainName ) - , sideAdd ( _sideAdd ) - , buttAdd ( _buttAdd ) - , cpyHist ( true ) - , addParentNamesAttributes( false ) -{} - - -//---------------------------------------------------------------------------------------- -// \ru Конструктор по другому именователю. \en Constructor by other name-maker. +// \ru Инициализировать по другому именователю. \en Initialize by another name-maker. // --- -inline MbSNameMaker::MbSNameMaker ( const MbSNameMaker & other ) - : SimpleNameArray( other.Count(), 2 ) - , MbNameMaker ( other ) - , sideAdd ( i_SideNone ) - , buttAdd ( 0 ) - , cpyHist ( true ) - , addParentNamesAttributes( false ) -{ - SetName( other, true ); -} - - -//---------------------------------------------------------------------------------------- -// \ru Установить имя \en Set name -// --- -inline void MbSNameMaker::SetName( const MbSNameMaker & other, bool setVersion ) +inline +void MbSNameMaker::SetNameMaker( const MbSNameMaker & other, bool setVersion ) { SetMainName( other.GetMainName() ); SimpleNameArray::operator = ( (SimpleNameArray&)other ); @@ -1186,7 +1350,8 @@ inline void MbSNameMaker::SetName( const MbSNameMaker & other, bool setVersion ) +1, if n1 > n2; \n \~ \ingroup Names */ -inline MATH_FUNC (int) MbMemDefNameCompare( const MbName & n1, const MbName & n2 ) +inline +MATH_FUNC (int) MbMemDefNameCompare( const MbName & n1, const MbName & n2 ) { int res = -1; size_t count1 = n1.defNames.CountAll(); @@ -1213,28 +1378,27 @@ inline MATH_FUNC (int) MbMemDefNameCompare( const MbName & n1, const MbName & n2 \ingroup Names */ // --- -inline MATH_FUNC (int) MbDefNameCompare( const MbName & n1, const MbName & n2 ) -{ - SimpleName hash1 = n1.defNames.Hash(); - SimpleName hash2 = n2.defNames.Hash(); - return ::SimpleNameCompare( hash1, hash2 ); +inline +MATH_FUNC (int) MbDefNameCompare( const MbName & n1, const MbName & n2 ) { + return ::SimpleNameCompare( n1.defNames.Hash(), n2.defNames.Hash() ); } //---------------------------------------------------------------------------------------- // \ru часто встречающаяся комбинация - hash от SArray'a \en Frequently occurring combination - hash of SArray // --- -inline SimpleName Hash32( const SArray & arr ) { - return Hash32( (uint8*)arr.GetAddr(), arr.Count() * sizeofSimpleName ); +inline +SimpleName Hash32( const SArray & arr ) { + return c3d::Hash32( (uint8*)arr.GetAddr(), arr.size() * sizeofSimpleName ); } //---------------------------------------------------------------------------------------- // \ru Расчет модификатора имени примитива \en Calculate of modifier of name of primitive // --- -inline SimpleName CalcNameModifier( const MbName & name, const SArray & path ) -{ - return ( (path.Count()) ? ::Hash32SN(name.Hash(), Hash32(path)) : name.Hash() ); +inline +SimpleName CalcNameModifier( const MbName & name, const SArray & path ) { + return ( path.size() ? c3d::Hash32SN( name.Hash(), ::Hash32(path) ) : name.Hash() ); } @@ -1248,8 +1412,18 @@ inline SimpleName CalcNameModifier( const MbName & name, const SArray & path1, - const SArray & path2 ); +template +bool IsEqualPaths( const SimpleNameVector & path1, const SimpleNameVector & path2 ) +{ + const size_t cnt = path1.size(); + if ( cnt != path2.size() ) + return false; + for ( size_t k = 0; k < cnt; ++k ) { + if ( path1[k] != path2[k] ) + return false; + } + return true; +} ////////////////////////////////////////////////////////////////////////////////////////// @@ -1266,20 +1440,21 @@ MATH_FUNC (bool) IsEqualPaths( const SArray & path1, \en Path as array of identifiers (path to component from upper component). \n \~ \ingroup Names */ // --- -class MbPath : public SArray -{ +class MbPath : public SArray { public: /// \ru Конструктор. \en Constructor. MbPath() : SArray ( 0, 1 ) {} /// \ru Конструктор копирования. \en Copy-constructor. MbPath( const MbPath & other ) : SArray ( other ) {} + /// \ru Вычислить хэш себя. \en Calculate hash of itself. - SimpleName Hash() const; + SimpleName Hash() const { return ::Hash32( *this ); } /// \ru Оператор равенства. \en An equality operator. - bool operator == ( const MbPath & ) const; - bool operator != ( const MbPath & ) const; + bool operator == ( const MbPath & other ) const { return IsEqualPaths( *this, other ); } + /// \ru Оператор неравенства. \en An inequality operator. + bool operator != ( const MbPath & other ) const { return !IsEqualPaths( *this, other ); } /// \ru Оператор присваивания. \en An assignment operator. - MbPath & operator = ( const MbPath & o ) { SArray::operator = (o); return *this; } + MbPath & operator = ( const MbPath & p ) { SArray::operator = (p); return *this; } /// \ru Оператор чтения. \en Read operator. friend reader & operator >> ( reader & in, MbPath & ref ); /// \ru Оператор записи. \en Write operator. @@ -1290,18 +1465,20 @@ public: //---------------------------------------------------------------------------------------- -// \ru чтение \en Reading +// \ru Чтение. \en Reading. // --- -inline reader & operator >> ( reader & in, MbPath & ref ) +inline +reader & operator >> ( reader & in, MbPath & ref ) { size_t count = ReadCOUNT( in, true/*uint_val*/ ); + if ( in.good() && count ) { ref.SetSize( count, true/*clear*/ ); if ( (ref.GetAddr() == NULL) && (count >= SYS_MAX_UINT32) ) // We could not allocate the required amount of memory in.setState( io::outOfMemory ); else { - for ( size_t i = 0; i < count && in.good(); i++ ) { + for ( size_t i = 0; i < count && in.good(); ++i ) { SimpleName item = ReadSimpleName( in ); ref.Add( item ); } @@ -1312,39 +1489,19 @@ inline reader & operator >> ( reader & in, MbPath & ref ) //---------------------------------------------------------------------------------------- -// \ru Запись \en Writing +// \ru Запись. \en Writing. // --- -inline writer & operator << ( writer & out, const MbPath & ref ) +inline +writer & operator << ( writer & out, const MbPath & ref ) { - size_t count = ref.Count(); - WriteCOUNT( out, count ); - for( size_t i = 0; i < count && out.good(); i++ ) - WriteSimpleName( out, (SimpleName&)ref[i] ); - + size_t count = ref.size(); + ::WriteCOUNT( out, count ); + + for( size_t i = 0; i < count && out.good(); ++i ) + ::WriteSimpleName( out, (SimpleName&)ref[i] ); + return out; } -//---------------------------------------------------------------------------------------- -// -// --- -inline SimpleName MbPath::Hash() const -{ - return ::Hash32( *this ); -} - - -//---------------------------------------------------------------------------------------- -// \ru Сравнить пути в виде массива идентификаторов \en Compare paths as array of identifiers -// --- -inline bool MbPath::operator == ( const MbPath & other ) const -{ - return IsEqualPaths( *this, other ); -} -inline bool MbPath::operator != ( const MbPath & other ) const -{ - return !IsEqualPaths( *this, other ); -} - - #endif // __NAME_ITEM_H diff --git a/C3d/Include/op_binding_data.h b/C3d/Include/op_binding_data.h index 044a3fd..53b8ec4 100644 --- a/C3d/Include/op_binding_data.h +++ b/C3d/Include/op_binding_data.h @@ -47,14 +47,14 @@ public: MbItemIndex() : itemIndex( SYS_MAX_T ) , point ( -DETERMINANT_MAX, -DETERMINANT_MAX, -DETERMINANT_MAX ) - , itemName ( SIMPLENAME_MAX ) + , itemName ( c3d::SIMPLENAME_MAX ) {} /// \ru Конструктор по индексу без точки привязки. \en Constructor by the index without anchor point. explicit MbItemIndex( size_t i ) : itemIndex( i ) , point ( -DETERMINANT_MAX, -DETERMINANT_MAX, -DETERMINANT_MAX ) - , itemName ( SIMPLENAME_MAX ) + , itemName ( c3d::SIMPLENAME_MAX ) {} /// \ru Конструктор по индексу с точкой привязки. \en Constructor by the index with anchor point. @@ -92,7 +92,7 @@ public: itemIndex = ind; if ( reset ) { point.Init( -DETERMINANT_MAX, -DETERMINANT_MAX, -DETERMINANT_MAX ); - itemName = SIMPLENAME_MAX; + itemName = c3d::SIMPLENAME_MAX; } } /// \ru Функция инициализации. \en Initialization function. @@ -294,7 +294,7 @@ public: , facePIndex( SYS_MAX_T ) , faceMIndex( SYS_MAX_T ) , point ( -DETERMINANT_MAX, -DETERMINANT_MAX, -DETERMINANT_MAX ) - , itemName ( SIMPLENAME_MAX ) + , itemName ( c3d::SIMPLENAME_MAX ) {} /// \ru Конструктор копирования. \en Copy-constructor. @@ -352,6 +352,78 @@ public: }; +//------------------------------------------------------------------------------ +/** \brief \ru Индекс грани и её ребер. + \en Index of face and it edges. \~ + \details \ru Индекс грани и её ребер, предназначенных для обработки. Индекс служит для поиска грани и ребра в оболочке. + Поиск объекта производится по имени, в случае неудаче - по номеру, и проверяется по контрольной точке \n + \en Index of face and it edges for process. Index is used to search for face end edges in the shell. + The face searching is performed by name. In failure case - by index, and checked by the control point \n \~ + \ingroup Build_Parameters +*/ +// --- +struct MATH_CLASS MbExtendedIndex { +public: + MbItemIndex faceIndex; ///< \ru Идентификаторы граней в оболочке. \en Identifier of a shell face. + std::vector edgeIndices; ///< \ru Идентификаторы обрабатываемых ребер грани. \en Identifiers of processed edges of the face. + +public: + /// \ru Конструктор. \en Constructor. + MbExtendedIndex( const MbItemIndex & fIndex, const std::vector & eIndices ) + : faceIndex( fIndex ) + , edgeIndices( eIndices ) + {} + /// \ru Конструктор копирования. \en Copy-constructor. + MbExtendedIndex( const MbExtendedIndex & other ) + : faceIndex( other.faceIndex ) + , edgeIndices( other.edgeIndices ) + {} + /// \ru Деструктор. \en Destructor. + ~MbExtendedIndex() {} + +public: + /// \ru Функция инициализации. \en Initialization function. + void Init( const MbExtendedIndex & other ) { + faceIndex = other.faceIndex; + edgeIndices.clear(); + edgeIndices = other.edgeIndices; + } + /// \ru Оператор присваивания. \en Assignment operator. + MbExtendedIndex & operator = ( const MbExtendedIndex & other ) { + Init( other ); + return *this; + } + + /// \ru Получить индекс грани. \en Get face index. + MbItemIndex & FaceIndex() { return faceIndex; } + const MbItemIndex & GetFaceIndex() const { return faceIndex; } + /// \ru Получить индексы рёбер грани. \en Get edges indices. + std::vector & EdgeIndices() { return edgeIndices; } + const std::vector & GetEdgeIndices() const { return edgeIndices; } + + /// \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. + void Transform( const MbMatrix3D & matr ); + /// \ru Сдвинуть объект вдоль вектора. \en Move an object along a vector. + void Move ( const MbVector3D & to ); + /// \ru Повернуть объект вокруг оси на заданный угол. \en Rotate an object at a given angle around an axis. + void Rotate ( const MbAxis3D & axis, double ang ); + /// \ru Являются ли объекты равными? \en Determine whether an object is equal? + bool IsSame( const MbExtendedIndex & other, double accuracy ) const; + + /// \ru Функция чтения. \en Read function. + friend MATH_FUNC (reader &) operator >> ( reader & in, MbExtendedIndex & ref ); + /// \ru Функция записи. \en Write function. + friend MATH_FUNC (writer &) operator << ( writer & out, const MbExtendedIndex & ref ); + /// \ru Функция записи. \en Write function. + friend MATH_FUNC (writer &) operator << ( writer & out, MbExtendedIndex & ref ) { + return operator << ( out,(const MbExtendedIndex &)ref ); + } + + DECLARE_NEW_DELETE_CLASS( MbExtendedIndex ) + DECLARE_NEW_DELETE_CLASS_EX( MbExtendedIndex ) +}; + + //////////////////////////////////////////////////////////////////////////////// // // \ru Неклассные функции. \en Out-of-class functions. diff --git a/C3d/Include/op_boolean_flags.h b/C3d/Include/op_boolean_flags.h index dc5cdaf..dce54ea 100644 --- a/C3d/Include/op_boolean_flags.h +++ b/C3d/Include/op_boolean_flags.h @@ -35,8 +35,11 @@ public: public: bool MergeFaces() const { return mergeFaces; } ///< \ru Сливать подобные грани (true). \en Whether to merge similar faces (true). bool MergeEdges() const { return mergeEdges; } ///< \ru Сливать подобные ребра (true). \en Whether to merge similar edges (true). + void SetMergingFaces( bool mf ) { mergeFaces = mf; } ///< \ru Сливать подобные грани (true). \en Whether to merge similar faces (true). + void SetMergingEdges( bool me ) { mergeEdges = me; } ///< \ru Сливать подобные ребра (true). \en Whether to merge similar edges (true). public: - MbMergingFlags & operator = ( const MbMergingFlags & f ) { mergeFaces = f.mergeFaces; mergeEdges = f.mergeEdges; return *this; } + MbMergingFlags & operator = ( const MbMergingFlags & f ) { mergeFaces = f.mergeFaces; mergeEdges = f.mergeEdges; return *this; } + bool operator == ( const MbMergingFlags & f ) const { return (mergeFaces == f.mergeFaces && mergeEdges == f.mergeEdges); } }; diff --git a/C3d/Include/op_duplication_parameter.h b/C3d/Include/op_duplication_parameter.h index a8413e2..440a802 100644 --- a/C3d/Include/op_duplication_parameter.h +++ b/C3d/Include/op_duplication_parameter.h @@ -1,416 +1,416 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Параметры размножения. - \en Parameters of duplication. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __OP_DUPLICATION_PARAMETERS_H -#define __OP_DUPLICATION_PARAMETERS_H - -#include -#include -#include -#include - - -class MATH_CLASS MbAxis3D; -class MbRegTransform; -class MbRegDuplicate; - - -//------------------------------------------------------------------------------ -/** \brief \ru Типы параметров размножения. - \en Types of parameters of duplication. \~ - \details \ru dt_Grid - Копии располагаются в узлах декартовой сетки, заданной двумя направлениями, - шагами и количеством шагов по каждому направлению, а также сдвигом относительно исходного положения.\n - Исходное тело находится в центре сетки.\n - O---O---O \n - / / / \n - O---O---O \n - / / / \n - [O]--O---O \n - dt_Polar - Копии располагаются в узлах полярной сетки, заданной вектором начального луча, - вектором оси вращения, шагом по лучу, углом поворота, числом шагов по лучу и угловых шагов, - а также сдвигом относительно исходного положения.\n - Исходное тело находится в центре сетки.\n - O O \n - \ / \n - O O \n - \ / \n - O--O-[O]-O--O \n - dt_Matrix - Параметры разложения - массив матриц. Количество копий равно количеству матриц. - Каждая копия получается из исходного тела трансформацией соответствующей матрицей. \n - \en Dt_Grid - Copies locate in the nodes of the Cartesian grid, that define by two directions, - steps and numbers of steps along of each directions, and also by a shift from the initial position.\n - The original solid locate in the center of the grid.\n - O---O---O \n - / / / \n - O---O---O \n - / / / \n - [O]--O---O \n - dt_Polar - Copies locate in the nodes of the polar grid, that define by directions of the initial ray and the axis of rotation, - steps on the ray, angle of rotation and number of steps on the ray and angular step, - and also by a shift from the initial position.\n - The original solid locate in the center of the grid.\n - O O \n - \ / \n - O O \n - \ / \n - O--O-[O]-O--O \n - dt_Matrix - Parameters of duplication is array of matrices. Number of copies equal to number of matrix. - Each copy is obtained by transformation of corresponding matrix. \n \~ - \ingroup Model_Creators - */ -// --- -enum MbeDuplicatesType -{ - dt_Grid = 0 , ///< \ru Копии располагаются в узлах декартовой сетки. \en Copies locate in nodes of Cartesian grid. - dt_Polar = 1 , ///< \ru Копии располагаются в узлах полярной сетки.\en Copies locate in nodes of a polar grid. - dt_Matrix = 2 , ///< \ru Копии трансформируются матрицами. \en Copies are transformed by matrices. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Абстрактный класс параметров размножения. - \en Abstract class of duplication parameters. \~ - \details \ru Родительский класс для всех видов параметров размножения. \n - \en Parent class for all types of parameters of duplication. \n \~ - \ingroup Build_Parameters -*/ -// --- -class MATH_CLASS DuplicationValues -{ -protected: - /** \brief \ru Конструктор по умолчанию. - \en Default constructor. \~ - */ - DuplicationValues() {} - -public: - /** \brief \ru Деструктор. - \en Destructor. \~ - */ - virtual ~DuplicationValues() {} - - /** \brief \ru Функция копирования. - \en Copy function. \~ - */ - virtual bool Init( const DuplicationValues & ) = 0; - - /** \brief \ru Тип параметров. - \en Type of parameters \~ - \details \ru Возвращает тип параметров размножения. \n - \en Return type of parameters of duplication. \n \~ - */ - virtual MbeDuplicatesType Type() const = 0; - - /** \brief \ru Преобразовать параметры согласно матрице. - \en Transform parameters according to the matrix. \~ - */ - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ) = 0; - - /** \brief \ru Сдвинуть параметры вдоль вектора. - \en Move parameters along a vector. \~ - \details \ru Сдвинуть параметры вдоль вектора. - \en Move parameters along a vector. \n \~ - */ - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ) = 0; - - /** \brief \ru Повернуть параметры вокруг оси на заданный угол. - \en Rotate parameters at a given angle around an axis. \~ - \details \ru Повернуть параметры вокруг оси на заданный угол. - \en Rotate parameters at a given angle around an axis. \n \~ - */ - virtual void Rotate ( const MbAxis3D &, double ang, MbRegTransform * = NULL ) = 0; - - /** \brief \ru Выдать свойства объекта. - \en Get properties of the object. \~ - \details \ru Выдать свойства объекта. \n - \en Get properties of the object. \n \~ - */ - virtual void GetProperties( MbProperties & ) = 0; - - /** \brief \ru Записать свойства объекта. - \en Set properties of the object. \~ - \details \ru Записать свойства объекта. \n - \en Set properties of the object. \n \~ - */ - virtual void SetProperties( const MbProperties & ) = 0; - - /** \brief \ru Являются ли объекты равными? - \en Determine whether an object is equal? \~ - \details \ru Являются ли объекты равными? \n - \en Determine whether an object is equal? \n \~ - */ - virtual bool IsSame( const DuplicationValues &, double accuracy ) const = 0; - - /** \brief \ru Построить копию объекта. - \en Create a copy of the object. \~ - \details \ru Построить копию объекта. \n - \en Create a copy of the object. \n \~ - */ - virtual DuplicationValues & Duplicate( MbRegDuplicate * = NULL ) const = 0; - - /** \brief \ru Сгенерировать матрицы трансформаций. - \en Generate matrices of transformations. \~ - \details \ru Сгенерировать матрицы трансформаций согласно параметрам. \n - \en Generate matrices of transformations according to parameters. \n \~ - */ - virtual void GenerateTransformMatrices( SArray & ) const = 0; - - /** \brief \ru Количество создаваемых копий. - \en Number of of copies. \~ - \details \ru Количество создаваемых копий. \n - \en Number of of copies. \n \~ - */ - virtual size_t Count() const = 0; - -OBVIOUS_PRIVATE_COPY( DuplicationValues ) -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Размножение по сетке. - \en Duplication by grid. \~ - \details \ru Параметры размножение по размножение по декартовой сетке или по полярной сетке.\n - Тип сетки определяется флагом 'isPolar':\n - false - dt_Grid, true - dt_Polar.\n - Исходное тело находится в центре сетки. \n - Вектор 'axis1' задает одно из направлений декартовой сетки или направление луча в полярной сетке. \n - Вектор 'axis2' задает другое направление декартовой сетки или ось вращения в полярной сетке, - точка оси не важна т.к. поворачиваются только вектора. \n - 'step1' и step2' задают шаги по направлениям декартовой сетки. \n - 'step1' - задает шаг по лучу полярной сетки, 'step2' задает угол поворота. \n - 'num1' и 'num2' задают кол-во шагов 'step1' и 'step2' соответственно. \n - Ориентация тел на сетке определяется флагом 'isAlongAxis'. \n - Если флаг 'isAlongAxis' = true, то тела ориентированы вдоль радиальной оси, false - параллельно исходному телу. \n - 'center' задает центр локальной системы координат. \n - ВАЖНО! У векторов 'axis1', 'axis2' учитываются только направления, при инициализации или изменении происходит нормирование. \n - \en Parameters of duplication by Cartesian grid or polar grid.\n - Type of grid is determined by flag 'isPolar':\n - false - dt_Grid, true - dt_Polar.\n - The original solid locate in the center of the grid. \n - Vector 'axis1' define one of the directions of the Cartesian grid or direction of the ray in the polar grid. \n - Vector 'axis2' define other directions of the Cartesian grid or the axis of rotation in the polar grid, - point of axis doesn't matter because only vector will be rotated. - 'step1' and 'step2' define steps along the directions. \n - 'step1' define 'step' along the ray of polar grid, step2 define angle of rotation. \n - 'num1 and 'num2' define number of steps 'step1' and 'step2'. \n - Orientation of the bodies on the grid is controlled by 'isAlongAxis' flag. \n - If 'isAlongAxis' = true, then bodies are oriented along radial axis, false - parallel to the origin body. \n - 'center' determines the origin of local coordinate system. \n \~ - \ingroup Build_Parameters -*/ -// --- -class MATH_CLASS DuplicationMeshValues: public DuplicationValues -{ -protected: - MbVector3D axis1; ///< \ru Направление размножения (dt_Grid и dt_Polar). \en Direction of duplication (dt_Grid and dt_Polar). - MbVector3D axis2; ///< \ru Направление размножения (dt_Grid), направление оси вращения (dt_Polar). \en Direction of duplication (dt_Grid), direction of axis of rotation (dt_Polar). - double step1; ///< \ru Шаг по направлению axis1 (dt_Grid и dt_Polar). \en Step along of axis1 (dt_Grid and dt_Polar). - double step2; ///< \ru Шаг по направлению axis2 (dt_Grid), угол поворота (dt_Polar). \en Step along of axis1 (dt_Grid), angle of rotation (dt_Polar). - uint num1; ///< \ru Кол-во шагов по направлению axis1 (dt_Grid и dt_Polar). \en Number of steps along of axis1 (dt_Grid and dt_Polar). - uint num2; ///< \ru Кол-во шагов по направлению axis2 (dt_Grid), кол-во угловых шагов (dt_Polar). \en Number of steps along of axis2 (dt_Grid), number of angular steps (dt_Polar). - bool isPolar; ///< \ru Тип сетки, false - dt_Grid, true - dt_Polar. Type of grid, false - dt_Grid, true - dt_Polar. \en . - bool isAlongAxis; ///< \ru true - тела расположены вдоль радиальной оси, false - параллельно исходному телу. \en true - along polar axis, false - parallel to initial body. - MbCartPoint3D center; ///< \ru Центр локальной системы координат (и точка приложения оси вращения в случае полярной системы). \en Origin of the coordinate system. -public: - /** \brief \ru Конструктор по типу. - \en Constructor by a type. \~ - \details \ru Конструктор размножения по сетке.\n - Вектора и значения инициализируются нулевыми, тип сетки инициализируется параметром, по умолчанию 'false'. \n - \en Constructor of duplication by grid. \n - Vectors and values are initialized to zero, type of grid is initialized by parameter, default 'false'. \n \~ - */ - DuplicationMeshValues( bool polar = false ); - - /** \brief \ru Конструктор по параметрам и типу. - \en Constructor by parameters and a type. \~ - \details \ru Конструктор размножения по сетке. \n - Тип сетки, направления осей, шаги и кол-во шагов инициализируются параметрами. \n - \en Constructor of duplication by grid. \n - Type of grid, vectors, steps and numbers of steps are initialized to parameters. \n \~ - */ - DuplicationMeshValues( bool isPolar, const MbVector3D & dir1, const double step1, const unsigned int num1, - const MbVector3D & dir2, const double step2, const unsigned int num2, - const MbCartPoint3D * center = NULL, bool isAlongAxis = false ); - - /// \ru Деструктор. \en Destructor. - virtual ~DuplicationMeshValues(); - - /// \ru Функция копирования. \en Copy function. - void Init( const DuplicationMeshValues & other ); - /// \ru Функция копирования. \en Copy function. - virtual bool Init( const DuplicationValues & other ); - /// \ru Тип параметров. \en Type of parameters. - virtual MbeDuplicatesType Type() const; - /// \ru Преобразовать сетку согласно матрице. \en Transform grid according to the matrix. - virtual void Transform( const MbMatrix3D &, MbRegTransform * ireg = NULL ); - /// \ru Сдвинуть сетку вдоль вектора. \en Move grid along a vector. - virtual void Move ( const MbVector3D &, MbRegTransform * ireg = NULL ); - /// \ru Повернуть сетку вокруг оси на заданный угол. \en Rotate grid at a given angle around an axis. - virtual void Rotate ( const MbAxis3D &, double ang, MbRegTransform * ireg = NULL ); - - /// \ru Выдать свойства объекта \en Get properties of the object - virtual void GetProperties( MbProperties & ); - /// \ru Записать свойства объекта \en Set properties of the object - virtual void SetProperties( const MbProperties & ); - - // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSame( const DuplicationValues &, double accuracy ) const; - - /// \ru Построить копию объекта. \en Create a copy of the object. - virtual DuplicationValues & Duplicate( MbRegDuplicate * ireg = NULL ) const; - - /// \ru Сгенерировать матрицы трансформации. \en Generate matrix of transformation by. - virtual void GenerateTransformMatrices( SArray & tfMatr ) const; - - /// \ru Количество создаваемых копий. \en Number of copies. - virtual size_t Count() const; - - /** \brief \ru Установить одно из направлений сетки. - \en Set one of the directions of grid. \~ - \details \ru Установить одно из направлений сетки. \n - \en Set one of the directions of grid. \n \~ - */ - void SetDirection( bool first, const MbVector3D & dir ); - - /** \brief \ru Получить одно из направлений сетки. - \en Get one of the directions of grid. \~ - \details \ru Получить одно из направлений сетки. \n - \en Get one of the directions of grid. \n \~ - */ - void GetDirection( bool first, MbVector3D & dir ) const; - - /** \brief \ru Установить шаг по одному из направлений сетки. - \en Set the step along of one of the directions of grid. \~ - \details \ru Установить шаг по одному из направлений сетки. \n - \en Set the step along of one of the directions of grid. \n \~ - */ - void SetStep( bool first, const double step ); - - /** \brief \ru Получить шаг по одному из направлений сетки. - \en Get the step along of one of the directions of grid. \~ - \details \ru Получить шаг по одному из направлений сетки. \n - \en Get the step along of one of the directions of grid. \n \~ - */ - void GetStep( bool first, double & step ) const; - - /** \brief \ru Установить количество шагов по одному из направлений сетки. - \en Set the number of steps along of one of the directions of grid. \~ - \details \ru Установить количество шагов по одному из направлений сетки. \n - \en Set the number of steps along of one of the directions of grid. \n \~ - */ - void SetNumStep( bool first, const uint num ); - - /** \brief \ru Полярная ли сетка? - \en Is mesh polar? \~ - \details \ru Задана ли сетка в полярной системе координат? \n - \en Is local system of mesh polar? \n \~ - */ - bool IsPolar() const { return isPolar; } - - /** \brief \ru Задать тип сетки. - \en Set mesh type. \~ - \details \ru Задать сетку в полярной или декартовой системе координат. \n - \en Set local system of mesh. \n \~ - */ - void SetPolar( bool p ) { isPolar = p; } - - /** \brief \ru Получить количество шагов по одному из направлений сетки. - \en Get the number of steps along of one of the directions of grid. \~ - \details \ru Получить количество шагов по одному из направлений сетки. \n - \en Get the number of steps along of one of the directions of grid. \n \~ - */ - void GetNumStep( bool first, uint & num ) const; - - /** \brief \ru Тело вдоль радиальной оси? - \en Is body along radial axis? \~ - \details \ru Тело вдоль радиальной оси? \n - \en Is body along radial axis? \n \~ - */ - bool IsAlongAxis() const { return isAlongAxis; } - - /** \brief \ru Вернуть центр полярной системы. - \en Return center of the polar system. \~ - \details \ru Вернуть центр полярной системы. \n - \en Return center of the polar system. \n \~ - */ - - MbCartPoint3D GetCenter() const { return center; } - - /** \brief \ru Установить центр полярной системы. - \en Set center of the polar system. \~ - \details \ru Установить центр полярной системы. \n - \en Set center of the polar system. \n \~ - */ - - void SetCenter( MbCartPoint3D & cntr ) { center = cntr; } - -KNOWN_OBJECTS_RW_REF_OPERATORS( DuplicationMeshValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. -OBVIOUS_PRIVATE_COPY( DuplicationMeshValues ) -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Размножение матрицами. - \en Duplication by matrices. \~ - \details \ru Размножение задается набором матриц трансформаций. Каждая копия это трансформация оригинального тела соответствующей матрицей. \n - \en Duplication is defined by set of transform matrices. Each copy is a transformation of original solid by corresponding matrix. \n \~ - \ingroup Build_Parameters -*/ -// --- -class MATH_CLASS DuplicationMatrixValues: public DuplicationValues -{ -public: - SArray matrices; -public: - /// \ru Конструктор по умолчанию. \en Default constructor. - DuplicationMatrixValues(); - /// \ru Конструктор по матрице. \en Constructor by matrix. - DuplicationMatrixValues( const MbMatrix3D & matr ); - /// \ru Конструктор по набору матриц. \en Constructor by set of matrices. - DuplicationMatrixValues( const SArray & matr ); - - /// \ru Деструктор. \en Destructor. - virtual ~DuplicationMatrixValues(); - - /// \ru Функция копирования. \en Copy function. - void Init( const DuplicationMatrixValues & other ); - /// \ru Функция копирования. \en Copy function. - virtual bool Init( const DuplicationValues & other ); - /// \ru Тип параметров. \en Type of parameters. - virtual MbeDuplicatesType Type() const; - /// \ru Преобразовать объект согласно матрице. \en Transform an object according to the matrix. - virtual void Transform( const MbMatrix3D &, MbRegTransform * ireg = NULL ); - /// \ru Сдвинуть объект вдоль вектора. \en Move an object along a vector. - virtual void Move ( const MbVector3D &, MbRegTransform * ireg = NULL ); - /// \ru Повернуть объект вокруг оси на заданный угол. \en Rotate an object at a given angle around an axis. - virtual void Rotate ( const MbAxis3D &, double ang, MbRegTransform * ireg = NULL ); - - /// \ru Выдать свойства объекта \en Get properties of the object - virtual void GetProperties( MbProperties & ); - /// \ru Записать свойства объекта \en Set properties of the object - virtual void SetProperties( const MbProperties & ); - - // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSame( const DuplicationValues &, double accuracy ) const; - - /// \ru Построить копию объекта. \en Create a copy of the object. - virtual DuplicationValues & Duplicate( MbRegDuplicate * ireg = NULL ) const; - - /// \ru Сгенерировать матрицы трансформации. \en Generate matrix of transformation. - virtual void GenerateTransformMatrices( SArray & tfMatr ) const; - - /// \ru Количество создаваемых копий. \en Number of copies. - virtual size_t Count() const; - -KNOWN_OBJECTS_RW_REF_OPERATORS( DuplicationMatrixValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. -OBVIOUS_PRIVATE_COPY( DuplicationMatrixValues ) -}; - - +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Параметры копирования. + \en Parameters of duplication. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __OP_DUPLICATION_PARAMETERS_H +#define __OP_DUPLICATION_PARAMETERS_H + +#include +#include +#include +#include + + +class MATH_CLASS MbAxis3D; +class MbRegTransform; +class MbRegDuplicate; + + +//------------------------------------------------------------------------------ +/** \brief \ru Способы копирования. + \en Types of parameters of duplication. \~ + \details \ru dt_Grid - Копии располагаются в узлах декартовой сетки, заданной двумя направлениями, + шагами и количеством шагов по каждому направлению, а также сдвигом относительно исходного положения.\n + Исходное тело находится в центре сетки.\n + O---O---O \n + / / / \n + O---O---O \n + / / / \n + [O]--O---O \n + dt_Polar - Копии располагаются в узлах полярной сетки, заданной вектором начального луча, + вектором оси вращения, шагом по лучу, углом поворота, числом шагов по лучу и угловых шагов, + а также сдвигом относительно исходного положения.\n + Исходное тело находится в центре сетки.\n + O O \n + \ / \n + O O \n + \ / \n + O--O-[O]-O--O \n + dt_Matrix - Параметры копирования - массив матриц. Количество копий равно количеству матриц. + Каждая копия получается из исходного тела трансформацией соответствующей матрицей. \n + \en Dt_Grid - Copies locate in the nodes of the Cartesian grid, that define by two directions, + steps and numbers of steps along of each directions, and also by a shift from the initial position.\n + The original solid locate in the center of the grid.\n + O---O---O \n + / / / \n + O---O---O \n + / / / \n + [O]--O---O \n + dt_Polar - Copies locate in the nodes of the polar grid, that define by directions of the initial ray and the axis of rotation, + steps on the ray, angle of rotation and number of steps on the ray and angular step, + and also by a shift from the initial position.\n + The original solid locate in the center of the grid.\n + O O \n + \ / \n + O O \n + \ / \n + O--O-[O]-O--O \n + dt_Matrix - Parameters of duplication is array of matrices. Number of copies equal to number of matrix. + Each copy is obtained by transformation of corresponding matrix. \n \~ + \ingroup Model_Creators + */ +// --- +enum MbeDuplicatesType +{ + dt_Grid = 0 , ///< \ru Копии располагаются в узлах декартовой сетки. \en Copies locate in nodes of Cartesian grid. + dt_Polar = 1 , ///< \ru Копии располагаются в узлах полярной сетки.\en Copies locate in nodes of a polar grid. + dt_Matrix = 2 , ///< \ru Копии трансформируются матрицами. \en Copies are transformed by matrices. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Абстрактный класс параметров копирования. + \en Abstract class of duplication parameters. \~ + \details \ru Родительский класс для всех видов параметров копирования. \n + \en Parent class for all types of parameters of duplication. \n \~ + \ingroup Build_Parameters +*/ +// --- +class MATH_CLASS DuplicationValues +{ +protected: + /** \brief \ru Конструктор по умолчанию. + \en Default constructor. \~ + */ + DuplicationValues() {} + +public: + /** \brief \ru Деструктор. + \en Destructor. \~ + */ + virtual ~DuplicationValues() {} + + /** \brief \ru Функция копирования. + \en Copy function. \~ + */ + virtual bool Init( const DuplicationValues & ) = 0; + + /** \brief \ru Тип параметров. + \en Type of parameters \~ + \details \ru Возвращает способ копирования. \n + \en Return type of parameters of duplication. \n \~ + */ + virtual MbeDuplicatesType Type() const = 0; + + /** \brief \ru Преобразовать параметры согласно матрице. + \en Transform parameters according to the matrix. \~ + */ + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ) = 0; + + /** \brief \ru Сдвинуть параметры вдоль вектора. + \en Move parameters along a vector. \~ + \details \ru Сдвинуть параметры вдоль вектора. + \en Move parameters along a vector. \n \~ + */ + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ) = 0; + + /** \brief \ru Повернуть параметры вокруг оси на заданный угол. + \en Rotate parameters at a given angle around an axis. \~ + \details \ru Повернуть параметры вокруг оси на заданный угол. + \en Rotate parameters at a given angle around an axis. \n \~ + */ + virtual void Rotate ( const MbAxis3D &, double ang, MbRegTransform * = NULL ) = 0; + + /** \brief \ru Выдать свойства объекта. + \en Get properties of the object. \~ + \details \ru Выдать свойства объекта. \n + \en Get properties of the object. \n \~ + */ + virtual void GetProperties( MbProperties & ) = 0; + + /** \brief \ru Записать свойства объекта. + \en Set properties of the object. \~ + \details \ru Записать свойства объекта. \n + \en Set properties of the object. \n \~ + */ + virtual void SetProperties( const MbProperties & ) = 0; + + /** \brief \ru Являются ли объекты равными? + \en Determine whether an object is equal? \~ + \details \ru Являются ли объекты равными? \n + \en Determine whether an object is equal? \n \~ + */ + virtual bool IsSame( const DuplicationValues &, double accuracy ) const = 0; + + /** \brief \ru Построить копию объекта. + \en Create a copy of the object. \~ + \details \ru Построить копию объекта. \n + \en Create a copy of the object. \n \~ + */ + virtual DuplicationValues & Duplicate( MbRegDuplicate * = NULL ) const = 0; + + /** \brief \ru Сгенерировать матрицы трансформаций. + \en Generate matrices of transformations. \~ + \details \ru Сгенерировать матрицы трансформаций согласно параметрам. \n + \en Generate matrices of transformations according to parameters. \n \~ + */ + virtual void GenerateTransformMatrices( std::vector & ) const = 0; + + /** \brief \ru Количество создаваемых копий. + \en Number of of copies. \~ + \details \ru Количество создаваемых копий. \n + \en Number of of copies. \n \~ + */ + virtual size_t Count() const = 0; + +OBVIOUS_PRIVATE_COPY( DuplicationValues ) +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Копирование по сетке. + \en Duplication by grid. \~ + \details \ru Параметры копирования по декартовой сетке или по полярной сетке.\n + Тип сетки определяется флагом 'isPolar':\n + false - dt_Grid, true - dt_Polar.\n + Исходное тело находится в центре сетки. \n + Вектор 'axis1' задает одно из направлений декартовой сетки или направление луча в полярной сетке. \n + Вектор 'axis2' задает другое направление декартовой сетки или ось вращения в полярной сетке, + точка оси не важна т.к. поворачиваются только вектора. \n + 'step1' и step2' задают шаги по направлениям декартовой сетки. \n + 'step1' - задает шаг по лучу полярной сетки, 'step2' задает угол поворота. \n + 'num1' и 'num2' задают кол-во шагов 'step1' и 'step2' соответственно. \n + Ориентация тел на сетке определяется флагом 'isAlongAxis'. \n + Если флаг 'isAlongAxis' = true, то тела ориентированы вдоль радиальной оси, false - параллельно исходному телу. \n + 'center' задает центр локальной системы координат. \n + ВАЖНО! У векторов 'axis1', 'axis2' учитываются только направления, при инициализации или изменении происходит нормирование. \n + \en Parameters of duplication by Cartesian grid or polar grid.\n + Type of grid is determined by flag 'isPolar':\n + false - dt_Grid, true - dt_Polar.\n + The original solid locate in the center of the grid. \n + Vector 'axis1' define one of the directions of the Cartesian grid or direction of the ray in the polar grid. \n + Vector 'axis2' define other directions of the Cartesian grid or the axis of rotation in the polar grid, + point of axis doesn't matter because only vector will be rotated. + 'step1' and 'step2' define steps along the directions. \n + 'step1' define 'step' along the ray of polar grid, step2 define angle of rotation. \n + 'num1 and 'num2' define number of steps 'step1' and 'step2'. \n + Orientation of the bodies on the grid is controlled by 'isAlongAxis' flag. \n + If 'isAlongAxis' = true, then bodies are oriented along radial axis, false - parallel to the origin body. \n + 'center' determines the origin of local coordinate system. \n \~ + \ingroup Build_Parameters +*/ +// --- +class MATH_CLASS DuplicationMeshValues: public DuplicationValues +{ +protected: + MbVector3D axis1; ///< \ru Направление копирования (dt_Grid и dt_Polar). \en Direction of duplication (dt_Grid and dt_Polar). + MbVector3D axis2; ///< \ru Направление копирования (dt_Grid), направление оси вращения (dt_Polar). \en Direction of duplication (dt_Grid), direction of axis of rotation (dt_Polar). + double step1; ///< \ru Шаг по направлению axis1 (dt_Grid и dt_Polar). \en Step along of axis1 (dt_Grid and dt_Polar). + double step2; ///< \ru Шаг по направлению axis2 (dt_Grid), угол поворота (dt_Polar). \en Step along of axis1 (dt_Grid), angle of rotation (dt_Polar). + uint num1; ///< \ru Кол-во шагов по направлению axis1 (dt_Grid и dt_Polar). \en Number of steps along of axis1 (dt_Grid and dt_Polar). + uint num2; ///< \ru Кол-во шагов по направлению axis2 (dt_Grid), кол-во угловых шагов (dt_Polar). \en Number of steps along of axis2 (dt_Grid), number of angular steps (dt_Polar). + bool isPolar; ///< \ru Тип сетки, false - dt_Grid, true - dt_Polar. Type of grid, false - dt_Grid, true - dt_Polar. \en . + bool isAlongAxis; ///< \ru true - тела расположены вдоль радиальной оси, false - параллельно исходному телу. \en true - along polar axis, false - parallel to initial body. + MbCartPoint3D center; ///< \ru Центр локальной системы координат (и точка приложения оси вращения в случае полярной системы). \en Origin of the coordinate system. +public: + /** \brief \ru Конструктор по типу. + \en Constructor by a type. \~ + \details \ru Конструктор копирования по сетке.\n + Вектора и значения инициализируются нулевыми, тип сетки инициализируется параметром, по умолчанию 'false'. \n + \en Constructor of duplication by grid. \n + Vectors and values are initialized to zero, type of grid is initialized by parameter, default 'false'. \n \~ + */ + DuplicationMeshValues( bool polar = false ); + + /** \brief \ru Конструктор по параметрам и типу. + \en Constructor by parameters and a type. \~ + \details \ru Конструктор копирования по сетке. \n + Тип сетки, направления осей, шаги и кол-во шагов инициализируются параметрами. \n + \en Constructor of duplication by grid. \n + Type of grid, vectors, steps and numbers of steps are initialized to parameters. \n \~ + */ + DuplicationMeshValues( bool isPolar, const MbVector3D & dir1, const double step1, const uint num1, + const MbVector3D & dir2, const double step2, const uint num2, + const MbCartPoint3D * center = NULL, bool isAlongAxis = false ); + + /// \ru Деструктор. \en Destructor. + virtual ~DuplicationMeshValues(); + + /// \ru Функция копирования. \en Copy function. + void Init( const DuplicationMeshValues & ); + /// \ru Функция копирования. \en Copy function. + virtual bool Init( const DuplicationValues & ); + /// \ru Тип параметров. \en Type of parameters. + virtual MbeDuplicatesType Type() const; + /// \ru Преобразовать сетку согласно матрице. \en Transform grid according to the matrix. + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); + /// \ru Сдвинуть сетку вдоль вектора. \en Move grid along a vector. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); + /// \ru Повернуть сетку вокруг оси на заданный угол. \en Rotate grid at a given angle around an axis. + virtual void Rotate ( const MbAxis3D &, double ang, MbRegTransform * = NULL ); + + /// \ru Выдать свойства объекта \en Get properties of the object + virtual void GetProperties( MbProperties & ); + /// \ru Записать свойства объекта \en Set properties of the object + virtual void SetProperties( const MbProperties & ); + + // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSame( const DuplicationValues &, double accuracy ) const; + + /// \ru Построить копию объекта. \en Create a copy of the object. + virtual DuplicationValues & Duplicate( MbRegDuplicate * = NULL ) const; + + /// \ru Сгенерировать матрицы трансформации. \en Generate matrix of transformation by. + virtual void GenerateTransformMatrices( std::vector & ) const; + + /// \ru Количество создаваемых копий. \en Number of copies. + virtual size_t Count() const; + + /** \brief \ru Установить одно из направлений сетки. + \en Set one of the directions of grid. \~ + \details \ru Установить одно из направлений сетки. \n + \en Set one of the directions of grid. \n \~ + */ + void SetDirection( bool first, const MbVector3D & dir ); + + /** \brief \ru Получить одно из направлений сетки. + \en Get one of the directions of grid. \~ + \details \ru Получить одно из направлений сетки. \n + \en Get one of the directions of grid. \n \~ + */ + void GetDirection( bool first, MbVector3D & dir ) const; + + /** \brief \ru Установить шаг по одному из направлений сетки. + \en Set the step along of one of the directions of grid. \~ + \details \ru Установить шаг по одному из направлений сетки. \n + \en Set the step along of one of the directions of grid. \n \~ + */ + void SetStep( bool first, const double step ); + + /** \brief \ru Получить шаг по одному из направлений сетки. + \en Get the step along of one of the directions of grid. \~ + \details \ru Получить шаг по одному из направлений сетки. \n + \en Get the step along of one of the directions of grid. \n \~ + */ + void GetStep( bool first, double & step ) const; + + /** \brief \ru Установить количество шагов по одному из направлений сетки. + \en Set the number of steps along of one of the directions of grid. \~ + \details \ru Установить количество шагов по одному из направлений сетки. \n + \en Set the number of steps along of one of the directions of grid. \n \~ + */ + void SetNumStep( bool first, const uint num ); + + /** \brief \ru Полярная ли сетка? + \en Is mesh polar? \~ + \details \ru Задана ли сетка в полярной системе координат? \n + \en Is local system of mesh polar? \n \~ + */ + bool IsPolar() const { return isPolar; } + + /** \brief \ru Задать тип сетки. + \en Set mesh type. \~ + \details \ru Задать сетку в полярной или декартовой системе координат. \n + \en Set local system of mesh. \n \~ + */ + void SetPolar( bool p ) { isPolar = p; } + + /** \brief \ru Получить количество шагов по одному из направлений сетки. + \en Get the number of steps along of one of the directions of grid. \~ + \details \ru Получить количество шагов по одному из направлений сетки. \n + \en Get the number of steps along of one of the directions of grid. \n \~ + */ + void GetNumStep( bool first, uint & num ) const; + + /** \brief \ru Тело вдоль радиальной оси? + \en Is body along radial axis? \~ + \details \ru Тело вдоль радиальной оси? \n + \en Is body along radial axis? \n \~ + */ + bool IsAlongAxis() const { return isAlongAxis; } + + /** \brief \ru Вернуть центр полярной системы. + \en Return center of the polar system. \~ + \details \ru Вернуть центр полярной системы. \n + \en Return center of the polar system. \n \~ + */ + MbCartPoint3D GetCenter() const { return center; } + + /** \brief \ru Установить центр полярной системы. + \en Set center of the polar system. \~ + \details \ru Установить центр полярной системы. \n + \en Set center of the polar system. \n \~ + */ + void SetCenter( MbCartPoint3D & cntr ) { center = cntr; } + +KNOWN_OBJECTS_RW_REF_OPERATORS( DuplicationMeshValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. +OBVIOUS_PRIVATE_COPY( DuplicationMeshValues ) +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Копирование с трансформациями по матрицам. + \en Duplication by matrices. \~ + \details \ru Копирование задается набором матриц трансформаций. Каждая копия оригинального тела трансформируется соответствующей матрицей. \n + \en Duplication is defined by set of transform matrices. Each copy of original solid is transforming by corresponding matrix. \n \~ + \ingroup Build_Parameters +*/ +// --- +class MATH_CLASS DuplicationMatrixValues: public DuplicationValues +{ +public: + std::vector matrices; +public: + /// \ru Конструктор по умолчанию. \en Default constructor. + DuplicationMatrixValues(); + /// \ru Конструктор по матрице. \en Constructor by matrix. + DuplicationMatrixValues( const MbMatrix3D & ); + /// \ru Конструктор по набору матриц. \en Constructor by set of matrices. + DuplicationMatrixValues( const SArray & ); + /// \ru Конструктор по набору матриц. \en Constructor by set of matrices. + DuplicationMatrixValues( const std::vector & ); + + /// \ru Деструктор. \en Destructor. + virtual ~DuplicationMatrixValues(); + + /// \ru Функция копирования. \en Copy function. + void Init( const DuplicationMatrixValues & ); + /// \ru Функция копирования. \en Copy function. + virtual bool Init( const DuplicationValues & ); + /// \ru Тип параметров. \en Type of parameters. + virtual MbeDuplicatesType Type() const; + /// \ru Преобразовать объект согласно матрице. \en Transform an object according to the matrix. + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); + /// \ru Сдвинуть объект вдоль вектора. \en Move an object along a vector. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); + /// \ru Повернуть объект вокруг оси на заданный угол. \en Rotate an object at a given angle around an axis. + virtual void Rotate ( const MbAxis3D &, double ang, MbRegTransform * = NULL ); + + /// \ru Выдать свойства объекта \en Get properties of the object + virtual void GetProperties( MbProperties & ); + /// \ru Записать свойства объекта \en Set properties of the object + virtual void SetProperties( const MbProperties & ); + + // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSame( const DuplicationValues &, double accuracy ) const; + + /// \ru Построить копию объекта. \en Create a copy of the object. + virtual DuplicationValues & Duplicate( MbRegDuplicate * = NULL ) const; + + /// \ru Сгенерировать матрицы трансформации. \en Generate matrix of transformation. + virtual void GenerateTransformMatrices( std::vector & ) const; + + /// \ru Количество создаваемых копий. \en Number of copies. + virtual size_t Count() const; + +KNOWN_OBJECTS_RW_REF_OPERATORS( DuplicationMatrixValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. +OBVIOUS_PRIVATE_COPY( DuplicationMatrixValues ) +}; + + #endif // __OP_DUPLICATION_PARAMETERS_H \ No newline at end of file diff --git a/C3d/Include/op_shell_parameter.h b/C3d/Include/op_shell_parameter.h index acb493d..f682aac 100644 --- a/C3d/Include/op_shell_parameter.h +++ b/C3d/Include/op_shell_parameter.h @@ -813,6 +813,47 @@ public: }; +class MbPatchCurveMating; +//------------------------------------------------------------------------------ +/** \brief \ru Сопряжение по кривой заплатки. + \en Patch curve conjugation. \~ + \details \ru Сопряжение по кривой заплатки. \n + \en Patch curve conjugation. \n \~ + \ingroup Build_Parameters +*/ +// --- +class MATH_CLASS MbPatchMating { + +protected: + /// \ru Конструктор. \en Constructor. + MbPatchMating() {} + /// \ru Конструктор. \en Constructor. + MbPatchMating( const MbPatchMating & ) {} +public: + /// \ru Деструктор. \en Destructor. + virtual ~MbPatchMating() {} + +public: + /// \ru Установить тип сопряжения. \en Set conjugation type. + virtual void SetMate( MbePatchMatingType newType, const MbSurface * newSurface ) = 0; + /// \ru Установить тип сопряжения сегмента. \en Set conjugation type of segment. + virtual void SetMate( size_t segInd, MbePatchMatingType newType, const MbSurface * newSurface ) = 0; + + /// \ru Выдать тип сопряжения. \en Get the type of conjugation. + virtual MbePatchMatingType GetMatingType() const = 0; + /// \ru Выдать посверхность. \en Get surface. + virtual const MbSurface * GetSurface() const = 0; + + /// \ru Сопряжение для сегмента номер segInd. \en The conjugation by segment number segInd. + virtual MbPatchCurveMating & GetSegmentMate( size_t segInd ) = 0; + /// \ru Чье сопряжение. \en The conjugation owner type. + virtual MbeSpaceType GetOwnerType() = 0; + +private: + MbPatchMating & operator = ( const MbPatchMating & ); +}; + + //------------------------------------------------------------------------------ /** \brief \ru Параметры заплатки. \en The parameters of patch. \~ @@ -834,11 +875,14 @@ public: ts_tang, ///< \ru По касательной. \en Along the tangent. ts_norm, ///< \ru По нормали. \en Along the normal. ts_none, ///< \ru Не определено. \en Undefined. - ts_plane ///< \ru Плоская заплатка. \en Plane patch. + ts_plane, ///< \ru Плоская заплатка. \en Plane patch. + ts_byCurves, ///< \ru Построение задается сопряжениями по каждой кривой. \en The construction is defined by conjugations on curves. }; private: - SurfaceType type; ///< \ru Тип заплатки. \en Type of patch. - bool checkSelfInt; ///< \ru Флаг проверки самопересечений (вычислительно "тяжелыми" методами). \en Flag for checking of self-intersection (computationally by "heavy" methods). + SurfaceType type; ///< \ru Тип заплатки. \en Type of patch. + bool checkSelfInt; ///< \ru Флаг проверки самопересечений (вычислительно "тяжелыми" методами). \en Flag for checking of self-intersection (computationally by "heavy" methods). + bool mergeEdges; ///< \ru Сливать подобные ребра (true). \en Whether to merge similar edges (true). + std::vector> curvesMatings; ///< \ru Сопряжения по кривым. Параметр используется при type == ts_byCurves. \en The conjugation by curves. public: /** \brief \ru Конструктор по умолчанию. @@ -849,11 +893,14 @@ public: PatchValues() : type ( ts_none ) , checkSelfInt( false ) + , mergeEdges ( true ) {} /// \ru Конструктор копирования. \en Copy-constructor. PatchValues( const PatchValues & other ) - : type ( other.type ) - , checkSelfInt( other.checkSelfInt ) + : type ( other.type ) + , checkSelfInt ( other.checkSelfInt ) + , mergeEdges ( other.mergeEdges ) + , curvesMatings( other.curvesMatings ) {} /// \ru Деструктор. \en Destructor. ~PatchValues() @@ -868,10 +915,27 @@ public: bool CheckSelfInt() const { return checkSelfInt; } /// \ru Установить флаг проверки самопересечений. \en Set the flag of checking self-intersection. void SetCheckSelfInt( bool c ) { checkSelfInt = c; } + /// \ru Сливать подобные ребра (true)? \en Whether to merge similar edges (true)? + bool MergeEdges() const { return mergeEdges; } + /// \ru Сливать подобные ребра. \en Whether to merge similar edges. + void SetMergingEdges( bool s ) { mergeEdges = s; } + + /// \ru Установить сопряжение кривой номер cInd. \en Set mate for curve number cInd. + void SetCurveMating( size_t cInd, MbePatchMatingType curveMate, const MbSurface * surface ); + /// \ru Выдать тип сопряжения по кривой cInd. \en Get the type of conjugation on curve cInd. + MbePatchMatingType GetCurveMatingType( size_t cInd ) const; + /// \ru Выдать поверхность сопряжения по кривой cInd. \en Get the surface of conjugation on curve cInd. + const MbSurface * GetCurveMatingSurface( size_t cInd ) const; + + /// \ru Установить сопряжение сегмента sInd контура cInd. \en Set mate for segment number sInd for contour number cInd. + void SetCurveMating( size_t cInd, size_t sInd, MbePatchMatingType curveMate, const MbSurface * surface ); + /// \ru Декомпозиция сопряжений контура номер cInd, segCount - число сегментов контура. \en Decomposition of cInd - contour mates, segCount - the number of contour segments. + void DecomposeMates( size_t cInd, size_t segCount ); + /// \ru Оператор присваивания. \en Assignment operator. - void operator = ( const PatchValues & other ) { type = other.type; checkSelfInt = other.checkSelfInt; } + void operator = ( const PatchValues & other ) { type = other.type; checkSelfInt = other.checkSelfInt; mergeEdges = other.mergeEdges; curvesMatings = other.curvesMatings; } /// \ru Являются ли объекты равными? \en Determine whether an object is equal? - bool IsSame( const PatchValues & obj, double ) const { return ((obj.type == type) && (obj.checkSelfInt == checkSelfInt)); } + bool IsSame( const PatchValues & obj, double ) const { return ((obj.type == type) && (obj.checkSelfInt == checkSelfInt) && (obj.mergeEdges == mergeEdges) && (obj.curvesMatings == curvesMatings)); } KNOWN_OBJECTS_RW_REF_OPERATORS( PatchValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. }; @@ -887,18 +951,18 @@ public: // --- class MATH_CLASS MbPatchCurve : public MbRefItem { private: - MbCurve3D * curve; ///< \ru Кривая. \en A curve. - double begTolerance; ///< \ru Толерантность привязки в начале. \en Binding tolerance at the start. - double endTolerance; ///< \ru Толерантность привязки в начале. \en Binding tolerance at the start. - bool isSurfaceOne; ///< \ru В ребре есть грань с первой поверхностью из кривой пересечения. \en There is face with the first surface from the intersection curve in the edge. - bool isSurfaceTwo; ///< \ru В ребре есть грань со второй поверхностью из кривой пересечения. \en There is face with the second surface from the intersection curve in the edge. - mutable bool isUsed; ///< \ru Флаг использования. \en An using flag. + c3d::SpaceCurveSPtr curve; ///< \ru Кривая. \en A curve. + double begTolerance; ///< \ru Толерантность привязки в начале. \en Binding tolerance at the start. + double endTolerance; ///< \ru Толерантность привязки в начале. \en Binding tolerance at the start. + bool isSurfaceOne; ///< \ru В ребре есть грань с первой поверхностью из кривой пересечения. \en There is face with the first surface from the intersection curve in the edge. + bool isSurfaceTwo; ///< \ru В ребре есть грань со второй поверхностью из кривой пересечения. \en There is face with the second surface from the intersection curve in the edge. + mutable bool isUsed; ///< \ru Флаг использования. \en An using flag. public: /// \ru Конструктор по кривой (копирует кривую, трансформируя по матрице). \en Constructor by a curve (copies a curve, transforms by the matrix). - MbPatchCurve( const MbCurve3D & crv, const MbMatrix3D & mtr ); + MbPatchCurve( const MbCurve3D &, const MbMatrix3D & ); /// \ru Конструктор по ребру (копирует кривую, трансформируя по матрице). \en Constructor by an edge (copies a curve, transforms by the matrix). - MbPatchCurve( const MbCurveEdge & edge, const MbMatrix3D & mtr ); + MbPatchCurve( const MbCurveEdge &, const MbMatrix3D & ); /// \ru Деструктор. \en Destructor. virtual ~MbPatchCurve(); @@ -1594,18 +1658,19 @@ private: MbeMatingType type1; ///< \ru Сопряжение на границе 1. \en Mate on the boundary 1. MbeMatingType type2; ///< \ru Сопряжение на границе 2. \en Mate on the boundary 2. MbeMatingType type3; ///< \ru Сопряжение на границе 3. \en Mate on the boundary 3. - - MbSurface * surface0; ///< \ru Сопрягаемая поверхность через границу 0 (curvesU[0]). \en Mating surface through the boundary 0 (curvesU[0]). - MbSurface * surface1; ///< \ru Сопрягаемая поверхность через границу 1 (curvesV[0]). \en Mating surface through the boundary 1 (curvesV[0]). - MbSurface * surface2; ///< \ru Сопрягаемая поверхность через границу 2 (curvesU[maxU]). \en Mating surface through the boundary 2 (curvesU[maxU]). - MbSurface * surface3; ///< \ru Сопрягаемая поверхность через границу 3 (curvesV[maxV]). \en Mating surface through the boundary 3 (curvesV[maxV]). - MbPoint3D * point; ///< \ru Точка на поверхности. Используется для уточнения. \en Point on the surface. Used for specializing. + + c3d::SurfacesVector surface0; /// \ru Сопрягаемые поверхности через curvesU[0] \en Mating surfaces through curvesU[0] + c3d::SurfacesVector surface1; /// \ru Сопрягаемые поверхности через curvesV[0] \en Mating surfaces through curvesV[0] + c3d::SurfacesVector surface2; /// \ru Сопрягаемые поверхности через curvesU[maxU] \en Mating surfaces through curvesU[maxU] + c3d::SurfacesVector surface3; /// \ru Сопрягаемые поверхности через curvesV[maxV] \en Mating surfaces through curvesV[maxV] + MbPoint3D * point; ///< \ru Точка на поверхности. Используется для уточнения. \en Point on the surface. Used for specializing. bool defaultDir0; ///< \ru Направление сопряжения на границе 0 по умолчанию. \en Default mate direction through the boundary 0. bool defaultDir1; ///< \ru Направление сопряжения на границе 1 по умолчанию. \en Default mate direction through the boundary 1. bool defaultDir2; ///< \ru Направление сопряжения на границе 2 по умолчанию. \en Default mate direction through the boundary 2. bool defaultDir3; ///< \ru Направление сопряжения на границе 3 по умолчанию. \en Default mate direction through the boundary 3. mutable uint8 directOrderV;///< \ru По второму семейству кривых порядок кривых совпадает. \en Order of the curves coincides by the second set of curves. + bool tesselate; ///< \ru Достраивать ли дополнительные сечения. \en Whether to build additional sections. private: /// \ru Конструктор копирования. \en Copy-constructor. MeshSurfaceValues( const MeshSurfaceValues &, MbRegDuplicate * ireg ); @@ -1628,24 +1693,33 @@ public: \en Closedness attribute along the u and v directions. \~ \param[in] checkSelfInt - \ru Флаг проверки на самопересечение. \en Flag of check for self-intersection. \~ + \param[in] tesselate - \ru Достраивать ли дополнительные сечения. + \en Whether to build additional sections. \~ \param[in] type0, type1, type2, type3 - \ru Типы сопряжений на границах. \en Mates types on the boundaries. \~ - \param[in] surfaces0, surfaces1, surfaces2, surfaces3 - \ru Соответствующие сопрягаемые поверхности. - \en Corresponding mating surfaces. \~ + \param[in] surf0, surf1, surf2, surf3 - \ru Соответствующие сопрягаемые поверхности. + \en Corresponding mating surfaces. \~ + \param[in] point - \ru Точка на поверхности. Используется для уточнения. + \en Point on the surface. Used for specializing.\~ \param[in] modify - \ru Флаг модификации кривых по сопряжениям. \en Flag of curves modification by mates. \~ + \param[in] direct0, direct1, direct2, direct3 - \ru Направление поверхности на границе сопряжения. + \en The direction of the surface at the border of mating. \~ + \return \ru Статус выполнения. + \en Execution status. */ bool Init( const RPArray & curvesU, bool uClosed, const RPArray & curvesV, bool vClosed, bool checkSelfInt, + bool tess = false, const RPArray * chainsU = NULL, const RPArray * chainsV = NULL, MbeMatingType type0 = trt_Position, MbeMatingType type1 = trt_Position, MbeMatingType type2 = trt_Position, MbeMatingType type3 = trt_Position, - const MbSurface * surf_0 = NULL, // \ru Сопрягаемые поверхности через curvesU[0] \en Mating surfaces through curvesU[0] - const MbSurface * surf_1 = NULL, // \ru Сопрягаемые поверхности через curvesV[0] \en Mating surfaces through curvesV[0] - const MbSurface * surf_2 = NULL, // \ru Сопрягаемые поверхности через curvesU[maxU] \en Mating surfaces through curvesU[maxU] - const MbSurface * surf_3 = NULL, // \ru Сопрягаемые поверхности через curvesV[maxV] \en Mating surfaces through curvesV[maxV] + const c3d::ConstSurfacesVector * surf0 = NULL, // \ru Сопрягаемые поверхности через curvesU[0] \en Mating surfaces through curvesU[0] + const c3d::ConstSurfacesVector * surf1 = NULL, // \ru Сопрягаемые поверхности через curvesV[0] \en Mating surfaces through curvesV[0] + const c3d::ConstSurfacesVector * surf2 = NULL, // \ru Сопрягаемые поверхности через curvesU[maxU] \en Mating surfaces through curvesU[maxU] + const c3d::ConstSurfacesVector * surf3 = NULL, // \ru Сопрягаемые поверхности через curvesV[maxV] \en Mating surfaces through curvesV[maxV] const MbPoint3D * pnt = NULL, bool modify = true, bool direct0 = true, bool direct1 = true, bool direct2 = true, bool direct3 = true ); @@ -1669,32 +1743,45 @@ public: \en Checking curve. \~ \param[in] surf - \ru Проверяемая поверхность. \en Checking surface. \~ + \return \ru Признак, что кривая лежит на поверхности. + \en A sign that the curve is on the surface. */ bool IsCurveOnSurface( const MbCurve3D & curve, const MbSurface & surf ) const; - /** \brief \ru Сопрягаются ли кривые с поверхностью точках пересечения с otherCurve. - \en Whether the curves are mated with surface in the points of intersection with otherCurve. \~ - \details \ru Сопрягаются ли кривые (касательно, по нормали, гладко) с поверхностью в точках пересечения с otherCurve. - \en Whether the curves are mated (tangentially, along the normal, smoothly) with surface in the points of intersection with otherCurve. \~ + /** \brief \ru Сопрягаются ли кривые с поверхностью в точках пересечения с borderCurve. + \en Whether the curves are mated with surface in the points of intersection with borderCurve. \~ + \details \ru Сопрягаются ли кривые (касательно, по нормали, гладко) с поверхностью в точках пересечения с borderCurve. + \en Whether the curves are mated (tangentially, along the normal, smoothly) with surface in the points of intersection with borderCurve. \~ \param[in] curves - \ru Набор проверяемых кривых. \en Set of checking curves. \~ - \param[in] surf - \ru Поверхность. - \en The surface. \~ - \param[in] otherCurve - \ru Кривая на этой поверхности. - \en Curve on this surface. \~ + \param[in] borderCurve- \ru Кривая на поверхности. + \en Curve on the surface. \~ + \param[in] matSurfaces- \ru Набор поверхностей сопряжения. + \en Set of mating surfaces. \~ \param[out] isTangent - \ru Признак касательного сопряжения. \en Attribute of the tangent mate. \~ \param[out] isNormal - \ru Признак сопряжения по нормали. \en Attribute of normal mate. \~ \param[out] isSmooth - \ru Признак гладкого сопряжения. \en Attribute of the smooth mate. \~ + \param[in] uc, vc - \ru Замкнутость поверхности по u и v. + \en Surface closeness by u and v. \~ + \return \ru Признак, что кривая лежит на поверхности. + \en A sign that the curve is on the surface. */ - void AreCurvesMatingToSurface( const RPArray & curves, - const MbSurface & surf, - const MbCurve3D * otherCurve, - bool & isTangent, - bool & isNormal, - bool & isSmooth ) const; + static bool AreCurvesMatingToSurface( const RPArray & curves, + const MbCurve3D * borderCurve, + const c3d::SurfacesVector & matSurfaces, + bool & isTangent, + bool & isNormal, + bool & isSmooth, + bool uc, + bool vc ); + + /// \ru Сопрягаются ли кривые с поверхностью в точках пересечения с поверхностной кривой (borderCurve). + /// \en Whether the curves are mated with surface in the points of intersection with the surface curve (borderCurve). + static bool AreCurvesMatingToSurface( const RPArray & curves, const MbCurve3D * borderCurve, + bool & isTangent, bool & isNormal, bool & isSmooth, bool uc, bool vc ); /// \ru Получить точки скрещивания-пересечения кривой с семейством кривых. \en Get crossing-intersection points of curves with the set of curves. bool GetPointsOfCrossing( const MbCurve3D & curve, const RPArray & otherCurves, @@ -1709,9 +1796,9 @@ public: /// \ru Получить тип сопряжения на границе с номером i. \en Get i-th mate type on the boundary. MbeMatingType GetTransitType( ptrdiff_t i ) const; /// \ru Получить поверхность сопряжения на границе с номером i. \en Get i-th mate surface on the boundary. - const MbSurface * GetSurface( size_t i ) const; + void GetSurface( size_t i, c3d::ConstSurfacesVector & surfaces ) const; /// \ru Получить поверхность сопряжения на границе с номером i. \en Get i-th mate surface on the boundary. - MbSurface * SetSurface( size_t i ); + void SetSurface( size_t i, c3d::SurfacesVector & surfaces ); /// \ru Получить направление сопряжения на границе с номером i. \en Get i-th mate direction on the boundary. bool IsDefaultDirection( size_t i ) const; @@ -1811,17 +1898,34 @@ public: /// \ru Повернуть кривые вокруг оси на заданный угол. \en Rotate curves at a given angle around an axis. void Rotate ( const MbAxis3D &, double angle, MbRegTransform * ireg ); /// \ru Привести кривые к поверхностной форме (кривые на поверхности) \en Convert curves to the surface form (curves on the surface) - bool TransformCurves(); + bool TransformCurves( ); + /// \ru Привести кривые к поверхностной форме (кривые на поверхности) \en Convert curves to the surface form (curves on the surface) + bool TransformForCompositeSurfaceMating(); + /// \ru Обеспечить непрерывность длины первой производной для кривых семейства dirU. + /// \en Ensure continuity of the length of the first derivative for the curves of the dirU family. + bool SetContinuousDerivativeLength( bool dirU, bool & smooth, VERSION version ); /** \} */ /// \ru Являются ли объекты равными? \en Determine whether an object is equal? bool IsSame( const MeshSurfaceValues &, double accuracy ) const; - /// \ru Набор граничных поверхность пуст? \en Whether the set of boundary surfaces is empty? - bool AreSurfacesEmpty() const { return (surface0 == NULL && surface1 == NULL && surface2 == NULL && surface3 == NULL); } /// \ru Нужно ли проверять самопересечения? \en Whether it is necessary to check self-intersections? bool CheckSelfInt() const { return checkSelfInt; } - + ///< \ru Достраивать ли дополнительные сечения. \en Whether to build additional sections. + bool IsTesselate() const { return tesselate; } + /// \ru Получить поверхность сопряжения к граничной кривой по параметру на кривой. + /// \en Get the mating surface to the border curve by the curve parameter. + static const MbSurface * + GetMatingSurface( const MbCurve3D * borderCurve, double param ); + /// \ru Получить поверхность сопряжения к граничной кривой в точке пересечения с трансверсальной кривой. + /// \en Get the mating surface to the border curve at the intersection with the transversal curve. + static const MbSurface * + GetMatingSurface( const MbCurve3D * borderCurve, const MbCurve3D * transCurve ); + /// \ru Получить поверхностную кривую в указанной точке на границе сопряжения. + /// \en Get a surface curve at a specified point on the mating border. + static bool GetSurfaceCurve( const MbCurve3D * borderCurve, double param, const MbSurfaceCurve *& sCurve, double & t ); + /// \ru Является ли кривая поверхностной по типу. \en Is the curve surface in type. + static bool IsSurfaceCurveType( const MbCurve3D * borderCurve ); private: void AddRefCurves(); // \ru Увеличить счетчик ссылок у кривых. \en Increase the reference count of curves. void AddRefPoint(); // \ru Увеличить счетчик ссылок у точки. \en Increase the reference count of point. @@ -1832,10 +1936,16 @@ private: // \ru Определить порядок следования кривых по второму направлению. \en Determine the order of curves along the second direction. void CalculateOrderV() const; // \ru Привести кривую к типу поверхностной кривой или к контура из SurfaceCurve. \en Convert the curve to type of surface curve or contour from SurfaceCurve. - bool TransformToSurfaceCurve( const MbCurve3D & initCurve, + static bool TransformToSurfaceCurve( const MbCurve3D & initCurve, + bool isWhole, const MbSurface & surface, const RPArray & constrCurves, - MbSurfaceCurve *& resCurve ) const; + MbSurfaceCurve *& resCurve ); + // \ru Привести кривую к типу поверхностной кривой или к контура из SurfaceCurve. \en Convert the curve to type of surface curve or contour from SurfaceCurve. + static bool TransformToSurfaceCurve( const MbCurve3D & initCurve, + const c3d::SurfacesVector & surfaces, + const RPArray & constrCurves, + MbCurve3D *& resCurve ); public: KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MeshSurfaceValues, MATH_FUNC_EX ) OBVIOUS_PRIVATE_COPY( MeshSurfaceValues ) @@ -2018,7 +2128,7 @@ public: enum ExtensionWay { ew_distance = -2, ///< \ru Продолжить на расстояние. \en Prolong on the distance. ew_vertex = -1, ///< \ru Продолжить до вершины. \en Prolong to the vertex. - ew_surface = 0, ///< \ru Продолжить до поверхности. \en Prolong to the surface. + ew_shell = 0, ///< \ru Продолжить до оболочки. \en Prolong to the shell. }; /** \brief \ru Способы построения боковых рёбер. \en Methods of construction of the lateral edges. \~ @@ -2040,7 +2150,7 @@ public: bool prolong; ///< \ru Продолжить по гладко стыкующимся рёбрам. \en Prolong along smoothly mating edges. bool combine; ///< \ru Объединять грани при возможности. \en Combine faces if it is possible. private: - MbFaceShell * shell; ///< \ru Оболочка. \en A shell. + MbFaceShell * shell; ///< \ru Оболочка, до которой продляются грани. \en A shell to which the faces are extended. MbItemIndex faceIndex; ///< \ru Номер грани в оболочке. \en The index of face in the shell. public: @@ -2095,7 +2205,7 @@ public: \param[in] s - \ru Тело для замены оболочки. \en Solid for replacement of shell. \~ */ - void InitBySurface ( ExtensionType t, LateralKind k, const MbFace * f, const MbSolid * s ); + void InitByShell ( ExtensionType t, LateralKind k, const MbFace * f, const MbSolid * s ); /// \ru Преобразовать объект согласно матрице. \en Transform an object according to the matrix. void Transform( const MbMatrix3D & matr, MbRegTransform * ireg = NULL ); @@ -2392,47 +2502,70 @@ public: */ // --- struct MATH_CLASS MedianShellValues { + public: + /** \brief \ru Тип расчета радиуса скругления между гранями срединной оболочки. + \en Type of fillet radius calculation between faces of median shell. \~ + \details \ru Флаг можно установить через вызов MedianShellValues::SetFilletType(). + \en The flag can be set by calling MedianShellValues::SetFilletType(). \~ + */ + enum FilletType { + tf_none, ///< \ru Не определено. \en Undefined. + tf_internal, ///< \ru По внутренней грани скругления. \en Along the tangent. + tf_external, ///< \ru По внешней грани скругления. \en Along the normal. + tf_average ///< \ru По среднему значению. \en Plane patch. + }; + public: - double position; ///< \ru Параметр смещения срединной оболочки относительно первой грани из пары. По умолчанию равен 50% расстояния между гранями. \en Parameter of shift the median surface from first face in faces pair. By default is 50% from distance between faces in pair. - double dmin; ///< \ru Минимальный параметр эквидистантности. \en Minimal equidistation value. - double dmax; ///< \ru Максимальный параметр эквидистантности. \en Maximal equidistation value. + FilletType filletType; + double position; ///< \ru Параметр смещения срединной оболочки относительно первой грани из пары. По умолчанию равен 50% расстояния между гранями. \en Parameter of shift the median surface from first face in faces pair. By default is 50% from distance between faces in pair. + double dmin; ///< \ru Минимальный параметр эквидистантности. \en Minimal equidistation value. + double dmax; ///< \ru Максимальный параметр эквидистантности. \en Maximal equidistation value. + public: /// \ru Конструктор по умолчанию. \en Default constructor. MedianShellValues() - : position ( 0.5 ) - , dmin ( 0.0 ) - , dmax ( 0.0 ) + : filletType ( tf_average ) + , position ( 0.5 ) + , dmin ( 0.0 ) + , dmax ( 0.0 ) {} /// \ru Конструктор копирования. \en Copy-constructor. MedianShellValues( const MedianShellValues & other ) - : position ( other.position ) - , dmin ( other.dmin ) - , dmax ( other.dmax ) + : filletType ( other.filletType ) + , position ( other.position ) + , dmin ( other.dmin ) + , dmax ( other.dmax ) {} /// \ru Конструктор по параметрам. \en Constructor by parameters. MedianShellValues( double pos, double d1, double d2 ) - : position ( pos ) - , dmin ( d1 ) - , dmax ( d2 ) + : filletType( tf_average ) + , position ( pos ) + , dmin ( d1 ) + , dmax ( d2 ) {} public: /// \ru Являются ли объекты равными? \en Determine whether an object is equal? bool IsSame( const MedianShellValues & obj, double accuracy ) const { - if ( (::fabs(dmin - obj.dmin) < accuracy) && + if ( filletType == obj.filletType && + (::fabs(dmin - obj.dmin) < accuracy) && (::fabs(dmax - obj.dmax) < accuracy) && - (::fabs( position - obj.position) < accuracy ) ) - { + (::fabs(position - obj.position) < accuracy) ) return true; - } return false; } + /// \ru Выдать тип заплатки. \en Get type of patch. + FilletType GetType() const { return filletType; } + /// \ru Выдать тип заплатки для изменения. \en Get type of patch for changing. + FilletType & SetType() { return filletType; } + public: /// \ru Оператор присваивания. \en Assignment operator. MedianShellValues & operator = ( const MedianShellValues & other ) { + filletType = other.filletType; position = other.position; dmin = other.dmin; dmax = other.dmax; @@ -2468,6 +2601,12 @@ public: facePairs = pairs; distances.resize( pairs.size() ); } + /// \ru Конструктор по параметрам. \en Constructor by parameters. + MedianShellFaces( const MedianShellFaces & other ) + { + facePairs = other.facePairs; + distances = other.distances; + } /// \ru Деструктор. \en Destructor. ~MedianShellFaces() {} @@ -2489,7 +2628,7 @@ public: distances.push_back( dist ); } /// \ru Получить пару граней по индексу. \en Get pair of faces by index. - const c3d::ItemIndexPair & _GetFacePair( size_t index ) const { return facePairs[index];} + const c3d::ItemIndexPair & _GetFacePair( size_t index ) const { return facePairs[index]; } /// \ru Удалить пару граней из набора. \en Remove pair of faces from set. void RemovePairByIndex( size_t index ) { @@ -2500,6 +2639,8 @@ public: const double & _GetDistance( size_t index ) const { return distances[index]; } /// \ru Установить расстояние между гранями. \en Set distance between faces. void _SetDistance( size_t index, double value ) { distances[index] = value; } + /// \ru Инвертировать пару граней в наборе. \en Inverse face pair. + void Inverse( size_t index ) { std::swap(facePairs[index].first, facePairs[index].second); } /// \ru Вернуть количество пар граней в наборе. \en Get count of pairs in given set. size_t Count() const { return facePairs.size(); } /// \ru Оператор присваивания. \en Assignment operator. @@ -2510,27 +2651,105 @@ public: } /// \ru Очистка текущего набора. \en Clear current faces set. void Clear() { facePairs.clear(); distances.clear(); } - + /// \ru Проверить наличие пары в наборе. \en Check if pair already in set. + size_t IsExist( const MbItemIndex & ind, size_t start_pos, size_t end_pos, bool & first ) const; KNOWN_OBJECTS_RW_REF_OPERATORS( MedianShellFaces ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. }; +//------------------------------------------------------------------------------ +/** \brief \ru Параметры сшивки. + \en Stitch parameters. \~ + \details \ru Параметры сшивки оболочек. \n + \en Shells stitch parameters. \n \~ + \ingroup Build_Parameters +*/ +// --- +struct MATH_CLASS MbShellStitchParams { +private: + MbSNameMaker nameMaker; ///< \ru Именователь операции. \en An object defining names generation in the operation. + double stitchAccuracy; ///< \ru Точность сшивки (точность поиска парных ребер). \en Stitching accuracy (search accuracy of edges pairs). + bool formSolidBody; ///< \ru Флаг формирования твердого тела из результирующей оболочки. \en Whether to form a solid solid from the resultant shell. + bool mergeEdges; ///< \ru Сливать подобные ребра (true). \en Whether to merge similar edges (true). +private: + MbShellStitchParams(); +public: + /** \brief \ru Конструктор параметров сшивки оболочек. + \en Stitch faces of several solids into single solid. \~ + \details \ru Сшить стыкующиеся друг с другом грани нескольких тел в одно тело. Ориентация граней может быть изменена. \n + \en Stitch faces of several solids with coincident edges into single solid. The faces orientation can be changed. \n \~ + \param[in] operNames - \ru Именователь операции. + \en An object defining names generation in the operation. \~ + \param[in] formBody - \ru Флаг формирования твердого тела из результирующей оболочки. + \en Whether to form a solid solid from the resultant shell. \~ + \param[in] sewingAccuracy - \ru Точность сшивки (точность поиска парных ребер). + \en Stitching accuracy (search accuracy of edges pairs). \~ + \param[in] edgesMerging - \ru Сливать подобные ребра (true). + \en Whether to merge similar edges (true). \~ + */ + MbShellStitchParams( const MbSNameMaker & operNames, bool formBody, double sewingAccuracy, bool edgesMerging = true ) + : nameMaker ( operNames ) + , stitchAccuracy( sewingAccuracy ) + , formSolidBody ( formBody ) + , mergeEdges ( edgesMerging ) + {} + ~MbShellStitchParams() + {} +public: + /// \ru Получить именователь операции. \en Get the object defining names generation in the operation. + const MbSNameMaker & GetNameMaker() const { return nameMaker; } + /// \ru Получить точность поиска парных ребер. \en Get search accuracy of edges pairs. + double GetStitchAccuracy() const { return stitchAccuracy; } + /// \ru Пытаться формировать твердое тело из результирующей оболочки.. \en Whether to try forming a solid solid from the resultant shell. + bool TryBodyForming() const { return formSolidBody; } + /// \ru Сливать подобные ребра (true). \en Whether to merge similar edges (true). + bool PerformEdgesMerging() const { return mergeEdges; } + +OBVIOUS_PRIVATE_COPY ( MbShellStitchParams ) +}; + + //------------------------------------------------------------------------------ /// \ru Типы продления секущих поверхностей. \en Prolongation types of cutter surfaces. // --- enum MbeSurfaceProlongType { - cspt_None = 0x00, // 0000 ///< \ru Не продлевать. \en Don't prolong. - cspt_Planar = 0x01, // 0001 ///< \ru Плоские поверхности. \en Planar surfaces. - cspt_RevolutionAxis = 0x02, // 0010 ///< \ru Поверхности вращения (вдоль оси). \en Revolution surfaces (along axis). - cspt_RevolutionAngle = 0x04, // 0100 ///< \ru Поверхности вращения (по углу). \en Revolution surfaces (by angle). - cspt_Revolution = 0x06, // 0110 ///< \ru Поверхности вращения. \en Revolution surfaces. + cspt_None = 0x00, // 00000 ///< \ru Не продлевать. \en Don't prolong. + cspt_Planar = 0x01, // 00001 ///< \ru Плоские поверхности. \en Planar surfaces. + cspt_RevolutionAxis = 0x02, // 00010 ///< \ru Поверхности вращения (вдоль оси). \en Revolution surfaces (along axis). + cspt_RevolutionAngle = 0x04, // 00100 ///< \ru Поверхности вращения (по углу). \en Revolution surfaces (by angle). + cspt_Revolution = 0x06, // 00110 ///< \ru Поверхности вращения. \en Revolution surfaces. + cspt_ExtrusionGeneratrix = 0x08, // 01000 ///< \ru Поверхности выдавливания (по образующей). \en Extrusion surfaces (by generatrix). + cspt_ExtrusionDistance = 0x10, // 10000 ///< \ru Поверхности выдавливания (по расстоянию). \en Extrusion surfaces (by distance). + cspt_Extrusion = 0x18, // 11000 ///< \ru Поверхности выдавливания. \en Extrusion surfaces. }; + //------------------------------------------------------------------------------ /** \brief \ru Параметры операции резки оболочки. \en Shell cutting operation parameters. \~ \details \ru Параметры операции резки оболочки. \n - \en Shell cutting operation parameters. \n \~ + Предполагается взаимоисключающее состояние флагов при их активном состоянии в #MbShellCuttingParams : \n + 1. #cspt_Planar - продлять или нет грани на основе плоскости; \n + 2. #cspt_Revolution - продлять или нет поверхности, имеющие ось вращения (у которых функция поверхности GetCylinderAxis возвращает true ), + причем возможно раздельное управление : \n + #cspt_RevolutionAngle - замкнуть поверхность по углу, \n + #cspt_RevolutionAxis - продлить поверхность вдоль оси так, чтобы включить габарит тела, если это возможно); \n + 3. #cspt_Extrusion - продлять или нет поверхности, являющиеся поверхностями выдавливания (поверхность выдавливания, цилиндрическая поверхность), + причем возможно раздельное управление : \n + #cspt_ExtrusionGeneratrix - продлить вдоль образующей для включения габарита тела или замкнуть, если образующая периодична (дуга), \n + #cspt_ExtrusionDistance - продлить поверхность вдоль направления выдавливания так, чтобы включить габарит тела, если это возможно). \n + \en Shell cutting operation parameters. \n + + The mutually exclusive state of flags is assumed when they are active in #MbShellCuttingParams: \n + 1. #cspt_Planar - extend or not the face based on the plane; \n + 2. #cspt_Revolution - extend or not surfaces having a rotation axis (for which the GetCylinderAxis surface function returns true), + moreover, separate control is possible: \n + #cspt_RevolutionAngle - close the surface by angle, \n + #cspt_RevolutionAxis - extend the surface along the axis so as to embrace the body, if it's possible; \n + 3. #cspt_Extrusion - to extend or not surfaces that are extrusion surfaces (extrusion surface, cylindrical surface), + moreover, separate control is possible: \n + #cspt_ExtrusionGeneratrix - extend along the generatrix to cover the body or close if the generatrix is ​​periodic (like an arc), \n + #cspt_ExtrusionDistance - extend the surface along the extrusion direction so as to embrace the body, if it's possible. \n \~ \ingroup Build_Parameters */ // --- @@ -2788,7 +3007,7 @@ public: const MbSNameMaker & snMaker ) { if ( cutterData.InitPlaneContour( place, dir, contour, sameContour ) ) { - nameMaker.SetName( snMaker, true ); + nameMaker.SetNameMaker( snMaker, true ); booleanFlags.InitCutting( cutAsClosed ); booleanFlags.SetMerging( mergingFlags ); SetRetainedPart( part ); @@ -2840,7 +3059,7 @@ public: const MbSNameMaker & snMaker ) { if ( cutterData.InitSurfaces( surface, sameSurface ) ) { - nameMaker.SetName( snMaker, true ); + nameMaker.SetNameMaker( snMaker, true ); booleanFlags.InitCutting( cutAsClosed ); booleanFlags.SetMerging( mergingFlags ); SetRetainedPart( part ); @@ -2890,7 +3109,7 @@ public: const MbSNameMaker & snMaker ) { if ( cutterData.InitSolid( solid, sameSolid, true ) ) { - nameMaker.SetName( snMaker, true ); + nameMaker.SetNameMaker( snMaker, true ); booleanFlags.InitCutting( cutAsClosed ); booleanFlags.SetMerging( mergingFlags ); SetRetainedPart( part ); @@ -2960,7 +3179,7 @@ public: /// \ru Получить тип продления режущей поверхности. \en Get cutter surface prolong type. const ProlongState & GetProlongState() const { return prolongState; } - /// \ru Получить тип продления режущей поверхности. \en Get cutter surface prolong type. + /// \ru Сбросить тип продления режущей поверхности. \en Reset cutter surface prolong type. void ResetProlongState() { prolongState.Reset(); } /// \ru Добавить тип продления режущей поверхности. \en Add cutter surface prolong type. void SetSurfaceProlongType( MbeSurfaceProlongType pt ) { prolongState.SetActiveType( true, pt ); } diff --git a/C3d/Include/op_swept_parameter.h b/C3d/Include/op_swept_parameter.h index e8dae02..2e272eb 100644 --- a/C3d/Include/op_swept_parameter.h +++ b/C3d/Include/op_swept_parameter.h @@ -1,1416 +1,2004 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Параметры операций над телами. - \en Parameters of operations on the solids. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __OP_SWEPT_PARAMETERS_H -#define __OP_SWEPT_PARAMETERS_H - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbPlacement3D; -class MATH_CLASS MbMatrix3D; -class MATH_CLASS MbAxis3D; -class MATH_CLASS MbCurve3D; -class MbRegTransform; -class MbRegDuplicate; - - -//------------------------------------------------------------------------------ -/** \brief \ru Данные об образующей. - \en The generating data. \~ - \details \ru Данные об образующей операции движения. \n - Образующая операции выдавливания, вращения или кинематической операции - может включать в себя набор двумерных контуров, набор трехмерных контуров, тело. \n - Для набора двумерных контуров на поверхности существуют следующие ограничения:\n - – может быть один или несколько контуров;\n - – если контуров несколько, они должны быть либо все замкнуты, либо все разомкнуты;\n - - если контуры замкнуты, они могут быть вложенными друг в друга, уровень вложенности не ограничивается;\n - – контуры не должны пересекаться между собой или самопересекаться.\n - Для двумерных контуров на не плоской поверхности есть дополнительное ограничение: - все контуры должны быть замкнуты.\n - Построение операции по двумерным контурам на не плоской поверхности рассчитано на указание пользователем - грани тела в качестве образующей. В этом случае данные для образующей можно получить - с помощью метода грани MbFace::GetSurfaceCurvesData.\n - Ограничения для трехмерных контуров:\n - – контуры не должны пересекаться между собой или самопересекаться.\n - \en Data about generating of movement operation. \n - Generating of extrusion operation, rotation or sweeping operation - can include a set of two-dimensional contours, a set of three-dimensional contours, solid. \n - For a set of two-dimensional contours on the surface, the following restrictions:\n - - can be one or multiple contours;\n - - If there are multiple contours, all of them must be either closed or open;\n - - if contours are closed, then they can be nested into each other, the level of nesting is not limited;\n - - contours can't overlap each other or self-intersect.\n - For two-dimensional contour on the non-planar surface is additional constraint: - all the contours must be closed.\n - Constructing operation by two-dimensional contours on non-planar surface it is necessary to specify the by the user - face of solid as generating. In this case, the generating data can be obtained - by the method of face MbFace::GetSurfaceCurvesData.\n - Constraints for three-dimensional contour:\n - - contours can't overlap each other or self-intersect.\n \~ - \ingroup Build_Parameters -*/ -// --- -class MATH_CLASS MbSweptData { - -private: - // \ru Данные о двумерных контурах на поверхности. \en Data about two-dimensional contours on the surface. - c3d::SurfaceSPtr surface; ///< \ru Поверхность. \en The surface. - c3d::PlaneContoursSPtrVector contours; ///< \ru Множество двумерных контуров. \en Set of two-dimensional contours. - // \ru Трехмерные контуры. \en Three-dimensional contours. - c3d::SpaceContoursSPtrVector contours3D; ///< \ru Множество трёхмерных контуров. \en Set of three-dimensional contours. - // \ru Тело. \en Solid. - c3d::SolidSPtr solid; ///< \ru Тело. \en A solid. - -public: - /// \ru Конструктор по умолчанию. \en Default constructor. - MbSweptData(); - /// \ru Конструктор копирования. \en Copy-constructor. - MbSweptData( const MbSweptData &, MbRegDuplicate * ireg = NULL ); - -public: - - /** \brief \ru Конструктор плоской образующей. - \en Constructor of planar swept. \~ - \details \ru Конструктор плоской образующей из одного контура. - \en Constructor of planar swept from one contour. \~ - \param[in] place - \ru Локальная система координат. - \en A local coordinate system. \~ - \param[in] contour - \ru Контур в параметрах заданной системы координат. Используется оригинал. - \en Contour in parameters of the given coordinate system. Used original. \~ - */ - MbSweptData( const MbPlacement3D & place, MbContour & contour ); - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор по набору контуров на поверхности. - \en Constructor by a set of contours on a surface. \~ - \param[in] _surface - \ru Поверхность. Используется оригинал. - \en The surface. Used original. \~ - \param[in] _contours - \ru Набор контуров. Используются оригиналы. - \en A set of contours. Used originals. \~ - */ - MbSweptData( MbSurface & _surface, RPArray & _contours ); - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор по набору контуров на поверхности. - \en Constructor by a set of contours on a surface. \~ - \param[in] _surface - \ru Поверхность. Используется оригинал. - \en The surface. Used original. \~ - \param[in] _contours - \ru Набор контуров. Используются оригиналы. - \en A set of contours. Used originals. \~ - */ - MbSweptData( MbSurface & _surface, c3d::PlaneContoursSPtrVector & _contours ); - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор по кривой. - \en Constructor by a contour. \~ - \param[in] _contour3d - \ru Кривая. Используются оригиналы. - \en A curve. Used originals. \~ - */ - MbSweptData( MbCurve3D & _curve3d ); - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор по контуру. - \en Constructor by a contour. \~ - \param[in] _contour3d - \ru Контур. Используются оригиналы. - \en A contour. Used originals. \~ - */ - MbSweptData( MbContour3D & _contour3d ); - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор по набору пространственных контуров. - \en Constructor by a set of spatial contours. \~ - \param[in] _contours3d - \ru Набор контуров. Используются оригиналы. - \en A set of contours. Used originals. \~ - */ - MbSweptData( RPArray & _contours3d ); - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор по набору пространственных контуров. - \en Constructor by a set of spatial contours. \~ - \param[in] _contours3d - \ru Набор контуров. Используются оригиналы. - \en A set of contours. Used originals. \~ - */ - MbSweptData( c3d::SpaceContoursSPtrVector & _contours3d ); - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор по телу. - \en Constructor by a solid. \~ - \param[in] _solid - \ru Тело. Используется оригинал объекта. - \en A solid. Used original of object. \~ - */ - MbSweptData( MbSolid & _solid ); - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор смешанной образующей. - \en Constructor of mixed swept. \~ - \param[in] _surface - \ru Поверхность. Используется оригинал. - \en The surface. Used original. \~ - \param[in] _contours - \ru Набор двумерных контуров в параметрах заданной поверхности. Используются оригиналы. - \en Set of two-dimensional contours in the parameters of the given surface. Used originals. \~ - \param[in] _contours3d - \ru Набор трехмерных контуров. Используются оригиналы. - \en A set of three-dimensional contours. Used originals. \~ - \param[in] _solid - \ru Тело. Используется оригинал объекта. - \en A solid. Used original of object. \~ - */ - MbSweptData( MbSurface * _surface, RPArray & _contours, - RPArray & _contours3d, MbSolid * _solid ); - - /// \ru Деструктор. \en Destructor. - ~MbSweptData(); - -public: - /** \brief \ru Добавить данные. - \en Add data. \~ - \details \ru Добавить данные о контурах на поверхности. - \en Add data about contours to the surface. \~ - \param[in] _surface - \ru Поверхность. Добавляется оригинал объекта. - \en The surface. Added original of the object. \~ - \param[in] _contours - \ru Набор контуров. Добавляются оригиналы. - \en A set of contours. Originals are added. \~ - */ - bool AddData( MbSurface & _surface, const RPArray & _contours ); - - /** \brief \ru Добавить данные. - \en Add data. \~ - \details \ru Добавить данные о контурах на поверхности. - \en Add data about contours to the surface. \~ - \param[in] _surface - \ru Поверхность. Добавляется оригинал объекта. - \en The surface. Added original of the object. \~ - \param[in] _contours - \ru Набор контуров. Добавляются оригиналы. - \en A set of contours. Originals are added. \~ - */ - bool AddData( MbSurface & _surface, c3d::PlaneContoursSPtrVector & _contours ); - - /** \brief \ru Количество всех кривых. - \en The count of all the curves. \~ - \details \ru Общее количество двумерных и трехмерных кривых. - \en The total count of two and three-dimensional curves. \~ - */ - size_t CurvesCount() const; - - /** \brief \ru Получить кривую по индексу. - \en Get the curve by the index. \~ - \details \ru Получить кривую из множества кривых на поверхности - и трехмерных кривых. - \en Get the curve from set of curves on the surface - and three-dimensional curves. \~ - \param[in] i - \ru Номер кривой в пределах от 0 до CurvesCount(). - \en The index of curve from 0 to CurvesCount(). \~ - \return \ru Кривую на поверхности или трехмерную кривую. - \en Curve on the surface or three-dimensional curve. \~ - */ - SPtr GetCurve3D( size_t i ) const; - - /// \ru Есть данные о двумерных кривых на поверхности? \en Is there data of two-dimensional curves on the surface? - bool IsSurfaceCurvesData() const; - /// \ru Есть данные о пространственных кривых? \en Is there data of spatial curves? - bool IsSpaceCurvesData() const; - /// \ru Есть данные о теле? \en Is there data about the solid? - bool IsSolidData() const; - - /// \ru Выдать поверхность. \en Get the surface. - const MbSurface * GetSurface() const { return surface; } - /// \ru Выдать поверхность для изменения. \en Get the surface for editing. - MbSurface * SetSurface() { return surface; } - /// \ru Положить поверхность. \en Set a surface. - void SetSurface( MbSurface * surf ) { surface = surf; } - /// \ru Выдать набор двумерных контуров. \en Get the set of two-dimensional contours. - const c3d::PlaneContoursSPtrVector & GetContours() const { return contours; } - /// \ru Выдать набор трехмерных контуров. \en Get the set of three-dimensional contours. - const c3d::SpaceContoursSPtrVector & GetContours3D() const { return contours3D; } - /// \ru Выдать тело. \en Get the solid. - const MbSolid * GetSolid() const { return solid; } - /// \ru Выдать тело для изменения. \en Get the solid for editing. - MbSolid * SetSolid() const { return solid; } - - /** \brief \ru Преобразовать объект. - \en Transform the object. \~ - \details \ru Преобразовать исходный объект согласно матрице c использованием регистратора. - \en Transform the initial object according to the matrix using the registrator. \~ - \param[in] matr - \ru Матрица преобразования. - \en A transformation matrix. \~ - \param[in] iReg - \ru Регистратор. - \en Registrator. \~ - */ - void Transform( const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); - /** \brief \ru Сдвинуть объект. - \en Move the object. \~ - \details \ru Сдвинуть геометрический объект вдоль вектора с использованием регистратора. - \en Move a geometric object along the vector using the registrator. \~ - \param[in] to - \ru Вектор сдвига. - \en Translation vector. \~ - \param[in] iReg - \ru Регистратор. - \en Registrator. \~ - */ - void Move ( const MbVector3D & to, MbRegTransform * iReg = NULL ); - /** \brief \ru Повернуть объект. - \en Rotate the object. \~ - \details \ru Повернуть объект вокруг оси на заданный угол с использованием регистратора. - \en Rotate an object about the axis by the given angle using the registrator. \~ - \param[in] axis - \ru Ось поворота. - \en The rotation axis. \~ - \param[in] angle - \ru Угол поворота. - \en The rotation angle. \~ - \param[in] iReg - \ru Регистратор. - \en Registrator. \~ - */ - void Rotate ( const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); - - /** \brief \ru Определить, являются ли объекты равными. - \en Determine whether the objects are equal. \~ - \details \ru Определить, являются ли объекты равными с заданной точностью. - \en Determine whether the objects are equal with defined accuracy. \~ - \param[in] other - \ru Объект для сравнения. - \en Object for comparison. \~ - \return \ru Подобны ли объекты. - \en Whether the objects are similar. \~ - */ - bool IsSame( const MbSweptData & other, double accuracy ) const; - /** \brief \ru Определить, являются ли объекты подобными. - \en Determine whether the objects are similar. \~ - \details \ru Подобный объект можно инициализировать по данным подобного ему объекта. - \en Similar object can be initialized by data of object which is similar to it. \~ - \param[in] other - \ru Объект для сравнения. - \en Object for comparison. \~ - \return \ru Подобны ли объекты. - \en Whether the objects are similar. \~ - */ - bool IsSimilar( const MbSweptData & other ) const; - /** \brief \ru Сделать объекты равным. - \en Make objects equal. \~ - \details \ru Равными можно сделать только подобные объекты. - \en It is possible to make equal only similar objects. \~ - \param[in] init - \ru Объект для инициализации. - \en Object for initialization. \~ - \return \ru Сделан ли объект равным присланному. - \en Whether the object is made equal to the given one. \~ - */ - bool SetEqual ( const MbSweptData & other ); - - /** \brief \ru Замкнуты ли все контуры. - \en Whether all contours are closed. \~ - \details \ru Замкнуты ли все контуры. \n - \en Whether all contours are closed. \n \~ - \return \ru Возвращает true, если все контуры замкнуты. - \en Returns true if all contours are closed. \~ - */ - bool IsContoursClosed() const; - - /// \ru Проверить, что нет разрывов между сегментами поверхностных контуров. \en Check that there are no gaps between the segments of the surface contours. - bool CheckSurfaceContourConnection( double eps ) const; - /// \ru Проверить, что нет разрывов между сегментами пространственных контуров. \en Check that there are no gaps between the segments of the spatial contours. - bool CheckSpaceContourConnection( double eps ) const; - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - MbSweptData & operator = ( const MbSweptData & ); - -KNOWN_OBJECTS_RW_REF_OPERATORS( MbSweptData ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Cпособ выдавливания/вращения. - \en Method of extrusion/rotation. \~ - \details \ru Cпособ построения выдавливания/вращения. \n - \en Method of extrusion/rotation constructing. \n \~ - \ingroup Build_Parameters -*/ -// --- -enum MbSweptWay { - sw_scalarValue = -2, ///< \ru Выдавить на заданную глубину / вращать на заданный угол. \en Extrude to a given depth / rotate by a given angle. - sw_shell = -1, ///< \ru До ближайшего объекта (тела). \en To the nearest object (solid). - sw_surface = 0, ///< \ru До поверхности. \en To the surface. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Параметры вращения и выдавливания. - \en Parameters of rotation and extrusion. \~ - \details \ru Данные о построении операции вращения или выдавливания - в одном из направлений: прямом или обратном. - \en Data about construction of rotation and extrusion - in one of directions: forward or backward. \~ - \ingroup Build_Parameters -*/ -// --- -class MATH_CLASS MbSweptSide { -public: - MbSweptWay way; ///< \ru Способ выдавливания/вращения. \en Method of extrusion/rotation. - double scalarValue; ///< \ru Угол вращения/глубина выдавливания. \en Angle of rotation/depth of extrusion. - - /** \brief \ru Расстояние от поверхности. - \en Distance from the surface. \~ - \details \ru Расстояние от поверхности, до которой строим операцию. - Задавать при построении операции до поверхности (way = sw_surface). - distance < 0.0 при построении операции за поверхность, - distance > 0.0 при построении операции до поверхности. - \en Distance from the surface to construct up to. - Set when constructing operation to the surface (way = sw_surface). - distance < 0.0 when constructing operation back of surface, - distance > 0.0 when constructing operation front of surface. \~ - */ - double distance; - - /** \brief \ru Угол уклона. - \en Draft angle. \~ - \details \ru Угол уклона при выдавливании.\n - Операцию выдавливания с уклоном можно построить только в случае плоской образующей. - \en Draft angle when extruding.\n - Extrusion operation with draft can be constructed in the case of planar swept. \~ - */ - double rake; - -protected: - /** \brief \ru Поверхность, до которой строим операцию. - \en The surface to construct up to. \~ - \details \ru Поверхность, до которой строим операцию.\n - Задавать при построении операции до поверхности (way = sw_surface). - \en The surface to construct up to.\n - Set when constructing operation to the surface (way = sw_surface). \~ - */ - MbSurface * surface; - - /** \brief \ru Признак совпадения нормали поверхности с нормалью грани. - \en An attribute of coincidence between the surface normal and the face normal. \~ - \details \ru Признак совпадения нормали поверхности, до которой строим операцию, с нормалью грани.\n - Задавать при построении операции до поверхности (way = sw_surface).\n - Указывает положение оболочки-результата относительно поверхности. - Используется при построении массива операций до поверхности. - Если у всех элементов массива признак должен быть одинаковым, - то при построении исходной операции нужно задать признак равным orient_BOTH (направление не определено). - При построении признак будет определен, и его значение нужно использовать для построения остальных элементов массива. - \en An attribute of coincidence between the face normal and the normal of surface to which to create operation.\n - Set when constructing operation to the surface (way = sw_surface).\n - Specifies the position of shell-result relative to the surface. - Used when constructing the array of operations to the surface. - If attributes of all the elements of array must be the same, - then when constructing of the original operation need to set attribute which is equal to orient_BOTH (the direction is not determined). - When constructing the attribute is determined and its value should be used for the construction of other elements of the array. \~ - */ - MbeSenseValue sameSense; - - -public: - /** \brief \ru Конструктор по умолчанию. - \en Default constructor. \~ - \details \ru Задает параметры операции со способом "на заданную глубину". - Для построения операции параметры нужно изменить, - например, указать глубину выдавливания (угол вращения). - \en Sets parameters of the operation with the method "to a given depth". - For construction of operation the parameters need to change, - for example: specify the depth of extrusion (angle of rotation). \~ - */ - MbSweptSide() - : way ( sw_scalarValue ) - , scalarValue( 0.0 ) - , distance ( 0.0 ) - , rake ( 0.0 ) - , surface ( NULL ) - , sameSense ( orient_BOTH ) - {} - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор на угол вращения\глубину выдавливания. - \en Constructor by angle of rotation\depth of extrusion. \~ - \param[in] sVal - \ru Угол вращения\глубина выдавливания. - \en Angle of rotation\depth of extrusion. \~ - */ - MbSweptSide( double sVal ) - : way ( sw_scalarValue ) - , scalarValue( sVal ) - , distance ( 0.0 ) - , rake ( 0.0 ) - , surface ( NULL ) - , sameSense ( orient_BOTH ) - {} - - /** \brief \ru Конструктор до поверхности. - \en Constructor to the surface. \~ - \details \ru Конструктор до поверхности. Расстояние от поверхности задается равным 0.0. - \en Constructor to the surface. Distance from the surface is set to 0.0. \~ - \param[in] surf - \ru Поверхность, до которой строится операция. - \en The surface to construct up to. \~ - */ - MbSweptSide( MbSurface * surf ); - - /** \brief \ru Конструктор до поверхности. - \en Constructor to the surface. \~ - \details \ru Конструктор до поверхности. Для элемента массива. - \en Constructor to the surface. For array element. \~ - \param[in] surf - \ru Поверхность, до которой строится операция. - \en The surface to construct up to. \~ - \param[in] sense - \ru Признак совпадения нормали заданной поверхности с нормалью грани. - Указывает, по какую сторону от поверхности должна находиться построенная оболочка. - \en An attribute of coincidence between the normal of given surface and the face normal. - Indicates at which side of the surface the must be located constructed shell. \~ - */ - MbSweptSide( MbSurface * surf, MbeSenseValue sense ); - - /** \brief \ru Конструктор копирования. - \en Copy-constructor. \~ - \details \ru Конструктор копирования данных с использованием той же поверхности. - \en Copy-constructor of data with using of the same surface. \~ - \param[in] other - \ru Исходные параметры. - \en Initial parameters. \~ - */ - MbSweptSide( const MbSweptSide & other ); - - /** \brief \ru Конструктор копирования с регистратором. - \en Copy-constructor with the registrator. \~ - \details \ru Конструктор копирования с регистратором. Поверхность копируется. - \en Copy-constructor with the registrator. Surface is copying. \~ - \param[in] other - \ru Исходные параметры. - \en Initial parameters. \~ - */ - MbSweptSide( const MbSweptSide & other, MbRegDuplicate * ireg ); - - /// \ru Деструктор. \en Destructor. - virtual ~MbSweptSide(); - - /// \ru Оператор присваивания данных с использованием той же поверхности. \en Assignment operator of data with using of the same surface. - MbSweptSide & operator = ( const MbSweptSide & other ); - - /// \ru Получить поверхность. \en Get the surface. - MbSurface * GetSurface() const { return surface; } - /// \ru Заменить поверхность. \en Replace surface. - void SetSurface( MbSurface * s ); - - /// \ru Получить признак совпадения нормали поверхности с нормалью грани. \en Get the attribute of coincidence between the surface normal and the face normal. - MbeSenseValue GetSameSense() const { return sameSense; } - /// \ru Установить признак совпадения нормали поверхности с нормалью грани. \en Set the attribute of coincidence between the surface normal and the face normal. - void SetSameSense( MbeSenseValue sense ) { sameSense = sense; } - /// \ru Доступ к признаку совпадения нормали поверхности с нормалью грани. \en Access to the attribute of coincidence between the surface normal and the face normal. - MbeSenseValue & SetSameSense() { return sameSense; } - - /// \ru Являются ли объекты равными? \en Determine whether an object is equal? - bool IsSame( const MbSweptSide & other, double accuracy ) const - { - if ( (other.way == way) && (other.sameSense == sameSense) ) { - if ( (::fabs(other.scalarValue - scalarValue) < accuracy) && - (::fabs(other.distance - distance) < accuracy) && - (::fabs(other.rake - rake) < accuracy) ) - { - bool isSurf1 = (surface != NULL); - bool isSurf2 = (other.surface != NULL); - - if ( isSurf1 == isSurf2 ) { - if ( isSurf1 && isSurf2 ) { - if ( !other.surface->IsSame( *surface, accuracy ) ) - return false; - } - return true; - } - } - } - - return false; - } -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Параметры формообразующей операции. - \en The parameters of form-generating operation. \~ - \details \ru Параметры построения формообразующей операции - (например, выдавливания, вращения, кинематической, по сечениям). \n - \en The construction parameters of form-generating operation. - (for example: extrusion, rotation, sweeping, loft). \n \~ - \ingroup Build_Parameters -*/ -// --- -struct MATH_CLASS SweptValues { -public: - - /** \brief \ru Толщина стенки (величина эквидистанты) в прямом направлении. - \en Wall thickness (offset distance) along the forward direction. \~ - \details \ru Толщина стенки (величина эквидистанты) в положительном направлении нормали объекта - (грани, поверхности, плоскости кривой). - \en Wall thickness (offset distance) along the positive direction of the normal of an object - (face, surface, plane of the curve). \~ - */ - double thickness1; - - /** \brief \ru Толщина стенки (величина эквидистанты) в обратном направлении. - \en Wall thickness (offset distance) along the backward direction. \~ - \details \ru Толщина стенки (величина эквидистанты) в отрицательном направлении нормали объекта - (грани, поверхности, плоскости кривой). - \en Wall thickness (offset distance) along the negative direction of the normal of an object - (face, surface, plane of the curve). \~ - */ - double thickness2; - - bool shellClosed; ///< \ru Замкнутость оболочки. \en Closedness of shell. - -private: - bool checkSelfInt; ///< \ru Флаг проверки самопересечений (вычислительно "тяжелыми" методами). \en Flag for checking of self-intersection (computationally by "heavy" methods). - bool mergeFaces; ///< \ru Сливать подобные грани (true). \en Whether to merge similar faces (true). - -public: - /// \ru Конструктор по умолчанию. \en Default constructor. - SweptValues() - : thickness1 ( 0.0 ) - , thickness2 ( 0.0 ) - , shellClosed ( true ) - , checkSelfInt( true ) - , mergeFaces ( true ) - {} - /// \ru Конструктор по толщинам и замкнутости. \en Constructor by thicknesses and closedness. - SweptValues( double t1, double t2, bool c = true ) - : thickness1 ( t1 ) - , thickness2 ( t2 ) - , shellClosed ( c ) - , checkSelfInt( true ) - , mergeFaces ( true ) - {} - /// \ru Конструктор копирования. \en Copy-constructor. - SweptValues( const SweptValues & other ) - : thickness1 ( other.thickness1 ) - , thickness2 ( other.thickness2 ) - , shellClosed ( other.shellClosed ) - , checkSelfInt( other.checkSelfInt ) - , mergeFaces ( other.mergeFaces ) - {} - /// \ru Деструктор. \en Destructor. - virtual ~SweptValues() {} - -public: - /// \ru Это параметры выдавливания? \en This is extrusion parameters? - virtual bool IsExtrusionValues() const { return false; } - /// \ru Это параметры вращения? \en This is rotation parameters? - virtual bool IsRevolutionValues() const { return false; } - /// \ru Это параметры кинематики? \en This is "evolution" parameters? - virtual bool IsEvolutionValues() const { return false; } - /// \ru Это параметры операции по сечениям? \en This is "lofted" parameters? - virtual bool IsLoftedValues() const { return false; } - /// \ru Это параметры операции ребра жесткости? \en This is "rib" parameters? - virtual bool IsRibValues() const { return false; } - - /// \ru Определить, являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSame( const SweptValues & other, double accuracy ) const; - /// \ru Определить, являются ли объекты подобными. \en Determine whether the objects are similar. \~ - virtual bool IsSimilar( const MbSweptData & other ) const; - /// \ru Сделать объекты равным. \en Make objects equal. \~ - virtual bool SetEqual ( const MbSweptData & other ); - -public: - /// \ru Функция копирования данных. \en Function of copying data. - void Init( const SweptValues & other ) { - thickness1 = other.thickness1; - thickness2 = other.thickness2; - shellClosed = other.shellClosed; - checkSelfInt = other.checkSelfInt; - mergeFaces = other.mergeFaces; - } - - /// \ru Получить состояние замкнутости. \en Get the closedness state. - bool IsShellClosed() const { return shellClosed; } - /// \ru Установит состояние замкнутости. \en Set the closedness state. - void SetShellClosed( bool cl ) { shellClosed = cl; } - /// \ru Получить состояние флага проверки самопересечений. \en Get the state of flag of checking self-intersection. - bool CheckSelfInt() const { return checkSelfInt; } - /// \ru Установить состояние флага проверки самопересечений. \en Set the state of flag of checking self-intersection. - void SetCheckSelfInt( bool c ) { checkSelfInt = c; } - /// \ru Сливать подобные грани (true). \en Whether to merge similar faces (true). - bool MergeFaces() const { return mergeFaces; } - /// \ru Сливать подобные грани (true). \en Whether to merge similar faces (true). - void SetMergeFaces( bool mf ) { mergeFaces = mf; } - - /// \ru Оператор присваивания. \en Assignment operator. - void operator = ( const SweptValues & other ) { Init( other ); } - - KNOWN_OBJECTS_RW_REF_OPERATORS( SweptValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Параметры выдавливания или вращения. - \en The parameters of extrusion or rotation. \~ - \details \ru Параметры выдавливания или вращения кривых с опциями по направлениям. \n - В операции выдавливания прямым направлением считается направление, сонаправленное - с вектором выдавливания, а обратным - противоположное направление. - В операции вращения прямое направлением определяется по оси вращения с помощью правила правой руки. - \en The parameters of extrusion or rotation of curves with options along the directions. \n - In the extrusion operations the forward direction is the direction collinear - with the vector of extrusion and back - the opposite direction. - In the rotation operation the forward direction is determined by the axis of rotation using the right hand rule. \~ - \ingroup Build_Parameters -*/ -// --- -class MATH_CLASS SweptValuesAndSides: public SweptValues { -public: - MbSweptSide side1; ///< \ru Параметры выдавливания/вращения в прямом направлении. \en The parameters of extrusion/rotation along the forward direction. - MbSweptSide side2; ///< \ru Параметры выдавливания/вращения в обратном направлении. \en The parameters of extrusion/rotation along the backward direction. - -public: - /** \brief \ru Конструктор по умолчанию. - \en Default constructor. \~ - \details \ru Конструктор параметров для построения замкнутой оболочки без тонкой стенки. - Способ построение в обоих направлениях - на заданную глубину, равную 0.0. - \en Constructor of parameters for construction of closed shell without the thin wall. - Method of construction in both directions - to a given depth equal to 0.0. \~ - */ - SweptValuesAndSides() - : SweptValues() - , side1 () - , side2 () - {} - /** \brief \ru Конструктор по углам вращения или глубинам выдавливания. - \en Constructor by rotation angles and extrusion depths. \~ - \details \ru Конструктор параметров для построения замкнутой оболочки без тонкой стенки. - Способ построение в обоих направлениях - на заданную глубину. - \en Constructor of parameters for construction of closed shell without the thin wall. - Method of construction in both directions - to a given depth. \~ - \param[in] scalarValue1 - \ru Угол вращения\глубина выдавливания в прямом направлении. - \en Angle of rotation\depth of extrusion along the forward direction. \~ - \param[in] scalarValue2 - \ru Угол вращения\глубина выдавливания в обратном направлении. - \en Angle of rotation\depth of extrusion along the backward direction. \~ - */ - SweptValuesAndSides( double scalarValue1, double scalarValue2 ) - : SweptValues( ) - , side1 ( scalarValue1 ) - , side2 ( scalarValue2 ) - {} - /// \ru Конструктор копирования данных на тех же поверхностях. \en Copy-constructor of data on the same surfaces. - SweptValuesAndSides( const SweptValuesAndSides & other ) - : SweptValues( other ) - , side1 ( other.side1 ) - , side2 ( other.side2 ) - {} - /// \ru Конструктор полного копирования данных. \en Constructor of complete copying of data. - SweptValuesAndSides( const SweptValuesAndSides & other, MbRegDuplicate * ireg ) - : SweptValues( other ) - , side1 ( other.side1, ireg ) - , side2 ( other.side2, ireg ) - {} - /// \ru Деструктор. \en Destructor. - virtual ~SweptValuesAndSides(); - -public: - // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSame( const SweptValues & other, double accuracy ) const - { - const SweptValuesAndSides * obj = dynamic_cast( &other ); - if ( obj != NULL ) { - if ( side1.IsSame( obj->side1, accuracy ) && side2.IsSame( obj->side2, accuracy ) ) { - if ( obj->SweptValues::IsSame( *this, accuracy ) ) { - return true; - } - } - } - return false; - } - -public: - /// \ru Оператор присваивания данных на тех же поверхностях. \en Assignment operator of data copying on the same surfaces. - void operator = ( const SweptValuesAndSides & other ) { - SweptValues::Init( other ); - side1 = other.side1; - side2 = other.side2; - } - - /** \brief \ru Преобразовать согласно матрице. - \en Transform according to the matrix. \~ - \details \ru Преобразовать согласно матрице поверхности в прямом и обратном направлении. - \en Transform according to the matrix of surface in the forward and backward direction. \~ - \param[in] matr - \ru Матрица преобразования. - \en A transformation matrix. \~ - \param[in] iReg - \ru Регистратор. - \en Registrator. \~ - */ - void Transform( const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); - /** \brief \ru Сдвинуть вдоль вектора. - \en Move along a vector. \~ - \details \ru Сдвинуть вдоль вектора поверхности в прямом и обратном направлении. - \en Move along the vector of the surface along the forward and backward direction. \~ - \param[in] to - \ru Вектор сдвига. - \en Translation vector. \~ - \param[in] iReg - \ru Регистратор. - \en Registrator. \~ - */ - void Move ( const MbVector3D & to, MbRegTransform * iReg = NULL ); - /** \brief \ru Повернуть вокруг оси. - \en Rotate around an axis. \~ - \details \ru Повернуть вокруг оси поверхности в прямом и обратном направлении. - \en Rotate around the axis of the surface along the forward and backward direction. \~ - \param[in] axis - \ru Ось поворота. - \en The rotation axis. \~ - \param[in] angle - \ru Угол поворота. - \en The rotation angle. \~ - \param[in] iReg - \ru Регистратор. - \en Registrator. \~ - */ - void Rotate ( const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); - - /** \brief \ru Сделать копии поверхностей. - \en Make copies of surfaces. \~ - \details \ru Если в каком-либо направлении задана поверхность, заменить эту поверхность на ее копию. - \en If the surface is given in any direction, then replace the surface with its copy. \~ - \param[in] ireg - \ru Регистратор копий. - \en Registrator of copies. \~ - \return \ru true, если хотя бы одна поверхность имелась и сдублирована. - \en True if at least one surface is had and copied. \~ - */ - bool DuplicateSurfaces( MbRegDuplicate * ireg = NULL ); - - /// \ru Получить поверхность в положительном направлении. \en Get the surface along the positive direction. - MbSurface * GetSurface1() const { return side1.GetSurface(); } - /// \ru Получить поверхность в отрицательном направлении. \en Get the surface along the negative direction. - MbSurface * GetSurface2() const { return side2.GetSurface(); } - /// \ru Установить поверхность в положительном направлении. \en Set the surface along the positive direction. - void SetSurface1( MbSurface * s ) { side1.SetSurface( s ); } - /// \ru Установить поверхность в отрицательном направлении. \en Set the surface along the negative direction. - void SetSurface2( MbSurface * s ) { side2.SetSurface( s ); } - /// \ru Поменять поверхности местами. \en Swap surfaces. - void ExchangeSurfaces(); -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Параметры операции выдавливания. - \en The parameters of extrusion operation. \~ - \details \ru Параметры операции выдавливания кривых с опциями по направлениям. \n - \en The parameters of extrusion operation of curves with options along directions. \n \~ - \ingroup Build_Parameters -*/ -// --- -class MATH_CLASS ExtrusionValues : public SweptValuesAndSides { -public: - - /** \brief \ru Конструктор по умолчанию. - \en Default constructor. \~ - \details \ru Конструктор параметров выдавливания для построения замкнутой оболочки без тонкой стенки - в прямом направлении на величину, равную 10.0. - \en Constructor of extrusion parameters for construction of closed shell without the thin wall. - along the forward direction by value 10.0. \~ - */ - ExtrusionValues() - : SweptValuesAndSides( 10., 0. ) {} - /** \brief \ru Конструктор по глубинам выдавливания. - \en Constructor by extrusion depths. \~ - \details \ru Конструктор параметров выдавливания для построения замкнутой оболочки без тонкой стенки. - Способ построение в обоих направлениях - на заданную глубину. - \en Constructor of extrusion parameters for construction of closed shell without the thin wall. - Method of construction in both directions - to a given depth. \~ - \param[in] scalarValue1 - \ru Глубина выдавливания в прямом направлении. - \en Depth of extrusion along the forward direction. \~ - \param[in] scalarValue2 - \ru Глубина выдавливания в обратном направлении. - \en Depth of extrusion along the backward direction. \~ - */ - ExtrusionValues( double scalarValue1, double scalarValue2 ) - : SweptValuesAndSides( scalarValue1, scalarValue2 ) {} - /// \ru Конструктор копирования, на тех же поверхностях. \en Copy-constructor on the same surfaces. - ExtrusionValues( const ExtrusionValues & other ) - : SweptValuesAndSides( other ) {} - /// \ru Конструктор копирования. \en Copy-constructor. - ExtrusionValues( const ExtrusionValues & other, MbRegDuplicate * ireg ) - : SweptValuesAndSides( other, ireg ) {} - /// \ru Деструктор. \en Destructor. - virtual ~ExtrusionValues(); - -public: - // \ru Это параметры выдавливания? \en This is extrusion parameters? - virtual bool IsExtrusionValues() const { return true; } - - // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSame( const SweptValues & other, double accuracy ) const - { - const ExtrusionValues * obj = dynamic_cast( &other ); - if ( obj != NULL ) { - if ( obj->SweptValuesAndSides::IsSame( *this, accuracy ) ) - return true; - } - return false; - } - -public: - /// \ru Оператор присваивания, на тех же поверхностях. \en Assignment operator on the same surfaces. - ExtrusionValues & operator = ( const ExtrusionValues & other ) { - *static_cast(this) = *static_cast(&other); - return *this; - } - - KNOWN_OBJECTS_RW_REF_OPERATORS( ExtrusionValues ) // \ru Для работы со ссылками и объектами класса \en For treatment of references and objects of the class -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Параметры операции вращения. - \en The parameters of revolution operation. \~ - \details \ru Параметры операции вращения кривых с опциями по направлениям. \n - \en The parameters of revolution operation of curves with options along directions. \n \~ - \ingroup Build_Parameters -*/ -// --- -class MATH_CLASS RevolutionValues : public SweptValuesAndSides { -public: - /** \brief \ru Форма топологии. - \en Topology shape. \~ - \details \ru Форма топологии: 0 - тело типа сферы, 1 - тело типа тора.\n - Если образующая - не замкнутая плоская кривая, и ось вращения лежит в плоскости кривой, - то возможно построение тела вращения с топологией типа сферы. В этом случае образующая достраивается до оси вращения. - \en Topology shape: 0 - sphere, 1 - torus.\n - If swept is non-closed planar curve and axis of rotation lies on the curve plane, - then is possible to construct revolution solids with the topology of sphere type. In this case the swept is being updated to the rotation axis. -I \~ */ - int shape; - -public: - /** \brief \ru Конструктор по умолчанию. - \en Default constructor. \~ - \details \ru Конструктор параметров вращения для построения замкнутой оболочки типа тора - без тонкой стенки в прямом направлении на полный оборот. - \en Constructor of revolution parameters for construction of closed shell of torus type - without thin wall along the forward direction at full turn. \~ - */ - RevolutionValues() - : SweptValuesAndSides( M_PI, 0. ) - , shape( 1 ) - {} - /** \brief \ru Конструктор по углам вращения. - \en Constructor by revolution angles. \~ - \details \ru Конструктор параметров вращения для построения замкнутой оболочки без тонкой стенки. - Способ построение в обоих направлениях - на заданную глубину (заданный угол). - \en Constructor of revolution parameters for construction of closed shell without the thin wall. - Method of construction in both directions - to a given depth (given angle). \~ - \param[in] scalarValue1 - \ru Угол вращение в прямом направлении. - \en Revolution angle along the forward direction. \~ - \param[in] scalarValue2 - \ru Угол вращения в обратном направлении. - \en Revolution angle along the backward direction. \~ - \param[in] s - \ru Форма топологии. - \en Topology shape. \~ - */ - RevolutionValues( double scalarValue1, double scalarValue2, int s ) - : SweptValuesAndSides( scalarValue1, scalarValue2 ) - , shape( s ) - {} - /// \ru Конструктор копирования, на тех же поверхностях. \en Copy-constructor on the same surfaces. - RevolutionValues( const RevolutionValues & other ) - : SweptValuesAndSides( other ) - , shape( other.shape ) - {} - /// \ru Конструктор копирования. \en Copy-constructor. - RevolutionValues( const RevolutionValues & other, MbRegDuplicate * ireg ) - : SweptValuesAndSides( other, ireg ) - , shape( other.shape ) - {} - /// \ru Деструктор. \en Destructor. - virtual ~RevolutionValues(); - -public: - // \ru Это параметры вращения? \en This is rotation parameters? - virtual bool IsRevolutionValues() const { return true; } - - // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSame( const SweptValues & other, double accuracy ) const - { - const RevolutionValues * obj = dynamic_cast( &other ); - if ( obj != NULL ) { - if ( obj->shape == shape ) { - if ( obj->SweptValuesAndSides::IsSame( *this, accuracy ) ) - return true; - } - } - return false; - } - -public: - /// \ru Оператор присваивания, на тех же поверхностях. \en Assignment operator on the same surfaces. - RevolutionValues & operator = ( const RevolutionValues & other ) { - *static_cast(this) = *static_cast(&other); - shape = other.shape; - return *this; - } - - KNOWN_OBJECTS_RW_REF_OPERATORS( RevolutionValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Параметры кинематической операции. - \en Parameters of the sweeping operation. \~ - \details \ru Параметры операции движения образующей по направляющей кривой. \n - \en The operation parameters of moving the generating curve along the spine curve. \n \~ - \ingroup Build_Parameters -*/ -// --- -struct MATH_CLASS EvolutionValues : public SweptValues { -public: - /** \brief \ru Способ переноса образующего контура вдоль направляющей. - \en Moving method of generating contour along the spine curve. \~ - \details \ru Способ переноса образующего контура вдоль направляющей: \n - parallel <= 0 - образующая переносится параллельно самой себе; \n - parallel == 1 - образующая при переносе сохраняет исходный угол с направляющей; \n - parallel >= 2 - плоскость образующей выставляется и сохраняется ортогональной направляющей. \n - \en Moving method of generating contour along the spine curve: \n - parallel <= 0 - generating curve is moved parallel to itself; \n - parallel == 1 - generating curve when moving preserves initial angle with spine; \n - parallel >= 2 - plane of generating curve is set and saved as orthogonal to spine. \n \~ - */ - int parallel; - // \ru Данные о функциях изменения образующих кривых вдоль напрвлябшей кривой (могут быть NULL). \en Data about changes of generating curves along the guide curve (can be NULL). - double range; ///< \ru Эквидистантное смещение точек образующей кривой в конце траектории. \en The offset range of generating curve on the end of spine curve. - SPtr scaling; ///< \ru Функция масштабирования образующей кривой. \en The fanction of curve scale. - SPtr winding; ///< \ru Функция вращения образующей кривой. \en The fanction of curve rotation. - -public: - - /** \brief \ru Конструктор по умолчанию. - \en Default constructor. \~ - \details \ru Конструктор параметров кинематической операции для построения замкнутой оболочки - без тонкой стенки с сохранением угла наклона. - \en Constructor of sweeping operation parameters for construction of closed shell - without the thin wall with keeping the angle inclination. \~ - */ - EvolutionValues() - : SweptValues( ) - , parallel ( 1 ) - , range ( 0.0 ) - , scaling ( NULL ) - , winding ( NULL ) - {} - /// \ru Конструктор копирования. \en Copy-constructor. - EvolutionValues( const EvolutionValues & other ); - /// \ru Деструктор. \en Destructor. - virtual ~EvolutionValues(); - -public: - // \ru Это параметры кинематики? \en This is "evolution" parameters? - virtual bool IsEvolutionValues() const { return true; } - - // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSame( const SweptValues & other, double accuracy ) const; - // \ru Определить, являются ли объекты подобными. \en Determine whether the objects are similar. \~ - virtual bool IsSimilar( const SweptValues & other ) const; - // \ru Сделать объекты равным. \en Make objects equal. \~ - virtual bool SetEqual ( const SweptValues & other ); - - /// \ru Выдать функцию масштабирования образующей кривой. \en Get the fanction of curve scale. - double GetRange() const { return range; } - double & SetRange() { return range; } - void SetRange( double r ) { range = r; } - - /** \brief \ru Добавить данные. - \en Add data. \~ - \details \ru Добавить данные об изменении образующих контурах на поверхности вдоль образующей кривой. - \en Add data about changes of generatig contours on the surface along the guide curve. \~ - \param[in] _scaling - \ru Масштабирование. - \en The scaling. \~ - \param[in] _winding - \ru Поворот. - \en The winding. \~ - */ - bool AddData( MbFunction & _scaling, MbFunction & _winding ); - - /// \ru Выдать функцию масштабирования образующей кривой. \en Get the fanction of curve scale. - const MbFunction* GetScaling() const { return scaling; } - MbFunction * SetScaling() { return scaling; } - - /// \ru Выдать функцию вращения образующей кривой. \en Get the fanction of curve rotation. - const MbFunction* GetWinding() const { return winding; } - MbFunction * SetWinding() { return winding; } - -public: - /// \ru Оператор присваивания. \en Assignment operator. - EvolutionValues & operator = ( const EvolutionValues & other ); - - KNOWN_OBJECTS_RW_REF_OPERATORS( EvolutionValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Параметры операции построения тела по плоским сечениям. - \en The operation parameters of constructing solid by lofted. \~ - \details \ru Параметры операции построения тела по плоским сечениям, заданных контурами. \n - \en The parameters of constructing operation by lofted which are given by contours. \n \~ - \ingroup Build_Parameters -*/ -// --- -struct MATH_CLASS LoftedValues : public SweptValues { -public: - bool closed; ///< \ru Замкнутость трубки сечений. \en Closedness of tube. - MbVector3D vector1; ///< \ru Производная в начале. \en The derivative at the start. - MbVector3D vector2; ///< \ru Производная в конце. \en The derivative at the end. - bool setNormal1; ///< \ru Установлена нормаль в начале. \en The normal is set at the start. - bool setNormal2; ///< \ru Установлена нормаль в конце. \en The normal is set at the end. - -public: - /** \brief \ru Конструктор по умолчанию. - \en Default constructor. \~ - \details \ru Конструктор параметров операции по сечениям для построения замкнутой оболочки без тонкой стенки. - \en Constructor of lofted operation parameters for construction of closed shell without the thin wall. \~ - */ - LoftedValues() - : SweptValues ( ) - , closed ( false ) - , vector1 ( 0.0, 0.0, 0.0 ) - , vector2 ( 0.0, 0.0, 0.0 ) - , setNormal1 ( false ) - , setNormal2 ( false ) - {} - /// \ru Конструктор копирования. \en Copy-constructor. - LoftedValues( const LoftedValues & other ) - : SweptValues ( other ) - , closed ( other.closed ) - , vector1 ( other.vector1 ) - , vector2 ( other.vector2 ) - , setNormal1 ( other.setNormal1 ) - , setNormal2 ( other.setNormal2 ) - {} - /// \ru Оператор присваивания. \en Assignment operator. - LoftedValues & operator = ( const LoftedValues & other ) - { - SweptValues::Init( other ); - closed = other.closed; - vector1 = other.vector1; - vector2 = other.vector2; - setNormal1 = other.setNormal1; - setNormal2 = other.setNormal2; - return *this; - } - /// \ru Деструктор. \en Destructor. - virtual ~LoftedValues(); - -public: - // \ru Это параметры операции по сечениям? \en This is "lofted" parameters? - virtual bool IsLoftedValues() const { return true; } - - // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSame( const SweptValues & other, double accuracy ) const - { - const LoftedValues * obj = dynamic_cast( &other ); - if ( obj != NULL ) { - if ( obj->closed == closed ) { - if ( c3d::EqualVectors(vector1, obj->vector1, accuracy) && c3d::EqualVectors(vector2, obj->vector2, accuracy) ) { - if ( obj->setNormal1 == setNormal1 && obj->setNormal2 == setNormal2 ) { - if ( obj->SweptValues::IsSame(*this, accuracy) ) { - return true; - } - } - } - } - } - return false; - } - -public: - /// \ru Преобразовать объект согласно матрице. \en Transform an object according to the matrix. - void Transform( const MbMatrix3D & matr ); - /// \ru Сдвинуть объект вдоль вектора. \en Move an object along a vector. - void Move ( const MbVector3D & to ); - /// \ru Повернуть объект вокруг оси на заданный угол. \en Rotate an object at a given angle around an axis. - void Rotate ( const MbAxis3D & axis, double ang ); - - KNOWN_OBJECTS_RW_REF_OPERATORS( LoftedValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Параметры ребра жёсткости. - \en Parameters of a rib. \~ - \details \ru Параметры построения ребра жёсткости по кривой, задающей его форму. \n - \en The construction parameters of rib by curve gives its shape. \n \~ - \ingroup Build_Parameters -*/ -// --- -struct MATH_CLASS RibValues : public SweptValues { -public: - /** \brief \ru Сторона заполнения пространства телом ребра. - \en The side to place the rib on. \~ - \details \ru С какой стороны от кривой располагается ребро. \n - \en With which side of the curve is rib. \n \~ - \ingroup Build_Parameters - */ - enum ExtrudeSide { - es_Left = 0, ///< \ru Ребро выдавливается в левую сторону от кривой вдоль плоскости. \en Rib is extruded to the left side of the curve along the plane. - es_Right, ///< \ru Ребро выдавливается в правую сторону от кривой вдоль плоскости. \en Rib is extruded to the right side of the curve along the plane. - es_Up, ///< \ru Ребро выдавливается в сторону нормали плоскости. \en Rib is extruded to the side of the surface normal. - es_Down, ///< \ru Ребро выдавливается в сторону против нормали плоскости. \en Rib is extruded to the side opposite to the surface normal. - }; - -public: - double angle1; ///< \ru Угол уклона плоскости в прямом направлении. \en Draft angle of the plane along the forward direction. - double angle2; ///< \ru Угол уклона плоскости в обратном направлении. \en Draft angle of the plane along the backward direction. - ExtrudeSide side; ///< \ru Сторона заполнения пространства телом ребра. \en The side to place the rib on. - -public: - /// \ru Конструктор по умолчанию. \en Default constructor. - RibValues() - : SweptValues( ) - , angle1 ( 0.0 ) - , angle2 ( 0.0 ) - , side ( es_Right ) - {} - /// \ru Конструктор по толщинам, углам и стороне заполнения пространства. \en Constructor by thickness, angles and filling space. - RibValues( double t1, double t2, double a1, double a2, int s ) - : SweptValues( t1, t2 ) - , angle1 ( a1 ) - , angle2 ( a2 ) - , side ( (ExtrudeSide)s ) - {} - /// \ru Конструктор копирования. \en Copy-constructor. - RibValues( const RibValues & other ) - : SweptValues( other ) - , angle1 ( other.angle1 ) - , angle2 ( other.angle2 ) - , side ( other.side ) - {} - /// \ru Деструктор. \en Destructor. - virtual ~RibValues(); - -public: - // \ru Это параметры операции ребра жесткости? \en This is "rib" parameters? - virtual bool IsRibValues() const { return true; } - - // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSame( const SweptValues & other, double accuracy ) const - { - const RibValues * obj = dynamic_cast( &other ); - - if ( obj != NULL ) { - if ( obj->side == side ) { - if ( ::fabs(obj->angle1 - angle1) < accuracy && ::fabs(obj->angle2 - angle2) < accuracy ) - return SweptValues::IsSame( *obj, accuracy ); - } - } - return false; - } - -public: - /// \ru Функция копирования. \en Copy function. - void Init( const RibValues & other ) - { - SweptValues::Init( other ); - angle1 = other.angle1; - angle2 = other.angle2; - side = other.side; - } - /// \ru Оператор присваивания. \en Assignment operator. - RibValues & operator = ( const RibValues & other ) - { - Init( other ); - return *this; - } - - KNOWN_OBJECTS_RW_REF_OPERATORS( RibValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Параметры ребра жёсткости листового тела. - \en Parameters of a sheet metal rib. \~ - \details \ru Параметры построения ребра жёсткости листового тела по кривой, задающей его форму. \n - \en The construction parameters of a sheet metal rib by curve gives its shape. \n \~ -\ingroup Build_Parameters -*/ -// --- -struct MATH_CLASS SheetRibValues: public RibValues { -public: - double radRibConvex; ///< \ru Радиус скругления выпуклой части ребра жесткости. \en Fillet radius of convex part of rib. - double radSideConcave; ///< \ru Радиус скругления примыкания вогнутой части ребра жесткости к листовому телу. \en Fillet radius of connection of concave part of rib and metal sheet. - -public: - /// \ru Конструктор по умолчанию. \en Default constructor. - SheetRibValues() - : RibValues ( ) - , radRibConvex ( 0.0 ) - , radSideConcave( 0.0 ) - {} - /// \ru Конструктор по параметрам. \en Constructor by parameters. - SheetRibValues( double t1, double t2, double a1, double a2, int s, double rFilletRib, const double & rFilletSide ) - : RibValues ( t1, t2, a1, a2, s ) - , radRibConvex ( ::fabs(rFilletRib) ) - , radSideConcave( ::fabs(rFilletSide) ) - {} - /// \ru Конструктор копирования. \en Copy-constructor. - SheetRibValues( const SheetRibValues & other ) - : RibValues ( other ) - , radRibConvex ( other.radRibConvex ) - , radSideConcave( other.radSideConcave ) - {} - /// \ru Деструктор. \en Destructor. - virtual ~SheetRibValues(); - -public: - // \ru Являются ли объекты равными? \en Determine whether an object is equal? - virtual bool IsSame( const SweptValues & other, double accuracy ) const - { - const SheetRibValues * obj = dynamic_cast( &other ); - - if ( obj != NULL ) { - if ( (::fabs(radRibConvex - obj->radRibConvex) < accuracy) && (::fabs(radSideConcave - obj->radSideConcave) < accuracy) ) - return RibValues::IsSame( *obj, accuracy ); - } - return false; - } - -public: - /// \ru Функция копирования. \en Copy function. - void Init( const SheetRibValues & other ) - { - RibValues::Init( other ); - radRibConvex = other.radRibConvex; - radSideConcave = other.radSideConcave; - } - - /// \ru Оператор присваивания. \en Assignment operator. - SheetRibValues & operator = ( const SheetRibValues & other ) { - Init( other ); - return *this; - } - - /// \ru Преобразовать объект согласно матрице. \en Transform an object according to the matrix. - void Transform( const MbMatrix3D & matr ); - - KNOWN_OBJECTS_RW_REF_OPERATORS( SheetRibValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. -}; - -//------------------------------------------------------------------------------ -/** \brief \ru Параметры булевой операции выдавливания или вращения до объекта. - \en The parameters of Boolean operation of extrusion or revolution to object. \~ - \details \ru Параметры булевой операции выдавливания или вращения до объекта. \n - Используется при булевой операции исходного тела - и построенной операции выдавливания или вращения двумерных контуров на поверхности. - \en The parameters of Boolean operation of extrusion or revolution to object. \n - Used in Boolean operation of initial solid - and constructed operation of extrusion or revolution of two-dimensional contours on the surface. \~ - \ingroup Build_Parameters -*/ -// --- -struct MATH_CLASS MbSweptLayout { - /** \brief \ru Направление выдавливания (вращения). - \en A direction of extrusion (revolution). \~ - \details \ru Направление выдавливания (вращения) по отношению к вектору выдавливания (оси вращения). - \en The direction of extrusion relative to the extrusion vector. \~ - */ - enum Direction { - ed_minus_minus = -2, ///< \ru В обратном направлении, для обеих строн. \en Along the backward direction, for both sides. - ed_minus = -1, ///< \ru В обратном направлении, для одной стороны. \en Along the backward direction, for one sides. - ed_both = 0, ///< \ru В обоих направлениях. \en Along both directions. - ed_plus = 1, ///< \ru В прямом направлении, для одной стороны. \en Along the forward direction, for one sides. - ed_plus_plus = 2, ///< \ru В прямом направлении, для обеих сторон. \en Along the forward direction, for both sides. - }; - Direction direction; ///< \ru Направление выдавливания относительно вектора. \en The direction of extrusion relative to the vector. - bool skipUnion; ///< \ru Создавать новое тело (Не приклеивать к телу). \en Create a new solid. - -protected: - SPtr surface; ///< \ru Поверхность, на которой размещена образующая. \en The surface, which contains the generating curve. - -protected: - /// \ru Конструктор. \en Constructor. - MbSweptLayout( const MbSurface & surf, Direction dir ) : surface( &surf ), direction( dir ), skipUnion( false ) {} - /// \ru Конструктор копирования. \en Copy-constructor. - MbSweptLayout( const MbSweptLayout & other ) : surface( other.surface ), direction( other.direction ), skipUnion( other.skipUnion ) {} - /// \ru Деструктор. \en Destructor. - virtual ~MbSweptLayout(); -public: - /// \ru Получить поверхность. \en Get the surface. - const MbSurface & GetSurface() const { return *surface; } - - /// \ru Создавать новое тело (Не приклеивать к телу). \en Create a new solid. - bool SkipUnion() const { return skipUnion; } - /// \ru Создавать новое тело (Не приклеивать к телу). \en Create a new solid. - void SkipUnion( bool su ) { skipUnion = su; } -public: - /// \ru Это параметры выдавливания? \en This is extrusion parameters? - virtual bool IsExtrusionLayout() const { return false; } - /// \ru Это параметры вращения? \en This is rotation parameters? - virtual bool IsRevolutionLayout() const { return false; } -public: - /// \ru Классификация точки относительно несущей поверхности. \en Classification point relative to the surface. - MbeItemLocation PointRelative( const MbCartPoint3D & p ) const; -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - MbSweptLayout & operator = ( const MbSweptLayout & ); -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Параметры булевой операции выдавливания до объекта. - \en The parameters of Boolean operation of extrusion to object. \~ - \details \ru Параметры булевой операции выдавливания до объекта. \n - Используется при булевой операции исходного тела - и построенной операции выдавливания двумерных контуров на поверхности. - \en The parameters of Boolean operation of extrusion to object. \n - Used in Boolean operation of initial solid - and constructed operation of extrusion of two-dimensional contours on the surface. \~ - \ingroup Build_Parameters -*/ -// --- -struct MATH_CLASS MbExtrusionLayout : public MbSweptLayout { - MbVector3D dirVector; ///< \ru Вектор выдавливания. \en An extrusion vector. -public: - /// \ru Конструктор. \en Constructor. - MbExtrusionLayout( const MbSurface & surf, Direction dir, const MbVector3D & dirVec ) : MbSweptLayout( surf, dir ), dirVector( dirVec ) {} - /// \ru Конструктор копирования. \en Copy-constructor. - MbExtrusionLayout( const MbExtrusionLayout & other ) : MbSweptLayout( other ), dirVector( other.dirVector ) {} - /// \ru Деструктор. \en Destructor. - virtual ~MbExtrusionLayout(); -public: - /// \ru Это параметры выдавливания? \en This is extrusion parameters? - virtual bool IsExtrusionLayout() const { return true; } -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - void operator = ( const MbExtrusionLayout & ); -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Параметры булевой операции вращения до объекта. - \en The parameters of Boolean operation of revolution to object. \~ - \details \ru Параметры булевой операции вращения до объекта. \n - Используется при булевой операции исходного тела - и построенной операции вращения двумерных контуров на поверхности. - \en The parameters of Boolean operation of revolution to object. \n - Used in Boolean operation of initial solid - and constructed operation of revolution of two-dimensional contours on the surface. \~ - \ingroup Build_Parameters -*/ -// --- -struct MATH_CLASS MbRevolutionLayout : public MbSweptLayout { - MbAxis3D revAxis; ///< \ru Ось вращения. \en An revolution axis. -public: - /// \ru Конструктор. \en Constructor. - MbRevolutionLayout( const MbSurface & surf, Direction dir, const MbAxis3D & rotAxis ) : MbSweptLayout( surf, dir ), revAxis( rotAxis ) {} - /// \ru Конструктор копирования. \en Copy-constructor. - MbRevolutionLayout( const MbRevolutionLayout & other ) : MbSweptLayout( other ), revAxis( other.revAxis ) {} - /// \ru Деструктор. \en Destructor. - virtual ~MbRevolutionLayout(); -public: - /// \ru Это параметры вращения? \en This is rotation parameters? - virtual bool IsRevolutionLayout() const { return true; } -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - void operator = ( const MbRevolutionLayout & ); -}; - - -#endif // __OP_SHELL_PARAMETERS_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Параметры операций над телами. + \en Parameters of operations on the solids. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __OP_SWEPT_PARAMETERS_H +#define __OP_SWEPT_PARAMETERS_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class MATH_CLASS MbPlacement3D; +class MATH_CLASS MbMatrix3D; +class MATH_CLASS MbAxis3D; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbPolyCurve; +class MbRegTransform; +class MbRegDuplicate; + + +//------------------------------------------------------------------------------ +/** \brief \ru Данные об образующей. + \en The generating data. \~ + \details \ru Данные об образующей операции движения. \n + Образующая операции выдавливания, вращения или кинематической операции + может включать в себя набор двумерных контуров, набор трехмерных контуров, тело. \n + Для набора двумерных контуров на поверхности существуют следующие ограничения:\n + – может быть один или несколько контуров;\n + – если контуров несколько, они должны быть либо все замкнуты, либо все разомкнуты;\n + - если контуры замкнуты, они могут быть вложенными друг в друга, уровень вложенности не ограничивается;\n + – контуры не должны пересекаться между собой или самопересекаться.\n + Для двумерных контуров на не плоской поверхности есть дополнительное ограничение: + все контуры должны быть замкнуты.\n + Построение операции по двумерным контурам на не плоской поверхности рассчитано на указание пользователем + грани тела в качестве образующей. В этом случае данные для образующей можно получить + с помощью метода грани MbFace::GetSurfaceCurvesData.\n + Ограничения для трехмерных контуров:\n + – контуры не должны пересекаться между собой или самопересекаться.\n + \en Data about generating of movement operation. \n + При указании тела и поверхности одновременно предполагается, что выполняется кинематическая операция над + телом вдоль кривой на этой поверхности, причем движение согласовано с нормалью. \n + Generating of extrusion operation, rotation or sweeping operation + can include a set of two-dimensional contours, a set of three-dimensional contours, solid. \n + For a set of two-dimensional contours on the surface, the following restrictions:\n + - can be one or multiple contours;\n + - If there are multiple contours, all of them must be either closed or open;\n + - if contours are closed, then they can be nested into each other, the level of nesting is not limited;\n + - contours can't overlap each other or self-intersect.\n + For two-dimensional contour on the non-planar surface is additional constraint: + all the contours must be closed.\n + Constructing operation by two-dimensional contours on non-planar surface it is necessary to specify the by the user + face of solid as generating. In this case, the generating data can be obtained + by the method of face MbFace::GetSurfaceCurvesData.\n + Constraints for three-dimensional contour:\n + - contours can't overlap each other or self-intersect.\n + When set a solid and a surface at the same time, we suppose that sweeping operation over solid along curve on surface + is done, and moving is according to surface normal. \n \~ + \ingroup Build_Parameters +*/ +// --- +class MATH_CLASS MbSweptData { + +private: + // \ru Данные о двумерных контурах на поверхности. \en Data about two-dimensional contours on the surface. + c3d::SurfaceSPtr surface; ///< \ru Поверхность. \en The surface. + c3d::PlaneContoursSPtrVector contours; ///< \ru Множество двумерных контуров. \en Set of two-dimensional contours. + // \ru Трехмерные контуры. \en Three-dimensional contours. + c3d::SpaceContoursSPtrVector contours3D; ///< \ru Множество трёхмерных контуров. \en Set of three-dimensional contours. + // \ru Тело. \en Solid. + c3d::SolidSPtr solid; ///< \ru Тело. \en A solid. + +public: + /// \ru Конструктор по умолчанию. \en Default constructor. + MbSweptData(); + /// \ru Конструктор копирования. \en Copy-constructor. + MbSweptData( const MbSweptData &, MbRegDuplicate * ireg = NULL ); + +public: + + /** \brief \ru Конструктор плоской образующей. + \en Constructor of planar swept. \~ + \details \ru Конструктор плоской образующей из одного контура. + \en Constructor of planar swept from one contour. \~ + \param[in] place - \ru Локальная система координат. + \en A local coordinate system. \~ + \param[in] contour - \ru Контур в параметрах заданной системы координат. Используется оригинал. + \en Contour in parameters of the given coordinate system. Used original. \~ + */ + MbSweptData( const MbPlacement3D & place, MbContour & contour ); + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор по набору контуров на поверхности. + \en Constructor by a set of contours on a surface. \~ + \param[in] _surface - \ru Поверхность. Используется оригинал. + \en The surface. Used original. \~ + \param[in] _contours - \ru Набор контуров. Используются оригиналы. + \en A set of contours. Used originals. \~ + */ + MbSweptData( MbSurface & _surface, RPArray & _contours ); + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор по набору контуров на поверхности. + \en Constructor by a set of contours on a surface. \~ + \param[in] _surface - \ru Поверхность. Используется оригинал. + \en The surface. Used original. \~ + \param[in] _contours - \ru Набор контуров. Используются оригиналы. + \en A set of contours. Used originals. \~ + */ + MbSweptData( MbSurface & _surface, c3d::PlaneContoursSPtrVector & _contours ); + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор по кривой. + \en Constructor by a contour. \~ + \param[in] _contour3d - \ru Кривая. Используются оригиналы. + \en A curve. Used originals. \~ + */ + MbSweptData( MbCurve3D & _curve3d ); + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор по контуру. + \en Constructor by a contour. \~ + \param[in] _contour3d - \ru Контур. Используются оригиналы. + \en A contour. Used originals. \~ + */ + MbSweptData( MbContour3D & _contour3d ); + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор по набору пространственных контуров. + \en Constructor by a set of spatial contours. \~ + \param[in] _contours3d - \ru Набор контуров. Используются оригиналы. + \en A set of contours. Used originals. \~ + */ + MbSweptData( RPArray & _contours3d ); + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор по набору пространственных контуров. + \en Constructor by a set of spatial contours. \~ + \param[in] _contours3d - \ru Набор контуров. Используются оригиналы. + \en A set of contours. Used originals. \~ + */ + MbSweptData( c3d::SpaceContoursSPtrVector & _contours3d ); + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор по телу. + \en Constructor by a solid. \~ + \param[in] _solid - \ru Тело. Используется оригинал объекта. + \en A solid. Used original of object. \~ + \param[in] _newMainName - \ru Новое главное имя для топологических элементов тела. + \en New main name for names of solid's topological elements. \~ + */ + MbSweptData( MbSolid & _solid, SimpleName newMainName = c3d::SIMPLENAME_MAX ); + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор смешанной образующей. + \en Constructor of mixed swept. \~ + \param[in] _surface - \ru Поверхность. Используется оригинал. + \en The surface. Used original. \~ + \param[in] _contours - \ru Набор двумерных контуров в параметрах заданной поверхности. Используются оригиналы. + \en Set of two-dimensional contours in the parameters of the given surface. Used originals. \~ + \param[in] _contours3d - \ru Набор трехмерных контуров. Используются оригиналы. + \en A set of three-dimensional contours. Used originals. \~ + \param[in] _solid - \ru Тело. Используется оригинал объекта. + \en A solid. Used original of object. \~ + */ + MbSweptData( MbSurface * _surface, RPArray & _contours, + RPArray & _contours3d, MbSolid * _solid ); + + /// \ru Деструктор. \en Destructor. + ~MbSweptData(); + +public: + /** \brief \ru Добавить данные. + \en Add data. \~ + \details \ru Добавить данные о контурах на поверхности. + \en Add data about contours to the surface. \~ + \param[in] _surface - \ru Поверхность. Добавляется оригинал объекта. + \en The surface. Added original of the object. \~ + \param[in] _contours - \ru Набор контуров. Добавляются оригиналы. + \en A set of contours. Originals are added. \~ + */ + bool AddData( MbSurface & _surface, const RPArray & _contours ); + + /** \brief \ru Добавить данные. + \en Add data. \~ + \details \ru Добавить данные о контурах на поверхности. + \en Add data about contours to the surface. \~ + \param[in] _surface - \ru Поверхность. Добавляется оригинал объекта. + \en The surface. Added original of the object. \~ + \param[in] _contours - \ru Набор контуров. Добавляются оригиналы. + \en A set of contours. Originals are added. \~ + */ + bool AddData( MbSurface & _surface, c3d::PlaneContoursSPtrVector & _contours ); + + /** \brief \ru Количество всех кривых. + \en The count of all the curves. \~ + \details \ru Общее количество двумерных и трехмерных кривых. + \en The total count of two and three-dimensional curves. \~ + */ + size_t CurvesCount() const; + + /** \brief \ru Получить кривую по индексу. + \en Get the curve by the index. \~ + \details \ru Получить кривую из множества кривых на поверхности и трехмерных кривых. + \en Get the curve from set of curves on the surface and three-dimensional curves. \~ + \param[in] i - \ru Номер кривой в пределах от 0 до CurvesCount(). + \en The index of curve from 0 to CurvesCount(). \~ + \return \ru Кривую на поверхности или трехмерную кривую. + \en Curve on the surface or three-dimensional curve. \~ + */ + SPtr GetCurve3D( size_t i ) const; + + /// \ru Есть данные о двумерных кривых на поверхности? \en Is there data of two-dimensional curves on the surface? + bool IsSurfaceCurvesData() const { return ((surface != NULL) && !contours.empty()); } + /// \ru Есть данные о пространственных кривых? \en Is there data of spatial curves? + bool IsSpaceCurvesData() const { return !contours3D.empty(); } + /// \ru Есть данные о теле? \en Is there data about the solid? + bool IsSolidData() const { return (solid != NULL); } + + /// \ru Выдать поверхность. \en Get the surface. + const MbSurface * GetSurface() const { return surface; } + /// \ru Выдать поверхность для изменения. \en Get the surface for editing. + MbSurface * SetSurface() { return surface; } + + /// \ru Положить поверхность. \en Set a surface. + + /** \brief \ru Установить поверхность. + \en Set a surface. \~ + \details \ru Установить новую поверхность как носитель двумерных контуров или как целевую поверхность для направляющей. + \en Set surface carrier of two-dimensional contours or desired surface-carrier of guide curve. \~ + \param[in] surf - \ru Новая поверхность как носитель для двумерных контуров или целевая поверхность для направляющей. + \en Surface carrier of two-dimensional contours or desired surface-carrier of guide curve. \~ + */ + void SetSurface( const MbSurface & surf ) { surface = const_cast( &surf ); } + + /// \ru Выдать набор двумерных контуров. \en Get the set of two-dimensional contours. + const c3d::PlaneContoursSPtrVector & GetContours() const { return contours; } + /// \ru Выдать набор трехмерных контуров. \en Get the set of three-dimensional contours. + const c3d::SpaceContoursSPtrVector & GetContours3D() const { return contours3D; } + /// \ru Выдать тело. \en Get the solid. + const MbSolid * GetSolid() const { return solid; } + /// \ru Выдать тело для изменения. \en Get the solid for editing. + MbSolid * SetSolid() const { return solid; } + + /** \brief \ru Преобразовать объект. + \en Transform the object. \~ + \details \ru Преобразовать исходный объект согласно матрице c использованием регистратора. + \en Transform the initial object according to the matrix using the registrator. \~ + \param[in] matr - \ru Матрица преобразования. + \en A transformation matrix. \~ + \param[in] iReg - \ru Регистратор. + \en Registrator. \~ + */ + void Transform( const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); + /** \brief \ru Сдвинуть объект. + \en Move the object. \~ + \details \ru Сдвинуть геометрический объект вдоль вектора с использованием регистратора. + \en Move a geometric object along the vector using the registrator. \~ + \param[in] to - \ru Вектор сдвига. + \en Translation vector. \~ + \param[in] iReg - \ru Регистратор. + \en Registrator. \~ + */ + void Move ( const MbVector3D & to, MbRegTransform * iReg = NULL ); + /** \brief \ru Повернуть объект. + \en Rotate the object. \~ + \details \ru Повернуть объект вокруг оси на заданный угол с использованием регистратора. + \en Rotate an object about the axis by the given angle using the registrator. \~ + \param[in] axis - \ru Ось поворота. + \en The rotation axis. \~ + \param[in] angle - \ru Угол поворота. + \en The rotation angle. \~ + \param[in] iReg - \ru Регистратор. + \en Registrator. \~ + */ + void Rotate ( const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); + /** \brief \ru Определить, являются ли объекты равными. + \en Determine whether the objects are equal. \~ + \details \ru Определить, являются ли объекты равными с заданной точностью. + \en Determine whether the objects are equal with defined accuracy. \~ + \param[in] other - \ru Объект для сравнения. + \en Object for comparison. \~ + \return \ru Подобны ли объекты. + \en Whether the objects are similar. \~ + */ + bool IsSame( const MbSweptData & other, double accuracy ) const; + /** \brief \ru Определить, являются ли объекты подобными. + \en Determine whether the objects are similar. \~ + \details \ru Подобный объект можно инициализировать по данным подобного ему объекта. + \en Similar object can be initialized by data of object which is similar to it. \~ + \param[in] other - \ru Объект для сравнения. + \en Object for comparison. \~ + \return \ru Подобны ли объекты. + \en Whether the objects are similar. \~ + */ + bool IsSimilar( const MbSweptData & other ) const; + /** \brief \ru Сделать объекты равным. + \en Make objects equal. \~ + \details \ru Равными можно сделать только подобные объекты. + \en It is possible to make equal only similar objects. \~ + \param[in] init - \ru Объект для инициализации. + \en Object for initialization. \~ + \return \ru Сделан ли объект равным присланному. + \en Whether the object is made equal to the given one. \~ + */ + bool SetEqual ( const MbSweptData & other ); + + /** \brief \ru Замкнуты ли все контуры. + \en Whether all contours are closed. \~ + \details \ru Замкнуты ли все контуры. \n + \en Whether all contours are closed. \n \~ + \return \ru Возвращает true, если все контуры замкнуты. + \en Returns true if all contours are closed. \~ + */ + bool IsContoursClosed() const; + + /// \ru Проверить, что нет разрывов между сегментами поверхностных контуров. \en Check that there are no gaps between the segments of the surface contours. + bool CheckSurfaceContourConnection( double eps ) const; + /// \ru Проверить, что нет разрывов между сегментами пространственных контуров. \en Check that there are no gaps between the segments of the spatial contours. + bool CheckSpaceContourConnection( double eps ) const; + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + MbSweptData & operator = ( const MbSweptData & ); + +KNOWN_OBJECTS_RW_REF_OPERATORS( MbSweptData ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Cпособ выдавливания/вращения. + \en Method of extrusion/rotation. \~ + \details \ru Cпособ построения выдавливания/вращения. \n + \en Method of extrusion/rotation constructing. \n \~ + \ingroup Build_Parameters +*/ +// --- +enum MbSweptWay { + sw_scalarValue = -2, ///< \ru Выдавить на заданную глубину / вращать на заданный угол. \en Extrude to a given depth / rotate by a given angle. + sw_shell = -1, ///< \ru До ближайшего объекта (тела). \en To the nearest object (solid). + sw_surface = 0, ///< \ru До поверхности. \en To the surface. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Параметры вращения и выдавливания. + \en Parameters of rotation and extrusion. \~ + \details \ru Данные о построении операции вращения или выдавливания + в одном из направлений: прямом или обратном. + \en Data about construction of rotation and extrusion + in one of directions: forward or backward. \~ + \ingroup Build_Parameters +*/ +// --- +class MATH_CLASS MbSweptSide { +public: + MbSweptWay way; ///< \ru Способ выдавливания/вращения. \en Method of extrusion/rotation. + double scalarValue; ///< \ru Угол вращения/глубина выдавливания. \en Angle of rotation/depth of extrusion. + + /** \brief \ru Расстояние от поверхности. + \en Distance from the surface. \~ + \details \ru Расстояние от поверхности, до которой строим операцию. + Задавать при построении операции до поверхности (way = sw_surface). + distance < 0.0 при построении операции за поверхность, + distance > 0.0 при построении операции до поверхности. + \en Distance from the surface to construct up to. + Set when constructing operation to the surface (way = sw_surface). + distance < 0.0 when constructing operation back of surface, + distance > 0.0 when constructing operation front of surface. \~ + */ + double distance; + + /** \brief \ru Угол уклона. + \en Draft angle. \~ + \details \ru Угол уклона при выдавливании.\n + Операцию выдавливания с уклоном можно построить только в случае плоской образующей. + \en Draft angle when extruding.\n + Extrusion operation with draft can be constructed in the case of planar swept. \~ + */ + double rake; + +protected: + /** \brief \ru Поверхность, до которой строим операцию. + \en The surface to construct up to. \~ + \details \ru Поверхность, до которой строим операцию.\n + Задавать при построении операции до поверхности (way = sw_surface). + \en The surface to construct up to.\n + Set when constructing operation to the surface (way = sw_surface). \~ + */ + MbSurface * surface; + + /** \brief \ru Признак совпадения нормали поверхности с нормалью грани. + \en An attribute of coincidence between the surface normal and the face normal. \~ + \details \ru Признак совпадения нормали поверхности, до которой строим операцию, с нормалью грани.\n + Задавать при построении операции до поверхности (way = sw_surface).\n + Указывает положение оболочки-результата относительно поверхности. + Используется при построении массива операций до поверхности. + Если у всех элементов массива признак должен быть одинаковым, + то при построении исходной операции нужно задать признак равным orient_BOTH (направление не определено). + При построении признак будет определен, и его значение нужно использовать для построения остальных элементов массива. + \en An attribute of coincidence between the face normal and the normal of surface to which to create operation.\n + Set when constructing operation to the surface (way = sw_surface).\n + Specifies the position of shell-result relative to the surface. + Used when constructing the array of operations to the surface. + If attributes of all the elements of array must be the same, + then when constructing of the original operation need to set attribute which is equal to orient_BOTH (the direction is not determined). + When constructing the attribute is determined and its value should be used for the construction of other elements of the array. \~ + */ + MbeSenseValue sameSense; + + +public: + /** \brief \ru Конструктор по умолчанию. + \en Default constructor. \~ + \details \ru Задает параметры операции со способом "на заданную глубину". + Для построения операции параметры нужно изменить, + например, указать глубину выдавливания (угол вращения). + \en Sets parameters of the operation with the method "to a given depth". + For construction of operation the parameters need to change, + for example: specify the depth of extrusion (angle of rotation). \~ + */ + MbSweptSide() + : way ( sw_scalarValue ) + , scalarValue( 0.0 ) + , distance ( 0.0 ) + , rake ( 0.0 ) + , surface ( NULL ) + , sameSense ( orient_BOTH ) + {} + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор на угол вращения\глубину выдавливания. + \en Constructor by angle of rotation\depth of extrusion. \~ + \param[in] sVal - \ru Угол вращения\глубина выдавливания. + \en Angle of rotation\depth of extrusion. \~ + */ + MbSweptSide( double sVal ) + : way ( sw_scalarValue ) + , scalarValue( sVal ) + , distance ( 0.0 ) + , rake ( 0.0 ) + , surface ( NULL ) + , sameSense ( orient_BOTH ) + {} + + /** \brief \ru Конструктор до поверхности. + \en Constructor to the surface. \~ + \details \ru Конструктор до поверхности. Расстояние от поверхности задается равным 0.0. + \en Constructor to the surface. Distance from the surface is set to 0.0. \~ + \param[in] surf - \ru Поверхность, до которой строится операция. + \en The surface to construct up to. \~ + */ + MbSweptSide( MbSurface * surf ); + + /** \brief \ru Конструктор до поверхности. + \en Constructor to the surface. \~ + \details \ru Конструктор до поверхности. Для элемента массива. + \en Constructor to the surface. For array element. \~ + \param[in] surf - \ru Поверхность, до которой строится операция. + \en The surface to construct up to. \~ + \param[in] sense - \ru Признак совпадения нормали заданной поверхности с нормалью грани. + Указывает, по какую сторону от поверхности должна находиться построенная оболочка. + \en An attribute of coincidence between the normal of given surface and the face normal. + Indicates at which side of the surface the must be located constructed shell. \~ + */ + MbSweptSide( MbSurface * surf, MbeSenseValue sense ); + + /** \brief \ru Конструктор копирования. + \en Copy-constructor. \~ + \details \ru Конструктор копирования данных с использованием той же поверхности. + \en Copy-constructor of data with using of the same surface. \~ + \param[in] other - \ru Исходные параметры. + \en Initial parameters. \~ + */ + MbSweptSide( const MbSweptSide & other ); + + /** \brief \ru Конструктор копирования с регистратором. + \en Copy-constructor with the registrator. \~ + \details \ru Конструктор копирования с регистратором. Поверхность копируется. + \en Copy-constructor with the registrator. Surface is copying. \~ + \param[in] other - \ru Исходные параметры. + \en Initial parameters. \~ + */ + MbSweptSide( const MbSweptSide & other, MbRegDuplicate * ireg ); + + /// \ru Деструктор. \en Destructor. + virtual ~MbSweptSide(); + + /// \ru Оператор присваивания данных с использованием той же поверхности. \en Assignment operator of data with using of the same surface. + MbSweptSide & operator = ( const MbSweptSide & other ); + + /// \ru Получить поверхность. \en Get the surface. + MbSurface * GetSurface() const { return surface; } + /// \ru Заменить поверхность. \en Replace surface. + void SetSurface( MbSurface * s ); + + /// \ru Получить признак совпадения нормали поверхности с нормалью грани. \en Get the attribute of coincidence between the surface normal and the face normal. + MbeSenseValue GetSameSense() const { return sameSense; } + /// \ru Установить признак совпадения нормали поверхности с нормалью грани. \en Set the attribute of coincidence between the surface normal and the face normal. + void SetSameSense( MbeSenseValue sense ) { sameSense = sense; } + /// \ru Доступ к признаку совпадения нормали поверхности с нормалью грани. \en Access to the attribute of coincidence between the surface normal and the face normal. + MbeSenseValue & SetSameSense() { return sameSense; } + + /// \ru Являются ли объекты равными? \en Determine whether an object is equal? + bool IsSame( const MbSweptSide & other, double accuracy ) const + { + if ( (other.way == way) && (other.sameSense == sameSense) ) { + if ( (::fabs(other.scalarValue - scalarValue) < accuracy) && + (::fabs(other.distance - distance) < accuracy) && + (::fabs(other.rake - rake) < accuracy) ) + { + bool isSurf1 = (surface != NULL); + bool isSurf2 = (other.surface != NULL); + + if ( isSurf1 == isSurf2 ) { + if ( isSurf1 && isSurf2 ) { + if ( !other.surface->IsSame( *surface, accuracy ) ) + return false; + } + return true; + } + } + } + + return false; + } +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Параметры формообразующей операции. + \en The parameters of form-generating operation. \~ + \details \ru Параметры построения формообразующей операции + (например, выдавливания, вращения, кинематической, по сечениям). \n + \en The construction parameters of form-generating operation. + (for example: extrusion, rotation, sweeping, loft). \n \~ + \ingroup Build_Parameters +*/ +// --- +struct MATH_CLASS SweptValues { +public: + + /** \brief \ru Толщина стенки (величина эквидистанты) в прямом направлении. + \en Wall thickness (offset distance) along the forward direction. \~ + \details \ru Толщина стенки (величина эквидистанты) в положительном направлении нормали объекта + (грани, поверхности, плоскости кривой). + \en Wall thickness (offset distance) along the positive direction of the normal of an object + (face, surface, plane of the curve). \~ + */ + double thickness1; + + /** \brief \ru Толщина стенки (величина эквидистанты) в обратном направлении. + \en Wall thickness (offset distance) along the backward direction. \~ + \details \ru Толщина стенки (величина эквидистанты) в отрицательном направлении нормали объекта + (грани, поверхности, плоскости кривой). + \en Wall thickness (offset distance) along the negative direction of the normal of an object + (face, surface, plane of the curve). \~ + */ + double thickness2; + + bool shellClosed; ///< \ru Замкнутость оболочки. \en Closedness of shell. + +private: + bool checkSelfInt; ///< \ru Флаг проверки самопересечений (вычислительно "тяжелыми" методами). \en Flag for checking of self-intersection (computationally by "heavy" methods). + bool mergeFaces; ///< \ru Сливать подобные грани (true). \en Whether to merge similar faces (true). + +public: + /// \ru Конструктор по умолчанию. \en Default constructor. + SweptValues() + : thickness1 ( 0.0 ) + , thickness2 ( 0.0 ) + , shellClosed ( true ) + , checkSelfInt( true ) + , mergeFaces ( true ) + {} + /// \ru Конструктор по толщинам и замкнутости. \en Constructor by thicknesses and closedness. + SweptValues( double t1, double t2, bool c = true ) + : thickness1 ( t1 ) + , thickness2 ( t2 ) + , shellClosed ( c ) + , checkSelfInt( true ) + , mergeFaces ( true ) + {} + /// \ru Конструктор копирования. \en Copy-constructor. + SweptValues( const SweptValues & other ) + : thickness1 ( other.thickness1 ) + , thickness2 ( other.thickness2 ) + , shellClosed ( other.shellClosed ) + , checkSelfInt( other.checkSelfInt ) + , mergeFaces ( other.mergeFaces ) + {} + /// \ru Деструктор. \en Destructor. + virtual ~SweptValues() {} + +public: + /// \ru Это параметры выдавливания? \en This is extrusion parameters? + virtual bool IsExtrusionValues() const { return false; } + /// \ru Это параметры вращения? \en This is rotation parameters? + virtual bool IsRevolutionValues() const { return false; } + /// \ru Это параметры кинематики? \en This is "evolution" parameters? + virtual bool IsEvolutionValues() const { return false; } + /// \ru Это параметры операции по сечениям? \en This is "lofted" parameters? + virtual bool IsLoftedValues() const { return false; } + /// \ru Это параметры операции ребра жесткости? \en This is "rib" parameters? + virtual bool IsRibValues() const { return false; } + + /// \ru Определить, являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSame( const SweptValues & other, double accuracy ) const; + /// \ru Определить, являются ли объекты подобными. \en Determine whether the objects are similar. \~ + virtual bool IsSimilar( const MbSweptData & other ) const; + /// \ru Сделать объекты равным. \en Make objects equal. \~ + virtual bool SetEqual ( const MbSweptData & other ); + +public: + /// \ru Функция копирования данных. \en Function of copying data. + void Init( const SweptValues & other ) { + thickness1 = other.thickness1; + thickness2 = other.thickness2; + shellClosed = other.shellClosed; + checkSelfInt = other.checkSelfInt; + mergeFaces = other.mergeFaces; + } + + /// \ru Получить состояние замкнутости. \en Get the closedness state. + bool IsShellClosed() const { return shellClosed; } + /// \ru Установит состояние замкнутости. \en Set the closedness state. + void SetShellClosed( bool cl ) { shellClosed = cl; } + /// \ru Получить состояние флага проверки самопересечений. \en Get the state of flag of checking self-intersection. + bool CheckSelfInt() const { return checkSelfInt; } + /// \ru Установить состояние флага проверки самопересечений. \en Set the state of flag of checking self-intersection. + void SetCheckSelfInt( bool c ) { checkSelfInt = c; } + /// \ru Сливать подобные грани (true). \en Whether to merge similar faces (true). + bool MergeFaces() const { return mergeFaces; } + /// \ru Сливать подобные грани (true). \en Whether to merge similar faces (true). + void SetMergeFaces( bool mf ) { mergeFaces = mf; } + + /// \ru Оператор присваивания. \en Assignment operator. + void operator = ( const SweptValues & other ) { Init( other ); } + + KNOWN_OBJECTS_RW_REF_OPERATORS( SweptValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Параметры выдавливания или вращения. + \en The parameters of extrusion or rotation. \~ + \details \ru Параметры выдавливания или вращения кривых с опциями по направлениям. \n + В операции выдавливания прямым направлением считается направление, сонаправленное + с вектором выдавливания, а обратным - противоположное направление. + В операции вращения прямое направлением определяется по оси вращения с помощью правила правой руки. + \en The parameters of extrusion or rotation of curves with options along the directions. \n + In the extrusion operations the forward direction is the direction collinear + with the vector of extrusion and back - the opposite direction. + In the rotation operation the forward direction is determined by the axis of rotation using the right hand rule. \~ + \ingroup Build_Parameters +*/ +// --- +class MATH_CLASS SweptValuesAndSides: public SweptValues { +public: + MbSweptSide side1; ///< \ru Параметры выдавливания/вращения в прямом направлении. \en The parameters of extrusion/rotation along the forward direction. + MbSweptSide side2; ///< \ru Параметры выдавливания/вращения в обратном направлении. \en The parameters of extrusion/rotation along the backward direction. + +public: + /** \brief \ru Конструктор по умолчанию. + \en Default constructor. \~ + \details \ru Конструктор параметров для построения замкнутой оболочки без тонкой стенки. + Способ построение в обоих направлениях - на заданную глубину, равную 0.0. + \en Constructor of parameters for construction of closed shell without the thin wall. + Method of construction in both directions - to a given depth equal to 0.0. \~ + */ + SweptValuesAndSides() + : SweptValues() + , side1 () + , side2 () + {} + /** \brief \ru Конструктор по углам вращения или глубинам выдавливания. + \en Constructor by rotation angles and extrusion depths. \~ + \details \ru Конструктор параметров для построения замкнутой оболочки без тонкой стенки. + Способ построение в обоих направлениях - на заданную глубину. + \en Constructor of parameters for construction of closed shell without the thin wall. + Method of construction in both directions - to a given depth. \~ + \param[in] scalarValue1 - \ru Угол вращения\глубина выдавливания в прямом направлении. + \en Angle of rotation\depth of extrusion along the forward direction. \~ + \param[in] scalarValue2 - \ru Угол вращения\глубина выдавливания в обратном направлении. + \en Angle of rotation\depth of extrusion along the backward direction. \~ + */ + SweptValuesAndSides( double scalarValue1, double scalarValue2 ) + : SweptValues( ) + , side1 ( scalarValue1 ) + , side2 ( scalarValue2 ) + {} + /// \ru Конструктор копирования данных на тех же поверхностях. \en Copy-constructor of data on the same surfaces. + SweptValuesAndSides( const SweptValuesAndSides & other ) + : SweptValues( other ) + , side1 ( other.side1 ) + , side2 ( other.side2 ) + {} + /// \ru Конструктор полного копирования данных. \en Constructor of complete copying of data. + SweptValuesAndSides( const SweptValuesAndSides & other, MbRegDuplicate * ireg ) + : SweptValues( other ) + , side1 ( other.side1, ireg ) + , side2 ( other.side2, ireg ) + {} + /// \ru Деструктор. \en Destructor. + virtual ~SweptValuesAndSides(); + +public: + // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSame( const SweptValues & other, double accuracy ) const + { + const SweptValuesAndSides * obj = dynamic_cast( &other ); + if ( obj != NULL ) { + if ( side1.IsSame( obj->side1, accuracy ) && side2.IsSame( obj->side2, accuracy ) ) { + if ( obj->SweptValues::IsSame( *this, accuracy ) ) { + return true; + } + } + } + return false; + } + +public: + /// \ru Оператор присваивания данных на тех же поверхностях. \en Assignment operator of data copying on the same surfaces. + void operator = ( const SweptValuesAndSides & other ) { + SweptValues::Init( other ); + side1 = other.side1; + side2 = other.side2; + } + + /** \brief \ru Преобразовать согласно матрице. + \en Transform according to the matrix. \~ + \details \ru Преобразовать согласно матрице поверхности в прямом и обратном направлении. + \en Transform according to the matrix of surface in the forward and backward direction. \~ + \param[in] matr - \ru Матрица преобразования. + \en A transformation matrix. \~ + \param[in] iReg - \ru Регистратор. + \en Registrator. \~ + */ + void Transform( const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); + /** \brief \ru Сдвинуть вдоль вектора. + \en Move along a vector. \~ + \details \ru Сдвинуть вдоль вектора поверхности в прямом и обратном направлении. + \en Move along the vector of the surface along the forward and backward direction. \~ + \param[in] to - \ru Вектор сдвига. + \en Translation vector. \~ + \param[in] iReg - \ru Регистратор. + \en Registrator. \~ + */ + void Move ( const MbVector3D & to, MbRegTransform * iReg = NULL ); + /** \brief \ru Повернуть вокруг оси. + \en Rotate around an axis. \~ + \details \ru Повернуть вокруг оси поверхности в прямом и обратном направлении. + \en Rotate around the axis of the surface along the forward and backward direction. \~ + \param[in] axis - \ru Ось поворота. + \en The rotation axis. \~ + \param[in] angle - \ru Угол поворота. + \en The rotation angle. \~ + \param[in] iReg - \ru Регистратор. + \en Registrator. \~ + */ + void Rotate ( const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); + + /** \brief \ru Сделать копии поверхностей. + \en Make copies of surfaces. \~ + \details \ru Если в каком-либо направлении задана поверхность, заменить эту поверхность на ее копию. + \en If the surface is given in any direction, then replace the surface with its copy. \~ + \param[in] ireg - \ru Регистратор копий. + \en Registrator of copies. \~ + \return \ru true, если хотя бы одна поверхность имелась и сдублирована. + \en True if at least one surface is had and copied. \~ + */ + bool DuplicateSurfaces( MbRegDuplicate * ireg = NULL ); + + /// \ru Получить поверхность в положительном направлении. \en Get the surface along the positive direction. + MbSurface * GetSurface1() const { return side1.GetSurface(); } + /// \ru Получить поверхность в отрицательном направлении. \en Get the surface along the negative direction. + MbSurface * GetSurface2() const { return side2.GetSurface(); } + /// \ru Установить поверхность в положительном направлении. \en Set the surface along the positive direction. + void SetSurface1( MbSurface * s ) { side1.SetSurface( s ); } + /// \ru Установить поверхность в отрицательном направлении. \en Set the surface along the negative direction. + void SetSurface2( MbSurface * s ) { side2.SetSurface( s ); } + /// \ru Поменять поверхности местами. \en Swap surfaces. + void ExchangeSurfaces(); +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Параметры операции выдавливания. + \en The parameters of extrusion operation. \~ + \details \ru Параметры операции выдавливания кривых с опциями по направлениям. \n + \en The parameters of extrusion operation of curves with options along directions. \n \~ + \ingroup Build_Parameters +*/ +// --- +class MATH_CLASS ExtrusionValues : public SweptValuesAndSides { +public: + + /** \brief \ru Конструктор по умолчанию. + \en Default constructor. \~ + \details \ru Конструктор параметров выдавливания для построения замкнутой оболочки без тонкой стенки + в прямом направлении на величину, равную 10.0. + \en Constructor of extrusion parameters for construction of closed shell without the thin wall. + along the forward direction by value 10.0. \~ + */ + ExtrusionValues() + : SweptValuesAndSides( 10., 0. ) {} + /** \brief \ru Конструктор по глубинам выдавливания. + \en Constructor by extrusion depths. \~ + \details \ru Конструктор параметров выдавливания для построения замкнутой оболочки без тонкой стенки. + Способ построение в обоих направлениях - на заданную глубину. + \en Constructor of extrusion parameters for construction of closed shell without the thin wall. + Method of construction in both directions - to a given depth. \~ + \param[in] scalarValue1 - \ru Глубина выдавливания в прямом направлении. + \en Depth of extrusion along the forward direction. \~ + \param[in] scalarValue2 - \ru Глубина выдавливания в обратном направлении. + \en Depth of extrusion along the backward direction. \~ + */ + ExtrusionValues( double scalarValue1, double scalarValue2 ) + : SweptValuesAndSides( scalarValue1, scalarValue2 ) {} + /// \ru Конструктор копирования, на тех же поверхностях. \en Copy-constructor on the same surfaces. + ExtrusionValues( const ExtrusionValues & other ) + : SweptValuesAndSides( other ) {} + /// \ru Конструктор копирования. \en Copy-constructor. + ExtrusionValues( const ExtrusionValues & other, MbRegDuplicate * ireg ) + : SweptValuesAndSides( other, ireg ) {} + /// \ru Деструктор. \en Destructor. + virtual ~ExtrusionValues(); + +public: + // \ru Это параметры выдавливания? \en This is extrusion parameters? + virtual bool IsExtrusionValues() const { return true; } + + // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSame( const SweptValues & other, double accuracy ) const + { + const ExtrusionValues * obj = dynamic_cast( &other ); + if ( obj != NULL ) { + if ( obj->SweptValuesAndSides::IsSame( *this, accuracy ) ) + return true; + } + return false; + } + +public: + /// \ru Оператор присваивания, на тех же поверхностях. \en Assignment operator on the same surfaces. + ExtrusionValues & operator = ( const ExtrusionValues & other ) { + *static_cast(this) = *static_cast(&other); + return *this; + } + + KNOWN_OBJECTS_RW_REF_OPERATORS( ExtrusionValues ) // \ru Для работы со ссылками и объектами класса \en For treatment of references and objects of the class +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Параметры операции вращения. + \en The parameters of revolution operation. \~ + \details \ru Параметры операции вращения кривых с опциями по направлениям. \n + \en The parameters of revolution operation of curves with options along directions. \n \~ + \ingroup Build_Parameters +*/ +// --- +class MATH_CLASS RevolutionValues : public SweptValuesAndSides { +public: + /** \brief \ru Форма топологии. + \en Topology shape. \~ + \details \ru Форма топологии: 0 - тело типа сферы, 1 - тело типа тора.\n + Если образующая - не замкнутая плоская кривая, и ось вращения лежит в плоскости кривой, + то возможно построение тела вращения с топологией типа сферы. В этом случае образующая достраивается до оси вращения. + \en Topology shape: 0 - sphere, 1 - torus.\n + If swept is non-closed planar curve and axis of rotation lies on the curve plane, + then is possible to construct revolution solids with the topology of sphere type. In this case the swept is being updated to the rotation axis. +I \~ */ + int shape; + +public: + /** \brief \ru Конструктор по умолчанию. + \en Default constructor. \~ + \details \ru Конструктор параметров вращения для построения замкнутой оболочки типа тора + без тонкой стенки в прямом направлении на полный оборот. + \en Constructor of revolution parameters for construction of closed shell of torus type + without thin wall along the forward direction at full turn. \~ + */ + RevolutionValues() + : SweptValuesAndSides( M_PI, 0. ) + , shape( 1 ) + {} + /** \brief \ru Конструктор по углам вращения. + \en Constructor by revolution angles. \~ + \details \ru Конструктор параметров вращения для построения замкнутой оболочки без тонкой стенки. + Способ построение в обоих направлениях - на заданную глубину (заданный угол). + \en Constructor of revolution parameters for construction of closed shell without the thin wall. + Method of construction in both directions - to a given depth (given angle). \~ + \param[in] scalarValue1 - \ru Угол вращение в прямом направлении. + \en Revolution angle along the forward direction. \~ + \param[in] scalarValue2 - \ru Угол вращения в обратном направлении. + \en Revolution angle along the backward direction. \~ + \param[in] s - \ru Форма топологии. + \en Topology shape. \~ + */ + RevolutionValues( double scalarValue1, double scalarValue2, int s ) + : SweptValuesAndSides( scalarValue1, scalarValue2 ) + , shape( s ) + {} + /// \ru Конструктор копирования, на тех же поверхностях. \en Copy-constructor on the same surfaces. + RevolutionValues( const RevolutionValues & other ) + : SweptValuesAndSides( other ) + , shape( other.shape ) + {} + /// \ru Конструктор копирования. \en Copy-constructor. + RevolutionValues( const RevolutionValues & other, MbRegDuplicate * ireg ) + : SweptValuesAndSides( other, ireg ) + , shape( other.shape ) + {} + /// \ru Деструктор. \en Destructor. + virtual ~RevolutionValues(); + +public: + // \ru Это параметры вращения? \en This is rotation parameters? + virtual bool IsRevolutionValues() const { return true; } + + // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSame( const SweptValues & other, double accuracy ) const + { + const RevolutionValues * obj = dynamic_cast( &other ); + if ( obj != NULL ) { + if ( obj->shape == shape ) { + if ( obj->SweptValuesAndSides::IsSame( *this, accuracy ) ) + return true; + } + } + return false; + } + +public: + /// \ru Оператор присваивания, на тех же поверхностях. \en Assignment operator on the same surfaces. + RevolutionValues & operator = ( const RevolutionValues & other ) { + *static_cast(this) = *static_cast(&other); + shape = other.shape; + return *this; + } + + KNOWN_OBJECTS_RW_REF_OPERATORS( RevolutionValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Параметры кинематической операции. + \en Parameters of the sweeping operation. \~ + \details \ru Параметры операции движения образующей по направляющей кривой. \n + \en The operation parameters of moving the generating curve along the spine curve. \n \~ + \ingroup Build_Parameters +*/ +// --- +struct MATH_CLASS EvolutionValues : public SweptValues { + + /// \ru Способы переноса образующего объекта вдоль направляющей. \en Moving method of generating object along the spine curve. + enum ModesList { + eom_Parallel = 0x00, // 00000 ///< \ru Образующая переносится параллельно самой себе. \en Generating curve is moved parallel to itself. + eom_KeepingAngle = 0x01, // 00001 ///< \ru Образующая при переносе сохраняет исходный угол с направляющей. \en Generating curve when moving preserves initial angle with spine. + eom_Orthogonal = 0x02, // 00010 ///< \ru Плоскость образующей выставляется и сохраняется ортогональной направляющей. \en Plane of generating curve is set and saved as orthogonal to spine. + eom_BySurfaceNormal = 0x04, // 00100 ///< \ru Образующая переносится согласованно с нормалью к поверхности. \en Generating object is moved according to surface normal. + }; + +protected: + /** \brief \ru Способ переноса образующего контура вдоль направляющей. + \en Moving method of generating contour along the spine curve. \~ + \details \ru Способ переноса образующего контура вдоль направляющей: \n + parallel <= 0 - Образующая переносится параллельно самой себе; \n + parallel == 1 - Образующая при переносе сохраняет исходный угол с направляющей; \n + parallel == 2 - Плоскость образующей выставляется и сохраняется ортогональной направляющей. \n + parallel > 3 - Образующая переносится согласованно с нормалью к поверхности. \n + \en Moving method of generating contour along the spine curve: \n + parallel <= 0 - Generating curve is moved parallel to itself; \n + parallel == 1 - Generating curve when moving preserves initial angle with spine; \n + parallel == 2 - Plane of generating curve is set and saved as orthogonal to spine. \n + parallel > 3 - Generating object is moved according to surface normal. \n \~ + */ + int mode; +public: + // \ru Данные о функциях изменения образующих кривых вдоль направляющей кривой (могут быть NULL). \en Data about changes of generating curves along the guide curve (can be NULL). + double range; ///< \ru Эквидистантное смещение точек образующей кривой в конце траектории. \en The offset range of generating curve on the end of spine curve. + SPtr scaling; ///< \ru Функция масштабирования образующей кривой. \en The function of curve scale. + SPtr winding; ///< \ru Функция вращения образующей кривой. \en The function of curve rotation. + c3d::ConstSurfaceSPtr surface; ///< \ru Поверхность для управления направляющей кривой MbSpine. \en The surface for guide curve control (for MbSpine). + +public: + + /** \brief \ru Конструктор по умолчанию. + \en Default constructor. \~ + \details \ru Конструктор параметров кинематической операции для построения замкнутой оболочки + без тонкой стенки с сохранением угла наклона. + \en Constructor of sweeping operation parameters for construction of closed shell + without the thin wall with keeping the angle inclination. \~ + */ + EvolutionValues() + : SweptValues( ) + , mode ( eom_KeepingAngle ) + , range ( 0.0 ) + , scaling ( NULL ) + , winding ( NULL ) + , surface ( NULL ) + {} + /// \ru Конструктор копирования. \en Copy-constructor. + EvolutionValues( const EvolutionValues & other ); + /// \ru Деструктор. \en Destructor. + virtual ~EvolutionValues(); + +public: + // \ru Это параметры кинематики? \en This is "evolution" parameters? + virtual bool IsEvolutionValues() const { return true; } + + // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSame( const SweptValues & other, double accuracy ) const; + // \ru Определить, являются ли объекты подобными. \en Determine whether the objects are similar. \~ + virtual bool IsSimilar( const SweptValues & other ) const; + // \ru Сделать объекты равным. \en Make objects equal. \~ + virtual bool SetEqual ( const SweptValues & other ); + + /// \ru Копировать значение режима операции. \en Copy operation mode. + void CopyMode( const EvolutionValues & ev ) { mode = ev.mode; } + /// \ru Получить значение режима операции. \en Get operation mode. + int GetMode() const { return mode; } + /// \ru Переносится ли образующая параллельно самой себе. \en Whether generating curve is moved parallel to itself. + bool IsParallel() const { return (mode < 1); } + /// \ru Сохраняет ли образующая при переносе исходный угол с направляющей. \en Whether generating curve when moving preserves initial angle with spine. + bool IsKeepingAngle() const { return !!(mode & eom_KeepingAngle); } + /// \ru Выставляется ли плоскость образующей ортогонально направляющей. \en Whether plane of generating curve is set and saved as orthogonal to spine. + bool IsOrthogonal() const { return !!(mode & eom_Orthogonal); } + /// \ru Переносится ли образующая согласованно с нормалью к поверхности. \en Whether generating object is moved according to surface normal. + bool BySurfaceNormal() const { return !!(mode & eom_BySurfaceNormal); } + + /// \ru Переносить образующая параллельно самой себе. \en Move generating curve parallel to itself. + void SetParallel() { mode = eom_Parallel; } + /// \ru Сохранять при переносе исходный угол между образующей и направляющей. \en Preserve initial angle between generatrix and spine when moving. + void SetKeepingAngle() { mode = eom_KeepingAngle; } + /// \ru Выставлять плоскость образующей ортогонально направляющей. \en Set and keep plane of generating curve as orthogonal to spine. + void SetOrthogonal() { mode = eom_Orthogonal; } + /// \ru Переносить образующую согласованно с нормалью к поверхности. \en Move generating object according to surface normal. + bool SetBySurfaceNormal( bool s ) + { + if ( !IsParallel() ) { + if ( s ) mode |= eom_BySurfaceNormal; + else mode ^= eom_BySurfaceNormal; + return true; + } + return false; + } + /// \ru Выдать функцию масштабирования образующей кривой. \en Get the function of curve scale. + double GetRange() const { return range; } + double & SetRange() { return range; } + void SetRange( double r ) { range = r; } + + /** \brief \ru Добавить данные. + \en Add data. \~ + \details \ru Добавить данные об изменении образующих контурах на поверхности вдоль образующей кривой. + \en Add data about changes of generating contours on the surface along the guide curve. \~ + \param[in] _scaling - \ru Масштабирование. + \en The scaling. \~ + \param[in] _winding - \ru Поворот. + \en The winding. \~ + */ + bool AddData( MbFunction & _scaling, MbFunction & _winding ); + + /// \ru Выдать функцию масштабирования образующей кривой. \en Get the function of curve scale. + const MbFunction * GetScaling() const { return scaling; } + MbFunction * SetScaling() { return scaling; } + + /// \ru Выдать функцию вращения образующей кривой. \en Get the function of curve rotation. + const MbFunction * GetWinding() const { return winding; } + MbFunction * SetWinding() { return winding; } + + ///< \ru Выдать поверхность для направляющей кривой MbSpine. \en Get the surface for guide curve MbSpine. + const MbSurface * GetSurface() const { return surface; } + void SetSurface( const MbSurface & surf ); + +public: + /// \ru Оператор присваивания. \en Assignment operator. + EvolutionValues & operator = ( const EvolutionValues & other ); + + KNOWN_OBJECTS_RW_REF_OPERATORS( EvolutionValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Параметры операции построения тела по плоским сечениям. + \en The operation parameters of constructing solid by lofted. \~ + \details \ru Параметры операции построения тела по плоским сечениям, заданных контурами. \n + \en The parameters of constructing operation by lofted which are given by contours. \n \~ + \ingroup Build_Parameters +*/ +// --- +struct MATH_CLASS LoftedValues : public SweptValues { +public: + bool closed; ///< \ru Замкнутость трубки сечений. \en Closedness of tube. + MbVector3D vector1; ///< \ru Производная в начале. \en The derivative at the start. + MbVector3D vector2; ///< \ru Производная в конце. \en The derivative at the end. + bool setNormal1; ///< \ru Установлена нормаль в начале, если начальное сечение точечное. \en The normal is set at the start, if first section is point curve. + bool setNormal2; ///< \ru Установлена нормаль в конце, если начальное сечение точечное. \en The normal is set at the end, if last section is point curve. + double derFactor1; ///< \ru Множитель величины производной при установке нормали в начале. По умолчанию 1.0. \en The modifier of the derivative when setting the normal at the beginning. The default is 1.0. + double derFactor2; ///< \ru Множитель величины производной при установке нормали в конце. По умолчанию 1.0. \en The modifier of the derivative when setting the normal at the end. The default is 1.0. + MbVector3D directSurf1; ///< \ru Ось направления движения поверхности в начале при установке нормали. \en Direction axis of the surface progress near the starting curve when setting the normal. + MbVector3D directSurf2; ///< \ru Ось направления движения поверхности в конце при установке нормали. \en Direction axis of the surface progress near the ending curve when setting the normal. + +public: + /** \brief \ru Конструктор по умолчанию. + \en Default constructor. \~ + \details \ru Конструктор параметров операции по сечениям для построения замкнутой оболочки без тонкой стенки. + \en Constructor of lofted operation parameters for construction of closed shell without the thin wall. \~ + */ + LoftedValues() + : SweptValues ( ) + , closed ( false ) + , vector1 ( 0.0, 0.0, 0.0 ) + , vector2 ( 0.0, 0.0, 0.0 ) + , setNormal1 ( false ) + , setNormal2 ( false ) + , derFactor1 ( 1.0 ) + , derFactor2 ( 1.0 ) + , directSurf1 ( UNDEFINED_DBL, UNDEFINED_DBL, UNDEFINED_DBL ) + , directSurf2 ( UNDEFINED_DBL, UNDEFINED_DBL, UNDEFINED_DBL ) + {} + /// \ru Конструктор копирования. \en Copy-constructor. + LoftedValues( const LoftedValues & other ) + : SweptValues ( other ) + , closed ( other.closed ) + , vector1 ( other.vector1 ) + , vector2 ( other.vector2 ) + , setNormal1 ( other.setNormal1 ) + , setNormal2 ( other.setNormal2 ) + , derFactor1 ( other.derFactor1 ) + , derFactor2 ( other.derFactor2 ) + , directSurf1 ( other.directSurf1 ) + , directSurf2 ( other.directSurf2 ) + {} + /// \ru Оператор присваивания. \en Assignment operator. + LoftedValues & operator = ( const LoftedValues & other ) + { + SweptValues::Init( other ); + closed = other.closed; + vector1 = other.vector1; + vector2 = other.vector2; + setNormal1 = other.setNormal1; + setNormal2 = other.setNormal2; + directSurf1 = other.directSurf1; + directSurf2 = other.directSurf2; + return *this; + } + /// \ru Деструктор. \en Destructor. + virtual ~LoftedValues(); + +public: + // \ru Это параметры операции по сечениям? \en This is "lofted" parameters? + virtual bool IsLoftedValues() const { return true; } + + // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSame( const SweptValues & other, double accuracy ) const + { + const LoftedValues * obj = dynamic_cast( &other ); + if ( obj != NULL ) { + if ( obj->closed == closed ) { + if ( c3d::EqualVectors(vector1, obj->vector1, accuracy) && c3d::EqualVectors(vector2, obj->vector2, accuracy) ) { + if ( obj->setNormal1 == setNormal1 && obj->setNormal2 == setNormal2 ) { + if ( (setNormal1 == false || obj->derFactor1 == derFactor1 || c3d::EqualVectors(directSurf1, obj->directSurf1, accuracy)) && // Фактор может различаться, если нормаль не установлена. + (setNormal2 == false || obj->derFactor2 == derFactor2 || c3d::EqualVectors(directSurf2, obj->directSurf2, accuracy)) ) { + if ( obj->SweptValues::IsSame(*this, accuracy) ) { + return true; + } + } + } + } + } + } + return false; + } + +public: + /// \ru Преобразовать объект согласно матрице. \en Transform an object according to the matrix. + void Transform( const MbMatrix3D & matr ); + /// \ru Сдвинуть объект вдоль вектора. \en Move an object along a vector. + void Move ( const MbVector3D & to ); + /// \ru Повернуть объект вокруг оси на заданный угол. \en Rotate an object at a given angle around an axis. + void Rotate ( const MbAxis3D & axis, double ang ); + + KNOWN_OBJECTS_RW_REF_OPERATORS( LoftedValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Параметры ребра жёсткости. + \en Parameters of a rib. \~ + \details \ru Параметры построения ребра жёсткости по кривой, задающей его форму. \n + \en The construction parameters of rib by curve gives its shape. \n \~ + \ingroup Build_Parameters +*/ +// --- +struct MATH_CLASS RibValues : public SweptValues { +public: + /** \brief \ru Сторона заполнения пространства телом ребра. + \en The side to place the rib on. \~ + \details \ru С какой стороны от кривой располагается ребро. \n + \en With which side of the curve is rib. \n \~ + \ingroup Build_Parameters + */ + enum ExtrudeSide { + es_Left = 0, ///< \ru Ребро выдавливается в левую сторону от кривой вдоль плоскости. \en Rib is extruded to the left side of the curve along the plane. + es_Right, ///< \ru Ребро выдавливается в правую сторону от кривой вдоль плоскости. \en Rib is extruded to the right side of the curve along the plane. + es_Up, ///< \ru Ребро выдавливается в сторону нормали плоскости. \en Rib is extruded to the side of the surface normal. + es_Down, ///< \ru Ребро выдавливается в сторону против нормали плоскости. \en Rib is extruded to the side opposite to the surface normal. + }; + +public: + double angle1; ///< \ru Угол уклона плоскости в прямом направлении. \en Draft angle of the plane along the forward direction. + double angle2; ///< \ru Угол уклона плоскости в обратном направлении. \en Draft angle of the plane along the backward direction. + ExtrudeSide side; ///< \ru Сторона заполнения пространства телом ребра. \en The side to place the rib on. + +public: + /// \ru Конструктор по умолчанию. \en Default constructor. + RibValues() + : SweptValues( ) + , angle1 ( 0.0 ) + , angle2 ( 0.0 ) + , side ( es_Right ) + {} + /// \ru Конструктор по толщинам, углам и стороне заполнения пространства. \en Constructor by thickness, angles and filling space. + RibValues( double t1, double t2, double a1, double a2, int s ) + : SweptValues( t1, t2 ) + , angle1 ( a1 ) + , angle2 ( a2 ) + , side ( (ExtrudeSide)s ) + {} + /// \ru Конструктор копирования. \en Copy-constructor. + RibValues( const RibValues & other ) + : SweptValues( other ) + , angle1 ( other.angle1 ) + , angle2 ( other.angle2 ) + , side ( other.side ) + {} + /// \ru Деструктор. \en Destructor. + virtual ~RibValues(); + +public: + // \ru Это параметры операции ребра жесткости? \en This is "rib" parameters? + virtual bool IsRibValues() const { return true; } + + // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSame( const SweptValues & other, double accuracy ) const + { + const RibValues * obj = dynamic_cast( &other ); + + if ( obj != NULL ) { + if ( obj->side == side ) { + if ( ::fabs(obj->angle1 - angle1) < accuracy && ::fabs(obj->angle2 - angle2) < accuracy ) + return SweptValues::IsSame( *obj, accuracy ); + } + } + return false; + } + +public: + /// \ru Функция копирования. \en Copy function. + void Init( const RibValues & other ) + { + SweptValues::Init( other ); + angle1 = other.angle1; + angle2 = other.angle2; + side = other.side; + } + /// \ru Оператор присваивания. \en Assignment operator. + RibValues & operator = ( const RibValues & other ) + { + Init( other ); + return *this; + } + + KNOWN_OBJECTS_RW_REF_OPERATORS( RibValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Параметры ребра жёсткости листового тела. + \en Parameters of a sheet metal rib. \~ + \details \ru Параметры построения ребра жёсткости листового тела по кривой, задающей его форму. \n + \en The construction parameters of a sheet metal rib by curve gives its shape. \n \~ +\ingroup Build_Parameters +*/ +// --- +struct MATH_CLASS SheetRibValues: public RibValues { +public: + double radRibConvex; ///< \ru Радиус скругления выпуклой части ребра жесткости. \en Fillet radius of convex part of rib. + double radSideConcave; ///< \ru Радиус скругления примыкания вогнутой части ребра жесткости к листовому телу. \en Fillet radius of connection of concave part of rib and metal sheet. + +public: + /// \ru Конструктор по умолчанию. \en Default constructor. + SheetRibValues() + : RibValues ( ) + , radRibConvex ( 0.0 ) + , radSideConcave( 0.0 ) + {} + /// \ru Конструктор по параметрам. \en Constructor by parameters. + SheetRibValues( double t1, double t2, double a1, double a2, int s, double rFilletRib, const double & rFilletSide ) + : RibValues ( t1, t2, a1, a2, s ) + , radRibConvex ( ::fabs(rFilletRib) ) + , radSideConcave( ::fabs(rFilletSide) ) + {} + /// \ru Конструктор копирования. \en Copy-constructor. + SheetRibValues( const SheetRibValues & other ) + : RibValues ( other ) + , radRibConvex ( other.radRibConvex ) + , radSideConcave( other.radSideConcave ) + {} + /// \ru Деструктор. \en Destructor. + virtual ~SheetRibValues(); + +public: + // \ru Являются ли объекты равными? \en Determine whether an object is equal? + virtual bool IsSame( const SweptValues & other, double accuracy ) const + { + const SheetRibValues * obj = dynamic_cast( &other ); + + if ( obj != NULL ) { + if ( (::fabs(radRibConvex - obj->radRibConvex) < accuracy) && (::fabs(radSideConcave - obj->radSideConcave) < accuracy) ) + return RibValues::IsSame( *obj, accuracy ); + } + return false; + } + +public: + /// \ru Функция копирования. \en Copy function. + void Init( const SheetRibValues & other ) + { + RibValues::Init( other ); + radRibConvex = other.radRibConvex; + radSideConcave = other.radSideConcave; + } + + /// \ru Оператор присваивания. \en Assignment operator. + SheetRibValues & operator = ( const SheetRibValues & other ) { + Init( other ); + return *this; + } + + /// \ru Преобразовать объект согласно матрице. \en Transform an object according to the matrix. + void Transform( const MbMatrix3D & matr ); + + KNOWN_OBJECTS_RW_REF_OPERATORS( SheetRibValues ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Параметры булевой операции выдавливания или вращения до объекта. + \en The parameters of Boolean operation of extrusion or revolution to object. \~ + \details \ru Параметры булевой операции выдавливания или вращения до объекта. \n + Используется при булевой операции исходного тела + и построенной операции выдавливания или вращения двумерных контуров на поверхности. + \en The parameters of Boolean operation of extrusion or revolution to object. \n + Used in Boolean operation of initial solid + and constructed operation of extrusion or revolution of two-dimensional contours on the surface. \~ + \ingroup Build_Parameters +*/ +// --- +struct MATH_CLASS MbSweptLayout { + /** \brief \ru Направление выдавливания (вращения). + \en A direction of extrusion (revolution). \~ + \details \ru Направление выдавливания (вращения) по отношению к вектору выдавливания (оси вращения). + \en The direction of extrusion relative to the extrusion vector. \~ + */ + enum Direction { + ed_minus_minus = -2, ///< \ru В обратном направлении, для обеих строн. \en Along the backward direction, for both sides. + ed_minus = -1, ///< \ru В обратном направлении, для одной стороны. \en Along the backward direction, for one sides. + ed_both = 0, ///< \ru В обоих направлениях. \en Along both directions. + ed_plus = 1, ///< \ru В прямом направлении, для одной стороны. \en Along the forward direction, for one sides. + ed_plus_plus = 2, ///< \ru В прямом направлении, для обеих сторон. \en Along the forward direction, for both sides. + }; + Direction direction; ///< \ru Направление выдавливания относительно вектора. \en The direction of extrusion relative to the vector. + bool skipUnion; ///< \ru Создавать новое тело (Не приклеивать к телу). \en Create a new solid. + +protected: + SPtr surface; ///< \ru Поверхность, на которой размещена образующая. \en The surface, which contains the generating curve. + +protected: + /// \ru Конструктор. \en Constructor. + MbSweptLayout( const MbSurface & surf, Direction dir ) : surface( &surf ), direction( dir ), skipUnion( false ) {} + /// \ru Конструктор копирования. \en Copy-constructor. + MbSweptLayout( const MbSweptLayout & other ) : surface( other.surface ), direction( other.direction ), skipUnion( other.skipUnion ) {} + /// \ru Деструктор. \en Destructor. + virtual ~MbSweptLayout(); +public: + /// \ru Получить поверхность. \en Get the surface. + const MbSurface & GetSurface() const { return *surface; } + + /// \ru Создавать новое тело (Не приклеивать к телу). \en Create a new solid. + bool SkipUnion() const { return skipUnion; } + /// \ru Создавать новое тело (Не приклеивать к телу). \en Create a new solid. + void SkipUnion( bool su ) { skipUnion = su; } +public: + /// \ru Это параметры выдавливания? \en This is extrusion parameters? + virtual bool IsExtrusionLayout() const { return false; } + /// \ru Это параметры вращения? \en This is rotation parameters? + virtual bool IsRevolutionLayout() const { return false; } +public: + /// \ru Классификация точки относительно несущей поверхности. \en Classification point relative to the surface. + MbeItemLocation PointRelative( const MbCartPoint3D & p ) const; +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + MbSweptLayout & operator = ( const MbSweptLayout & ); +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Параметры булевой операции выдавливания до объекта. + \en The parameters of Boolean operation of extrusion to object. \~ + \details \ru Параметры булевой операции выдавливания до объекта. \n + Используется при булевой операции исходного тела + и построенной операции выдавливания двумерных контуров на поверхности. + \en The parameters of Boolean operation of extrusion to object. \n + Used in Boolean operation of initial solid + and constructed operation of extrusion of two-dimensional contours on the surface. \~ + \ingroup Build_Parameters +*/ +// --- +struct MATH_CLASS MbExtrusionLayout : public MbSweptLayout { + MbVector3D dirVector; ///< \ru Вектор выдавливания. \en An extrusion vector. +public: + /// \ru Конструктор. \en Constructor. + MbExtrusionLayout( const MbSurface & surf, Direction dir, const MbVector3D & dirVec ) : MbSweptLayout( surf, dir ), dirVector( dirVec ) {} + /// \ru Конструктор копирования. \en Copy-constructor. + MbExtrusionLayout( const MbExtrusionLayout & other ) : MbSweptLayout( other ), dirVector( other.dirVector ) {} + /// \ru Деструктор. \en Destructor. + virtual ~MbExtrusionLayout(); +public: + /// \ru Это параметры выдавливания? \en This is extrusion parameters? + virtual bool IsExtrusionLayout() const { return true; } +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + void operator = ( const MbExtrusionLayout & ); +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Параметры булевой операции вращения до объекта. + \en The parameters of Boolean operation of revolution to object. \~ + \details \ru Параметры булевой операции вращения до объекта. \n + Используется при булевой операции исходного тела + и построенной операции вращения двумерных контуров на поверхности. + \en The parameters of Boolean operation of revolution to object. \n + Used in Boolean operation of initial solid + and constructed operation of revolution of two-dimensional contours on the surface. \~ + \ingroup Build_Parameters +*/ +// --- +struct MATH_CLASS MbRevolutionLayout : public MbSweptLayout { + MbAxis3D revAxis; ///< \ru Ось вращения. \en An revolution axis. +public: + /// \ru Конструктор. \en Constructor. + MbRevolutionLayout( const MbSurface & surf, Direction dir, const MbAxis3D & rotAxis ) : MbSweptLayout( surf, dir ), revAxis( rotAxis ) {} + /// \ru Конструктор копирования. \en Copy-constructor. + MbRevolutionLayout( const MbRevolutionLayout & other ) : MbSweptLayout( other ), revAxis( other.revAxis ) {} + /// \ru Деструктор. \en Destructor. + virtual ~MbRevolutionLayout(); +public: + /// \ru Это параметры вращения? \en This is rotation parameters? + virtual bool IsRevolutionLayout() const { return true; } +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + void operator = ( const MbRevolutionLayout & ); +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Данные края сечения поверхности. + \en The surface section control function. \~ + \details \ru Точку края сечения определяют: или рёбра, или кривые. Направление сечения на краю определяют: илди поверхности смежных граней рёбер, или поверхности граней, или функция угла наклона. + \en The end of the section is determined as point either edges, or curves. The direction on the end of the section is determined as either the surfaces of edges, or the surfaces of faces, or a function of the angle. + \ingroup Build_Parameters +*/ +// --- +class MATH_CLASS MbSectionRail { + +private: + std::vector edges; ///< \ru Направляющие рёбра (могут отсутствовать). \en The guide edges (may be empty). + std::vector edgeSide; ///< \ru Какую сторону кривой гладко стыковать с поверхностью (синхронно с guideEdges). \en Which side should the surface join smoothly to (synchronously with guideEdges). + std::vector faces; ///< \ru Опорные грани (могут отсутствовать). \en The reference faces (may be empty). + std::vector faceSide; ///< \ru С каких сторон касаться поверхностей при form==cs_Linea (синхронно с surfaces). \en On which sides to touch surfaces when form==cs_Linea (synchronously with surfaces). + std::vector curves; ///< \ru Направляющие кривые (могут отсутствовать). \en The guide curves (may be empty). + MbFunction * angle; ///< \ru Функция угла наклона (может отсутствовать). \en The function of the angle of inclination (may be NULL). + ThreeStates state; ///< \ru Как использовать angle: угол к хорде (ts_neutral), отклонение от касательной поверхности (ts_positive), отклонение от нормали к поверхности (ts_negative). + ///< \en How to use angle: angle to chord (ts_neutral), deviation from tangent surface (ts_positive), deviation from normal to surface (ts_negative). + +public: + + /// \ru Конструктор по умолчанию. \en Empty constructor. + MbSectionRail() + : edges () + , edgeSide() + , faces () + , faceSide() + , curves () + , angle ( NULL ) + , state( ts_neutral ) + {} + + /** \brief \ru Конструктор по параметрам. + \en Constructor by parameters. \~ + \param[in] eds - \ru Направляющие рёбра. + \en The guide edges. \~ + \param[in] eSide - \ru Какую сторону кривой гладко стыковать с поверхностью (синхронно с edges). + \en Which side should the surface join smoothly to (synchronously with edges). \~ + \param[in] fcs - \ru Направляющие грани (могут отсутствовать). + \en The guide faces (may be empty). \~ + \param[in] fSide - \ru С каких сторон касаться поверхностей (синхронно с faces). + \en On which sides to touch surfaces (synchronously with faces). \~ + \param[in] cs - \ru Направляющие кривые (могут отсутствовать). + \en The guide curves (may be empty). \~ + \param[in] ang - \ru Функция угла наклона (может быть NULL). + \en The function of the angle of inclination (may be NULL). \~ + \param[in] st - \ru Как использовать ang. + \en How to use ang. \~ + */ + MbSectionRail( std::vector & edges_, std::vector & eSides, + std::vector & faces_, std::vector & fSides, + std::vector & cs, + MbFunction * ang, ThreeStates st ); + /// \ru Конструктор копирования. \en Copy-constructor. + MbSectionRail( const MbSectionRail & other ); + /// \ru Конструктор копирования. \en Copy-constructor. + MbSectionRail( const MbSectionRail & other, MbRegDuplicate * ireg ); + /// \ru Деструктор. \en Destructor. + ~MbSectionRail(); + +public: + + /// \ru Добавить в данные направляющую кривую. \en Add guiding to data. \~ + void AddEdge( MbCurveEdge & _edge, bool side ); + /// \ru Выдать направляющие рёбра. \en Get guide edges. + void GetEdges( std::vector & eds ) const; + void GetEdges( RPArray & eds ) const; + /// \ru Какую сторону кривой гладко стыковать с поверхностью? \en Which side should the surface join smoothly to? + void GetEdgeSide( std::vector & eSide ) const; + /// \ru Выдать количество направляющих ребер. \en Get guide edges count. + size_t GetEdgesCount() const { return edges.size(); } + size_t GetEdgeSideCount() const { return edgeSide.size(); } + /// \ru Выдать направляющее ребро. \en Get guide edge. + MbCurveEdge * SetEdge( size_t i ) { return ( i < edges.size() ) ? edges[i] : NULL; } + + /// \ru Добавить в данные поверхность. \en Add surface to data. \~ + void AddFace( MbFace & _face, bool side ); + /// \ru Выдать грани. \en Get faces. + void GetFaces( std::vector & fas ) const; + void GetFaces( RPArray & fas ) const; + /// \ru С каких сторон касаться поверхностей? \en On which sides to touch surfaces? + void GetFaceSide( std::vector & fSide ) const; + /// \ru Выдать количество направляющих граней. \en Get guide faces count. + size_t GetFacesCount() const { return faces.size(); } + size_t GetFaceSideCount() const { return faceSide.size(); } + /// \ru Выдать направляющую грань. \en Get guide face. + MbFace * SetFace( size_t i ) { return ( i < faces.size() ) ? faces[i] : NULL; } + + /// \ru Добавить в данные кривую. \en Add curve to data. \~ + void AddCurve( MbCurve3D & _curve ); + /// \ru Добавить в данные кривые. \en Add curves to data. \~ + void AddCurves( std::vector & _curves ); + /// \ru Выдать дополнительные направляющие кривые. \en Get additional guide curves. + void GetCurves( std::vector & crs ) const; + /// \ru Выдать количество дополнительных направляющих кривых. \en Get additional guide curves count. + size_t GetCurvesCount() const { return curves.size(); } + MbCurve3D * SetCurve( size_t i ) { return ( i < curves.size() ) ? curves[i] : NULL; } + + /// \ru Установить функцию управления сечением. \en Set section control function. + void SetAngle( MbFunction & an ); + /// \ru Выдать функцию управления сечением (радиус или дискриминант). \en Get section control function (radius or discriminant). + const MbFunction * GetAngle() const { return angle; } + MbFunction * SetAngle() { return angle; } + + /// \ru Выдать образующую кривую. \en Get forming curve. + const ThreeStates GetState() const { return state; } + void SetState( ThreeStates st ) { state = st; } + + /// \ru Преобразовать объект. \en Transform the object. \~ + void Transform( const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); + /// \ru Сдвинуть объект. \en Move the object. \~ + void Move ( const MbVector3D & to, MbRegTransform * iReg = NULL ); + /// \ru Повернуть объект. \en Rotate the object. \~ + void Rotate ( const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); + /// \ru Определить, являются ли объекты равными? \en Determine whether an object is equal? + bool IsSame( const MbSectionRail & other, double accuracy ) const; + /// \ru Определить, являются ли объекты подобными. \en Determine whether the objects are similar. \~ + bool IsSimilar( const MbSectionRail & other ) const; + /// \ru Сделать объекты равным. \en Make objects equal. \~ + bool SetEqual ( const MbSectionRail & other ); + + // Дать любую точку и ориентировочную длину. + double GetAnyPopint( MbCartPoint3D & p0 ); + + /// \ru Оператор присваивания без копирования данных. \en Assignment operator without copying. + void operator = ( const MbSectionRail & other ); + + KNOWN_OBJECTS_RW_REF_OPERATORS( MbSectionRail ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. + +}; // MbSectionRail + + +//------------------------------------------------------------------------------ +/** \brief \ru Функция управления сечением поверхности. + \en The surface section control function. \~ + \details \ru Форму сечения поверхности заметания определяет функция управления сечением (радиус или дискриминант). + Если функция управления сечением не определена, то она рассчитывается покривой, через которую должно пройти сечение, или поверхности, которой должно касаться сечение. \n + \en The surface section form is determined by the section control function (radius or discriminant). + If the section control function is not determined, it is calculated with curve that the section should pass through or surface that the section should touch. \n + \ingroup Build_Parameters +*/ +// --- +struct MATH_CLASS MbSectionRule { + +public: + MbFunction * function; ///< \ru Функция управления сечением (радиус или дискриминант, может быть NULL). \en Section control function (radius or discriminant). + MbCurve3D * curve; ///< \ru Кривая, через которую должно пройти сечение. \en The curve that the section should pass through. + MbSurface * surface; ///< \ru Поверхность, которой должно касаться сечение. \en The surface that the section should touch. + +public: + /// \ru Конструктор по умолчанию. \en Empty constructor. + MbSectionRule(); + /// \ru Конструктор по функции. \en The constructor by function. + MbSectionRule( MbFunction * fun ); + /// \ru Конструктор по кривой. \en The constructor by function. + MbSectionRule( MbCurve3D * cur ); + /// \ru Конструктор по поверхности. \en The constructor by surface. + MbSectionRule( MbSurface * sur ); + /// \ru Конструктор копирования. \en Copy-constructor. + MbSectionRule( const MbSectionRule & other ); + /// \ru Конструктор копирования. \en Copy-constructor. + MbSectionRule( const MbSectionRule & other, MbRegDuplicate * ireg ); + /// \ru Деструктор. \en Destructor. + ~MbSectionRule(); + +public: + + /// \ru Выдать функцию управления сечением. \en Get section control function. + const MbFunction * GetFunction() const { return function; } + /// \ru Установить функцию управления сечением. \en Set section control function. + void SetFunction( MbFunction & f ); + void SetFunction( double f ); + /// \ru Выдать кривую управления сечением. \en Get section control curve. + const MbCurve3D * GetCurve() const { return curve; } + /// \ru Установить кривую управления сечением. \en Set section control curve. + void SetCurve( MbCurve3D & c ); + /// \ru Выдать поверхность управления сечением. \en Get section control surface. + const MbSurface * GetSurface() const { return surface; } + /// \ru Установить поверхность управления сечением. \en Set section control surface. + void SetSurface( MbSurface & s ); + + /// \ru Преобразовать объект. \en Transform the object. \~ + void Transform( const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); + /// \ru Сдвинуть объект. \en Move the object. \~ + void Move ( const MbVector3D & to, MbRegTransform * iReg = NULL ); + /// \ru Повернуть объект. \en Rotate the object. \~ + void Rotate ( const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); + /// \ru Определить, являются ли объекты равными? \en Determine whether an object is equal? + bool IsSame( const MbSectionRule & other, double accuracy ) const; + /// \ru Определить, являются ли объекты подобными. \en Determine whether the objects are similar. \~ + bool IsSimilar( const MbSectionRule & other ) const; + /// \ru Сделать объекты равным. \en Make objects equal. \~ + bool SetEqual ( const MbSectionRule & other ); + + /// \ru Оператор присваивания без копирования данных. \en Assignment operator without copying. + void operator = ( const MbSectionRule & other ); + + KNOWN_OBJECTS_RW_REF_OPERATORS( MbSectionRule ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. + +}; // MbSectionRule + + +//------------------------------------------------------------------------------ +/** \brief \ru Параметры операции построения поверхности заметания переменного сечения. + \en The parameters for buyilding the swept mutable section surface. \~ + \details \ru Поверхность заметания строится путем движения плоского сечения вдоль опорной кривой. + Плоское сечение может начинаться на направляющей кривой и заканчиваться на другой направляющей кривой. + Направляющих кривых может быть две, одна или ни одной. Кроме того, в построении могут использоваться управляющие кривые. \n + Сечение поверхности плоскостью, перпендикулярной опорной кривой, может иметь одну из пяти форм и некоторые из них могут меняться по заданному закону. + Сечение может иметь форму окружности (или её дуги), отрезка прямой, кривой второго порядка, кривой третьего порядка или заданной сплайновой кривой. \n + При наличие направляющих кривых и их несущих поверхностей построенная поверхность заметания гладко стыкуется с несущими поверхностями. \n + \en The swept mutable section surface is form-generating by moving the flat section along the reference curve. \n + The flat section can start on a guide curve and end on another guide curve. + There can be two guide curves, one or none. In addition, control curves can be used in the construction. \n + The cross section of a surface with a plane perpendicular to the reference curve can have one of five shapes, and some of them can change according to a given law. + The cross section can take the form of a circle (or its arc), a straight line segment, a second-order curve, a third-order curve, or a given spline curve. \n + If there are guide curves and their bearing surfaces, the constructed sweep surface is smoothly joined to the bearing surfaces. \n \~ + \ingroup Build_Parameters +*/ +// --- +class MATH_CLASS MbSectionData { + +private: + MbCurve3D * spine; ///< \ru Опорная кривая. \en The reference curve. + MbeSectionShape form; ///< \ru Форма сечения поверхности. \en The surface cross-section shape. + MbSectionRail rail1; ///< \ru Данные начального края сечения. \en The data of the begining of section. + MbSectionRail rail2; ///< \ru Данные конечного края сечения. \en The data of the end of section. + MbCurve3D * curve; ///< \ru Кривая вершин (может отсутствовать). \en The apex curve (may be NULL). + MbSectionRule descript; ///< \ru Функция управления сечением поверхности (радиус или дискриминант, может быть NULL). \en The section control function (radius or discriminant). + MbPolyCurve * pattern; ///< \ru Образующая кривая при form==cs_Shape (для других форм NULL). \en Forming curve for form==cs_Shape (NULL on other case). + double uMin; ///< \ru Минимальное значение первого параметра. \en Minimal value of the first parameter. + double uMax; ///< \ru Максимальное значение первого параметра. \en Maximal value of the first parameter. + double buildSag; ///< \ru Угловое отклонение при движении по кривым и поверхностям. \en Angular deviation while moving along curves and surfaces. + double accuracy; ///< \ru Точность построения толерантных объектов. \en An accuracy of building tolerant objects. + +public: + /// \ru Конструктор по умолчанию. \en Empty constructor. + MbSectionData() + : spine ( NULL ) + , form ( cs_Round ) + , rail1 () + , rail2 () + , curve ( NULL ) + , descript() + , pattern ( NULL ) + , uMin ( 0.0 ) + , uMax ( 1.0 ) + , buildSag( Math::deviateSag ) + , accuracy( Math::metricPrecision ) + {} + + /** \brief \ru Конструктор по параметрам. + \en Constructor by parameters. \~ + \param[in] sp - \ru Опорная кривая. + \en The reference curve. \~ + \param[in] f - \ru Форма сечения поверхности. + \en The surface cross-section shape. \~ + \param[in] r1 - \ru Данные начального края сечения. + \en The data of the begining of section. \~ + \param[in] r2 - \ru Данные конечного края сечения. + \en The data of the end of section. \~ + \param[in] ap - \ru Кривая вершин (может быть NULL). + \en The apex curve (may be empty). \~ + \param[in] desc - \ru Функция управления сечением (может быть NULL). + \en Section control function (may be NULL). \~ + \param[in] patt - \ru Образующая кривая (может быть NULL). + \en Forming curve (may be NULL). \~ + */ + MbSectionData( MbCurve3D & sp, + MbeSectionShape f, + MbSectionRail & r1, + MbSectionRail & r2, + MbCurve3D * ap, + MbSectionRule & desc, + MbPolyCurve * patt ); + /// \ru Конструктор копирования. \en Copy-constructor. + MbSectionData( const MbSectionData & other ); + /// \ru Конструктор копирования. \en Copy-constructor. + MbSectionData( const MbSectionData & other, MbRegDuplicate * ireg ); + /// \ru Деструктор. \en Destructor. + ~MbSectionData(); + +public: + + /// \ru Установить опорную кривую. \en Set reference curve. + void SetSpine( MbCurve3D & s ); + //< \ru Установить вектор направления опорной кривой (если spine==NULL). \en Set the direction vector of the reference curve (if spine= = NULL). + void SetSpine( const MbVector3D & a ); + /// \ru Выдать опорную кривую. \en Get reference curve. + const MbCurve3D * GetSpine() const { return spine; } + MbCurve3D * SetSpine() { return spine; } + + /// \ru Выдать форму сечения поверхности. \en Get cross-section shape. + MbeSectionShape GetForm() const { return form; } + /// \ru Установить форму сечения поверхности. \en Set cross-section shape. + void SetForm( MbeSectionShape f ) { form = f; } + + ///< \ru Данные начального края сечения. \en The data of the begining of section. + MbSectionRail & GetRrail1() { return rail1; } + ///< \ru Данные конечного края сечения. \en The data of the end of section. + MbSectionRail & GetRrail2() { return rail2; } + + /// \ru Добавить в данные направляющее ребро. \en Add guiding to data. \~ + void AddEdge1( MbCurveEdge & _edge, bool side ) { rail1.AddEdge( _edge, side ); } + void AddEdge2( MbCurveEdge & _edge, bool side ) { rail2.AddEdge( _edge, side ); } + /// \ru Выдать направляющие рёбра. \en Get guide edges. + void GetEdges1( std::vector & eds ) const { rail1.GetEdges( eds ); } + void GetEdges1( RPArray & eds ) const { rail1.GetEdges( eds ); } + /// \ru Выдать направляющие рёбра. \en Get guide edges. + void GetEdges2( std::vector & eds ) const { rail2.GetEdges( eds ); } + void GetEdges2( RPArray & eds ) const { rail2.GetEdges( eds ); } + /// \ru Какую сторону кривой гладко стыковать с поверхностью? \en Which side should the surface join smoothly to? + void GetEdgeSide1( std::vector & eSide ) const { rail1.GetEdgeSide( eSide ); } + /// \ru Какую сторону кривой гладко стыковать с поверхностью? \en Which side should the surface join smoothly to? + void GetEdgeSide2( std::vector & eSide ) const { rail2.GetEdgeSide( eSide ); } + /// \ru Выдать количество направляющих ребер. \en Get guide edges count. + size_t GetEdgesCount1() const { return rail1.GetEdgesCount(); } + size_t GetEdgesCount2() const { return rail2.GetEdgesCount(); } + size_t GetEdgeSideCount1() const { return rail1.GetEdgeSideCount(); } + size_t GetEdgeSideCount2() const { return rail2.GetEdgeSideCount(); } + /// \ru Выдать направляющее ребро. \en Get guide edge. + MbCurveEdge * SetEdge1( size_t i ) { return rail1.SetEdge( i ); } + /// \ru Выдать направляющее ребро. \en Get guide edge. + MbCurveEdge * SetEdge2( size_t i ) { return rail2.SetEdge( i ); } + + /// \ru Добавить направляющую грань. \en Add guide face. + void AddFace1( MbFace & _face, bool side ) { rail1.AddFace( _face, side ); } + void AddFace2( MbFace & _face, bool side ) { rail2.AddFace( _face, side ); } + /// \ru Выдать поверхности. \en Get surfaces. + void GetFaces1( std::vector & fas ) const { rail1.GetFaces( fas ); } + void GetFaces1( RPArray & fas ) const { rail1.GetFaces( fas ); } + /// \ru Выдать поверхности. \en Get surfaces. + void GetFaces2( std::vector & fas ) const { rail2.GetFaces( fas ); } + void GetFaces2( RPArray & fas ) const { rail2.GetFaces( fas ); } + /// \ru С каких сторон касаться поверхностей? \en On which sides to touch surfaces? + void GetFaceSide1( std::vector & fSide ) const { rail1.GetFaceSide( fSide ); } + /// \ru С каких сторон касаться поверхностей? \en On which sides to touch surfaces? + void GetFaceSide2( std::vector & fSide ) const { rail2.GetFaceSide( fSide ); } + /// \ru Выдать количество направляющих граней. \en Get guide faces count. + size_t GetFacesCount1() const { return rail1.GetFacesCount(); } + size_t GetFacesCount2() const { return rail2.GetFacesCount(); } + size_t GetFaceSideCount1() const { return rail1.GetFaceSideCount(); } + size_t GetFaceSideCount2() const { return rail2.GetFaceSideCount(); } + /// \ru Выдать направляющую грань. \en Get guide face. + MbFace * SetFace1( size_t i ) { return rail1.SetFace( i ); } + /// \ru Выдать направляющую грань. \en Get guide face. + MbFace * SetFace2( size_t i ) { return rail2.SetFace( i ); } + + /// \ru Добавить в данные направляющую кривую. \en Add guiding to data. \~ + void AddCurve1( MbCurve3D & crv ) { rail1.AddCurve( crv ); } + void AddCurve2( MbCurve3D & crv ) { rail2.AddCurve( crv ); } + /// \ru Выдать дополнительные направляющие кривые. \en Get additional guide curves. + void GetCurves1( std::vector & crs ) const { rail1.GetCurves( crs ); } + /// \ru Выдать дополнительные направляющие кривые. \en Get additional guide curves. + void GetCurves2( std::vector & crs ) const { rail2.GetCurves( crs ); } + /// \ru Выдать количество дополнительных направляющих кривых. \en Get additional guide curves count. + size_t GetCurvesCount1() const { return rail1.GetCurvesCount(); } + size_t GetCurvesCount2() const { return rail2.GetCurvesCount(); } + /// \ru Выдать направляющую кривую. \en Get guide curve. + MbCurve3D * SetCurve1( size_t i ) { return rail1.SetCurve( i ); } + /// \ru Выдать направляющую кривую. \en Get guide curve. + MbCurve3D * SetCurve2( size_t i ) { return rail2.SetCurve( i ); } + + /// \ru Добавить в данные функции. \en Add functions to data. \~ + void SetAngle1( MbFunction & ang ) { rail1.SetAngle( ang ); } + /// \ru Добавить в данные функции. \en Add functions to data. \~ + void SetAngle2( MbFunction & ang ) { rail2.SetAngle( ang ); } + /// \ru Выдать функции углов наклона. \en Get angle functions. + const MbFunction * GetAngle1() const { return rail1.GetAngle(); } + /// \ru Выдать функции углов наклона. \en Get angle functions. + const MbFunction * GetAngle2() const { return rail2.GetAngle(); } + /// \ru Выдать функцию угла наклона. \en Get angle function. + MbFunction * SetAngle1() { return rail1.SetAngle(); } + /// \ru Выдать функцию угла наклона. \en Get angle function. + MbFunction * SetAngle2() { return rail2.SetAngle(); } + + /// \ru Добавить в данные кривую. \en Set curve. \~ + void SetCurve( MbCurve3D & curv ); + /// \ru Выдать направляющую кривую. \en Get guide curve. + MbCurve3D * SetCurve() { return curve; } + /// \ru Выдать дополнительные направляющие кривые. \en Get additional guide curves. + const MbCurve3D * GetCurve() const { return curve; } + + /// \ru Выдать данные управления сечением. \en Get section control data. + const MbSectionRule & GetSectionRule() const { return descript; } + MbSectionRule & SetSectionRule() { return descript; } + /// \ru Выдать функцию управления сечением (радиус или дискриминант). \en Get section control function (radius or discriminant). + const MbFunction * GetFunction() const { return descript.function; } + MbFunction * SetFunction() { return descript.function; } + /// \ru Установить функцию управления сечением. \en Set section control function. + void SetFunction( MbFunction & f ) { descript.SetFunction( f ); } + void SetFunction( double f ) { descript.SetFunction( f ); } + + /// \ru Выдать образующую кривую. \en Get forming curve. + const MbPolyCurve * GetPattern() const { return pattern; } + MbPolyCurve * SetPattern() { return pattern; } + /// \ru Установить образующую кривую. \en Set forming curve. + void SetPattern( MbPolyCurve & p ); + + /// \ru Минимальное значение первого параметра. \en Minimal value of the first parameter. + double GetUMin() const { return uMin; } + /// \ru Максимальное значение первого параметра. \en Maximal value of the first parameter. + double GetUMax() const { return uMax; } + /// \ru Установить область определения первого параметра поверхностей. \en Set the first parameter region of the surface. + void SetUParams( double u1, double u2 ); + /// \ru Угловое отклонение при движении по кривым и поверхностям. \en Angular deviation while moving along curves and surfaces. + double GetBuildSag() const { return buildSag; } + ///< \ru Точность построения толерантных объектов. \en An accuracy of building tolerant objects. + double GetAccuracy() const { return accuracy; } + void SetAccuracy( double acc ); + + /// \ru Преобразовать объект. \en Transform the object. \~ + void Transform( const MbMatrix3D & matr, MbRegTransform * iReg = NULL ); + /// \ru Сдвинуть объект. \en Move the object. \~ + void Move ( const MbVector3D & to, MbRegTransform * iReg = NULL ); + /// \ru Повернуть объект. \en Rotate the object. \~ + void Rotate ( const MbAxis3D & axis, double angle, MbRegTransform * iReg = NULL ); + /// \ru Определить, являются ли объекты равными? \en Determine whether an object is equal? + bool IsSame( const MbSectionData & other, double accuracy ) const; + /// \ru Определить, являются ли объекты подобными. \en Determine whether the objects are similar. \~ + bool IsSimilar( const MbSectionData & other ) const; + /// \ru Сделать объекты равным. \en Make objects equal. \~ + bool SetEqual ( const MbSectionData & other ); + + /// \ru Оператор присваивания без копирования данных. \en Assignment operator without copying. + void operator = ( const MbSectionData & other ); + + KNOWN_OBJECTS_RW_REF_OPERATORS( MbSectionData ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. + +}; // MbSectionData + + +//------------------------------------------------------------------------------ +/** \brief \ru Данные о поверхности переменного сечения. + \en Data about swept mutable section surface. \~ + \details \ru Данные содержат номера рёбер и граней исходной оболочки, на которых строится поверхность. + \en The data contains the numbers of edges and faces of the original shell on which the surface is built. + \ingroup Model_Creators +*/ +// --- +class MATH_CLASS MbSectionCode { + +public: + SArray edgeIndex1; ///< \ru Номера рёбер первой направляющей кривой. \en The edge numbers of the first guide curve. + SArray faceIndex1; ///< \ru Номера граней первой направляющей поверхности. \en The face numbers of the first guide surface. + SArray edgeIndex2; ///< \ru Номера рёбер второй направляющей кривой. \en The edge numbers of the second guide curve. + SArray faceIndex2; ///< \ru Номера граней второй направляющей поверхности. \en The face numbers of the second guide surface. + +public: + /// \ru Конструктор по умолчанию. \en Default constructor. + MbSectionCode() + : edgeIndex1( 0, 1 ) + , faceIndex1( 0, 1 ) + , edgeIndex2( 0, 1 ) + , faceIndex2( 0, 1 ) + {} + /// \ru Конструктор. \en Constructor. + MbSectionCode( SArray & edI1, SArray & faI1, + SArray & edI2, SArray & faI2 ) + : edgeIndex1( edI1 ) + , faceIndex1( faI1 ) + , edgeIndex2( edI2 ) + , faceIndex2( faI2 ) + {} + /// \ru Конструктор копирования. \en Copy-constructor. + MbSectionCode( const MbSectionCode & other ) + : edgeIndex1( other.edgeIndex1 ) + , faceIndex1( other.faceIndex1 ) + , edgeIndex2( other.edgeIndex2 ) + , faceIndex2( other.faceIndex2 ) + {} + /// \ru Деструктор. \en Destructor. + ~MbSectionCode() {} + +public: + + const SArray & GetEdgeIndex1() const { return edgeIndex1; } + const SArray & GetFaceIndex1() const { return faceIndex1; } + const SArray & GetEdgeIndex2() const { return edgeIndex2; } + const SArray & GetFaceIndex2() const { return faceIndex2; } + + SArray & GetEdgeIndex1() { return edgeIndex1; } + SArray & GetFaceIndex1() { return faceIndex1; } + SArray & GetEdgeIndex2() { return edgeIndex2; } + SArray & GetFaceIndex2() { return faceIndex2; } + + /// \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. + void Transform( const MbMatrix3D & matr ); + /// \ru Сдвинуть объект вдоль вектора. \en Move an object along a vector. + void Move ( const MbVector3D & to ); + /// \ru Повернуть объект вокруг оси на заданный угол. \en Rotate an object at a given angle around an axis. + void Rotate ( const MbAxis3D & axis, double ang ); + /// \ru Являются ли объекты равными? \en Determine whether an object is equal? + bool IsSame( const MbSectionCode & other, double accuracy ) const; + + // \ru Оператор присваивания. \en The assignment operator. + void operator = ( const MbSectionCode & other ) { + edgeIndex1 = other.edgeIndex1; + faceIndex1 = other.faceIndex1; + edgeIndex2 = other.edgeIndex2; + faceIndex2 = other.faceIndex2; + } + + KNOWN_OBJECTS_RW_REF_OPERATORS( MbSectionCode ) // \ru Для работы со ссылками и объектами класса. \en For working with references and objects of the class. + //DECLARE_NEW_DELETE_CLASS( MbSectionCode ) + //DECLARE_NEW_DELETE_CLASS_EX( MbSectionCode ) +}; // MbSectionCode + + +#endif // __OP_SWEPT_PARAMETERS_H diff --git a/C3d/Include/pars_equation_tree.h b/C3d/Include/pars_equation_tree.h index bf685e8..a3aab3a 100644 --- a/C3d/Include/pars_equation_tree.h +++ b/C3d/Include/pars_equation_tree.h @@ -1,1549 +1,1573 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Узел бинарного дерева. - \en Node of a binary tree. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __PARS_EQUATION_TREE_H -#define __PARS_EQUATION_TREE_H - - -#include // \ru СМВ для компиляции ICC \en СМВ for compilation by ICC -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Количество параметров. - \en Parameters count. \~ - \details \ru Выдать количество параметров для типа операции.\n - \en Get the count of parameters for type of operation.\n \~ - \param[in] operationType - \ru Тип операцции. - \en An operation type. \~ - \return \ru Количество параметров операции. - \en Operation parameters count. \~ - \ingroup Parser -*/ -// --- -uint inline GetCountOfParams( PceOperationType operationType ) -{ - uint res = 1; - if ( operationType < oprt_BinaryOperation ) - res = 3; - else if ( operationType < oprt_UnaryOperation ) - res = 2; - - return res; -} - - -//----------------------------------------------------------------------------- -/** \brief \ru Информация о характерных точках дерева. - \en Information about characteristic points of a tree. \~ - \details \ru Информация о характерных точках дерева. \n - \en Information about characteristic points of a tree. \n \~ - \ingroup Parser -*/ -// --- -class BTreeNode; -struct CharacterPointInfo -{ - /** \brief \ru Тип характерной точки. - \en Type of a characteristic point. \~ - \details \ru Тип характерной точки.\n - \en Type of a characteristic point.\n \~ - */ - enum EquCharacterPointType - { - equPoint_DefRangeRight, ///< \ru Граница области определения. \en Boundary of the definition domain. - equPoint_DefRangeLeft, ///< \ru Граница области определения. \en Boundary of the definition domain. - equPoint_Extr, ///< \ru Экстремум. \en Extremum. - equPoint_Break1, ///< \ru Разрыв первого рода. \en Discontinuity of the first kind. - equPoint_Break2, ///< \ru Разрыв второго рода. \en Discontinuity of the second kind. - equPoint_DerBreak1 ///< \ru Разрыв производной. \en Derivative discontinuity. - }; - - const BTreeNode * m_tree; ///< \ru Узел дерева. Не равен NULL. \en Node of a tree. Not equal to NULL. - EquCharacterPointType m_type; ///< \ru Тип характорной точки. \en Type of a characteristic point. - double m_ph; ///< \ru Значение параметра функции. \en The value of the function parameter. - double m_period; ///< \ru Период функции. \en A period of a function. - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор.\n - \en Constructor.\n \~ - \param[in] tree - \ru Узел дерева. - \en Node of a tree. \~ - \param[in] type - \ru Тип характорной точки. - \en Type of a characteristic point. \~ - \param[in] ph - \ru Значение параметра функции. - \en Value of a function parameter. \~ - \param[in] period - \ru Период функции. - \en Period of a function. \~ - */ - CharacterPointInfo( const BTreeNode & tree, EquCharacterPointType type, double ph, double period ) - : m_tree ( &tree ) - , m_type ( type ) - , m_ph ( ph ) - , m_period( period ) - {} - -}; - - -//----------------------------------------------------------------------------- -/** \brief \ru Элемент области определения функции. - \en Element of the function definition domain. \~ - \details \ru Элемент области определения функции. \n - \en Element of the function definition domain. \n \~ - \ingroup Parser -*/ -// --- -struct DefRangeItem -{ - /** \brief \ru Тип элемента. - \en A type of the element. \~ - \details \ru Тип элемента области определения функции. \n - \en Type of an element of the function definition domain. \n \~ - */ - enum RangeItemType - { - range_def, ///< \ru Область определения. \en Definition domain. - range_break, ///< \ru Разрыв. \en Discontinuity. - range_extr, ///< \ru Экстремум. \en Extremum. - }; - - double lbound; ///< \ru Левая граница элемента области определения. \en Left bound of an element of the definition domain. - double rbound; ///< \ru Правая граница элемента области определения. \en Right bound of an element of the definition domain. - RangeItemType type; ///< \ru Тип элемента. \en A type of an element. - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор. \n - \en Constructor. \n \~ - \param[in] l - \ru Левая граница элемента области определения. - \en The left bound of an element of the definition domain. \~ - \param[in] r - \ru Правая граница элемента области определения. - \en The right bound of an element of the definition domain. \~ - \param[in] t - \ru Тип элемента. - \en A type of the element. \~ - */ - DefRangeItem( double l, double r, RangeItemType t = range_def ) - : lbound( l ) - , rbound( r ) - , type( t ) - { - if ( lbound > rbound ) - std::swap( lbound, rbound ); - } -}; - - -namespace std { -//----------------------------------------------------------------------------- -/** \brief \ru Сравнение элементов области определения. - \en Comparison of elements of the definition domain. \~ - \details \ru Сравнение элементов области определения функции. \n - \en Comparison of elements of the function definition domain. \n \~ - \ingroup Parser -*/ -// --- -template<> -struct less -{ - /** \brief \ru Сравнение элементов области определения. - \en Comparison of elements of the definition domain. \~ - \details \ru Сравнение элементов области определения функции. \n - \en Comparison of elements of the function definition domain. \n \~ - \param[in] _Left - \ru Первый элемент. - \en The first element. \~ - \param[in] _Right - \ru Второй элемент - \en The second element \~ - \return \ru true, если первый элемент меньше второго. - \en true if the first element is less than second one. \~ - */ - bool operator()(const DefRangeItem & _Left, const DefRangeItem & _Right) const - { return ( _Right.lbound - _Left.rbound > -METRIC_EPSILON ); } -}; - -} // namespace std - - -//----------------------------------------------------------------------------- -/** \brief \ru Область определения функции. - \en The function domain. \~ - \details \ru Область определения функции. \n - \en The function domain. \n \~ - \ingroup Parser -*/ -// --- -class DefRange -{ - std::set arr; - bool m_hasbreak; -public: - static const double eps; ///< \ru Погрешность. \en Tolerance. - -public: - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор области определения без разрывов. \n - \en Constructor of the definition domain without discontinuities. \n \~ - \param[in] l - \ru Левая граница области определения. - \en Left bound of the definition domain. \~ - \param[in] r - \ru Правая граница области определения. - \en Right bound of the definition domain. \~ - */ - DefRange( double l, double r ) - : m_hasbreak( false ) - { - if ( l > r ) - std::swap( l, r ); - arr.insert( DefRangeItem(l - METRIC_EPSILON, r + METRIC_EPSILON) ); - } - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор области определения без разрывов. \n - \en Constructor of the definition domain without discontinuities. \n \~ - */ - DefRange() : m_hasbreak( false ) {}; - - /// \ru Дать набор элементов области определения. \en Get a set of elements of the definition domain. - const std::set & GetSet() const { return arr; } - - /// \ru Есть ли разрывы в области определения. \en Whether the discontinuities is in the definition domain. - bool HasBreaks() const { return m_hasbreak; } - - /// \ru Разрезать область определения. \en Cut the domain. - bool Cut( DefRangeItem & i ); - /// \ru Добавить элемент области определения. \en Add element of the definition domain. - void Add( DefRangeItem & i ); - /// \ru Оператор сравнения. \en Comparison operator. - bool operator == ( const DefRange & other ); -}; - - -class BTreeConst; -class BTreeIdent; -class BTreeFunction; -class BTreeOperation; -class BTreeOperation1Arg; -class BTreeOperation3Args; -class BTreeUserFunc; - - -//----------------------------------------------------------------------------- -/** \brief \ru Значение функции и производных. - \en Value of the function and derivatives. \~ - \details \ru Значение функции и её первой, второй и третьей производных. \n - \en Values of function, its first, second and third derivatives. \n \~ - \ingroup Parser -*/ -// --- -struct DerivesValues -{ - double value; ///< \ru Значение функции. \en The value of function. - double firstDer; ///< \ru Первая производная. \en The first derivative. - double secondDer; ///< \ru Вторая производная. \en The second derivative. - double thirdDer; ///< \ru Третья производная. \en The third derivative. -}; - -//----------------------------------------------------------------------------- -/** \brief \ru Базовый класс для узлов дерева выражения. - \en Base class for nodes of the expression tree. \~ - \details \ru Базовый класс для узлов дерева выражения. \n - \en Base class for nodes of the expression tree. \n \~ - \ingroup Parser -*/ -// --- -class MATH_CLASS BTreeNode : public TapeBase -{ -public: - typedef std::map EqualVarsMap; ///< \ru Набор пар переменных. \en A set of pairs of variables. - typedef std::map VarsDerives; ///< \ru Набор пар: координата - значение и производные. \en A set of pairs: coordinate - value and derivatives. - -public: - BteNodeType type; ///< \ru Тип узла. \en A type of node. \~ \internal \ru Для отладки. \en For debugging. \~ \endinternal -protected: - mutable VarsDerives varDers; ///< \ru Рабочие переменные. \en Working variables. \~ - -protected: - /// \ru Конструктор по умолчанию. \en Default constructor. - BTreeNode() {} - -public: - virtual ~BTreeNode() {} - /// \ru Выдать тип узла дерева. \en Get type of a tree node. - virtual BteNodeType IsA () const = 0; - /// \ru Создать копию объекта. \en Create a copy of the object. - virtual BTreeNode * Duplicate() const = 0; - - /// \ru Функция линейная. \en Function is linear. - virtual bool IsLine() const = 0; - - /**\ru \name Функции для вычисления значения и производной. - \en \name Functions for calculation of the value and the derivative. - \{ */ - - /** \brief \ru Вычислить значение. - \en Calculate value. \~ - \details \ru Вычислить значение узла.\n - \en Calculate value of node.\n \~ - \param[out] fValue - \ru Значение. - \en Value. \~ - \return \ru Код результата разбора строки. - \en String parsing result code. \~ - */ - virtual EquTreeResCode GetValue( double & fValue ) const = 0; - - /** \brief \ru Установить значение. - \en Set value. \~ - \details \ru Устанавливает значение v узлу дерева. - \en Set 'v' value to a tree node. \~ - \param[in] v - \ru Желаемое значение. - \en Desirable value. \~ - \param[in] unfixedDVars - \ru Переменные, значение которых можно менять. - \en Variables which values can be changed. \~ - */ - virtual bool SetValue( double v, const std::set & unfixedDVars ) = 0; - - /** \brief \ru Вычислить значение и производные. - \en Calculate a value and derivatives. \~ - \details \ru Вычислить значение и производные. \n - \en Calculate a value and derivatives. \n \~ - \param[out] fValue - \ru Значение. - \en Value. \~ - \param[out] derive1 - \ru Первая производная. - \en The first derivative. \~ - \param[out] derive2 - \ru Вторая производная. - \en The second derivative. \~ - \param[out] derive3 - \ru Третья производная. - \en The third derivative. \~ - \param[in] ders - \ru Набор значений и производных. - \en Set of values and derivatives. \~ - \return \ru Код результата разбора строки. - \en String parsing result code. \~ - */ - virtual EquTreeResCode CalculateDerives( double & fValue, double & derive1, - double & derive2, double & derive3, const VarsDerives & ders ) const = 0; - - /** \brief \ru Вычислить значение и производные. - \en Calculate a value and derivatives. \~ - \details \ru Вычислить значение и производные. \n - \en Calculate a value and derivatives. \n \~ - \param[in] coord - \ru Координата. - \en Coordinate. \~ - \param[out] v - \ru Значение. - \en Value. \~ - \param[out] fd - \ru Первая производная. - \en The first derivative. \~ - \param[out] sd - \ru Вторая производная. - \en The second derivative. \~ - \param[out] td - \ru Третья производная. - \en The third derivative. \~ - \return \ru Код результата разбора строки. - \en String parsing result code. \~ - */ - EquTreeResCode CalculateDerives( const ItCoord * coord, double & v, double & fd, double & sd, double & td ) const; - - /** \brief \ru Выдать использованные переменные. - \en Get the used variables. \~ - \details \ru Выдать использованные переменные. \n - \en Get the used variables. \n \~ - \param[out] arr - \ru Переменные. - \en Variables. \~ - \param[out] funcs - \ru Пользовательские функции. - \en User functions. \~ - */ - virtual void GetUsedVariables( SSArray & arr, SSArray & funcs ) const = 0; - - /** \} */ - /**\ru \name Функции замены переменных по именам. - \en \name Functions for replacing variables by names. - \{ */ - - /** \brief \ru Заменить переменные. - \en Replace variables. \~ - \details \ru Заменить все переменные с указанными именем на новую переменную.\n - \en Replace all variables with the specified name by a new variable.\n \~ - \param[out] varName - \ru Имя. - \en Name. \~ - \param[out] newVar - \ru Новая переменная. - \en New variable. \~ - */ - virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ) = 0; - - /** \brief \ru Заменить узел. - \en Replace a node. \~ - \details \ru Заменить узел на копию нового, если заданная переменная использована.\n - \en Replace a node with a copy of a new one if the given variable is used.\n \~ - \param[out] var - \ru Переменная. - \en Variable. \~ - \param[out] subTree - \ru Новый узел. - \en New node. \~ - */ - virtual void ReplaceParVariable( const ItTreeVariable & var, const BTreeNode & subTree ) = 0; - - /** \brief \ru Заменить переменные. - \en Replace variables. \~ - \details \ru Заменить все переменные с указанными именем на новую переменную.\n - \en Replace all variables with the specified name by a new variable.\n \~ - */ - virtual void ReplaceIntVariable( const c3d::string_t &, ItIntervalTreeVariable & ) {} - - /** \} */ - /**\ru \name Функции доступа к данным. - \en \name Functions for access to data. - \{ */ - - /** \brief \ru Выдать значение параметра экстремума. - \en Get value of parameter of extremum. \~ - \details \ru Выдать значение параметра экстремума, если это возможно.\n - \en Get value of parameter of extremum if it is possible.\n \~ - \param[in] interval - \ru Интервал для поиска. - \en Interval to search. \~ - \param[in] var - \ru Переменная. - \en Variable. \~ - \param[out] points - \ru Точки экстремума. - \en Points of extremum. \~ - */ - bool GetExtremumPoints( std::pair interval, ItTreeVariable & var, - std::vector & points ); - - /** \brief \ru Область определения. - \en Domain. \~ - \details \ru Область определения.\n - \en Domain.\n \~ - \param[in,out] defRange - \ru Область определения. - \en Domain. \~ - \param[out] var - \ru Переменная. - \en Variable. \~ - \param[in] stopOnBreak - \ru Не искать разрывы области определения. - \en Not to search the discontinuities in the definition domain. \~ - */ - virtual bool GetDefRange( DefRange & defRange, ItTreeVariable & var, bool stopOnBreak ) const = 0; - - /** \brief \ru Только для внутреннего использования! Порядок переменной. - \en For internal use only! Order of variable. \~ - \details \ru Порядок переменной.\n - \en Order of variable.\n \~ - \param[in] var - \ru Переменная. - \en Variable. \~ - \return \ru Порядок. - \en Order. \~ - */ - virtual size_t GetPseudoOrderByVar ( ItTreeVariable & var ) const = 0; - - /** \brief \ru Фиксированные переменные. - \en Fixed variables. \~ - \details \ru Фиксированные переменные.\n - \en Fixed variables.\n \~ - \param[in] unfixedVars - \ru Набор нефиксированных переменных. Если переменная нашлась в наборе, фиксировать копию. - \en A set of unfixed variables. If variable was found in set, then fix the copy. \~ - \param[in] newFuncs - \ru Пользовательские функции. - \en User functions. \~ - \param[out] code - \ru Коды результата разбора строки. - \en Result codes of string parsing. \~ - \return \ru Переменную для фиксирования. - \en Variable for fixation. \~ - */ - virtual std_unique_ptr FixVars ( const RPArray & unfixedVars, - PArray & newFuncs, EquTreeResCode & code ) const = 0; - - /// \ru Дать эквивалентный узел. \en Get equivalent node. - virtual std_unique_ptr GetCalcEquivalent() const = 0; - - /** \brief \ru Дать строку. - \en Get string. \~ - \details \ru Дать строку выражения.\n - \en Get expression string.\n \~ - \param[out] - \ru Строка. - \en String. \~ - */ - virtual void GetString( c3d::string_t & ) const = 0; - - /// \ru Вычислить размер в байтах. \en Get size in bytes. - virtual size_t SizeOf() const = 0; - - /** \} */ - /**\ru \name Функции сравнения. - \en \name Comparison function. - \{ */ - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether the node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether the node is equal to the given node.\n \~ - \param[in] other - \ru Узел для сравнения. - \en Node for comparison. \~ - \param[in] varsMap - \ru Набор пар равных переменных. - \en A set of pairs of equal variables. \~ - \return \ru true, если узлы равны. - \en true if nodes are equal. \~ - */ - virtual bool IsEqual( const BTreeNode & other, const EqualVarsMap & varsMap ) const = 0; - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether the node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether the node is equal to the given node.\n \~ - \return false. - */ - virtual bool IsEqual( const BTreeConst & , const EqualVarsMap & ) const { return false; } - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether the node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether the node is equal to the given node.\n \~ - \return false. - */ - virtual bool IsEqual( const BTreeIdent & , const EqualVarsMap & ) const { return false; } - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether the node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether the node is equal to the given node.\n \~ - \return false. - */ - virtual bool IsEqual( const BTreeFunction & , const EqualVarsMap & ) const { return false; } - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether the node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether the node is equal to the given node.\n \~ - \return false. - */ - virtual bool IsEqual( const BTreeOperation & , const EqualVarsMap & ) const { return false; } - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether the node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether the node is equal to the given node.\n \~ - \return false. - */ - virtual bool IsEqual( const BTreeOperation1Arg & , const EqualVarsMap & ) const { return false; } - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether the node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether the node is equal to the given node.\n \~ - \return false. - */ - virtual bool IsEqual( const BTreeOperation3Args & , const EqualVarsMap & ) const { return false; } - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether the node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether the node is equal to the given node.\n \~ - \return false. - */ - virtual bool IsEqual( const BTreeUserFunc & , const EqualVarsMap & ) const { return false; } - /** \} */ - -private: - // \ru не реализовано \en not implemented - BTreeNode( const BTreeNode & ); - void operator =( const BTreeNode & ); - - DECLARE_PERSISTENT_CLASS( BTreeNode ) -}; - -IMPL_PERSISTENT_OPS( BTreeNode ) - -//----------------------------------------------------------------------------- -/** \brief \ru Типы узлов бинарного дерева. - \en Types of nodes of the binary tree. \~ - \details \ru Типы узлов бинарного дерева. \n - \en Types of nodes of the binary tree. \n \~ - \ingroup Parser -*/ -// --- -enum TeIntervalNodeType -{ - tei_Const, ///< \ru Константа. \en Constant. - tei_Ident ///< \ru Идентификатор. \en Identifier. -}; - - -//----------------------------------------------------------------------------- -/** \brief \ru Узел дерева интервального выражения. - \en Node of interval expression tree. \~ - \details \ru Узел дерева интервального выражения. \n - \en Node of interval expression tree. \n \~ - \ingroup Parser -*/ -// --- -class MATH_CLASS TreeIntervalNode : public TapeBase/*, public BTreeBaseNode*/ -{ -protected: - TreeIntervalNode() {} - -public: - /// \ru Выдать тип узла дерева. \en Get type of a tree node. - virtual TeIntervalNodeType IsA () const = 0; - /// \ru Выдать копию объекта. \en Get a copy of the object. - virtual TreeIntervalNode * Duplicate() const = 0; - - /// \ru Дать первую переменную. \en Get the first variable. - virtual EquTreeResCode GetFirstValue ( double & ) const = 0; - /// \ru Дать вторую переменную. \en Get the second variable. - virtual EquTreeResCode GetSecondValue ( double & ) const = 0; - /// \ru Дать строку. \en Get a string. - virtual void GetString ( c3d::string_t & ) const = 0; - - /** \brief \ru Установить значение. - \en Set value. \~ - \details \ru Попытаться установить значение [f;s] узлу дерева. - \en Try to set value [f;s] to a tree node. \~ - \param[in] f - \ru Нижняя граница интервала. - \en Lower bound of the interval. \~ - \param[in] s - \ru Верхняя граница интервала. - \en Upper bound of the interval. \~ - \param[in] unfixedIVars - \ru Mножество интервальных переменных, которые можно менять. - \en Set of interval variables, which can be changed. \~ - \param[in] unfixedDVars - \ru Mножество вещественных переменных, которые можно менять. - \en Set of real variables, which can be changed. \~ - \access public - \return \ru Истину, если удалось установить значение. - \en True if it was succeeded to set value. \~ - */ - virtual std::pair SetValue ( double f, double s - , const std::set & unfixedIVars - , const std::set & unfixedDVars ) = 0; - - /** \brief \ru Заменить переменные. - \en Replace variables. \~ - \details \ru Заменить все переменные с указанными именем на новую переменную.\n - \en Replace all variables with the specified name by a new variable.\n \~ - \param[out] varName - \ru Имя. - \en Name. \~ - \param[out] newVar - \ru Новая переменная. - \en New variable. \~ - */ - virtual void ReplaceParVariable ( const c3d::string_t & varName, ItTreeVariable & newVar ) = 0; - -private: - // \ru не реализовано \en not implemented - TreeIntervalNode( const TreeIntervalNode & ); - void operator = ( const TreeIntervalNode & ); - - DECLARE_PERSISTENT_CLASS( TreeIntervalNode ) -}; - -IMPL_PERSISTENT_OPS( TreeIntervalNode ) - -//----------------------------------------------------------------------------- -/** \brief \ru Интервал простых выражений. - \en Interval of simple expressions. \~ - \details \ru Операция [] - получение интервала из простых выражений.\n - \en operation [] - obtaining of interval of simple expressions.\n \~ - \ingroup Parser -*/ -// --- -class MATH_CLASS IntervalConstNode : public TreeIntervalNode -{ - BTreeNode * m_firstValue; ///< \ru Первое значение (всегда не NULL). \en First value (always not NULL). - BTreeNode * m_secondValue; ///< \ru Второе значение (всегда не NULL). \en Second value (always not NULL). - -public: - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор.\n - \en Constructor.\n \~ - \param[in] firstValue - \ru Первый узел. - \en The first node. \~ - \param[in] secondValue - \ru Второй узел. - \en The second node. \~ - */ - IntervalConstNode( BTreeNode & firstValue, BTreeNode & secondValue ) - : m_firstValue ( &firstValue ) - , m_secondValue( &secondValue ) - {} - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор копирования.\n - \en Copy-constructor.\n \~ - \param[in] other - \ru Копируемый объект. - \en Object to copy. \~ - */ - IntervalConstNode( const IntervalConstNode & other ) - : m_firstValue( other.m_firstValue ) - , m_secondValue( other.m_secondValue ) - {} - -public: - /**\ru \name Функции узла дерева интервального выражения. - \en \name Functions of interval expression tree node. - \{ */ - - virtual TeIntervalNodeType IsA () const { return tei_Const; } // \ru выдать тип узла дерева \en get type of a tree node - virtual IntervalConstNode * Duplicate() const { return new IntervalConstNode( *this ); } - - // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V - virtual EquTreeResCode GetFirstValue ( double & v ) const { return m_firstValue->GetValue( v ); } - virtual EquTreeResCode GetSecondValue ( double & v ) const { return m_secondValue->GetValue( v ); } - - // \ru Установить значение. \en Set value. - virtual std::pair SetValue ( double f, double s - , const std::set & unfixedIVars - , const std::set & unfixedDVars ); - - virtual void GetString( c3d::string_t & ) const {} - virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ); - - /** \} */ - /**\ru \name Функции операции []. - \en \name Functions of operation []. - \{ */ - - /// \ru Дать первый узел. \en Get the first node. - BTreeNode & GetFirstTree () { return *m_firstValue; } - /// \ru Дать второй узел. \en Get the second node. - BTreeNode & GetSecondTree () { return *m_secondValue; } - - /** \brief \ru Заменить переменные. - \en Replace variables. \~ - \details \ru Заменить все переменные с указанными именем на новую интервальную переменную.\n - \en Replace all variables with the specified names by a new interval variable.\n \~ - */ - virtual void ReplaceIntVariable( const c3d::string_t & /*varName*/, ItIntervalTreeVariable & /*newVar*/ ) {} - /** \} */ -// ССА K13 virtual size_t SizeOf() const = 0; - -private: - // \ru не реализовано \en not implemented - void operator =( const TreeIntervalNode & ); - - DECLARE_PERSISTENT_CLASS_NEW_DEL( IntervalConstNode ) -}; - -IMPL_PERSISTENT_OPS( IntervalConstNode ) - -//----------------------------------------------------------------------------- -/** \brief \ru Интервальная переменная как узел бинарного дерева. - \en Interval variable as a node of a binary tree. \~ - \details \ru Узел дерева - интервальная переменная.\n - \en Tree node is an interval variable.\n \~ - \ingroup Parser -*/ -// --- -class MATH_CLASS IntervalIdentNode : public TreeIntervalNode -{ - ItIntervalTreeVariable * m_ident; ///< \ru Всегда не NULL. \en Always not NULL. - -public: - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор по интервальной переменной.\n - \en Constructor by an interval variable.\n \~ - \param[in] ident - \ru Интервальная переменная. - \en Interval variable. \~ - */ - IntervalIdentNode( ItIntervalTreeVariable & ident ) - : m_ident( &ident ) - {} - - /// \ru Конструктор копирования. \en Copy-constructor. - IntervalIdentNode( const IntervalIdentNode & other ); - -public: - /**\ru \name Функции узла дерева интервального выражения. - \en \name Functions of the interval expression tree node. - \{ */ - virtual TeIntervalNodeType IsA () const { return tei_Ident; } // \ru выдать тип узла дерева \en get type of a tree node - virtual IntervalIdentNode * Duplicate() const { return new IntervalIdentNode( *this ); } - - // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V - virtual EquTreeResCode GetFirstValue ( double & v ) const; - virtual EquTreeResCode GetSecondValue ( double & v ) const; - virtual void GetString( c3d::string_t & ) const {}; - virtual void ReplaceParVariable( const c3d::string_t & /*varName*/, ItTreeVariable & /*newVar*/ ){} - - // \ru Установить значение. \en Set value. - virtual std::pair SetValue ( double f, double s - , const std::set & unfixedIVars - , const std::set & unfixedDVars ); - /** \} */ - -// ССА K13 virtual void ReplaceIntVariable( const string & varName, ItIntervalTreeVariable & newVar ){} -// ССА K13 virtual size_t SizeOf() const; - -private: - void operator =( const IntervalIdentNode & ); - - DECLARE_PERSISTENT_CLASS_NEW_DEL( IntervalIdentNode ) -}; - -IMPL_PERSISTENT_OPS( IntervalIdentNode ) - -//------------------------------------------------------------------------------ -/** \brief \ru Константа как узел бинарного дерева. - \en Constant as a node of a binary tree. \~ - \details \ru Узел бинарного дерева, обозначающий константу. \n - \en Node of a binary tree denoting a constant. \n \~ - \ingroup Parser -*/ -// --- -class MATH_CLASS BTreeConst : public BTreeNode -{ -private: - double value; ///< \ru Значение константы. \en Value of a constant. - c3d::string_t m_name; ///< \ru Имя константы, если есть, в файл не пишется. \en A name of a constant if it exists, not written to file. - -public: - - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор по значению.\n - \en Constructor by value.\n \~ - \param[in] val - \ru Значение константы. - \en Value of a constant. \~ - \param[in] name - \ru Имя константы. - \en Name of a constant. \~ - */ - BTreeConst( double val, const c3d::string_t & name = _T("") ); - - /// \ru Конструктор копирования. \en Copy-constructor. - BTreeConst( const BTreeConst & ); - -public: - // \ru выдать тип узла дерева \en get type of a tree node - virtual BteNodeType IsA() const; - virtual BTreeNode * Duplicate() const; - - // \ru Функция линейная. \en Function is linear. - virtual bool IsLine() const { return false; } - - /**\ru \name Функции для вычисления значения и производной. - \en \name Functions for calculation of the value and the derivative. - \{ */ - // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V - virtual EquTreeResCode GetValue ( double & fvalue ) const; - virtual EquTreeResCode CalculateDerives( double &, double &, double &, double &, const VarsDerives & ) const; - - virtual bool SetValue( double, const std::set & ) { return false; } - - /** \} */ - /**\ru \name Функции замены переменных по именам. - \en \name Functions for replacing variables by names. - \{ */ - - // \ru Заменить все переменные с именем varName на переменную newVar \en Replace all variables with name 'varName' by variable 'newVar' - virtual void ReplaceParVariable( const c3d::string_t & /*varName*/, ItTreeVariable & /*newVar*/ ) {} - virtual void ReplaceParVariable( const ItTreeVariable & /*var*/, const BTreeNode & /*subTree*/ ) {} - - /** \} */ - /**\ru \name Функции доступа к данным. - \en \name Functions for access to data. - \{ */ - - /** \brief \ru Получить вложенный узел. - \en Get a child node. \~ - \details \ru Получить вложенный узел по индексу.\n - \en Get a child node by an index.\n \~ - */ - virtual BTreeNode * GetSubNode( size_t /*i*/ ) { return NULL; } - - virtual bool GetDefRange(DefRange &, ItTreeVariable &, bool /*stopOnBreak*/ ) const { return true; } - - virtual void GetUsedVariables( SSArray &, SSArray & )const{} - - virtual std_unique_ptr FixVars( const RPArray & /*unfixedVars*/, - PArray & /*newFuncs*/, EquTreeResCode & ) const - { return std_unique_ptr(Duplicate()); } - - virtual std_unique_ptr GetCalcEquivalent() const - { return std_unique_ptr(Duplicate()); } - - virtual void GetString( c3d::string_t & str ) const; - virtual size_t SizeOf() const; - - // \ru доступ к данным \en access to data - double GetValue() const { return value; } ///< \ru Дать переменную. \en Get variable. - void SetValue( double val ) { value = val; } ///< \ru Установить значение переменную. \en Set value of a variable. - - /** \} */ - /**\ru \name Функции доступа к данным. - \en \name Functions for access to data. - \{ */ - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether the node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether a node is equal to the given node.\n \~ - \param[in] other - \ru Узел для сравнения. - \en Node for comparison. \~ - \param[in] equVars - \ru Набор пар равных переменных. - \en A set of pairs of equal variables. \~ - \return \ru true, если узлы равны. - \en true if nodes are equal. \~ - */ - virtual bool IsEqual ( const BTreeNode & other, const EqualVarsMap & equVars ) const { return other.IsEqual( *this, equVars ); } - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether a node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether a node is equal to the given node.\n \~ - \param[in] other - \ru Узел для сравнения. - \en Node for comparison. \~ - \return \ru true, если узлы равны. - \en true if nodes are equal. \~ - */ - virtual bool IsEqual ( const BTreeConst & other, const EqualVarsMap & ) const { return other.value == value; } - /** \} */ -private: - void operator =( const BTreeConst & ); // \ru не реализовано \en not implemented - virtual size_t GetPseudoOrderByVar( ItTreeVariable & ) const { return 0; } // \ru Только для внутреннего использования! \en For internal use only! - - DECLARE_PERSISTENT_CLASS_NEW_DEL( BTreeConst ) -}; - -IMPL_PERSISTENT_OPS( BTreeConst ) - -//------------------------------------------------------------------------------ -/** \brief \ru Переменная как узел бинарного дерева. - \en Variable as a node of a binary tree. \~ - \details \ru Узел бинарного дерева, обозначающий переменную. \n - \en Node of a binary tree denoting variable. \n \~ - \ingroup Parser -*/ -// --- -class MATH_CLASS BTreeIdent : public BTreeNode -{ -private: - ItTreeVariable * id; - -public : - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор по переменной. \n - \en Constructor by a variable \n \~ - */ - BTreeIdent( ItTreeVariable & ); - - /// \ru Копирующий конструктор. \en Copy-constructor. - BTreeIdent( const BTreeIdent & ); - - // \ru выдать тип узла дерева \en get type of a tree node - virtual BteNodeType IsA () const; - virtual BTreeNode * Duplicate() const; - - // \ru Функция линейная. \en Function is linear. - virtual bool IsLine() const; - - /**\ru \name Функции для вычисления значения и производной. - \en \name Functions for calculation of the value and the derivative. - \{ */ - - virtual EquTreeResCode GetValue ( double & fvalue ) const; - virtual EquTreeResCode CalculateDerives( double &, double &, double &, double &, const VarsDerives & ) const; - virtual void GetUsedVariables ( SSArray &, SSArray & ) const; - - virtual bool SetValue( double, const std::set & ); - - /** \} */ - /**\ru \name Функции замены переменных по именам. - \en \name Functions for replacing variables by names. - \{ */ - - // \ru Заменить все переменные с именем varName на переменную newVar \en Replace all variables with name 'varName' by variable 'newVar' - virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ); - virtual void ReplaceParVariable( const ItTreeVariable &, const BTreeNode & /*subTree*/ ) {} - - /** \} */ - /**\ru \name Функции замены переменных по именам. - \en \name Functions for replacing variables by names. - \{ */ - - /// \ru Дать вложенный узел по индексу. \en Get a child node by an index. - virtual BTreeNode * GetSubNode( size_t /*i*/ ) { return NULL; } - - virtual bool GetDefRange( DefRange &, ItTreeVariable &, bool /*stopOnBreak*/ ) const{ return true; } - - virtual std_unique_ptr FixVars( const RPArray & unfixed, - PArray & newFuncs, EquTreeResCode & ) const; - - virtual std_unique_ptr GetCalcEquivalent() const { return std_unique_ptr(Duplicate()); } - - virtual void GetString( c3d::string_t & str ) const { str += id->GetName(); } - virtual size_t SizeOf() const; - - // \ru доступ к данным \en access to data - ItTreeVariable & GetVariable() const { C3D_ASSERT( id ); return *id; } ///< \ru Дать параметрический идентификатор (переменную). \en Get parametric identifier (variable). - void SetVariable( ItTreeVariable & var ) { id = &var; } ///< \ru Установить параметрический идентификатор (переменную) \en Set a parametric identifier (variable). - - /** \} */ - /**\ru \name Функции сравнения. - \en \name Comparison function. - \{ */ - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether a node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether a node is equal to the given node.\n \~ - \param[in] other - \ru Узел для сравнения. - \en Node for comparison. \~ - \param[in] equVars - \ru Набор пар равных переменных. - \en A set of pairs of equal variables. \~ - \return \ru true, если узлы равны. - \en true if nodes are equal. \~ - */ - virtual bool IsEqual( const BTreeNode & other, const EqualVarsMap & equVars ) const { return other.IsEqual( *this, equVars ); } - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether a node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether a node is equal to the given node.\n \~ - \param[in] other - \ru Узел для сравнения. - \en Node for comparison. \~ - \param[in] equVars - \ru Набор пар равных переменных. - \en A set of pairs of equal variables. \~ - \return \ru true, если узлы равны. - \en true if nodes are equal. \~ - */ - virtual bool IsEqual( const BTreeIdent & other, const EqualVarsMap & equVars ) const; - -private: - void operator = ( const BTreeIdent & ); // \ru не реализовано \en not implemented - virtual size_t GetPseudoOrderByVar( ItTreeVariable & var ) const { return ( &var == id ) ? 1 : 0; } // \ru Только для внутреннего использования! \en For internal use only! - - DECLARE_PERSISTENT_CLASS_NEW_DEL( BTreeIdent ) -}; - -IMPL_PERSISTENT_OPS( BTreeIdent ) - -//------------------------------------------------------------------------------ -/** \brief \ru Функция как узел бинарного дерева. - \en Function as node of a binary tree. \~ - \details \ru Узел бинарного дерева, обозначающий функцию. \n - \en Node of a binary tree denoting a function. \n \~ - \ingroup Parser -*/ -// --- -class BTreeFunction : public BTreeNode -{ -public: - - /** \brief \ru Типы функций. - \en Types of functions. \~ - \details \ru Типы функций. \n - \en Types of functions. \n \~ - */ - // \ru ССА K13 не менять! Пишутся в файл! \en ССА K13 not to change! Are written to file! - enum EquFnCode - { - eFnCode_unknown = -1, ///< \ru Неизвестный тип. \en An unknown type. - eFnCode_first = 0, ///< \ru Начало диапазона известных функций. \en Start of the range of known functions. - eFnCode_sin = eFnCode_first, ///< \ru Синус. \en Sine. - eFnCode_cos, ///< \ru Косинус. \en Cosine. - eFnCode_tan, ///< \ru Тангенс. \en Tangent. - eFnCode_sqrt, ///< \ru Квадратный корень. \en Square root. - eFnCode_atan, ///< \ru Арктангенс. \en Arctangent. - eFnCode_exp, ///< \ru Экспонента. \en Exponent. - eFnCode_ln, ///< \ru Натуральный логарифм. \en Natural logarithm. - eFnCode_abs, ///< \ru Модуль. \en Absolute value. - eFnCode_DegBegin, ///< \ru Начала диапазона функций с аргументом в градусах. \en Start of the range of functions with argument in degrees. - eFnCode_sind = eFnCode_DegBegin, ///< \ru Синус угла в градусах. \en Sine of the angle in degrees. - eFnCode_cosd, ///< \ru Косинус угла в градусах. \en Cosine of the angle in degrees. - eFnCode_tand, ///< \ru Тангенс угла в градусах. \en Tangent of the angle in degrees. - eFnCode_DegEnd = eFnCode_tand, ///< \ru Конец диапазона функций с аргументом в градусах. \en End of the range of functions with argument in degrees. - eFnCode_atand, ///< \ru Арктангенс с результатом в градусах. \en Arctangent with the result in degrees. - eFnCode_lg, ///< \ru Десятичный логарифм. \en Decimal logarithm. - eFnCode_ceil, ///< \ru Ближайшее большее целое число. \en Nearest largest integer number. - eFnCode_floor, ///< \ru Ближайшее меньшее целое число. \en Nearest smallest integer number. - eFnCode_round, ///< \ru Ближайшее целое число. \en Nearest integer number. - eFnCode_acos, ///< \ru Арккосинус. \en Arccosine. - eFnCode_acosd, ///< \ru Арккосинус с результатом в градусах. \en Arccosine with the result in degrees. - eFnCode_asin, ///< \ru Арксинус. \en Arcsine. - eFnCode_asind, ///< \ru Арксинус с результатом в градусах. \en Arcsine with the result in degrees. - eFnCode_rad, ///< \ru Перевод из градусов в радианы. \en Conversion from degrees to radians. - eFnCode_deg, ///< \ru Перевод из радиан в градусы. \en Conversion from radians to degrees. - eFnCode_last = eFnCode_deg ///< \ru Конец диапазона известных функций. \en End of the range of known functions. - }; - -private : - EquFnCode fnCode; // \ru код функции \en code of function - BTreeNode * par; // \ru параметр функции \en parameter of the function - -public : - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор. \n - \en Constructor. \n \~ - \param[in] code - \ru Тип функции. - \en Type of a function. \~ - \param[in] p - \ru Параметр функции. - \en Parameter of a function. \~ - */ - BTreeFunction( EquFnCode code, BTreeNode & p ); - - /// \ru Копирующий конструктор. \en Copy-constructor. - BTreeFunction( const BTreeFunction & ); - - /// \ru Деструктор. \en Destructor. - virtual ~BTreeFunction(); - -public : - // \ru выдать тип узла дерева \en get type of a tree node - virtual BteNodeType IsA () const; - virtual BTreeNode * Duplicate() const; - - // \ru Функция линейная. \en Function is linear. - virtual bool IsLine() const; - - /**\ru \name Функции для вычисления значения и производной. - \en \name Functions for calculation of the value and the derivative. - \{ */ - - virtual EquTreeResCode GetValue ( double & value ) const; - // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V - virtual EquTreeResCode CalculateDerives ( double &, double &, double &, double &, const VarsDerives & ) const; - virtual void GetUsedVariables ( SSArray &, SSArray & ) const; - - virtual bool SetValue ( double, const std::set & ) { return false; } // \ru не реализовано \en not implemented - /** \} */ - /**\ru \name Функции замены переменных по именам. - \en \name Functions for replacing variables by names. - \{ */ - - virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ); - virtual void ReplaceParVariable( const ItTreeVariable & var, const BTreeNode & subTree ); - - /** \} */ - /**\ru \name Функции доступа к данным. - \en \name Functions for access to data. - \{ */ - - /// \ru Дать вложенный узел по индексу. \en Get a child node by an index. - virtual BTreeNode * GetSubNode( size_t i ); - virtual bool GetDefRange( DefRange &, ItTreeVariable &, bool stopOnBreak ) const; - virtual std_unique_ptr FixVars( const RPArray & unfixed, - PArray & newFuncs, EquTreeResCode & ) const; - - virtual std_unique_ptr GetCalcEquivalent() const; - - virtual void GetString( c3d::string_t & str ) const; - virtual size_t SizeOf() const; - - /** \} */ - /**\ru \name Функции сравнения. - \en \name Comparison function. - \{ */ - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether a node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether a node is equal to the given node.\n \~ - \param[in] other - \ru Узел для сравнения. - \en Node for comparison. \~ - \param[in] equVars - \ru Набор пар равных переменных. - \en A set of pairs of equal variables. \~ - \return \ru true, если узлы равны. - \en true if nodes are equal. \~ - */ - virtual bool IsEqual ( const BTreeNode & other, const EqualVarsMap & equVars ) const { return other.IsEqual( *this, equVars ); } - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether a node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether a node is equal to the given node.\n \~ - \param[in] other - \ru Узел для сравнения. - \en Node for comparison. \~ - \param[in] equVars - \ru Набор пар равных переменных. - \en A set of pairs of equal variables. \~ - \return \ru true, если узлы равны. - \en true if nodes are equal. \~ - */ - virtual bool IsEqual ( const BTreeFunction & other, const EqualVarsMap & equVars )const; - /** \} */ - - bool IsCos() const; ///< \ru имеет ли вид a * cos() + b \en looks like a * cos() + b - - - -private: - // \ru выдать значение параметра для экстремума( если это возможно ). \en get value of parameter of extremum( if it is possible ). - virtual bool GetCharacterPoints( std::vector & ) const; - EquTreeResCode GetValue( double arg, double & value ) const; - // \ru Только для внутреннего использования! \en For internal use only! - virtual size_t GetPseudoOrderByVar( ItTreeVariable & var ) const; - void operator = ( const BTreeFunction & ); // \ru не реализовано \en not implemented - - DECLARE_PERSISTENT_CLASS_NEW_DEL( BTreeFunction ) -}; - -IMPL_PERSISTENT_OPS( BTreeFunction ) - -//------------------------------------------------------------------------------ -/** \brief \ru Дать тип функции. - \en Get type of function. \~ - \details \ru Дать тип функции по имени.\n - \en Get type of function by name.\n \~ - \param[in] name - \ru Имя функции. - \en Name of function. \~ - \return \ru Тип функции. - \en Type of a function. \~ - \ingroup Parser -*/ -// --- -BTreeFunction::EquFnCode GetFunCodeByName( const c3d::string_t & name ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Дать имя функции. - \en Get name of function. \~ - \details \ru Дать имя функции по типу.\n - \en Get name of function by type.\n \~ - \param[in] code - \ru Тип функции. - \en Type of a function. \~ - \param[out] name - \ru Имя функции. - \en Name of function. \~ - \ingroup Parser -*/ -// --- -void GetFunNameByCode( BTreeFunction::EquFnCode code, c3d::string_t & name ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Операция с двумя аргументами как узел бинарного дерева. - \en Operation with two arguments as node of binary tree. \~ - \details \ru Узел бинарного дерева, обозначающий операцию с двумя аргументами. \n - \en Node of binary tree denoting operation with two arguments. \n \~ - \ingroup Parser -*/ -// --- -class MATH_CLASS BTreeOperation : public BTreeNode -{ -private: - PceOperationType opCode; ///< \ru Код операции. \en Code of operation. - BTreeNode * op1; ///< \ru Первый операнд. \en The first operand. - BTreeNode * op2; ///< \ru Второй операнд. \en The second operand. - - static SArray varsDerives; ///< \ru Производные аргументов для вычисления производных показательно-степенной функции \en Derivatives of arguments for calculation of derivatives of exponential-power function - static std_unique_ptr deriveFunc; ///< \ru Функция для вычисления производной показательно-степенной функции \en Function for calculation of derivative of the exponential-power function - -public: - /** \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор.\n - \en Constructor.\n \~ - \param[in] code - \ru Код операции. - \en Code of operation. \~ - \param[in] op1 - \ru Первый операнд. - \en The first operand. \~ - \param[in] op2 - \ru Второй операнд. - \en The second operand. \~ - \ingroup Parser - */ - BTreeOperation( PceOperationType code, BTreeNode & op1, BTreeNode & op2 ); - - /// \ru Конструктор копирования. \en Copy-constructor. - BTreeOperation( const BTreeOperation & ); - - /// \ru Деструктор. \en Destructor. - virtual ~BTreeOperation(); - -public: - // \ru выдать тип узла дерева \en get type of a tree node - virtual BteNodeType IsA () const; - virtual BTreeNode * Duplicate() const; - - // \ru Функция линейная. \en Function is linear. - virtual bool IsLine() const; - - /**\ru \name Функции для вычисления значения и производной. - \en \name Functions for calculation of the value and the derivative. - \{ */ - - // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V - virtual EquTreeResCode GetValue ( double &fvalue ) const; - virtual EquTreeResCode CalculateDerives( double &, double &, double &, double &, const VarsDerives & ) const; - virtual void GetUsedVariables( SSArray &, SSArray & ) const; - - virtual bool SetValue ( double, const std::set & ) { return false; }// \ru не реализовано \en not implemented - - /** \} */ - /**\ru \name Функции замены переменных по именам. - \en \name Functions for replacing variables by names. - \{ */ - - // \ru Заменить все переменные с именем varName на переменную newVar \en Replace all variables with name 'varName' by variable 'newVar' - virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ); - virtual void ReplaceParVariable( const ItTreeVariable & var, const BTreeNode & subTree ); - - /** \} */ - /**\ru \name Функции доступа к данным. - \en \name Functions for access to data. - \{ */ - - /// \ru Дать вложенный узел по индексу. \en Get a child node by an index. - virtual BTreeNode * GetSubNode( size_t i ); - virtual bool GetDefRange( DefRange &, ItTreeVariable &, bool stopOnBreak ) const; - virtual std_unique_ptr FixVars( const RPArray & unfixed, - PArray & newFuncs, EquTreeResCode & ) const; - - virtual std_unique_ptr GetCalcEquivalent() const; - virtual void GetString( c3d::string_t & str ) const; - virtual size_t SizeOf() const; - - /** \} */ - /**\ru \name Функции сравнения. - \en \name Comparison function. - \{ */ - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether a node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether a node is equal to the given node.\n \~ - \param[in] other - \ru Узел для сравнения. - \en Node for comparison. \~ - \param[in] equVars - \ru Набор пар равных переменных. - \en A set of pairs of equal variables. \~ - \return \ru true, если узлы равны. - \en true if nodes are equal. \~ - */ - virtual bool IsEqual( const BTreeNode & other, const EqualVarsMap & equVars ) const { return other.IsEqual( *this, equVars ); } - - /** \brief \ru Равен ли узел заданному узлу. - \en Whether a node is equal to the given node. \~ - \details \ru Равен ли узел заданному узлу.\n - \en Whether a node is equal to the given node.\n \~ - \param[in] other - \ru Узел для сравнения. - \en Node for comparison. \~ - \param[in] equVars - \ru Набор пар равных переменных. - \en A set of pairs of equal variables. \~ - \return \ru true, если узлы равны. - \en true if nodes are equal. \~ - */ - virtual bool IsEqual( const BTreeOperation & other, const EqualVarsMap & equVars ) const; - - /** \} */ - /**\ru \name Функции доступа к данным. - \en \name Functions for access to data. - \{ */ - - /// \ru Код операции. \en Code of operation. - int16 GetOperationCode() const { return (int16)opCode; } - /// \ru Первый операнд. \en The first operand. - BTreeNode & GetFirstOperand () const { C3D_ASSERT( op1 ); return *op1; } - /// \ru Второй операнд. \en The second operand. - BTreeNode & GetSecondOperand() const { C3D_ASSERT( op2 ); return *op2; } - /** \} */ - - ///< \ru имеет ли вид a * cos() + b. \en look like a * cos() + b. - virtual bool IsCos ( double &a, double& b ) const; - -private: - void operator = ( const BTreeOperation & ); // \ru не реализовано \en not implemented - EquTreeResCode GetValue( double par1, double par2, double & value ) const; - // \ru выдать значение параметра для экстремума( если это возможно ). \en get value of parameter of extremum( if it is possible ). - virtual bool GetCharacterPoints( std::vector &, const ItTreeVariable & ) const; - // \ru Только для внутреннего использования! \en For internal use only! - virtual size_t GetPseudoOrderByVar( ItTreeVariable & var ) const; - - static MbUserFunc & GetFuncForDerInvolving(); - - DECLARE_PERSISTENT_CLASS_NEW_DEL( BTreeOperation ) -}; - -IMPL_PERSISTENT_OPS( BTreeOperation ) - -//------------------------------------------------------------------------------ -/** \brief \ru Операция с одним аргументом как узел бинарного дерева. - \en Operation with one argument as a node of a binary tree. \~ - \details \ru Узел бинарного дерева, обозначающий операцию с одним аргументом. \n - \en Node of a binary tree denoting an operation with one argument. \n \~ - \ingroup Parser -*/ -// --- -class BTreeOperation1Arg : public BTreeNode -{ -private : - PceOperationType opCode; ///< \ru Код операции. \en Code of operation. - BTreeNode * op; ///< \ru Операнд. \en Operand. - -public : - BTreeOperation1Arg( PceOperationType code, BTreeNode & ); - BTreeOperation1Arg( const BTreeOperation1Arg & ); - virtual ~BTreeOperation1Arg(); - - // \ru выдать тип узла дерева \en get type of a tree node - virtual BteNodeType IsA () const; - PceOperationType GetOperationType() const { return opCode; } - virtual BTreeNode * Duplicate() const; - - // \ru Функция линейная. \en Function is linear. - virtual bool IsLine() const; - - // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V - virtual EquTreeResCode GetValue ( double &fvalue ) const; - virtual EquTreeResCode CalculateDerives( double &, double &, double &, double &, const VarsDerives & ) const; - virtual void GetUsedVariables( SSArray &, SSArray & ) const; - - virtual bool SetValue ( double, const std::set & ); - - /// \ru Заменить все переменные с именем varName на переменную newVar \en Replace all variables with name 'varName' by variable 'newVar' - virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ); - virtual void ReplaceParVariable( const ItTreeVariable &, const BTreeNode & ); - virtual BTreeNode * GetSubNode( size_t i ); - // \ru выдать значение параметра для экстремума( если это возможно ). \en get value of parameter of extremum( if it is possible ). - virtual bool GetCharacterPoints( std::vector & ) const; - virtual bool GetDefRange( DefRange & range, ItTreeVariable & var, bool stopOnBreak ) const { C3D_ASSERT( op ); - return op->GetDefRange( range, var, stopOnBreak ); } - virtual std_unique_ptr FixVars( const RPArray & unfixed, - PArray & newFuncs, EquTreeResCode & ) const; - - virtual std_unique_ptr GetCalcEquivalent() const; - virtual void GetString( c3d::string_t & str ) const; - - virtual bool IsEqual ( const BTreeNode & other, const EqualVarsMap & equVars ) const { return other.IsEqual( *this, equVars ); } - virtual bool IsEqual ( const BTreeOperation1Arg & other, const EqualVarsMap & equVars ) const; - virtual size_t SizeOf() const; - -private: - void operator =( const BTreeOperation1Arg & ); // \ru не реализовано \en not implemented - EquTreeResCode GetValue( double arg, double & value ) const; - // \ru Только для внутреннего использования! \en For internal use only! - virtual size_t GetPseudoOrderByVar( ItTreeVariable & var ) const { return opCode == oprt_NOT ? SYS_MAX_T : op->GetPseudoOrderByVar(var); } - - DECLARE_PERSISTENT_CLASS_NEW_DEL( BTreeOperation1Arg ) -}; - -IMPL_PERSISTENT_OPS( BTreeOperation1Arg ) - -//------------------------------------------------------------------------------ -/** \brief \ru Операция с тремя аргументами как узел синтаксического дерева. - \en Operation with three arguments as node of syntax tree. \~ - \details \ru Узел бинарного дерева, обозначающий операцию с тремя аргументами. \n - \en Node of binary tree denoting operation with three arguments. \n \~ - \ingroup Parser -*/ -// --- -class BTreeOperation3Args : public BTreeNode -{ -private : - BTreeNode * op1; ///< \ru Первый операнд. \en The first operand. - BTreeNode * op2; ///< \ru Второй операнд. \en The second operand. - BTreeNode * op3; ///< \ru Третий операнд. \en The third operand. - -public : - BTreeOperation3Args( BTreeNode &, BTreeNode &, BTreeNode & ); - BTreeOperation3Args( const BTreeOperation3Args & ); - virtual ~BTreeOperation3Args(); - - // \ru выдать тип узла дерева \en get type of a tree node - virtual BteNodeType IsA() const; - virtual BTreeNode * Duplicate() const; - - // \ru Функция линейная. \en Function is linear. - virtual bool IsLine() const; - - // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V - virtual EquTreeResCode GetValue ( double & fVal ) const; - virtual EquTreeResCode CalculateDerives ( double &, double &, double &, double &, const VarsDerives & ) const; - virtual void GetUsedVariables ( SSArray &, SSArray & ) const; - - virtual bool SetValue ( double, const std::set & ){ return false;} // \ru не реализовано \en not implemented - - /// \ru Заменить все переменные с именем varName на переменную newVar \en Replace all variables with name 'varName' by variable 'newVar' - virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ); - virtual void ReplaceParVariable( const ItTreeVariable &, const BTreeNode & ); - virtual BTreeNode * GetSubNode( size_t i ); - virtual bool GetDefRange( DefRange &, ItTreeVariable &, bool stopOnBreak ) const; - virtual std_unique_ptr FixVars( const RPArray & unfixed, - PArray & newFuncs, EquTreeResCode & ) const; - - virtual std_unique_ptr GetCalcEquivalent() const; - virtual void GetString( c3d::string_t & str ) const; - - virtual bool IsEqual ( const BTreeNode & other, const EqualVarsMap & equVars ) const { return other.IsEqual( *this, equVars ); } - virtual bool IsEqual ( const BTreeOperation3Args & other, const EqualVarsMap & equVars ) const; - virtual size_t SizeOf() const; - -private: - // \ru выдать значение параметра для экстремума( если это возможно ). \en get value of parameter of extremum( if it is possible ). - virtual bool GetCharacterPoints( std::vector &, ItTreeVariable & ) const; - // \ru Только для внутреннего использования! \en For internal use only! - virtual size_t GetPseudoOrderByVar( ItTreeVariable & ) const { return SYS_MAX_T; } - void operator =( const BTreeOperation3Args & ); // \ru не реализовано \en not implemented - - DECLARE_PERSISTENT_CLASS_NEW_DEL( BTreeOperation3Args ) -}; - -IMPL_PERSISTENT_OPS( BTreeOperation3Args ) - -//------------------------------------------------------------------------------ -/** \brief \ru Пользовательская функция как узел бинарного дерева. - \en User-defined function as a node of a binary tree. \~ - \details \ru Узел бинарного дерева, обозначающий пользовательскую функцию. \n - \en Node of a binary tree denoting a user-defined function. \n \~ - \ingroup Parser -*/ -// --- -class BTreeUserFunc : public BTreeNode -{ - ItUserFunc * func; ///< \ru Пользовательская функция. \en A user-defined function. - PArray pars; - -public: - BTreeUserFunc( const BTreeUserFunc & other ); - BTreeUserFunc( ItUserFunc & _func, const RPArray & _pars ); - - // \ru выдать тип узла дерева \en get type of a tree node - virtual BteNodeType IsA () const { return bt_Function; } - virtual BTreeNode * Duplicate() const { return new BTreeUserFunc( *this ); } - - // \ru Функция линейная. \en Function is linear. - virtual bool IsLine() const {return false; } - - virtual bool SetValue( double, const std::set & ) { return false; } // \ru не реализовано \en not implemented - - // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V - virtual EquTreeResCode GetValue ( double & ) const; - virtual EquTreeResCode CalculateDerives( double &, double &, double &, double &, const VarsDerives & ) const; - virtual void GetUsedVariables( SSArray &, SSArray & ) const; - /// \ru Заменить все переменные с именем varName на переменную newVar \en Replace all variables with name 'varName' by variable 'newVar' - virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ); - virtual void ReplaceParVariable( const ItTreeVariable &, const BTreeNode & ); - virtual BTreeNode * GetSubNode ( size_t i ); - // \ru выдать значение параметра для экстремума( если это возможно ). \en get value of parameter of extremum( if it is possible ). -// ССА K13 virtual bool GetCharacterPoints( std::vector & ) const { return false; } - virtual bool GetDefRange( DefRange &, ItTreeVariable &, bool stopOnBreak ) const; - virtual std_unique_ptr FixVars( const RPArray & unfixed, - PArray & newFuncs, EquTreeResCode & ) const; - - virtual std_unique_ptr GetCalcEquivalent() const; - - virtual void GetString( c3d::string_t & str ) const; - - virtual bool IsEqual ( const BTreeNode & other, const EqualVarsMap & equVars ) const { return other.IsEqual( *this, equVars ); } - virtual bool IsEqual ( const BTreeUserFunc & other, const EqualVarsMap & equVars ) const; - - virtual size_t SizeOf() const { return 0; }; - -private: - void operator =( const BTreeOperation3Args & ); // \ru не реализовано \en not implemented - EquTreeResCode GetValue( SArray &, double & value ) const; - // \ru Только для внутреннего использования! \en For internal use only! - virtual size_t GetPseudoOrderByVar( ItTreeVariable & var ) const; - - DECLARE_PERSISTENT_CLASS_NEW_DEL( BTreeUserFunc ); -}; - -IMPL_PERSISTENT_OPS( BTreeUserFunc ) - -#endif // __PARS_EQUATION_TREE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Узел бинарного дерева. + \en Node of a binary tree. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __PARS_EQUATION_TREE_H +#define __PARS_EQUATION_TREE_H + + +#include // \ru СМВ для компиляции ICC \en СМВ for compilation by ICC +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Количество параметров. + \en Parameters count. \~ + \details \ru Выдать количество параметров для типа операции.\n + \en Get the count of parameters for type of operation.\n \~ + \param[in] operationType - \ru Тип операции. + \en An operation type. \~ + \return \ru Количество параметров операции. + \en Operation parameters count. \~ + \ingroup Parser +*/ +// --- +inline +uint GetCountOfParams( PceOperationType operationType ) +{ + uint res = 1; + if ( operationType < oprt_BinaryOperation ) + res = 3; + else if ( operationType < oprt_UnaryOperation ) + res = 2; + + return res; +} + + +//----------------------------------------------------------------------------- +/** \brief \ru Информация о характерных точках дерева. + \en Information about characteristic points of a tree. \~ + \details \ru Информация о характерных точках дерева. \n + \en Information about characteristic points of a tree. \n \~ + \ingroup Parser +*/ +// --- +class BTreeNode; +struct CharacterPointInfo +{ + /** \brief \ru Тип характерной точки. + \en Type of a characteristic point. \~ + \details \ru Тип характерной точки.\n + \en Type of a characteristic point.\n \~ + */ + enum EquCharacterPointType + { + equPoint_DefRangeRight, ///< \ru Граница области определения. \en Boundary of the definition domain. + equPoint_DefRangeLeft, ///< \ru Граница области определения. \en Boundary of the definition domain. + equPoint_Extr, ///< \ru Экстремум. \en Extremum. + equPoint_Break1, ///< \ru Разрыв первого рода. \en Discontinuity of the first kind. + equPoint_Break2, ///< \ru Разрыв второго рода. \en Discontinuity of the second kind. + equPoint_DerBreak1 ///< \ru Разрыв производной. \en Derivative discontinuity. + }; + + const BTreeNode * m_tree; ///< \ru Узел дерева. Не равен NULL. \en Node of a tree. Not equal to NULL. + EquCharacterPointType m_type; ///< \ru Тип характерной точки. \en Type of a characteristic point. + double m_ph; ///< \ru Значение параметра функции. \en The value of the function parameter. + double m_period; ///< \ru Период функции. \en A period of a function. + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор.\n + \en Constructor.\n \~ + \param[in] tree - \ru Узел дерева. + \en Node of a tree. \~ + \param[in] type - \ru Тип характерной точки. + \en Type of a characteristic point. \~ + \param[in] ph - \ru Значение параметра функции. + \en Value of a function parameter. \~ + \param[in] period - \ru Период функции. + \en Period of a function. \~ + */ + CharacterPointInfo( const BTreeNode & tree, EquCharacterPointType type, double ph, double period ) + : m_tree ( &tree ) + , m_type ( type ) + , m_ph ( ph ) + , m_period( period ) + {} + +}; + + +//----------------------------------------------------------------------------- +/** \brief \ru Элемент области определения функции. + \en Element of the function definition domain. \~ + \details \ru Элемент области определения функции. \n + \en Element of the function definition domain. \n \~ + \ingroup Parser +*/ +// --- +struct DefRangeItem +{ + /** \brief \ru Тип элемента. + \en A type of the element. \~ + \details \ru Тип элемента области определения функции. \n + \en Type of an element of the function definition domain. \n \~ + */ + enum RangeItemType + { + range_def, ///< \ru Область определения. \en Definition domain. + range_break, ///< \ru Разрыв. \en Discontinuity. + range_extr, ///< \ru Экстремум. \en Extremum. + }; + + double lbound; ///< \ru Левая граница элемента области определения. \en Left bound of an element of the definition domain. + double rbound; ///< \ru Правая граница элемента области определения. \en Right bound of an element of the definition domain. + RangeItemType type; ///< \ru Тип элемента. \en A type of an element. + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор. \n + \en Constructor. \n \~ + \param[in] l - \ru Левая граница элемента области определения. + \en The left bound of an element of the definition domain. \~ + \param[in] r - \ru Правая граница элемента области определения. + \en The right bound of an element of the definition domain. \~ + \param[in] t - \ru Тип элемента. + \en A type of the element. \~ + */ + DefRangeItem( double l, double r, RangeItemType t = range_def ) + : lbound( l ) + , rbound( r ) + , type( t ) + { + if ( lbound > rbound ) + std::swap( lbound, rbound ); + } +}; + + +namespace std { +//----------------------------------------------------------------------------- +/** \brief \ru Сравнение элементов области определения. + \en Comparison of elements of the definition domain. \~ + \details \ru Сравнение элементов области определения функции. \n + \en Comparison of elements of the function definition domain. \n \~ + \ingroup Parser +*/ +// --- +template<> +struct less +{ + /** \brief \ru Сравнение элементов области определения. + \en Comparison of elements of the definition domain. \~ + \details \ru Сравнение элементов области определения функции. \n + \en Comparison of elements of the function definition domain. \n \~ + \param[in] _Left - \ru Первый элемент. + \en The first element. \~ + \param[in] _Right - \ru Второй элемент + \en The second element \~ + \return \ru true, если первый элемент меньше второго. + \en true if the first element is less than second one. \~ + */ + bool operator()(const DefRangeItem & _Left, const DefRangeItem & _Right) const + { return ( _Right.lbound - _Left.rbound > -METRIC_EPSILON ); } +}; + +} // namespace std + + +//----------------------------------------------------------------------------- +/** \brief \ru Область определения функции. + \en The function domain. \~ + \details \ru Область определения функции. \n + \en The function domain. \n \~ + \ingroup Parser +*/ +// --- +class DefRange +{ + std::set arr; + bool m_hasbreak; +public: + static const double eps; ///< \ru Погрешность. \en Tolerance. + +public: + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор области определения без разрывов. \n + \en Constructor of the definition domain without discontinuities. \n \~ + \param[in] l - \ru Левая граница области определения. + \en Left bound of the definition domain. \~ + \param[in] r - \ru Правая граница области определения. + \en Right bound of the definition domain. \~ + */ + DefRange( double l, double r ) + : m_hasbreak( false ) + { + if ( l > r ) + std::swap( l, r ); + arr.insert( DefRangeItem(l - METRIC_EPSILON, r + METRIC_EPSILON) ); + } + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор области определения без разрывов. \n + \en Constructor of the definition domain without discontinuities. \n \~ + */ + DefRange() : m_hasbreak( false ) {}; + + /// \ru Дать набор элементов области определения. \en Get a set of elements of the definition domain. + const std::set & GetSet() const { return arr; } + + /// \ru Есть ли разрывы в области определения. \en Whether the discontinuities is in the definition domain. + bool HasBreaks() const { return m_hasbreak; } + + /// \ru Разрезать область определения. \en Cut the domain. + bool Cut( DefRangeItem & i ); + /// \ru Добавить элемент области определения. \en Add element of the definition domain. + void Add( DefRangeItem & i ); + /// \ru Оператор сравнения. \en Comparison operator. + bool operator == ( const DefRange & other ); +}; + + +class BTreeConst; +class BTreeIdent; +class BTreeFunction; +class BTreeOperation; +class BTreeOperation1Arg; +class BTreeOperation3Args; +class BTreeUserFunc; + + +//----------------------------------------------------------------------------- +/** \brief \ru Значение функции и производных. + \en Value of the function and derivatives. \~ + \details \ru Значение функции и её первой, второй и третьей производных. \n + \en Values of function, its first, second and third derivatives. \n \~ + \ingroup Parser +*/ +// --- +struct DerivesValues +{ + double value; ///< \ru Значение функции. \en The value of function. + double firstDer; ///< \ru Первая производная. \en The first derivative. + double secondDer; ///< \ru Вторая производная. \en The second derivative. + double thirdDer; ///< \ru Третья производная. \en The third derivative. +}; + + +//----------------------------------------------------------------------------- +/** \brief \ru Базовый класс для узлов дерева выражения. + \en Base class for nodes of the expression tree. \~ + \details \ru Базовый класс для узлов дерева выражения. \n + \en Base class for nodes of the expression tree. \n \~ + \ingroup Parser +*/ +// --- +class MATH_CLASS BTreeNode : public TapeBase +{ +public: + typedef std::map EqualVarsMap; ///< \ru Набор пар переменных. \en A set of pairs of variables. + typedef std::map VarsDerives; ///< \ru Набор пар: координата - значение и производные. \en A set of pairs: coordinate - value and derivatives. + +public: + BteNodeType type; ///< \ru Тип узла. \en A type of node. \~ \internal \ru Для отладки. \en For debugging. \~ \endinternal +protected: + mutable VarsDerives varDers; ///< \ru Рабочие переменные. \en Working variables. \~ + +protected: + /// \ru Конструктор по умолчанию. \en Default constructor. + BTreeNode() {} + +public: + virtual ~BTreeNode() {} + /// \ru Выдать тип узла дерева. \en Get type of a tree node. + virtual BteNodeType IsA () const = 0; + /// \ru Создать копию объекта. \en Create a copy of the object. + virtual BTreeNode * Duplicate() const = 0; + + /// \ru Функция линейная. \en Function is linear. + virtual bool IsLine() const = 0; + + /**\ru \name Функции для вычисления значения и производной. + \en \name Functions for calculation of the value and the derivative. + \{ */ + + /** \brief \ru Вычислить значение. + \en Calculate value. \~ + \details \ru Вычислить значение узла.\n + \en Calculate value of node.\n \~ + \param[out] fValue - \ru Значение. + \en Value. \~ + \return \ru Код результата разбора строки. + \en String parsing result code. \~ + */ + virtual EquTreeResCode GetValue( double & fValue ) const = 0; + + /** \brief \ru Установить значение. + \en Set value. \~ + \details \ru Устанавливает значение v узлу дерева. + \en Set 'v' value to a tree node. \~ + \param[in] v - \ru Желаемое значение. + \en Desirable value. \~ + \param[in] unfixedDVars - \ru Переменные, значение которых можно менять. + \en Variables which values can be changed. \~ + */ + virtual bool SetValue( double v, const std::set & unfixedDVars ) = 0; + + /** \brief \ru Вычислить значение и производные. + \en Calculate a value and derivatives. \~ + \details \ru Вычислить значение и производные. \n + \en Calculate a value and derivatives. \n \~ + \param[out] fValue - \ru Значение. + \en Value. \~ + \param[out] derive1 - \ru Первая производная. + \en The first derivative. \~ + \param[out] derive2 - \ru Вторая производная. + \en The second derivative. \~ + \param[out] derive3 - \ru Третья производная. + \en The third derivative. \~ + \param[in] ders - \ru Набор значений и производных. + \en Set of values and derivatives. \~ + \return \ru Код результата разбора строки. + \en String parsing result code. \~ + */ + virtual EquTreeResCode CalculateDerives( double & fValue, double & derive1, + double & derive2, double & derive3, const VarsDerives & ders ) const = 0; + + /** \brief \ru Вычислить значение и производные. + \en Calculate a value and derivatives. \~ + \details \ru Вычислить значение и производные. \n + \en Calculate a value and derivatives. \n \~ + \param[in] coord - \ru Координата. + \en Coordinate. \~ + \param[out] v - \ru Значение. + \en Value. \~ + \param[out] fd - \ru Первая производная. + \en The first derivative. \~ + \param[out] sd - \ru Вторая производная. + \en The second derivative. \~ + \param[out] td - \ru Третья производная. + \en The third derivative. \~ + \return \ru Код результата разбора строки. + \en String parsing result code. \~ + */ + EquTreeResCode CalculateDerives( const ItCoord * coord, double & v, double & fd, double & sd, double & td ) const; + + /** \brief \ru Выдать использованные переменные. + \en Get the used variables. \~ + \details \ru Выдать использованные переменные. \n + \en Get the used variables. \n \~ + \param[out] arr - \ru Переменные. + \en Variables. \~ + \param[out] funcs - \ru Пользовательские функции. + \en User functions. \~ + */ + virtual void GetUsedVariables( SSArray & arr, SSArray & funcs ) const = 0; + + /** \} */ + /**\ru \name Функции замены переменных по именам. + \en \name Functions for replacing variables by names. + \{ */ + + /** \brief \ru Заменить переменные. + \en Replace variables. \~ + \details \ru Заменить все переменные с указанными именем на новую переменную.\n + \en Replace all variables with the specified name by a new variable.\n \~ + \param[out] varName - \ru Имя. + \en Name. \~ + \param[out] newVar - \ru Новая переменная. + \en New variable. \~ + */ + virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ) = 0; + + /** \brief \ru Заменить узел. + \en Replace a node. \~ + \details \ru Заменить узел на копию нового, если заданная переменная использована.\n + \en Replace a node with a copy of a new one if the given variable is used.\n \~ + \param[out] var - \ru Переменная. + \en Variable. \~ + \param[out] subTree - \ru Новый узел. + \en New node. \~ + */ + virtual void ReplaceParVariable( const ItTreeVariable & var, const BTreeNode & subTree ) = 0; + + /** \brief \ru Заменить переменные. + \en Replace variables. \~ + \details \ru Заменить все переменные с указанными именем на новую переменную.\n + \en Replace all variables with the specified name by a new variable.\n \~ + */ + virtual void ReplaceIntVariable( const c3d::string_t &, ItIntervalTreeVariable & ) {} + + /** \} */ + /**\ru \name Функции доступа к данным. + \en \name Functions for access to data. + \{ */ + + /** \brief \ru Выдать значение параметра экстремума. + \en Get value of parameter of extremum. \~ + \details \ru Выдать значение параметра экстремума, если это возможно.\n + \en Get value of parameter of extremum if it is possible.\n \~ + \param[in] interval - \ru Интервал для поиска. + \en Interval to search. \~ + \param[in] var - \ru Переменная. + \en Variable. \~ + \param[out] points - \ru Точки экстремума. + \en Points of extremum. \~ + */ + bool GetExtremumPoints( const c3d::DoublePair & interval, + ItTreeVariable & var, + c3d::DoubleVector & points ); + /** \brief \ru Область определения. + \en Domain. \~ + \details \ru Область определения.\n + \en Domain.\n \~ + \param[in,out] defRange - \ru Область определения. + \en Domain. \~ + \param[out] var - \ru Переменная. + \en Variable. \~ + \param[in] stopOnBreak - \ru Не искать разрывы области определения. + \en Not to search the discontinuities in the definition domain. \~ + */ + virtual bool GetDefRange( DefRange & defRange, ItTreeVariable & var, bool stopOnBreak ) const = 0; + + /** \brief \ru Только для внутреннего использования! Порядок переменной. + \en For internal use only! Order of variable. \~ + \details \ru Порядок переменной.\n + \en Order of variable.\n \~ + \param[in] var - \ru Переменная. + \en Variable. \~ + \return \ru Порядок. + \en Order. \~ + */ + virtual size_t GetPseudoOrderByVar ( ItTreeVariable & var ) const = 0; + + /** \brief \ru Фиксированные переменные. + \en Fixed variables. \~ + \details \ru Фиксированные переменные.\n + \en Fixed variables.\n \~ + \param[in] unfixedVars - \ru Набор нефиксированных переменных. Если переменная нашлась в наборе, фиксировать копию. + \en A set of unfixed variables. If variable was found in set, then fix the copy. \~ + \param[in] newFuncs - \ru Пользовательские функции. + \en User functions. \~ + \param[out] code - \ru Коды результата разбора строки. + \en Result codes of string parsing. \~ + \return \ru Переменную для фиксирования. + \en Variable for fixation. \~ + */ + virtual std_unique_ptr FixVars( const RPArray & unfixedVars, + PArray & newFuncs, EquTreeResCode & code ) const = 0; + /// \ru Дать эквивалентный узел. \en Get equivalent node. + virtual std_unique_ptr GetCalcEquivalent() const = 0; + + /** \brief \ru Дать строку. + \en Get string. \~ + \details \ru Дать строку выражения.\n + \en Get expression string.\n \~ + \param[out] - \ru Строка. + \en String. \~ + */ + virtual void GetString( c3d::string_t & ) const = 0; + + /// \ru Вычислить размер в байтах. \en Get size in bytes. + virtual size_t SizeOf() const = 0; + + /** \} */ + /**\ru \name Функции сравнения. + \en \name Comparison function. + \{ */ + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether the node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether the node is equal to the given node.\n \~ + \param[in] other - \ru Узел для сравнения. + \en Node for comparison. \~ + \param[in] varsMap - \ru Набор пар равных переменных. + \en A set of pairs of equal variables. \~ + \return \ru true, если узлы равны. + \en true if nodes are equal. \~ + */ + virtual bool IsEqual( const BTreeNode & other, const EqualVarsMap & varsMap ) const = 0; + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether the node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether the node is equal to the given node.\n \~ + \return false. + */ + virtual bool IsEqual( const BTreeConst & , const EqualVarsMap & ) const { return false; } + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether the node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether the node is equal to the given node.\n \~ + \return false. + */ + virtual bool IsEqual( const BTreeIdent & , const EqualVarsMap & ) const { return false; } + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether the node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether the node is equal to the given node.\n \~ + \return false. + */ + virtual bool IsEqual( const BTreeFunction & , const EqualVarsMap & ) const { return false; } + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether the node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether the node is equal to the given node.\n \~ + \return false. + */ + virtual bool IsEqual( const BTreeOperation & , const EqualVarsMap & ) const { return false; } + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether the node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether the node is equal to the given node.\n \~ + \return false. + */ + virtual bool IsEqual( const BTreeOperation1Arg & , const EqualVarsMap & ) const { return false; } + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether the node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether the node is equal to the given node.\n \~ + \return false. + */ + virtual bool IsEqual( const BTreeOperation3Args & , const EqualVarsMap & ) const { return false; } + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether the node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether the node is equal to the given node.\n \~ + \return false. + */ + virtual bool IsEqual( const BTreeUserFunc & , const EqualVarsMap & ) const { return false; } + /** \} */ + +private: + // \ru не реализовано \en not implemented + BTreeNode( const BTreeNode & ); + void operator =( const BTreeNode & ); + + DECLARE_PERSISTENT_CLASS( BTreeNode ) +}; + +IMPL_PERSISTENT_OPS( BTreeNode ) + + +//----------------------------------------------------------------------------- +/** \brief \ru Типы узлов бинарного дерева. + \en Types of nodes of the binary tree. \~ + \details \ru Типы узлов бинарного дерева. \n + \en Types of nodes of the binary tree. \n \~ + \ingroup Parser +*/ +// --- +enum TeIntervalNodeType +{ + tei_Const, ///< \ru Константа. \en Constant. + tei_Ident ///< \ru Идентификатор. \en Identifier. +}; + + +//----------------------------------------------------------------------------- +/** \brief \ru Узел дерева интервального выражения. + \en Node of interval expression tree. \~ + \details \ru Узел дерева интервального выражения. \n + \en Node of interval expression tree. \n \~ + \ingroup Parser +*/ +// --- +class MATH_CLASS TreeIntervalNode : public TapeBase +{ +protected: + TreeIntervalNode() {} +public: + virtual ~TreeIntervalNode() {} + +public: + /// \ru Выдать тип узла дерева. \en Get type of a tree node. + virtual TeIntervalNodeType IsA () const = 0; + /// \ru Выдать копию объекта. \en Get a copy of the object. + virtual TreeIntervalNode * Duplicate() const = 0; + + /// \ru Дать первую переменную. \en Get the first variable. + virtual EquTreeResCode GetFirstValue ( double & ) const = 0; + /// \ru Дать вторую переменную. \en Get the second variable. + virtual EquTreeResCode GetSecondValue( double & ) const = 0; + /// \ru Дать строку. \en Get a string. + virtual void GetString( c3d::string_t & ) const = 0; + /** \brief \ru Установить значение. + \en Set value. \~ + \details \ru Попытаться установить значение [f;s] узлу дерева. + \en Try to set value [f;s] to a tree node. \~ + \param[in] f - \ru Нижняя граница интервала. + \en Lower bound of the interval. \~ + \param[in] s - \ru Верхняя граница интервала. + \en Upper bound of the interval. \~ + \param[in] unfixedIVars - \ru Множество интервальных переменных, которые можно менять. + \en Set of interval variables, which can be changed. \~ + \param[in] unfixedDVars - \ru Множество вещественных переменных, которые можно менять. + \en Set of real variables, which can be changed. \~ + \access public + \return \ru Истину, если удалось установить значение. + \en True if it was succeeded to set value. \~ + */ + virtual c3d::BoolPair SetValue( double f, double s + , const std::set & unfixedIVars + , const std::set & unfixedDVars ) = 0; + /** \brief \ru Заменить переменные. + \en Replace variables. \~ + \details \ru Заменить все переменные с указанными именем на новую переменную.\n + \en Replace all variables with the specified name by a new variable.\n \~ + \param[out] varName - \ru Имя. + \en Name. \~ + \param[out] newVar - \ru Новая переменная. + \en New variable. \~ + */ + virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ) = 0; + +DECLARE_PERSISTENT_CLASS( TreeIntervalNode ) +OBVIOUS_PRIVATE_COPY( TreeIntervalNode ) +}; + +IMPL_PERSISTENT_OPS( TreeIntervalNode ) + + +//----------------------------------------------------------------------------- +/** \brief \ru Интервал простых выражений. + \en Interval of simple expressions. \~ + \details \ru Операция [] - получение интервала из простых выражений.\n + \en operation [] - obtaining of interval of simple expressions.\n \~ + \ingroup Parser +*/ +// --- +class MATH_CLASS IntervalConstNode : public TreeIntervalNode +{ + BTreeNode * m_firstValue; ///< \ru Первое значение (всегда не NULL). \en First value (always not NULL). + BTreeNode * m_secondValue; ///< \ru Второе значение (всегда не NULL). \en Second value (always not NULL). + +public: + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор.\n + \en Constructor.\n \~ + \param[in] firstValue - \ru Первый узел. + \en The first node. \~ + \param[in] secondValue - \ru Второй узел. + \en The second node. \~ + */ + IntervalConstNode( BTreeNode & firstValue, BTreeNode & secondValue ) + : m_firstValue ( &firstValue ) + , m_secondValue( &secondValue ) + {} + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор копирования.\n + \en Copy-constructor.\n \~ + \param[in] other - \ru Копируемый объект. + \en Object to copy. \~ + */ + IntervalConstNode( const IntervalConstNode & other ) + : m_firstValue ( other.m_firstValue->Duplicate() ) // ( other.m_firstValue ) + , m_secondValue( other.m_secondValue->Duplicate() ) // ( other.m_secondValue ) + {} + /** \brief \ru Деструктор. + \en Destructor. \~ + \details \ru Деструктор.\n + \en Destructor.\n \~ + */ + virtual ~IntervalConstNode(); + +public: + /**\ru \name Функции узла дерева интервального выражения. + \en \name Functions of interval expression tree node. + \{ */ + + virtual TeIntervalNodeType IsA () const { return tei_Const; } // \ru выдать тип узла дерева \en get type of a tree node + virtual IntervalConstNode * Duplicate() const { return new IntervalConstNode( *this ); } + + // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V + virtual EquTreeResCode GetFirstValue ( double & v ) const { return m_firstValue->GetValue( v ); } + virtual EquTreeResCode GetSecondValue( double & v ) const { return m_secondValue->GetValue( v ); } + virtual void GetString( c3d::string_t & ) const {} + // \ru Установить значение. \en Set value. + virtual c3d::BoolPair SetValue( double f, double s + , const std::set & unfixedIVars + , const std::set & unfixedDVars ); + virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ); + /** \brief \ru Заменить переменные. + \en Replace variables. \~ + \details \ru Заменить все переменные с указанными именем на новую интервальную переменную.\n + \en Replace all variables with the specified names by a new interval variable.\n \~ + */ + virtual void ReplaceIntVariable( const c3d::string_t & /*varName*/, ItIntervalTreeVariable & /*newVar*/ ) {} + + /** \} */ + + /**\ru \name Функции операции []. + \en \name Functions of operation []. + \{ */ + + /// \ru Дать первый узел. \en Get the first node. + const BTreeNode & GetFirstTree () const { return *m_firstValue; } + /// \ru Дать второй узел. \en Get the second node. + const BTreeNode & GetSecondTree() const { return *m_secondValue; } + /// \ru Дать первый узел. \en Get the first node. + BTreeNode & SetFirstTree () { return *m_firstValue; } + /// \ru Дать второй узел. \en Get the second node. + BTreeNode & SetSecondTree() { return *m_secondValue; } + + + /** \} */ + //virtual size_t SizeOf() const = 0; + +private: + // \ru не реализовано \en not implemented + void operator =( const TreeIntervalNode & ); + +DECLARE_PERSISTENT_CLASS_NEW_DEL( IntervalConstNode ) +}; + +IMPL_PERSISTENT_OPS( IntervalConstNode ) + +//----------------------------------------------------------------------------- +/** \brief \ru Интервальная переменная как узел бинарного дерева. + \en Interval variable as a node of a binary tree. \~ + \details \ru Узел дерева - интервальная переменная.\n + \en Tree node is an interval variable.\n \~ + \ingroup Parser +*/ +// --- +class MATH_CLASS IntervalIdentNode : public TreeIntervalNode +{ + ItIntervalTreeVariable * m_ident; ///< \ru Всегда не NULL. \en Always not NULL. + +public: + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор по интервальной переменной.\n + \en Constructor by an interval variable.\n \~ + \param[in] ident - \ru Интервальная переменная. + \en Interval variable. \~ + */ + IntervalIdentNode( ItIntervalTreeVariable & ident ) + : m_ident( &ident ) + {} + + /// \ru Конструктор копирования. \en Copy-constructor. + IntervalIdentNode( const IntervalIdentNode & other ); + +public: + /**\ru \name Функции узла дерева интервального выражения. + \en \name Functions of the interval expression tree node. + \{ */ + virtual TeIntervalNodeType IsA () const { return tei_Ident; } // \ru выдать тип узла дерева \en get type of a tree node + virtual IntervalIdentNode * Duplicate() const { return new IntervalIdentNode( *this ); } + + // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V + virtual EquTreeResCode GetFirstValue ( double & ) const; + virtual EquTreeResCode GetSecondValue( double & ) const; + virtual void GetString ( c3d::string_t & ) const {} + // \ru Установить значение. \en Set value. + virtual std::pair SetValue( double f, double s + , const std::set & unfixedIVars + , const std::set & unfixedDVars ); + + virtual void ReplaceParVariable( const c3d::string_t & /*varName*/, ItTreeVariable & /*newVar*/ ) {} + //virtual void ReplaceIntVariable( const string & varName, ItIntervalTreeVariable & newVar ){} + + /** \} */ + +private: + void operator = ( const IntervalIdentNode & ); + +DECLARE_PERSISTENT_CLASS_NEW_DEL( IntervalIdentNode ) +}; + +IMPL_PERSISTENT_OPS( IntervalIdentNode ) + + +//------------------------------------------------------------------------------ +/** \brief \ru Константа как узел бинарного дерева. + \en Constant as a node of a binary tree. \~ + \details \ru Узел бинарного дерева, обозначающий константу. \n + \en Node of a binary tree denoting a constant. \n \~ + \ingroup Parser +*/ +// --- +class MATH_CLASS BTreeConst : public BTreeNode +{ +private: + double value; ///< \ru Значение константы. \en Value of a constant. + c3d::string_t m_name; ///< \ru Имя константы, если есть, в файл не пишется. \en A name of a constant if it exists, not written to file. + +public: + + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор по значению.\n + \en Constructor by value.\n \~ + \param[in] val - \ru Значение константы. + \en Value of a constant. \~ + \param[in] name - \ru Имя константы. + \en Name of a constant. \~ + */ + BTreeConst( double val, const c3d::string_t & name = _T("") ); + + /// \ru Конструктор копирования. \en Copy-constructor. + BTreeConst( const BTreeConst & ); + + virtual ~BTreeConst(); + +public: + // \ru выдать тип узла дерева \en get type of a tree node + virtual BteNodeType IsA() const; + virtual BTreeNode * Duplicate() const; + + // \ru Функция линейная. \en Function is linear. + virtual bool IsLine() const { return false; } + + /**\ru \name Функции для вычисления значения и производной. + \en \name Functions for calculation of the value and the derivative. + \{ */ + // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V + virtual EquTreeResCode GetValue ( double & fvalue ) const; + virtual EquTreeResCode CalculateDerives( double &, double &, double &, double &, const VarsDerives & ) const; + + virtual bool SetValue( double, const std::set & ) { return false; } + + /** \} */ + /**\ru \name Функции замены переменных по именам. + \en \name Functions for replacing variables by names. + \{ */ + + // \ru Заменить все переменные с именем varName на переменную newVar \en Replace all variables with name 'varName' by variable 'newVar' + virtual void ReplaceParVariable( const c3d::string_t & /*varName*/, ItTreeVariable & /*newVar*/ ) {} + virtual void ReplaceParVariable( const ItTreeVariable & /*var*/, const BTreeNode & /*subTree*/ ) {} + + /** \} */ + /**\ru \name Функции доступа к данным. + \en \name Functions for access to data. + \{ */ + + /** \brief \ru Получить вложенный узел. + \en Get a child node. \~ + \details \ru Получить вложенный узел по индексу.\n + \en Get a child node by an index.\n \~ + */ + virtual BTreeNode * GetSubNode( size_t /*i*/ ) { return NULL; } + + virtual bool GetDefRange(DefRange &, ItTreeVariable &, bool /*stopOnBreak*/ ) const { return true; } + + virtual void GetUsedVariables( SSArray &, SSArray & )const{} + + virtual std_unique_ptr FixVars( const RPArray & /*unfixedVars*/, + PArray & /*newFuncs*/, EquTreeResCode & ) const + { return std_unique_ptr(Duplicate()); } + + virtual std_unique_ptr GetCalcEquivalent() const + { return std_unique_ptr(Duplicate()); } + + virtual void GetString( c3d::string_t & str ) const; + virtual size_t SizeOf() const; + + // \ru доступ к данным \en access to data + double GetValue() const { return value; } ///< \ru Дать переменную. \en Get variable. + void SetValue( double val ) { value = val; } ///< \ru Установить значение переменную. \en Set value of a variable. + + /** \} */ + /**\ru \name Функции доступа к данным. + \en \name Functions for access to data. + \{ */ + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether the node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether a node is equal to the given node.\n \~ + \param[in] other - \ru Узел для сравнения. + \en Node for comparison. \~ + \param[in] equVars - \ru Набор пар равных переменных. + \en A set of pairs of equal variables. \~ + \return \ru true, если узлы равны. + \en true if nodes are equal. \~ + */ + virtual bool IsEqual ( const BTreeNode & other, const EqualVarsMap & equVars ) const { return other.IsEqual( *this, equVars ); } + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether a node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether a node is equal to the given node.\n \~ + \param[in] other - \ru Узел для сравнения. + \en Node for comparison. \~ + \return \ru true, если узлы равны. + \en true if nodes are equal. \~ + */ + virtual bool IsEqual ( const BTreeConst & other, const EqualVarsMap & ) const { return other.value == value; } + /** \} */ +private: + void operator =( const BTreeConst & ); // \ru не реализовано \en not implemented + virtual size_t GetPseudoOrderByVar( ItTreeVariable & ) const { return 0; } // \ru Только для внутреннего использования! \en For internal use only! + + DECLARE_PERSISTENT_CLASS_NEW_DEL( BTreeConst ) +}; + +IMPL_PERSISTENT_OPS( BTreeConst ) + +//------------------------------------------------------------------------------ +/** \brief \ru Переменная как узел бинарного дерева. + \en Variable as a node of a binary tree. \~ + \details \ru Узел бинарного дерева, обозначающий переменную. \n + \en Node of a binary tree denoting variable. \n \~ + \ingroup Parser +*/ +// --- +class MATH_CLASS BTreeIdent : public BTreeNode +{ +private: + ItTreeVariable * id; + +public : + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор по переменной. \n + \en Constructor by a variable \n \~ + */ + BTreeIdent( ItTreeVariable & ); + + /// \ru Копирующий конструктор. \en Copy-constructor. + BTreeIdent( const BTreeIdent & ); + + // \ru выдать тип узла дерева \en get type of a tree node + virtual BteNodeType IsA () const; + virtual BTreeNode * Duplicate() const; + + // \ru Функция линейная. \en Function is linear. + virtual bool IsLine() const; + + /**\ru \name Функции для вычисления значения и производной. + \en \name Functions for calculation of the value and the derivative. + \{ */ + + virtual EquTreeResCode GetValue ( double & fvalue ) const; + virtual EquTreeResCode CalculateDerives( double &, double &, double &, double &, const VarsDerives & ) const; + virtual void GetUsedVariables ( SSArray &, SSArray & ) const; + + virtual bool SetValue( double, const std::set & ); + + /** \} */ + /**\ru \name Функции замены переменных по именам. + \en \name Functions for replacing variables by names. + \{ */ + + // \ru Заменить все переменные с именем varName на переменную newVar \en Replace all variables with name 'varName' by variable 'newVar' + virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ); + virtual void ReplaceParVariable( const ItTreeVariable &, const BTreeNode & /*subTree*/ ) {} + + /** \} */ + /**\ru \name Функции замены переменных по именам. + \en \name Functions for replacing variables by names. + \{ */ + + /// \ru Дать вложенный узел по индексу. \en Get a child node by an index. + virtual BTreeNode * GetSubNode( size_t /*i*/ ) { return NULL; } + + virtual bool GetDefRange( DefRange &, ItTreeVariable &, bool /*stopOnBreak*/ ) const{ return true; } + + virtual std_unique_ptr FixVars( const RPArray & unfixed, + PArray & newFuncs, EquTreeResCode & ) const; + + virtual std_unique_ptr GetCalcEquivalent() const { return std_unique_ptr(Duplicate()); } + + virtual void GetString( c3d::string_t & str ) const { str += id->GetName(); } + virtual size_t SizeOf() const; + + // \ru доступ к данным \en access to data + ItTreeVariable & GetVariable() const { C3D_ASSERT( id ); return *id; } ///< \ru Дать параметрический идентификатор (переменную). \en Get parametric identifier (variable). + void SetVariable( ItTreeVariable & var ) { id = &var; } ///< \ru Установить параметрический идентификатор (переменную) \en Set a parametric identifier (variable). + + /** \} */ + /**\ru \name Функции сравнения. + \en \name Comparison function. + \{ */ + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether a node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether a node is equal to the given node.\n \~ + \param[in] other - \ru Узел для сравнения. + \en Node for comparison. \~ + \param[in] equVars - \ru Набор пар равных переменных. + \en A set of pairs of equal variables. \~ + \return \ru true, если узлы равны. + \en true if nodes are equal. \~ + */ + virtual bool IsEqual( const BTreeNode & other, const EqualVarsMap & equVars ) const { return other.IsEqual( *this, equVars ); } + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether a node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether a node is equal to the given node.\n \~ + \param[in] other - \ru Узел для сравнения. + \en Node for comparison. \~ + \param[in] equVars - \ru Набор пар равных переменных. + \en A set of pairs of equal variables. \~ + \return \ru true, если узлы равны. + \en true if nodes are equal. \~ + */ + virtual bool IsEqual( const BTreeIdent & other, const EqualVarsMap & equVars ) const; + +private: + void operator = ( const BTreeIdent & ); // \ru не реализовано \en not implemented + virtual size_t GetPseudoOrderByVar( ItTreeVariable & var ) const { return ( &var == id ) ? 1 : 0; } // \ru Только для внутреннего использования! \en For internal use only! + + DECLARE_PERSISTENT_CLASS_NEW_DEL( BTreeIdent ) +}; + +IMPL_PERSISTENT_OPS( BTreeIdent ) + +//------------------------------------------------------------------------------ +/** \brief \ru Функция как узел бинарного дерева. + \en Function as node of a binary tree. \~ + \details \ru Узел бинарного дерева, обозначающий функцию. \n + \en Node of a binary tree denoting a function. \n \~ + \ingroup Parser +*/ +// --- +class MATH_CLASS BTreeFunction : public BTreeNode +{ +public: + + /** \brief \ru Типы функций. + \en Types of functions. \~ + \details \ru Типы функций. \n + \en Types of functions. \n \~ + */ + // \ru ССА K13 не менять! Пишутся в файл! \en ССА K13 not to change! Are written to file! + enum EquFnCode + { + eFnCode_unknown = -1, ///< \ru Неизвестный тип. \en An unknown type. + eFnCode_first = 0, ///< \ru Начало диапазона известных функций. \en Start of the range of known functions. + eFnCode_sin = eFnCode_first, ///< \ru Синус. \en Sine. + eFnCode_cos, ///< \ru Косинус. \en Cosine. + eFnCode_tan, ///< \ru Тангенс. \en Tangent. + eFnCode_sqrt, ///< \ru Квадратный корень. \en Square root. + eFnCode_atan, ///< \ru Арктангенс. \en Arctangent. + eFnCode_exp, ///< \ru Экспонента. \en Exponent. + eFnCode_ln, ///< \ru Натуральный логарифм. \en Natural logarithm. + eFnCode_abs, ///< \ru Модуль. \en Absolute value. + eFnCode_DegBegin, ///< \ru Начала диапазона функций с аргументом в градусах. \en Start of the range of functions with argument in degrees. + eFnCode_sind = eFnCode_DegBegin, ///< \ru Синус угла в градусах. \en Sine of the angle in degrees. + eFnCode_cosd, ///< \ru Косинус угла в градусах. \en Cosine of the angle in degrees. + eFnCode_tand, ///< \ru Тангенс угла в градусах. \en Tangent of the angle in degrees. + eFnCode_DegEnd = eFnCode_tand, ///< \ru Конец диапазона функций с аргументом в градусах. \en End of the range of functions with argument in degrees. + eFnCode_atand, ///< \ru Арктангенс с результатом в градусах. \en Arctangent with the result in degrees. + eFnCode_lg, ///< \ru Десятичный логарифм. \en Decimal logarithm. + eFnCode_ceil, ///< \ru Ближайшее большее целое число. \en Nearest largest integer number. + eFnCode_floor, ///< \ru Ближайшее меньшее целое число. \en Nearest smallest integer number. + eFnCode_round, ///< \ru Ближайшее целое число. \en Nearest integer number. + eFnCode_acos, ///< \ru Арккосинус. \en Arccosine. + eFnCode_acosd, ///< \ru Арккосинус с результатом в градусах. \en Arccosine with the result in degrees. + eFnCode_asin, ///< \ru Арксинус. \en Arcsine. + eFnCode_asind, ///< \ru Арксинус с результатом в градусах. \en Arcsine with the result in degrees. + eFnCode_rad, ///< \ru Перевод из градусов в радианы. \en Conversion from degrees to radians. + eFnCode_deg, ///< \ru Перевод из радиан в градусы. \en Conversion from radians to degrees. + eFnCode_last = eFnCode_deg ///< \ru Конец диапазона известных функций. \en End of the range of known functions. + }; + +private : + EquFnCode fnCode; // \ru код функции \en code of function + BTreeNode * par; // \ru параметр функции \en parameter of the function + +public : + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор. \n + \en Constructor. \n \~ + \param[in] code - \ru Тип функции. + \en Type of a function. \~ + \param[in] p - \ru Параметр функции. + \en Parameter of a function. \~ + */ + BTreeFunction( EquFnCode code, BTreeNode & p ); + + /// \ru Копирующий конструктор. \en Copy-constructor. + BTreeFunction( const BTreeFunction & ); + + /// \ru Деструктор. \en Destructor. + virtual ~BTreeFunction(); + +public : + // \ru выдать тип узла дерева \en get type of a tree node + virtual BteNodeType IsA () const; + virtual BTreeNode * Duplicate() const; + + // \ru Функция линейная. \en Function is linear. + virtual bool IsLine() const; + + /**\ru \name Функции для вычисления значения и производной. + \en \name Functions for calculation of the value and the derivative. + \{ */ + + virtual EquTreeResCode GetValue ( double & value ) const; + // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V + virtual EquTreeResCode CalculateDerives ( double &, double &, double &, double &, const VarsDerives & ) const; + virtual void GetUsedVariables ( SSArray &, SSArray & ) const; + + virtual bool SetValue ( double, const std::set & ) { return false; } // \ru не реализовано \en not implemented + /** \} */ + /**\ru \name Функции замены переменных по именам. + \en \name Functions for replacing variables by names. + \{ */ + + virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ); + virtual void ReplaceParVariable( const ItTreeVariable & var, const BTreeNode & subTree ); + + /** \} */ + /**\ru \name Функции доступа к данным. + \en \name Functions for access to data. + \{ */ + + /// \ru Дать вложенный узел по индексу. \en Get a child node by an index. + virtual BTreeNode * GetSubNode( size_t i ); + virtual bool GetDefRange( DefRange &, ItTreeVariable &, bool stopOnBreak ) const; + virtual std_unique_ptr FixVars( const RPArray & unfixed, + PArray & newFuncs, EquTreeResCode & ) const; + + virtual std_unique_ptr GetCalcEquivalent() const; + + virtual void GetString( c3d::string_t & str ) const; + virtual size_t SizeOf() const; + + /** \} */ + /**\ru \name Функции сравнения. + \en \name Comparison function. + \{ */ + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether a node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether a node is equal to the given node.\n \~ + \param[in] other - \ru Узел для сравнения. + \en Node for comparison. \~ + \param[in] equVars - \ru Набор пар равных переменных. + \en A set of pairs of equal variables. \~ + \return \ru true, если узлы равны. + \en true if nodes are equal. \~ + */ + virtual bool IsEqual ( const BTreeNode & other, const EqualVarsMap & equVars ) const { return other.IsEqual( *this, equVars ); } + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether a node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether a node is equal to the given node.\n \~ + \param[in] other - \ru Узел для сравнения. + \en Node for comparison. \~ + \param[in] equVars - \ru Набор пар равных переменных. + \en A set of pairs of equal variables. \~ + \return \ru true, если узлы равны. + \en true if nodes are equal. \~ + */ + virtual bool IsEqual ( const BTreeFunction & other, const EqualVarsMap & equVars )const; + /** \} */ + + bool IsCos() const; ///< \ru имеет ли вид a * cos() + b \en looks like a * cos() + b + + + +private: + // \ru выдать значение параметра для экстремума( если это возможно ). \en get value of parameter of extremum( if it is possible ). + virtual bool GetCharacterPoints( std::vector & ) const; + EquTreeResCode GetValue( double arg, double & value ) const; + // \ru Только для внутреннего использования! \en For internal use only! + virtual size_t GetPseudoOrderByVar( ItTreeVariable & var ) const; + void operator = ( const BTreeFunction & ); // \ru не реализовано \en not implemented + + DECLARE_PERSISTENT_CLASS_NEW_DEL( BTreeFunction ) +}; + +IMPL_PERSISTENT_OPS( BTreeFunction ) + +//------------------------------------------------------------------------------ +/** \brief \ru Дать тип функции. + \en Get type of function. \~ + \details \ru Дать тип функции по имени.\n + \en Get type of function by name.\n \~ + \param[in] name - \ru Имя функции. + \en Name of function. \~ + \return \ru Тип функции. + \en Type of a function. \~ + \ingroup Parser +*/ +// --- +BTreeFunction::EquFnCode GetFunCodeByName( const c3d::string_t & name ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Дать имя функции. + \en Get name of function. \~ + \details \ru Дать имя функции по типу.\n + \en Get name of function by type.\n \~ + \param[in] code - \ru Тип функции. + \en Type of a function. \~ + \param[out] name - \ru Имя функции. + \en Name of function. \~ + \ingroup Parser +*/ +// --- +void GetFunNameByCode( BTreeFunction::EquFnCode code, c3d::string_t & name ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Операция с двумя аргументами как узел бинарного дерева. + \en Operation with two arguments as node of binary tree. \~ + \details \ru Узел бинарного дерева, обозначающий операцию с двумя аргументами. \n + \en Node of binary tree denoting operation with two arguments. \n \~ + \ingroup Parser +*/ +// --- +class MATH_CLASS BTreeOperation : public BTreeNode +{ +private: + PceOperationType opCode; ///< \ru Код операции. \en Code of operation. + BTreeNode * op1; ///< \ru Первый операнд. \en The first operand. + BTreeNode * op2; ///< \ru Второй операнд. \en The second operand. + + static SArray varsDerives; ///< \ru Производные аргументов для вычисления производных показательно-степенной функции \en Derivatives of arguments for calculation of derivatives of exponential-power function + static std_unique_ptr deriveFunc; ///< \ru Функция для вычисления производной показательно-степенной функции \en Function for calculation of derivative of the exponential-power function + +public: + /** \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор.\n + \en Constructor.\n \~ + \param[in] code - \ru Код операции. + \en Code of operation. \~ + \param[in] op1 - \ru Первый операнд. + \en The first operand. \~ + \param[in] op2 - \ru Второй операнд. + \en The second operand. \~ + \ingroup Parser + */ + BTreeOperation( PceOperationType code, BTreeNode & op1, BTreeNode & op2 ); + + /// \ru Конструктор копирования. \en Copy-constructor. + BTreeOperation( const BTreeOperation & ); + + /// \ru Деструктор. \en Destructor. + virtual ~BTreeOperation(); + +public: + // \ru выдать тип узла дерева \en get type of a tree node + virtual BteNodeType IsA () const; + virtual BTreeNode * Duplicate() const; + + // \ru Функция линейная. \en Function is linear. + virtual bool IsLine() const; + + /**\ru \name Функции для вычисления значения и производной. + \en \name Functions for calculation of the value and the derivative. + \{ */ + + // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V + virtual EquTreeResCode GetValue ( double & fvalue ) const; + virtual EquTreeResCode CalculateDerives( double &, double &, double &, double &, const VarsDerives & ) const; + virtual void GetUsedVariables( SSArray &, SSArray & ) const; + virtual bool SetValue ( double, const std::set & ) { return false; }// \ru не реализовано \en not implemented + + /** \} */ + /**\ru \name Функции замены переменных по именам. + \en \name Functions for replacing variables by names. + \{ */ + + // \ru Заменить все переменные с именем varName на переменную newVar \en Replace all variables with name 'varName' by variable 'newVar' + virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ); + virtual void ReplaceParVariable( const ItTreeVariable & var, const BTreeNode & subTree ); + + /** \} */ + /**\ru \name Функции доступа к данным. + \en \name Functions for access to data. + \{ */ + + /// \ru Дать вложенный узел по индексу. \en Get a child node by an index. + virtual BTreeNode * GetSubNode( size_t i ); + virtual bool GetDefRange( DefRange &, ItTreeVariable &, bool stopOnBreak ) const; + + virtual std_unique_ptr FixVars( const RPArray & unfixed, + PArray & newFuncs, EquTreeResCode & ) const; + virtual std_unique_ptr GetCalcEquivalent() const; + + virtual void GetString( c3d::string_t & str ) const; + virtual size_t SizeOf() const; + + /** \} */ + /**\ru \name Функции сравнения. + \en \name Comparison function. + \{ */ + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether a node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether a node is equal to the given node.\n \~ + \param[in] other - \ru Узел для сравнения. + \en Node for comparison. \~ + \param[in] equVars - \ru Набор пар равных переменных. + \en A set of pairs of equal variables. \~ + \return \ru true, если узлы равны. + \en true if nodes are equal. \~ + */ + virtual bool IsEqual( const BTreeNode & other, const EqualVarsMap & equVars ) const { return other.IsEqual( *this, equVars ); } + + /** \brief \ru Равен ли узел заданному узлу. + \en Whether a node is equal to the given node. \~ + \details \ru Равен ли узел заданному узлу.\n + \en Whether a node is equal to the given node.\n \~ + \param[in] other - \ru Узел для сравнения. + \en Node for comparison. \~ + \param[in] equVars - \ru Набор пар равных переменных. + \en A set of pairs of equal variables. \~ + \return \ru true, если узлы равны. + \en true if nodes are equal. \~ + */ + virtual bool IsEqual( const BTreeOperation & other, const EqualVarsMap & equVars ) const; + + /** \} */ + /**\ru \name Функции доступа к данным. + \en \name Functions for access to data. + \{ */ + + /// \ru Код операции. \en Code of operation. + int16 GetOperationCode() const { return (int16)opCode; } + /// \ru Первый операнд. \en The first operand. + BTreeNode & GetFirstOperand () const { C3D_ASSERT( op1 ); return *op1; } + /// \ru Второй операнд. \en The second operand. + BTreeNode & GetSecondOperand() const { C3D_ASSERT( op2 ); return *op2; } + /** \} */ + + ///< \ru имеет ли вид a * cos() + b. \en look like a * cos() + b. + virtual bool IsCos ( double &a, double& b ) const; + +private: + void operator = ( const BTreeOperation & ); // \ru не реализовано \en not implemented + EquTreeResCode GetValue( double par1, double par2, double & value ) const; + // \ru выдать значение параметра для экстремума( если это возможно ). \en get value of parameter of extremum( if it is possible ). + virtual bool GetCharacterPoints( std::vector &, const ItTreeVariable & ) const; + // \ru Только для внутреннего использования! \en For internal use only! + virtual size_t GetPseudoOrderByVar( ItTreeVariable & var ) const; + + static MbUserFunc & GetFuncForDerInvolving(); + + DECLARE_PERSISTENT_CLASS_NEW_DEL( BTreeOperation ) +}; + +IMPL_PERSISTENT_OPS( BTreeOperation ) + +//------------------------------------------------------------------------------ +/** \brief \ru Операция с одним аргументом как узел бинарного дерева. + \en Operation with one argument as a node of a binary tree. \~ + \details \ru Узел бинарного дерева, обозначающий операцию с одним аргументом. \n + \en Node of a binary tree denoting an operation with one argument. \n \~ + \ingroup Parser +*/ +// --- +class MATH_CLASS BTreeOperation1Arg : public BTreeNode +{ +private : + PceOperationType opCode; ///< \ru Код операции. \en Code of operation. + BTreeNode * op; ///< \ru Операнд. \en Operand. + +public : + BTreeOperation1Arg( PceOperationType code, BTreeNode & ); + BTreeOperation1Arg( const BTreeOperation1Arg & ); + virtual ~BTreeOperation1Arg(); + + // \ru выдать тип узла дерева \en get type of a tree node + virtual BteNodeType IsA () const; + PceOperationType GetOperationType() const { return opCode; } + virtual BTreeNode * Duplicate() const; + + // \ru Функция линейная. \en Function is linear. + virtual bool IsLine() const; + + // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V + virtual EquTreeResCode GetValue ( double &fvalue ) const; + virtual EquTreeResCode CalculateDerives( double &, double &, double &, double &, const VarsDerives & ) const; + virtual void GetUsedVariables( SSArray &, SSArray & ) const; + + virtual bool SetValue ( double, const std::set & ); + + /// \ru Заменить все переменные с именем varName на переменную newVar \en Replace all variables with name 'varName' by variable 'newVar' + virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ); + virtual void ReplaceParVariable( const ItTreeVariable &, const BTreeNode & ); + virtual BTreeNode * GetSubNode( size_t i ); + // \ru выдать значение параметра для экстремума( если это возможно ). \en get value of parameter of extremum( if it is possible ). + virtual bool GetCharacterPoints( std::vector & ) const; + virtual bool GetDefRange( DefRange & range, ItTreeVariable & var, bool stopOnBreak ) const { C3D_ASSERT( op ); + return op->GetDefRange( range, var, stopOnBreak ); } + virtual std_unique_ptr FixVars( const RPArray & unfixed, + PArray & newFuncs, EquTreeResCode & ) const; + virtual std_unique_ptr GetCalcEquivalent() const; + + virtual void GetString( c3d::string_t & str ) const; + virtual bool IsEqual ( const BTreeNode & other, const EqualVarsMap & equVars ) const { return other.IsEqual( *this, equVars ); } + virtual bool IsEqual ( const BTreeOperation1Arg & other, const EqualVarsMap & equVars ) const; + virtual size_t SizeOf() const; + +private: + void operator =( const BTreeOperation1Arg & ); // \ru не реализовано \en not implemented + EquTreeResCode GetValue( double arg, double & value ) const; + // \ru Только для внутреннего использования! \en For internal use only! + virtual size_t GetPseudoOrderByVar( ItTreeVariable & var ) const { return opCode == oprt_NOT ? SYS_MAX_T : op->GetPseudoOrderByVar(var); } + + DECLARE_PERSISTENT_CLASS_NEW_DEL( BTreeOperation1Arg ) +}; + +IMPL_PERSISTENT_OPS( BTreeOperation1Arg ) + +//------------------------------------------------------------------------------ +/** \brief \ru Операция с тремя аргументами как узел синтаксического дерева. + \en Operation with three arguments as node of syntax tree. \~ + \details \ru Узел бинарного дерева, обозначающий операцию с тремя аргументами. \n + \en Node of binary tree denoting operation with three arguments. \n \~ + \ingroup Parser +*/ +// --- +class MATH_CLASS BTreeOperation3Args : public BTreeNode +{ +private : + BTreeNode * op1; ///< \ru Первый операнд. \en The first operand. + BTreeNode * op2; ///< \ru Второй операнд. \en The second operand. + BTreeNode * op3; ///< \ru Третий операнд. \en The third operand. + +public : + BTreeOperation3Args( BTreeNode &, BTreeNode &, BTreeNode & ); + BTreeOperation3Args( const BTreeOperation3Args & ); + virtual ~BTreeOperation3Args(); + + // \ru выдать тип узла дерева \en get type of a tree node + virtual BteNodeType IsA() const; + virtual BTreeNode * Duplicate() const; + + // \ru Функция линейная. \en Function is linear. + virtual bool IsLine() const; + + // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V + virtual EquTreeResCode GetValue ( double & fVal ) const; + virtual EquTreeResCode CalculateDerives ( double &, double &, double &, double &, const VarsDerives & ) const; + virtual void GetUsedVariables ( SSArray &, SSArray & ) const; + + virtual bool SetValue ( double, const std::set & ){ return false;} // \ru не реализовано \en not implemented + + /// \ru Заменить все переменные с именем varName на переменную newVar \en Replace all variables with name 'varName' by variable 'newVar' + virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ); + virtual void ReplaceParVariable( const ItTreeVariable &, const BTreeNode & ); + virtual BTreeNode * GetSubNode( size_t i ); + virtual bool GetDefRange( DefRange &, ItTreeVariable &, bool stopOnBreak ) const; + + virtual std_unique_ptr FixVars( const RPArray & unfixed, + PArray & newFuncs, EquTreeResCode & ) const; + virtual std_unique_ptr GetCalcEquivalent() const; + + virtual void GetString( c3d::string_t & str ) const; + virtual bool IsEqual ( const BTreeNode & other, const EqualVarsMap & equVars ) const { return other.IsEqual( *this, equVars ); } + virtual bool IsEqual ( const BTreeOperation3Args & other, const EqualVarsMap & equVars ) const; + virtual size_t SizeOf() const; + +private: + // \ru выдать значение параметра для экстремума( если это возможно ). \en get value of parameter of extremum( if it is possible ). + virtual bool GetCharacterPoints( std::vector &, ItTreeVariable & ) const; + // \ru Только для внутреннего использования! \en For internal use only! + virtual size_t GetPseudoOrderByVar( ItTreeVariable & ) const { return SYS_MAX_T; } + void operator =( const BTreeOperation3Args & ); // \ru не реализовано \en not implemented + + DECLARE_PERSISTENT_CLASS_NEW_DEL( BTreeOperation3Args ) +}; + +IMPL_PERSISTENT_OPS( BTreeOperation3Args ) + +//------------------------------------------------------------------------------ +/** \brief \ru Пользовательская функция как узел бинарного дерева. + \en User-defined function as a node of a binary tree. \~ + \details \ru Узел бинарного дерева, обозначающий пользовательскую функцию. \n + \en Node of a binary tree denoting a user-defined function. \n \~ + \ingroup Parser +*/ +// --- +class MATH_CLASS BTreeUserFunc : public BTreeNode +{ + ItUserFunc * func; ///< \ru Пользовательская функция. \en A user-defined function. + PArray pars; + +public: + // Конструктор копирования + BTreeUserFunc( const BTreeUserFunc & other ) + : func( other.func ) + { + for ( size_t i = 0, c = other.pars.size(); i < c; ++i ) + pars.Add( other.pars[i]->Duplicate() ); + } + // Конструктор + template + BTreeUserFunc( ItUserFunc & _func, const BTreeNodesVector & _pars ) + : func( &_func ) + { + C3D_ASSERT( _pars.size() == func->GetParsCount() ); + pars.reserve( _pars.size() ); + for ( size_t i = 0, c = _pars.size(); i < c; ++i ) { + pars.push_back( _pars[i] ); + } + } + + // \ru выдать тип узла дерева \en get type of a tree node + virtual BteNodeType IsA () const { return bt_Function; } + virtual BTreeNode * Duplicate() const { return new BTreeUserFunc( *this ); } + + // \ru Функция линейная. \en Function is linear. + virtual bool IsLine() const { return false; } + + virtual bool SetValue( double, const std::set & ) { return false; } // \ru не реализовано \en not implemented + + // \ru вычисление значения функции и производной по переменной V \en calculate the value of a function and the derivative with respect to V + virtual EquTreeResCode GetValue ( double & ) const; + virtual EquTreeResCode CalculateDerives( double &, double &, double &, double &, const VarsDerives & ) const; + virtual void GetUsedVariables( SSArray &, SSArray & ) const; + /// \ru Заменить все переменные с именем varName на переменную newVar \en Replace all variables with name 'varName' by variable 'newVar' + virtual void ReplaceParVariable( const c3d::string_t & varName, ItTreeVariable & newVar ); + virtual void ReplaceParVariable( const ItTreeVariable &, const BTreeNode & ); + virtual BTreeNode * GetSubNode ( size_t i ); + // \ru выдать значение параметра для экстремума( если это возможно ). \en get value of parameter of extremum( if it is possible ). +// ССА K13 virtual bool GetCharacterPoints( std::vector & ) const { return false; } + virtual bool GetDefRange( DefRange &, ItTreeVariable &, bool stopOnBreak ) const; + + virtual std_unique_ptr FixVars( const RPArray & unfixed, + PArray & newFuncs, EquTreeResCode & ) const; + virtual std_unique_ptr GetCalcEquivalent() const; + + virtual void GetString( c3d::string_t & str ) const; + virtual bool IsEqual ( const BTreeNode & other, const EqualVarsMap & equVars ) const { return other.IsEqual( *this, equVars ); } + virtual bool IsEqual ( const BTreeUserFunc & other, const EqualVarsMap & equVars ) const; + virtual size_t SizeOf() const { return 0; }; + +private: + void operator =( const BTreeOperation3Args & ); // \ru не реализовано \en not implemented + EquTreeResCode GetValue( SArray &, double & value ) const; + // \ru Только для внутреннего использования! \en For internal use only! + virtual size_t GetPseudoOrderByVar( ItTreeVariable & var ) const; + +DECLARE_PERSISTENT_CLASS_NEW_DEL( BTreeUserFunc ) +}; + +IMPL_PERSISTENT_OPS( BTreeUserFunc ) + +#endif // __PARS_EQUATION_TREE_H diff --git a/C3d/Include/pars_list.h b/C3d/Include/pars_list.h index a108591..525deb7 100644 --- a/C3d/Include/pars_list.h +++ b/C3d/Include/pars_list.h @@ -20,7 +20,7 @@ //------------------------------------------------------------------------------ /** \brief \ru Список переменных. \en List of variables. \~ - \details \ru Список переменных симфольной записи выражения. \n + \details \ru Список переменных символьной записи выражения. \n \en A list of variables with symbol writing of expression. \n \~ \ingroup Parser */ @@ -41,12 +41,11 @@ public: /// \ru Количество переменных. \en The number of variables. size_t CountVariables() const; /// \ru Добавить переменную. \en Add a variable. - void AddVariable ( MbVar * var ); + void AddVariable ( MbVar * ); /// \ru Убрать переменную. \en Remove a variable. - void RemoveVariable( MbVar * var ); -private: - MbListVars( const MbListVars & other ); - MbListVars & operator = ( const MbListVars & other ); + void RemoveVariable( MbVar * ); + +OBVIOUS_PRIVATE_COPY( MbListVars ) }; diff --git a/C3d/Include/pars_tree_variable.h b/C3d/Include/pars_tree_variable.h index 1a9edd4..c6fd543 100644 --- a/C3d/Include/pars_tree_variable.h +++ b/C3d/Include/pars_tree_variable.h @@ -1,233 +1,233 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Интерфейс переменной. - \en Interface of variable. -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __ITTREEVARS_H -#define __ITTREEVARS_H - -#include - -class DefRange; -class BTreeNode; -struct DerivesValues; - - -//------------------------------------------------------------------------------ -/** \brief \ru Типы узлов бинарного дерева. - \en Types nodes of binary tree. \~ - \details \ru Типы узлов бинарного дерева.\n - \en Types nodes of binary tree.\n \~ - \ingroup Parser -*/ -// --- -enum BteNodeType -{ - bt_Const, ///< \ru Константа. \en Constant. - bt_Ident, ///< \ru Идентификатор. \en Identifier. - bt_Function, ///< \ru Функция. \en A function. - bt_Operation2Args, ///< \ru Операция c двумя аргументами. \en Operation with two arguments. - bt_Operation1Arg, ///< \ru Операция c одним аргументом. \en Operation with one argument. - bt_Operation3Args, ///< \ru Операция c тремя аргументами. \en Operation with three arguments. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Типы операций. - \en Operations types. \~ - \details \ru Типы операций.\n - \en Operations types.\n \~ - \attention \ru Значения пишутся в файл. \en Values are written in file. \~ - \ingroup Parser -*/ -// --- -enum PceOperationType -{ - oprt_TernaryOperation = 0, ///< \ru Тернарная операция. \en Ternary operation. - oprt_BinaryOperation = 8, ///< \ru Бинарная операция. \en Binary operation. - oprt_Addition = oprt_BinaryOperation, ///< \ru Сложение. \en Addition. - oprt_Subtraction = 9, ///< \ru Вычитание. \en Subtraction. - oprt_Division = 10, ///< \ru Деление. \en Division. - oprt_Multiplication = 11, ///< \ru Умножение. \en Multiplication - oprt_IntDivision = 12, ///< \ru Целочисленное деление. \en Integer division. - oprt_OR = 13, ///< \ru Или. \en Or. - oprt_AND = 14, ///< \ru И. \en And. - oprt_NEQU = 15, ///< \ru Не равно. \en Not equal. - oprt_EQU = 16, ///< \ru Равно. \en Equal. - oprt_GT = 17, ///< \ru Больше. \en More. - oprt_GE = 18, ///< \ru Больше или равно. \en More or equal. - oprt_LT = 19, ///< \ru Меньше. \en Less. - oprt_LE = 20, ///< \ru Меньше или равно. \en Less or equal. - oprt_Involution = 21, ///< \ru Возведение в степень. \en Involution. - oprt_UnaryOperation = 22, ///< \ru Унарная операция. \en Unary operation. - oprt_NOT = oprt_UnaryOperation, ///< \ru Не. \en Not. - oprt_UnaryMinus = 23, ///< \ru Унарный минус. \en Unary minus. - oprt_UnaryPlus = 24, ///< \ru Унарный плюс. \en Unary plus. - oprt_Parentheses = 25 ///< \ru Скобки. \en Brackets. -}; - - -//----------------------------------------------------------------------------- -/** \brief \ru Коды результата разбора строки. - \en Result codes of string parsing. \~ - \details \ru Коды результата разбора строки.\n - \en Result codes of string parsing.\n \~ - \ingroup Parser -*/ -// --- -enum EquTreeResCode { - // \ru Эта группа кодов ошибок полностью повторяет то, что было до V9. \en This group of result codes fully repeats all that was before V9. - equTreeResCode_Ok = 0, ///< \ru Все хорошо. \en Everything is OK. - equTreeResCode_First = 1, ///< \ru Начало диапазона ошибок. \en Start of errors range. - equTreeResCode_SyntaxError = equTreeResCode_First, ///< \ru Ошибка: Синтаксическая ошибка в выражении. \en Error: Syntax error in expression. - equTreeResCode_TooComplex, ///< \ru Ошибка: Слишком сложное выражение. \en Error: Expression is too complex. - equTreeResCode_InvalidAssignment, ///< \ru Ошибка: Переменная присваивается самой себе. \en Error: Variable is assigned by itself. - equTreeResCode_NoVariables, ///< \ru Ошибка: В выражении должна быть хотя бы одна переменная. \en Error: There should be at least one variable in expression. - equTreeResCode_TooLargeIdent, ///< \ru Ошибка: Превышено количество символов в имени переменной. \en Error: The number of symbols in a name of variable is exceeded. - equTreeResCode_TangentsDomain , ///< \ru Ошибка: Аргумент тангенса не в области определения. \en Error: An argument of tangent is out of domain. - equTreeResCode_SqrtDomain, ///< \ru Ошибка: Недопустимое значение аргумента для sqrt. \en Error: Invalid argument value for sqrt. - equTreeResCode_LogarithmDomain, ///< \ru Ошибка: Недопустимое значение аргумента для логарифмической функции. \en Error: Invalid argument value for logarithmic function. - equTreeResCode_ZeroDivide, ///< \ru Ошибка: Деление на ноль. \en Error: Division by zero. - equTreeResCode_TrigonometricDomain, ///< \ru Ошибка: Аргумент тригонометрической функции не в области определения. \en Error: An argument of trigonometric function is out of domain. - equTreeResCode_CyclicRelation, ///< \ru Ошибка: Найдена замкнутая зависимость. \en Error: There is found a closed dependence. - equTreeResCode_PowDomain, ///< \ru Ошибка: недопустимое значение аргумента для степенной функции. \en Error: Invalid argument value for power function. - equTreeResCode_WrongFuncFormat, ///< \ru Ошибка: Выражение содержит функцию, не соответствующую своему формату. \en Error: Expression contains a function which does not correspond to its format. - - // \ru ДОБАВЛЯТЬ НОВЫЕ СООБЩЕНИЯ ТОЛЬКО ПЕРЕД ЭТОЙ СТРОКОЙ; \en ADD NEW MESSAGES ONLY BEFORE THIS STRING; - equTreeResCode_Last ///< \ru Конец диапазона ошибок. \en End of errors range. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Интерфейс координаты. - \en Interface of coordinate. \~ - \details \ru Интерфейс координаты. \n - \en Interface of coordinate. \n \~ - \ingroup Parser -*/ -// --- -struct ItCoord -{ - virtual void SetValue( double v ) = 0; ///< \ru Установить переменную. \en Set variable. - virtual double GetValue() const = 0; ///< \ru Дать переменную. \en Get variable. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Интерфейс переменной. - \en Interface of variable. \~ - \details \ru Интерфейс переменной. \n - \en Interface of variable. \n \~ - \ingroup Parser -*/ -// --- -struct ItTreeVariable -{ - ItTreeVariable() {} - - /// \ru Дать имя. \en Get name. - virtual const c3d::string_t & GetName() const = 0; - /// \ru Установить имя. \en Set name. - virtual void SetName( const c3d::string_t & ) = 0; - /// \ru Дать переменную. \en Get variable. - virtual double GetValue() const = 0; - /// \ru Установить переменную. \en Set variable. - virtual void SetValue( double ) = 0; - /// \ru Дать координату. \en Get coordinate. - virtual const ItCoord & GetCoord() const = 0; - /// \ru Вычислить размер в байтах. \en Get size in bytes. - virtual size_t SizeOf() const = 0; - - /// \ru Захватить \en Catch. - virtual refcount_t AddRef () const = 0; - /// \ru Отпустить. \en Free. - virtual refcount_t Release() const = 0; - - /// \ru Установить имя. Обработка нулевого указателя. \en Set name. Null pointer processing. - virtual void SetName( const TCHAR* s ) { SetName( s ? s : _T("") ); }; - - /// \ru Операторы чтения, записи. \en Reading and writing operators. - DECLARE_PERSISTENT_OPS( ItTreeVariable ); -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Интерфейс интервальной переменной. - \en Interface of interval variable. \~ - \details \ru Интерфейс интервальной переменной. \n - \en Interface of interval variable. \n \~ - \ingroup Parser -*/ -// --- -struct ItIntervalTreeVariable -{ - ItIntervalTreeVariable() {} - - /// \ru Дать имя. \en Get name. - virtual const c3d::string_t & GetName() const = 0; - /// \ru Установить имя. \en Set name. - virtual void SetValue( double f, double s ) = 0; - /// \ru Первая граница. \en The first boundary. - virtual double First() const = 0; - /// \ru Вторая граница. \en The second boundary. - virtual double Second() const = 0; - - /// \ru Захватить. \en Catch. - virtual refcount_t AddRef() const = 0; - /// \ru Отпустить. \en Free. - virtual refcount_t Release() const = 0; - - /// \ru Операторы чтения, записи. \en Reading and writing operators. - DECLARE_PERSISTENT_OPS( ItIntervalTreeVariable ); -}; - - -class MbUserFunc; - -//------------------------------------------------------------------------------ -/** \brief \ru Интерфейс функции. - \en Interface of function. \~ - \details \ru Интерфейс функции. \n - \en Interface of function. \n \~ - \ingroup Parser -*/ -// --- -struct ItUserFunc -{ - ItUserFunc(){}; - virtual ~ItUserFunc(){}; - - /// \ru Дать копию объекта. \en Get a copy of the object. - virtual ItUserFunc & Duplicate() const = 0; - /// \ru Дать имя. \en Get name. - virtual const c3d::string_t & GetName() const = 0; - /// \ru Дать количество параметров. \en Get count of parameters.. - virtual size_t GetParsCount() const = 0; - /// \ru Значение функции. \en Value of function. - virtual EquTreeResCode GetValue ( const SArray &, double & ) const = 0; - /// \ru Значение функции и производных. \en Value of function and derivatives. - virtual EquTreeResCode GetDerivates( const SArray &, - double & v, double & f, - double & s, double & t, - size_t dIndex = 0 ) const = 0; - /// \ru Внешние переменные. \en External variables. - virtual void GetExternalVars( SSArray & vars, - SSArray & funcs ) const = 0; - /// \ru Только для внутреннего использования! Степень функции по индексу переменной. \en For internal use only! Function degree by the index of variable. - virtual size_t GetPseudoOrderByPar ( size_t index ) const = 0; - /// \ru Область определения. \en Domain. - virtual bool GetDefRange( DefRange &, ItTreeVariable & var, - const std::vector & ) const = 0; - /// \ru Равны ли функции. \en Whether functions are equal. - virtual bool IsEqual ( const ItUserFunc & other ) const = 0; - /// \ru Равны ли функции. \en Whether functions are equal. - virtual bool IsEqual ( const MbUserFunc & other ) const = 0; - - /// \ru Операторы чтения, записи. \en Reading and writing operators. - DECLARE_PERSISTENT_OPS( ItUserFunc ); -}; - +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Интерфейс переменной. + \en Interface of variable. +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ITTREEVARS_H +#define __ITTREEVARS_H + +#include + +class DefRange; +class BTreeNode; +struct DerivesValues; + + +//------------------------------------------------------------------------------ +/** \brief \ru Типы узлов бинарного дерева. + \en Types nodes of binary tree. \~ + \details \ru Типы узлов бинарного дерева.\n + \en Types nodes of binary tree.\n \~ + \ingroup Parser +*/ +// --- +enum BteNodeType +{ + bt_Const, ///< \ru Константа. \en Constant. + bt_Ident, ///< \ru Идентификатор. \en Identifier. + bt_Function, ///< \ru Функция. \en A function. + bt_Operation2Args, ///< \ru Операция c двумя аргументами. \en Operation with two arguments. + bt_Operation1Arg, ///< \ru Операция c одним аргументом. \en Operation with one argument. + bt_Operation3Args, ///< \ru Операция c тремя аргументами. \en Operation with three arguments. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Типы операций. + \en Operations types. \~ + \details \ru Типы операций.\n + \en Operations types.\n \~ + \attention \ru Значения пишутся в файл. \en Values are written in file. \~ + \ingroup Parser +*/ +// --- +enum PceOperationType +{ + oprt_TernaryOperation = 0, ///< \ru Тернарная операция. \en Ternary operation. + oprt_BinaryOperation = 8, ///< \ru Бинарная операция. \en Binary operation. + oprt_Addition = oprt_BinaryOperation, ///< \ru Сложение. \en Addition. + oprt_Subtraction = 9, ///< \ru Вычитание. \en Subtraction. + oprt_Division = 10, ///< \ru Деление. \en Division. + oprt_Multiplication = 11, ///< \ru Умножение. \en Multiplication + oprt_IntDivision = 12, ///< \ru Целочисленное деление. \en Integer division. + oprt_OR = 13, ///< \ru Или. \en Or. + oprt_AND = 14, ///< \ru И. \en And. + oprt_NEQU = 15, ///< \ru Не равно. \en Not equal. + oprt_EQU = 16, ///< \ru Равно. \en Equal. + oprt_GT = 17, ///< \ru Больше. \en More. + oprt_GE = 18, ///< \ru Больше или равно. \en More or equal. + oprt_LT = 19, ///< \ru Меньше. \en Less. + oprt_LE = 20, ///< \ru Меньше или равно. \en Less or equal. + oprt_Involution = 21, ///< \ru Возведение в степень. \en Involution. + oprt_UnaryOperation = 22, ///< \ru Унарная операция. \en Unary operation. + oprt_NOT = oprt_UnaryOperation, ///< \ru Не. \en Not. + oprt_UnaryMinus = 23, ///< \ru Унарный минус. \en Unary minus. + oprt_UnaryPlus = 24, ///< \ru Унарный плюс. \en Unary plus. + oprt_Parentheses = 25 ///< \ru Скобки. \en Brackets. +}; + + +//----------------------------------------------------------------------------- +/** \brief \ru Коды результата разбора строки. + \en Result codes of string parsing. \~ + \details \ru Коды результата разбора строки.\n + \en Result codes of string parsing.\n \~ + \ingroup Parser +*/ +// --- +enum EquTreeResCode { + // \ru Эта группа кодов ошибок полностью повторяет то, что было до V9. \en This group of result codes fully repeats all that was before V9. + equTreeResCode_Ok = 0, ///< \ru Все хорошо. \en Everything is OK. + equTreeResCode_First = 1, ///< \ru Начало диапазона ошибок. \en Start of errors range. + equTreeResCode_SyntaxError = equTreeResCode_First, ///< \ru Ошибка: Синтаксическая ошибка в выражении. \en Error: Syntax error in expression. + equTreeResCode_TooComplex, ///< \ru Ошибка: Слишком сложное выражение. \en Error: Expression is too complex. + equTreeResCode_InvalidAssignment, ///< \ru Ошибка: Переменная присваивается самой себе. \en Error: Variable is assigned by itself. + equTreeResCode_NoVariables, ///< \ru Ошибка: В выражении должна быть хотя бы одна переменная. \en Error: There should be at least one variable in expression. + equTreeResCode_TooLargeIdent, ///< \ru Ошибка: Превышено количество символов в имени переменной. \en Error: The number of symbols in a name of variable is exceeded. + equTreeResCode_TangentsDomain , ///< \ru Ошибка: Аргумент тангенса не в области определения. \en Error: An argument of tangent is out of domain. + equTreeResCode_SqrtDomain, ///< \ru Ошибка: Недопустимое значение аргумента для sqrt. \en Error: Invalid argument value for sqrt. + equTreeResCode_LogarithmDomain, ///< \ru Ошибка: Недопустимое значение аргумента для логарифмической функции. \en Error: Invalid argument value for logarithmic function. + equTreeResCode_ZeroDivide, ///< \ru Ошибка: Деление на ноль. \en Error: Division by zero. + equTreeResCode_TrigonometricDomain, ///< \ru Ошибка: Аргумент тригонометрической функции не в области определения. \en Error: An argument of trigonometric function is out of domain. + equTreeResCode_CyclicRelation, ///< \ru Ошибка: Найдена замкнутая зависимость. \en Error: There is found a closed dependence. + equTreeResCode_PowDomain, ///< \ru Ошибка: недопустимое значение аргумента для степенной функции. \en Error: Invalid argument value for power function. + equTreeResCode_WrongFuncFormat, ///< \ru Ошибка: Выражение содержит функцию, не соответствующую своему формату. \en Error: Expression contains a function which does not correspond to its format. + + // \ru ДОБАВЛЯТЬ НОВЫЕ СООБЩЕНИЯ ТОЛЬКО ПЕРЕД ЭТОЙ СТРОКОЙ; \en ADD NEW MESSAGES ONLY BEFORE THIS STRING; + equTreeResCode_Last ///< \ru Конец диапазона ошибок. \en End of errors range. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Интерфейс координаты. + \en Interface of coordinate. \~ + \details \ru Интерфейс координаты. \n + \en Interface of coordinate. \n \~ + \ingroup Parser +*/ +// --- +struct ItCoord +{ + virtual void SetValue( double v ) = 0; ///< \ru Установить переменную. \en Set variable. + virtual double GetValue() const = 0; ///< \ru Дать переменную. \en Get variable. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Интерфейс переменной. + \en Interface of variable. \~ + \details \ru Интерфейс переменной. \n + \en Interface of variable. \n \~ + \ingroup Parser +*/ +// --- +struct ItTreeVariable +{ + ItTreeVariable() {} + + /// \ru Дать имя. \en Get name. + virtual const c3d::string_t & GetName() const = 0; + /// \ru Установить имя. \en Set name. + virtual void SetName( const c3d::string_t & ) = 0; + /// \ru Дать переменную. \en Get variable. + virtual double GetValue() const = 0; + /// \ru Установить переменную. \en Set variable. + virtual void SetValue( double ) = 0; + /// \ru Дать координату. \en Get coordinate. + virtual const ItCoord & GetCoord() const = 0; + /// \ru Вычислить размер в байтах. \en Get size in bytes. + virtual size_t SizeOf() const = 0; + + /// \ru Захватить \en Catch. + virtual refcount_t AddRef () const = 0; + /// \ru Отпустить. \en Free. + virtual refcount_t Release() const = 0; + + /// \ru Установить имя. Обработка нулевого указателя. \en Set name. Null pointer processing. + virtual void SetName( const TCHAR * s ) { SetName( s ? s : _T("") ); }; + + /// \ru Операторы чтения, записи. \en Reading and writing operators. + DECLARE_PERSISTENT_OPS( ItTreeVariable ) +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Интерфейс интервальной переменной. + \en Interface of interval variable. \~ + \details \ru Интерфейс интервальной переменной. \n + \en Interface of interval variable. \n \~ + \ingroup Parser +*/ +// --- +struct ItIntervalTreeVariable +{ + ItIntervalTreeVariable() {} + + /// \ru Дать имя. \en Get name. + virtual const c3d::string_t & GetName() const = 0; + /// \ru Установить имя. \en Set name. + virtual void SetValue( double f, double s ) = 0; + /// \ru Первая граница. \en The first boundary. + virtual double First() const = 0; + /// \ru Вторая граница. \en The second boundary. + virtual double Second() const = 0; + + /// \ru Захватить. \en Catch. + virtual refcount_t AddRef() const = 0; + /// \ru Отпустить. \en Free. + virtual refcount_t Release() const = 0; + + /// \ru Операторы чтения, записи. \en Reading and writing operators. + DECLARE_PERSISTENT_OPS( ItIntervalTreeVariable ) +}; + + +class MbUserFunc; + +//------------------------------------------------------------------------------ +/** \brief \ru Интерфейс функции. + \en Interface of function. \~ + \details \ru Интерфейс функции. \n + \en Interface of function. \n \~ + \ingroup Parser +*/ +// --- +struct ItUserFunc +{ + ItUserFunc() {}; + virtual ~ItUserFunc() {}; + + /// \ru Дать копию объекта. \en Get a copy of the object. + virtual ItUserFunc & Duplicate() const = 0; + /// \ru Дать имя. \en Get name. + virtual const c3d::string_t & GetName() const = 0; + /// \ru Дать количество параметров. \en Get count of parameters.. + virtual size_t GetParsCount() const = 0; + /// \ru Значение функции. \en Value of function. + virtual EquTreeResCode GetValue ( const SArray &, double & ) const = 0; + /// \ru Значение функции и производных. \en Value of function and derivatives. + virtual EquTreeResCode GetDerivates( const SArray &, + double & v, double & f, + double & s, double & t, + size_t dIndex = 0 ) const = 0; + /// \ru Внешние переменные. \en External variables. + virtual void GetExternalVars( SSArray & vars, + SSArray & funcs ) const = 0; + /// \ru Только для внутреннего использования! Степень функции по индексу переменной. \en For internal use only! Function degree by the index of variable. + virtual size_t GetPseudoOrderByPar ( size_t index ) const = 0; + /// \ru Область определения. \en Domain. + virtual bool GetDefRange( DefRange &, ItTreeVariable & var, + const std::vector & ) const = 0; + /// \ru Равны ли функции. \en Whether functions are equal. + virtual bool IsEqual ( const ItUserFunc & other ) const = 0; + /// \ru Равны ли функции. \en Whether functions are equal. + virtual bool IsEqual ( const MbUserFunc & other ) const = 0; + + /// \ru Операторы чтения, записи. \en Reading and writing operators. + DECLARE_PERSISTENT_OPS( ItUserFunc ) +}; + #endif //__ITTREEVARS_H \ No newline at end of file diff --git a/C3d/Include/pars_user_function.h b/C3d/Include/pars_user_function.h index 4d01112..1f7e73a 100644 --- a/C3d/Include/pars_user_function.h +++ b/C3d/Include/pars_user_function.h @@ -1,114 +1,114 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Пользовательская функция. - \en User-defined function. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __PARS_USER_FUNCTION_H -#define __PARS_USER_FUNCTION_H - - -#include -#include -#include -#include -#include -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Пользовательская функция. - \en User-defined function. \~ - \details \ru Пользовательская функция. \n - \en User-defined function. \n \~ - \ingroup Parser -*/ -// --- -class MATH_CLASS MbUserFunc : public TapeBase, public ItUserFunc, public MbSyncItem -{ -protected: - c3d::string_t m_name; ///< \ru Имя пользовательской функции. \en A name of user-defined function. - c3d::string_t m_expression; ///< \ru Выражение. \en Expression. - BTreeNode * m_tree; ///< \ru Дерево разбора выражения (обязательно должно быть). \en A tree of expression parsing (it should be for sure). - IFC_Array m_vars; ///< \ru Формальные параметры функции. \en Formal parameters of function. - PArray m_intFuncs; ///< \ru Внутренние функции. \en Internal functions. - -public: - MbUserFunc( const c3d::string_t & name, const std::vector & pars ); ///< \ru Конструктор по имени функции и массиву аргументов. \en Constructor by function name and array of arguments. - MbUserFunc( const MbUserFunc & other ); ///< \ru Конструктор копирования. \en Copy-constructor. - MbUserFunc( const c3d::string_t & name ); ///< \ru Конструктор по имени функции. \en Constructor by function name. - virtual ~MbUserFunc(); - -public: - /// \ru Сделать копию. \en Create a copy. - virtual ItUserFunc & Duplicate() const { return *new MbUserFunc(*this); } - /// \ru Получить имя функции. \en Get function name. - virtual const c3d::string_t & GetName() const { return m_name; } - /// \ru Получить область определения функции. \en Get function domain. - virtual bool GetDefRange( DefRange &, ItTreeVariable & var, - const std::vector & ) const; - /// \ru Получить область определения функции. \en Get function domain. - bool GetDefRange( DefRange &, size_t ind, bool stopOnBreak ) const; - - /// \ru Количество аргументов (параметров). \en Parameters (arguments) count. - virtual size_t GetParsCount() const { return m_vars.Count(); } - /// \ru Внешние переменные. \en External variables. - virtual void GetExternalVars( SSArray & vars - , SSArray & funcs ) const; - - /// \ru Сравнить пользовательские функции. \en Compare symbolic (user-defined) functions. - virtual bool IsEqual( const ItUserFunc & other ) const { return other.IsEqual( *this ); } - /// \ru Сравнить пользовательские функции. \en Compare symbolic (user-defined) functions. - virtual bool IsEqual( const MbUserFunc & ) const; - /// \ru Вычислить значение функции в случае массива аргументов \en Calculate the value of a function in a case of arguments array - virtual EquTreeResCode GetValue( const SArray & params, double & v ) const; - /// \ru Вычислить значение функции и производные в случае массива аргументов \en Calculate the value of a function and derivatives in a case of arguments array - virtual EquTreeResCode GetDerivates( const SArray & params, - double & v, double & fd, - double & sd, double & td, - size_t dIndex = 0 ) const; - /// \ru Вычислить значение функции в случае одного аргумента \en Calculate the value of a function in a case of one argument - EquTreeResCode GetValue( double t, double & v ) const; - /// \ru Вычислить значение функции и производные в случае одного аргумента \en Calculate the value of a function and derivatives in a case of one argument - EquTreeResCode GetDerivates( double t, - double & v, double & fd, - double & sd, double & td ) const; - /// \ru Функция константная. \en Function is const. - bool IsConst() const; - /// \ru Функция линейная. \en Function is linear. - bool IsLine() const; - - /// \ru Установить выражение. \en Set expression. - EquTreeResCode SetExpression( const c3d::string_t & expr, const BTreeNode & tree ); - EquTreeResCode SetExpression( const TCHAR* expr, const BTreeNode & tree ); - /// \ru Получить выражение. \en Get expression. - const c3d::string_t & GetExpression() const { return m_expression; } - /// \ru Получить дерево разбора выражения \en Get expression parsing tree. - const BTreeNode & GetTree() const { return *m_tree; } - /// \ru Сравнение с пользовательской функцией. \en Comparison with user-defined function. - bool IsEqualValue( const MbUserFunc & other ) const; - /// \ru Получить массив параметров особых точек для данного аргумента, заданного индексом на заданном интервале. \en Get an array of parameters of singular points for the argument which is given by the index on the given interval. - bool GetExtremumPoints( size_t parIndex, std::pair interval, std::vector & points ); - /// \ru Получить массив параметров. \en Get array of parameters. - void GetPars ( RPArray & pars ) const; - /// \ru Получить аргумент по индексу. \en Get argument by index. - ItTreeVariable * GetPar( size_t i ) const { return i < m_vars.Count() ? m_vars[i] : NULL; } - /// \ru Поготовить объект к записи. \en Prepare an object for writing. - void WritingBeginEnd( bool begin ) { RegisterVars( begin ? registrable : noRegistrable ); } - /// \ru Оператор присваивания. \en Assignment operator. - const MbUserFunc & operator = ( const MbUserFunc & ); -private: - /// \ru Только для внутреннего использования! Порядок функции по номеру параметра. \en For internal use only! Degree of function by the number of parameter. - virtual size_t GetPseudoOrderByPar( size_t index ) const; - void RegisterVars( RegistrableRec ) const; - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbUserFunc ) -}; - -IMPL_PERSISTENT_OPS( MbUserFunc ) - -#endif // __PARS_USER_FUNCTION_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Пользовательская функция. + \en User-defined function. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __PARS_USER_FUNCTION_H +#define __PARS_USER_FUNCTION_H + + +#include +#include +#include +#include +#include +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Пользовательская функция. + \en User-defined function. \~ + \details \ru Пользовательская функция. \n + \en User-defined function. \n \~ + \ingroup Parser +*/ +// --- +class MATH_CLASS MbUserFunc : public TapeBase, public ItUserFunc, public MbSyncItem +{ +protected: + c3d::string_t m_name; ///< \ru Имя пользовательской функции. \en A name of user-defined function. + c3d::string_t m_expression; ///< \ru Выражение. \en Expression. + BTreeNode * m_tree; ///< \ru Дерево разбора выражения (обязательно должно быть). \en A tree of expression parsing (it should be for sure). + IFC_Array m_vars; ///< \ru Формальные параметры функции. \en Formal parameters of function. + PArray m_intFuncs; ///< \ru Внутренние функции. \en Internal functions. + +public: + MbUserFunc( const c3d::string_t & name, const std::vector & pars ); ///< \ru Конструктор по имени функции и массиву аргументов. \en Constructor by function name and array of arguments. + MbUserFunc( const MbUserFunc & other ); ///< \ru Конструктор копирования. \en Copy-constructor. + MbUserFunc( const c3d::string_t & name ); ///< \ru Конструктор по имени функции. \en Constructor by function name. + virtual ~MbUserFunc(); + +public: + /// \ru Сделать копию. \en Create a copy. + virtual ItUserFunc & Duplicate() const { return *new MbUserFunc(*this); } + /// \ru Получить имя функции. \en Get function name. + virtual const c3d::string_t & GetName() const { return m_name; } + /// \ru Получить область определения функции. \en Get function domain. + virtual bool GetDefRange( DefRange &, ItTreeVariable & var, + const std::vector & ) const; + /// \ru Получить область определения функции. \en Get function domain. + bool GetDefRange( DefRange &, size_t ind, bool stopOnBreak ) const; + + /// \ru Количество аргументов (параметров). \en Parameters (arguments) count. + virtual size_t GetParsCount() const { return m_vars.Count(); } + /// \ru Внешние переменные. \en External variables. + virtual void GetExternalVars( SSArray & vars + , SSArray & funcs ) const; + + /// \ru Сравнить пользовательские функции. \en Compare symbolic (user-defined) functions. + virtual bool IsEqual( const ItUserFunc & other ) const { return other.IsEqual( *this ); } + /// \ru Сравнить пользовательские функции. \en Compare symbolic (user-defined) functions. + virtual bool IsEqual( const MbUserFunc & ) const; + /// \ru Вычислить значение функции в случае массива аргументов \en Calculate the value of a function in a case of arguments array + virtual EquTreeResCode GetValue( const SArray & params, double & v ) const; + /// \ru Вычислить значение функции и производные в случае массива аргументов \en Calculate the value of a function and derivatives in a case of arguments array + virtual EquTreeResCode GetDerivates( const SArray & params, + double & v, double & fd, + double & sd, double & td, + size_t dIndex = 0 ) const; + /// \ru Вычислить значение функции в случае одного аргумента \en Calculate the value of a function in a case of one argument + EquTreeResCode GetValue( double t, double & v ) const; + /// \ru Вычислить значение функции и производные в случае одного аргумента \en Calculate the value of a function and derivatives in a case of one argument + EquTreeResCode GetDerivates( double t, + double & v, double & fd, + double & sd, double & td ) const; + /// \ru Функция константная. \en Function is const. + bool IsConst() const; + /// \ru Функция линейная. \en Function is linear. + bool IsLine() const; + + /// \ru Установить выражение. \en Set expression. + EquTreeResCode SetExpression( const c3d::string_t & expr, const BTreeNode & tree ); + EquTreeResCode SetExpression( const TCHAR* expr, const BTreeNode & tree ); + /// \ru Получить выражение. \en Get expression. + const c3d::string_t & GetExpression() const { return m_expression; } + /// \ru Получить дерево разбора выражения \en Get expression parsing tree. + const BTreeNode & GetTree() const { return *m_tree; } + /// \ru Сравнение с пользовательской функцией. \en Comparison with user-defined function. + bool IsEqualValue( const MbUserFunc & other ) const; + /// \ru Получить массив параметров особых точек для данного аргумента, заданного индексом на заданном интервале. \en Get an array of parameters of singular points for the argument which is given by the index on the given interval. + bool GetExtremumPoints( size_t parIndex, const c3d::DoublePair & interval, c3d::DoubleVector & points ); + /// \ru Получить массив параметров. \en Get array of parameters. + void GetPars ( RPArray & pars ) const; + /// \ru Получить аргумент по индексу. \en Get argument by index. + ItTreeVariable * GetPar( size_t i ) const { return i < m_vars.Count() ? m_vars[i] : NULL; } + /// \ru Подготовить объект к записи. \en Prepare an object for writing. + void WritingBeginEnd( bool begin ) { RegisterVars( begin ? registrable : noRegistrable ); } + /// \ru Оператор присваивания. \en Assignment operator. + const MbUserFunc & operator = ( const MbUserFunc & ); +private: + /// \ru Только для внутреннего использования! Порядок функции по номеру параметра. \en For internal use only! Degree of function by the number of parameter. + virtual size_t GetPseudoOrderByPar( size_t index ) const; + void RegisterVars( RegistrableRec ) const; + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbUserFunc ) +}; + +IMPL_PERSISTENT_OPS( MbUserFunc ) + +#endif // __PARS_USER_FUNCTION_H diff --git a/C3d/Include/pars_var.h b/C3d/Include/pars_var.h index 72f032e..fb8aaef 100644 --- a/C3d/Include/pars_var.h +++ b/C3d/Include/pars_var.h @@ -11,7 +11,6 @@ #define __VAR_H #include -//#include #include #include diff --git a/C3d/Include/pars_variable.h b/C3d/Include/pars_variable.h index 67cedeb..88a056f 100644 --- a/C3d/Include/pars_variable.h +++ b/C3d/Include/pars_variable.h @@ -1,100 +1,101 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Переменная. - \en Variable. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __MBVARIABLE_H -#define __MBVARIABLE_H - -#include -#include - - -//----------------------------------------------------------------------------- -/** \brief \ru Координата. - \en Coordinate. \~ - \details \ru Координата. \n - \en Coordinate. \n \~ - \ingroup Parser -*/ -// --- -class MbCoord : public ItCoord -{ -private: - double m_coord; // \ru Значение координаты. \en A value of coordinate. -public: - /// \ru Конструктор по значению. \en Constructor by the value. - MbCoord( double v ) : m_coord(v) {} - /// \ru Установить значение. \en Set value. - virtual void SetValue( double v ){ m_coord = v; } - /// \ru Получить значение. \en Get the value. - virtual double GetValue() const { return m_coord; } - /// \ru Вычислить размер координаты в байтах. \en Get size of coordinate in bytes. - size_t SizeOf() const { return sizeof(double);} - - /// \ru Операторы чтения, записи. \en Reading and writing operators. - KNOWN_OBJECTS_RW_REF_OPERATORS( MbCoord ); -}; - - -//----------------------------------------------------------------------------- -/** \brief \ru Переменная. - \en Variable. \~ - \details \ru Переменная. \n - \en Variable. \n \~ - \ingroup Parser -*/ -// --- -class MATH_CLASS MbTreeVariable : public TapeBase, public ItTreeVariable -{ -private: - MbCoord m_coord; ///< \ru Координата. \en Coordinate. - c3d::string_t m_name; ///< \ru Имя переменной. \en A name of variable. - mutable size_t useCount; ///< \ru Количество использований. \en The number of uses. - -public: - /// \ru Конструктор по имени и значению. \en Constructor by the name and the value. - MbTreeVariable( const c3d::string_t & name, double v ); - virtual ~MbTreeVariable(); - -public: - /// \ru Получить имя. \en Get name. - virtual const c3d::string_t & GetName () const { return m_name; } - /// \ru Установить имя. \en Set name. - virtual void SetName ( const c3d::string_t & name ) { m_name = name; } - /// \ru Установить имя. Обработка нулевого указателяю \en Set name. Null pointer processing. - virtual void SetName( const TCHAR* s ) { m_name.assign( s ? s : _T("") ); }; - /// \ru Получение значение. \en Get value. - virtual double GetValue () const { return m_coord.GetValue(); } - /// \ru Установить значение. \en Set value. - virtual void SetValue ( double v ) { m_coord.SetValue( v ); } - /// \ru Получить координату. \en Get coordinate. - virtual const MbCoord & GetCoord () const { return m_coord; } - /// \ru Вычислить размер переменной в байтах. \en Get size of variable in bytes. - virtual size_t SizeOf () const { -#ifdef C3D_WINDOWS //_MSC_VER // method SizeOf() - return /*m_name.*/sizeof( m_name ) + sizeof( TCHAR ) * (m_name.length()) + m_coord.SizeOf(); -#else // C3D_WINDOWS - // \ru необходимо корректное вычилсение размера занимаемой памяти std::string \en there must be a correct calculation of size of memory allocated for std::string - return sizeof(m_name) + m_coord.SizeOf(); // \ru если данный SizeOf требуется для выделения памяти \en if the given SizeOf is required for the memory allocation - // \ru то все Ок, поскольку std::string сам разберется с выделением памяти себе. \en then everything is OK because std::string controls the memory allocation for itself. -#endif // C3D_WINDOWS - } - /// \ru Создать копию переменной. \en Create a copy of variable. - MbTreeVariable & Duplicate() const { return *new MbTreeVariable( GetName(), GetValue() ); } - /// \ru Увеличить счетчик использований. \en Increase a counter of uses. - virtual refcount_t AddRef () const { return ++useCount; } - /// \ru Уменьшить счетчик использований и удалить объект, если он никому уже не нужен. \en Decrease a counter of uses and delete an object if it is not used any more. - virtual refcount_t Release() const; - - /// \ru Операторы чтения, записи. \en Reading and writing operators. - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbTreeVariable ); -}; - -IMPL_PERSISTENT_OPS( MbTreeVariable ) - +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Переменная. + \en Variable. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __MBVARIABLE_H +#define __MBVARIABLE_H + +#include +#include +#include + + +//----------------------------------------------------------------------------- +/** \brief \ru Координата. + \en Coordinate. \~ + \details \ru Координата. \n + \en Coordinate. \n \~ + \ingroup Parser +*/ +// --- +class MbCoord : public ItCoord +{ +private: + double m_coord; // \ru Значение координаты. \en A value of coordinate. +public: + /// \ru Конструктор по значению. \en Constructor by the value. + MbCoord( double v ) : m_coord(v) {} + /// \ru Установить значение. \en Set value. + virtual void SetValue( double v ){ m_coord = v; } + /// \ru Получить значение. \en Get the value. + virtual double GetValue() const { return m_coord; } + /// \ru Вычислить размер координаты в байтах. \en Get size of coordinate in bytes. + size_t SizeOf() const { return sizeof(double);} + + /// \ru Операторы чтения, записи. \en Reading and writing operators. + KNOWN_OBJECTS_RW_REF_OPERATORS( MbCoord ); +}; + + +//----------------------------------------------------------------------------- +/** \brief \ru Переменная. + \en Variable. \~ + \details \ru Переменная. \n + \en Variable. \n \~ + \ingroup Parser +*/ +// --- +class MATH_CLASS MbTreeVariable : public TapeBase, public ItTreeVariable +{ +private: + MbCoord m_coord; ///< \ru Координата. \en Coordinate. + c3d::string_t m_name; ///< \ru Имя переменной. \en A name of variable. + mutable size_t useCount; ///< \ru Количество использований. \en The number of uses. + +public: + /// \ru Конструктор по имени и значению. \en Constructor by the name and the value. + MbTreeVariable( const c3d::string_t & name, double v ); + virtual ~MbTreeVariable(); + +public: + /// \ru Получить имя. \en Get name. + virtual const c3d::string_t & GetName () const { return m_name; } + /// \ru Установить имя. \en Set name. + virtual void SetName ( const c3d::string_t & name ) { m_name = name; } + /// \ru Установить имя. Обработка нулевого указателя. \en Set name. Null pointer processing. + virtual void SetName( const TCHAR* s ) { m_name.assign( s ? s : _T("") ); }; + /// \ru Получение значение. \en Get value. + virtual double GetValue () const { return m_coord.GetValue(); } + /// \ru Установить значение. \en Set value. + virtual void SetValue ( double v ) { m_coord.SetValue( v ); } + /// \ru Получить координату. \en Get coordinate. + virtual const MbCoord & GetCoord () const { return m_coord; } + /// \ru Вычислить размер переменной в байтах. \en Get size of variable in bytes. + virtual size_t SizeOf () const { +#ifdef C3D_WINDOWS //_MSC_VER // method SizeOf() + return /*m_name.*/sizeof( m_name ) + sizeof( TCHAR ) * (m_name.length()) + m_coord.SizeOf(); +#else // C3D_WINDOWS + // \ru необходимо корректное вычилсение размера занимаемой памяти std::string \en there must be a correct calculation of size of memory allocated for std::string + return sizeof(m_name) + m_coord.SizeOf(); // \ru если данный SizeOf требуется для выделения памяти \en if the given SizeOf is required for the memory allocation + // \ru то все Ок, поскольку std::string сам разберется с выделением памяти себе. \en then everything is OK because std::string controls the memory allocation for itself. +#endif // C3D_WINDOWS + } + /// \ru Создать копию переменной. \en Create a copy of variable. + MbTreeVariable & Duplicate() const { return *new MbTreeVariable( GetName(), GetValue() ); } + /// \ru Увеличить счетчик использований. \en Increase a counter of uses. + virtual refcount_t AddRef () const { return ++useCount; } + /// \ru Уменьшить счетчик использований и удалить объект, если он никому уже не нужен. \en Decrease a counter of uses and delete an object if it is not used any more. + virtual refcount_t Release() const; + +/// \ru Операторы чтения, записи. \en Reading and writing operators. +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbTreeVariable ) +}; + +IMPL_PERSISTENT_OPS( MbTreeVariable ) + #endif // __MBVARIABLE_H \ No newline at end of file diff --git a/C3d/Include/pars_yacc.h b/C3d/Include/pars_yacc.h index 67993f0..973e75f 100644 --- a/C3d/Include/pars_yacc.h +++ b/C3d/Include/pars_yacc.h @@ -1,181 +1,176 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Алгоритм синтаксического разбора алгебраического выражения. - \en Algorithm of syntax parsing of algebraic expression. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __YACC_H -#define __YACC_H - -#include -#include -#include -#include - -class BTreeNode; -struct ItTreeVariable; -struct ItUserFunc; -struct ItIntervalTreeVariable; -class TreeIntervalNode; - -template class RPArray; - - -// \ru Максимальная длина переменной (BUG_20492) используется при создании переменной и при разборе уравнения в калькуляторе. \en Maximum length of variable (BUG_20492) is used when creating a variable and parsing of equation in calculator. - -/// \ru Максимальная длина переменной. \en Maximum length of variable. -#define MAX_VARIABLE_NAME_LENGTH 512 - -/// \ru Максимальная длина выражения. \en Maximum length of expression. -#define MAX_EQU_LENGTH 2048 - - -//------------------------------------------------------------------------------- -// -// --- -struct ItEquVarCreator -{ - virtual ~ItEquVarCreator() - { - } - - virtual ItTreeVariable * GetVariable( const c3d::string_t & name ) = 0; - virtual ItIntervalTreeVariable * GetIntervalVariable( const c3d::string_t & name ) = 0; - virtual ItUserFunc * GetFunc ( const c3d::string_t & name, size_t parCount ) = 0; - virtual bool CreateFunc ( const c3d::string_t & name, const std::vector & parNames ) = 0; - virtual bool CreateInterval( const c3d::string_t & name ) = 0; - virtual bool CreateVariable( const c3d::string_t & name ) = 0; - - virtual ItTreeVariable * GetVariable( const TCHAR* name ) = 0; - virtual ItIntervalTreeVariable * GetIntervalVariable( const TCHAR* name ) = 0; - virtual ItUserFunc * GetFunc ( const TCHAR* name, size_t parCount ) = 0; - virtual bool CreateInterval( const TCHAR* name ) = 0; - virtual bool CreateVariable( const TCHAR* name ) = 0; - virtual unsigned int GetUsedCoordsCount() const = 0; - virtual ItTreeVariable & CreateAuxVar() = 0; - - // \ru Использовались ли какие-либо переменные? \en Were any variables used? - virtual bool WereVarsUsed() const = 0; - // \ru Использовались ли несуществующие (новые) переменные? \en Were any not existed (new) variables used? - virtual bool WereNewVarsUsed() const = 0; -}; - - -//------------------------------------------------------------------------------- -/** \brief \ru Создать дерево уравнения (a = b + c) через параметрический калькулятор. - \en Create a tree of equations (a = b + c) by parametric calculator. \~ - \details \ru Создать дерево уравнения a = b + c) через параметрический калькулятор. \n - \en Create a tree of equations (a = b + c) by parametric calculator. \n \~ - \return \ru Код результата операции. - \en Operation result code. \~ - \ingroup Parser -*/ -// --- -MATH_FUNC(EquTreeResCode) CreateBTreeForEquation( const c3d::string_t & equstr - , ItEquVarCreator & varsCreator - , std_unique_ptr & dRoot - ); - - -//------------------------------------------------------------------------------- -/** \brief \ru Создать дерево выражения (b + c + d) через параметрический калькулятор. - \en Create a tree of equations (b + c + d) by parametric calculator. \~ - \details \ru Создать дерево выражения (b + c + d) через параметрический калькулятор. \n - \en Create a tree of equations (b + c + d) by parametric calculator. \n \~ - \return \ru Код результата операции. - \en Operation result code. \~ - \ingroup Parser -*/ -// --- -MATH_FUNC(EquTreeResCode) CreateBTreeForExpression( const c3d::string_t & equstr - , ItEquVarCreator * varsCreator - , std_unique_ptr & root - , std_unique_ptr & iRoot - ); - - -//------------------------------------------------------------------------------- -/** \brief \ru Создать переменную. - \en Create variable. \~ - \details \ru Создать переменную по строке. \n - \en Create variable by string. \n \~ - \return \ru Код результата операции. - \en Operation result code. \~ - \ingroup Parser -*/ -// --- -MATH_FUNC(EquTreeResCode) CreateVariable( const c3d::string_t & expression, ItEquVarCreator & ); - - -//------------------------------------------------------------------------------- -/** \brief \ru Является ли выражение неравенством. - \en Whether expression is inequation. \~ - \details \ru Является ли выражение неравенством. \n - \en Whether expression is inequation. \n \~ - \ingroup Parser -*/ -// --- -MATH_FUNC(bool) IsInequality( const c3d::string_t & equstr ); - - -//------------------------------------------------------------------------------- -/** \brief \ru Создать дерево уравнения (a = b + c) через параметрический калькулятор. - \en Create a tree of equations (a = b + c) by parametric calculator. \~ - \details \ru Создать дерево уравнения a = b + c) через параметрический калькулятор. \n - \en Create a tree of equations (a = b + c) by parametric calculator. \n \~ - \return \ru Код результата операции. - \en Operation result code. \~ - \ingroup Parser -*/ -// --- -MATH_FUNC(EquTreeResCode) CreateBTreeForEquation( const TCHAR * equstr - , ItEquVarCreator & varsCreator - , std_unique_ptr & dRoot - ); - - -//------------------------------------------------------------------------------- -/** \brief \ru Создать дерево выражения (b + c + d) через параметрический калькулятор. - \en Create a tree of equations (b + c + d) by parametric calculator. \~ - \details \ru Создать дерево выражения (b + c + d) через параметрический калькулятор. \n - \en Create a tree of equations (b + c + d) by parametric calculator. \n \~ - \return \ru Код результата операции. - \en Operation result code. \~ - \ingroup Parser -*/ -// --- -MATH_FUNC(EquTreeResCode) CreateBTreeForExpression( const TCHAR * equstr - , ItEquVarCreator * varsCreator - , std_unique_ptr & root - , std_unique_ptr & iRoot - ); - - -//------------------------------------------------------------------------------- -/** \brief \ru Создать переменную. - \en Create variable. \~ - \details \ru Создать переменную по строке. \n - \en Create variable by string. \n \~ - \return \ru Код результата операции. - \en Operation result code. \~ - \ingroup Parser -*/ -// --- -MATH_FUNC(EquTreeResCode) CreateVariable( const TCHAR* expression, ItEquVarCreator & ); - - -//------------------------------------------------------------------------------- -/** \brief \ru Является ли выражение неравенством. - \en Whether expression is inequation. \~ - \details \ru Является ли выражение неравенством. \n - \en Whether expression is inequation. \n \~ - \ingroup Parser -*/ -// --- -MATH_FUNC(bool) IsInequality( const TCHAR* equstr ); - - +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Алгоритм синтаксического разбора алгебраического выражения. + \en Algorithm of syntax parsing of algebraic expression. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __YACC_H +#define __YACC_H + +#include +#include +#include +#include + +class MATH_CLASS BTreeNode; +class MATH_CLASS TreeIntervalNode; +struct ItTreeVariable; +struct ItUserFunc; +struct ItIntervalTreeVariable; + + +/// \ru Максимальная длина переменной. \en Maximum length of variable. +const_expr size_t MAX_VARIABLE_NAME_LENGTH = 512; +/// \ru Максимальная длина выражения. \en Maximum length of expression. +const_expr size_t MAX_EQU_LENGTH = 2048; + + +//------------------------------------------------------------------------------- +// +// --- +struct ItEquVarCreator +{ + virtual ~ItEquVarCreator() + { + } + + virtual ItTreeVariable * GetVariable( const c3d::string_t & ) = 0; + virtual ItIntervalTreeVariable * GetIntervalVariable( const c3d::string_t & ) = 0; + virtual ItUserFunc * GetFunc ( const c3d::string_t &, size_t parCount ) = 0; + virtual bool CreateFunc ( const c3d::string_t &, const std::vector & parNames ) = 0; + virtual bool CreateInterval( const c3d::string_t & ) = 0; + virtual bool CreateVariable( const c3d::string_t &) = 0; + + virtual ItTreeVariable * GetVariable( const TCHAR * ) = 0; + virtual ItIntervalTreeVariable * GetIntervalVariable( const TCHAR * ) = 0; + virtual ItUserFunc * GetFunc ( const TCHAR *, size_t parCount ) = 0; + virtual bool CreateInterval( const TCHAR * ) = 0; + virtual bool CreateVariable( const TCHAR * ) = 0; + virtual unsigned int GetUsedCoordsCount() const = 0; + virtual ItTreeVariable & CreateAuxVar() = 0; + + // \ru Использовались ли какие-либо переменные? \en Were any variables used? + virtual bool WereVarsUsed() const = 0; + // \ru Использовались ли несуществующие (новые) переменные? \en Were any not existed (new) variables used? + virtual bool WereNewVarsUsed() const = 0; +}; + + +//------------------------------------------------------------------------------- +/** \brief \ru Создать дерево уравнения (a = b + c) через параметрический калькулятор. + \en Create a tree of equations (a = b + c) by parametric calculator. \~ + \details \ru Создать дерево уравнения a = b + c) через параметрический калькулятор. \n + \en Create a tree of equations (a = b + c) by parametric calculator. \n \~ + \return \ru Код результата операции. + \en Operation result code. \~ + \ingroup Parser +*/ +// --- +MATH_FUNC(EquTreeResCode) CreateBTreeForEquation( const c3d::string_t & equstr + , ItEquVarCreator & varsCreator + , std_unique_ptr & dRoot + ); + + +//------------------------------------------------------------------------------- +/** \brief \ru Создать дерево выражения (b + c + d) через параметрический калькулятор. + \en Create a tree of equations (b + c + d) by parametric calculator. \~ + \details \ru Создать дерево выражения (b + c + d) через параметрический калькулятор. \n + \en Create a tree of equations (b + c + d) by parametric calculator. \n \~ + \return \ru Код результата операции. + \en Operation result code. \~ + \ingroup Parser +*/ +// --- +MATH_FUNC(EquTreeResCode) CreateBTreeForExpression( const c3d::string_t & equstr + , ItEquVarCreator * varsCreator + , std_unique_ptr & root + , std_unique_ptr & iRoot + ); + + +//------------------------------------------------------------------------------- +/** \brief \ru Создать переменную. + \en Create variable. \~ + \details \ru Создать переменную по строке. \n + \en Create variable by string. \n \~ + \return \ru Код результата операции. + \en Operation result code. \~ + \ingroup Parser +*/ +// --- +MATH_FUNC(EquTreeResCode) CreateVariable( const c3d::string_t & expression, ItEquVarCreator & ); + + +//------------------------------------------------------------------------------- +/** \brief \ru Является ли выражение неравенством. + \en Whether expression is inequation. \~ + \details \ru Является ли выражение неравенством. \n + \en Whether expression is inequation. \n \~ + \ingroup Parser +*/ +// --- +MATH_FUNC(bool) IsInequality( const c3d::string_t & ); + + +//------------------------------------------------------------------------------- +/** \brief \ru Создать дерево уравнения (a = b + c) через параметрический калькулятор. + \en Create a tree of equations (a = b + c) by parametric calculator. \~ + \details \ru Создать дерево уравнения a = b + c) через параметрический калькулятор. \n + \en Create a tree of equations (a = b + c) by parametric calculator. \n \~ + \return \ru Код результата операции. + \en Operation result code. \~ + \ingroup Parser +*/ +// --- +MATH_FUNC(EquTreeResCode) CreateBTreeForEquation( const TCHAR * equstr + , ItEquVarCreator & varsCreator + , std_unique_ptr & dRoot + ); + + +//------------------------------------------------------------------------------- +/** \brief \ru Создать дерево выражения (b + c + d) через параметрический калькулятор. + \en Create a tree of equations (b + c + d) by parametric calculator. \~ + \details \ru Создать дерево выражения (b + c + d) через параметрический калькулятор. \n + \en Create a tree of equations (b + c + d) by parametric calculator. \n \~ + \return \ru Код результата операции. + \en Operation result code. \~ + \ingroup Parser +*/ +// --- +MATH_FUNC(EquTreeResCode) CreateBTreeForExpression( const TCHAR * equstr + , ItEquVarCreator * varsCreator + , std_unique_ptr & root + , std_unique_ptr & iRoot + ); + + +//------------------------------------------------------------------------------- +/** \brief \ru Создать переменную. + \en Create variable. \~ + \details \ru Создать переменную по строке. \n + \en Create variable by string. \n \~ + \return \ru Код результата операции. + \en Operation result code. \~ + \ingroup Parser +*/ +// --- +MATH_FUNC(EquTreeResCode) CreateVariable( const TCHAR * expression, ItEquVarCreator & ); + + +//------------------------------------------------------------------------------- +/** \brief \ru Является ли выражение неравенством. + \en Whether expression is inequation. \~ + \details \ru Является ли выражение неравенством. \n + \en Whether expression is inequation. \n \~ + \ingroup Parser +*/ +// --- +MATH_FUNC(bool) IsInequality( const TCHAR * ); + + #endif \ No newline at end of file diff --git a/C3d/Include/plane_instance.h b/C3d/Include/plane_instance.h index d6211dc..b385b7d 100644 --- a/C3d/Include/plane_instance.h +++ b/C3d/Include/plane_instance.h @@ -1,197 +1,328 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Вставка двумерного объекта. - \en Instance of a two-dimensional object. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __PLANE_INSTANCE_H -#define __PLANE_INSTANCE_H - - -#include -#include -#include -#include - - -class MATH_CLASS MbPlaneInstance; -namespace c3d // namespace C3D -{ -typedef SPtr PInstanceSPtr; -typedef SPtr ConstPInstanceSPtr; - -typedef std::vector PInstancesVector; -typedef std::vector ConstPInstancesVector; - -typedef std::vector PInstancesSPtrVector; -typedef std::vector ConstPInstancesSPtrVector; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Вставка двумерных объектов эскиза. - \en Instance of a two-dimensional sketch objects. \~ - \details \ru Вставка позволяет работать с двумерными геометрическими объектами как с объектом геометрической модели. - Двумерные геометрические объекты MbPlaneItem располагаются в плоскости XOY локальной системы координат MbPlacement3D.\n - \en The instance allows to deal with two-dimensional geometric objects as with object of geometric model. - Two-dimensional MbPlaneItem geometric objects located in XOY-plane of MbPlacement3D local coordinate system.\n \~ - \ingroup Model_Items -*/ -// --- -class MATH_CLASS MbPlaneInstance : public MbItem { -protected : - MbPlacement3D place; ///< \ru Локальная система координат объекта. \en Local coordinate system of the object. - std::vector planeItems; ///< \ru Множество двумерных объектов эскиза. \en A set of two-dimensional sketch objects. - -protected : - /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. - explicit MbPlaneInstance( const MbPlaneInstance &, MbRegDuplicate * ); -public : - /// \ru Конструктор по локальной системе координат без двумерных объектов. \en Constructor by a local coordinate system without two-dimensional objects. - MbPlaneInstance( const MbPlacement3D & ); - /// \ru Конструктор по двумерному объекту (используется оригинал) и локальной системе координат. \en Constructor by two-dimensional object (original is used) and a local coordinate system. - MbPlaneInstance( const MbPlaneItem &, const MbPlacement3D & ); - /// \ru Конструктор по двумерным объектам (используются оригиналы) и локальной системе координат. \en Constructor by two-dimensional object (originals are used) and a local coordinate system. - template - MbPlaneInstance( const MbPlacement3D &, const PlaneItems & ); - /// \ru Деструктор \en Destructor - virtual ~MbPlaneInstance(); - -public : - VISITING_CLASS( MbPlaneInstance ); - - // \ru Общие функции геометрического объекта. \en Common functions of a geometric object. - - virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en A type of an object. - virtual MbSpaceItem & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Создать копию. \en Create a copy. - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвинуть вдоль вектора. \en Translate along a vector. - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. - virtual bool IsSame ( const MbSpaceItem &, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Are the objects equal? - virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными? \en Are the objects similar? - virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать объекты равными. \en Make the objects equal. - virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. - virtual void AddYourGabaritTo( MbCube & ) const; // \ru Добавь свой габарит в куб. \en Add bounding box into a cube. - virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate bounding box in the local coordinate system. - virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. - - virtual MbProperty & CreateProperty( MbePrompt ) const; // \ru Создать собственное свойство. \en Create a custom property. - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. - virtual void GetBasisPoints( MbControlData & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - // \ru Получить локальную систему координат объекта. \en Get the local coordinate system of an object. - virtual bool GetPlacement( MbPlacement3D & ) const; - // \ru Установить локальную систему координат объекта. \en Set the local coordinate system of an object. - virtual bool SetPlacement( const MbPlacement3D & ); - - // \ru Создать полигональный объект - упрощенную копию данного объекта. \en Create a polygonal object - a simplified copy of the given object. - virtual MbItem * CreateMesh( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * iReg ) const; - // \ru Найти объект по геометрическому объекту (MbPlaneItem). \en Find the object by a geometric object (MbSpaceItem). - virtual const MbItem * FindItem( const MbPlaneItem * s, MbPath & path, MbMatrix3D & from ) const; - - /** \ru \name Общие функции вставки двумерного объекта. - \en \name Common functions of instance of two-dimensional object. - \{ */ - /// \ru Выдать число двумерных геометрических объектов. \en Get number of two-dimensional geometric objects. - size_t PlaneItemsCount() const; - /// \ru Выдать двумерный геометрический объект. \en Get two-dimensional geometric object. - const MbPlaneItem * GetPlaneItem( size_t ind = 0 ) const; - /// \ru Выдать двумерный геометрический объект для возможного редактирования. \en Get two-dimensional object for possible editing. - MbPlaneItem * SetPlaneItem( size_t ind = 0 ); - /// \ru Заменить двумерный геометрический объект. \en Replace two-dimensional geometric object. - bool SetPlaneItem( MbPlaneItem * init, size_t ind = 0 ); - /// \ru Добавить двумерный геометрический объект. \en Add two-dimensional geometric object. The method returns the index of added or existing object in MbPlaneInstance (the method returns SYS_MAX_T if the object is NULL). - size_t AddPlaneItem( MbPlaneItem * init ); - /// \ru Метод возвращает индекс двумерного геометрического объекта. \en The method returns the index of two-dimensional geometric object in MbPlaneInstance (the method returns SYS_MAX_T if the object was not finded). - size_t GetIndex( MbPlaneItem * init ); - /// \ru Выдать локальную систему координат объекта. \en Get the local coordinate system of an object. - const MbPlacement3D & GetPlacement() const { return place; } - /// \ru Выдать локальную систему координат объекта для редактирования. \en Get the local coordinate system of an object for editing. - MbPlacement3D & SetPlacement() { return place; } - - /// \ru Преобразовать двумерный объект согласно матрице. \en Transform two-dimensional object according to the matrix. - void Transform( const MbMatrix &, MbRegTransform * iReg = NULL ); - /// \ru Сдвинуть двумерный объект вдоль вектора. \en Translate two-dimensional object along a vector. - void Move ( const MbVector &, MbRegTransform * iReg = NULL ); - /// \ru Повернуть двумерный объект вокруг точки на заданный угол. \en Rotate two-dimensional object at a given angle around an axis. - void Rotate ( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * iReg = NULL ); - /// \ru Повернуть двумерный объект вокруг точки на заданный угол. \en Rotate two-dimensional object at a given angle around an axis. - void Rotate ( const MbCartPoint & pnt, double angle, MbRegTransform * iReg = NULL ); - - /// \ru Удалить все объекты эскиза. \en Delete all the sketch items. - void DeleteItems(); - /// \ru Удалить объект эскиза. \en Delete the sketch item by index. - bool DeleteItem( size_t ind ); - /// \ru Удалить объект эскиза. \en Delete the sketch item by index. - MbPlaneItem * DetachItem( size_t ind ); - - /** \brief \ru Заменить объект. - \en Replace an item. \~ - \details \ru Заменить объект новым. - \en Replace an item by a new one. \~ - \param[in] item - \ru Заменяемый объект. - \en An item to be replaced. \~ - \param[in] newItem - \ru Новый объект. - \en A new item. \~ - \return \ru Возвращает true, если замена была выполнена. - \en Returns true if the replacement has been performed. \~ - */ - bool ReplaceItem( const MbPlaneItem & item, MbPlaneItem & newItem ); - - /// \ru Выдать все объекты. \en Get all the items. - template - void GetItems( PlaneItems & ) const; - - /** \} */ - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbPlaneInstance ) -OBVIOUS_PRIVATE_COPY( MbPlaneInstance ) -}; - -IMPL_PERSISTENT_OPS( MbPlaneInstance ); - -//------------------------------------------------------------------------------ -// Конструктор -// --- -template -inline MbPlaneInstance::MbPlaneInstance( const MbPlacement3D & p, const PlaneItems & inits ) - : MbItem ( ) - , place ( p ) - , planeItems( ) -{ - size_t addCnt = inits.size(); - planeItems.reserve( addCnt ); - for ( size_t k = 0; k < addCnt; ++k ) { - const MbPlaneItem * planeItem = inits[k]; - if ( planeItem != NULL ) { - planeItem->AddRef(); - planeItems.push_back( const_cast( planeItem ) ); - } - } - C3D_ASSERT( !planeItems.empty() ); -} - - -//------------------------------------------------------------------------------ -// дать все объекты -// --- -template -void MbPlaneInstance::GetItems( PlaneItems & items ) const -{ - size_t addCnt = planeItems.size(); - items.reserve( items.size() + addCnt ); - SPtr item_i; - for ( size_t i = 0; i < addCnt; ++i ) { - if ( planeItems[i] != NULL ) { - item_i = planeItems[i]; - items.push_back( item_i ); - } - } -} - - -#endif // __PLANE_INSTANCE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Вставка двумерного объекта. + \en Instance of a two-dimensional object. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __PLANE_INSTANCE_H +#define __PLANE_INSTANCE_H + + +#include +#include +#include +#include + + +class MATH_CLASS MbPlaneInstance; +namespace c3d // namespace C3D +{ +typedef SPtr PInstanceSPtr; +typedef SPtr ConstPInstanceSPtr; + +typedef std::vector PInstancesVector; +typedef std::vector ConstPInstancesVector; + +typedef std::vector PInstancesSPtrVector; +typedef std::vector ConstPInstancesSPtrVector; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Вставка двумерных объектов эскиза. + \en Instance of a two-dimensional sketch objects. \~ + \details \ru Вставка позволяет работать с двумерными геометрическими объектами как с объектом геометрической модели. + Двумерные геометрические объекты MbPlaneItem располагаются в плоскости XOY локальной системы координат MbPlacement3D.\n + \en The instance allows to deal with two-dimensional geometric objects as with object of geometric model. + Two-dimensional MbPlaneItem geometric objects located in XOY-plane of MbPlacement3D local coordinate system.\n \~ + \ingroup Model_Items +*/ +// --- +class MATH_CLASS MbPlaneInstance : public MbItem { +protected : + MbPlacement3D place; ///< \ru Локальная система координат объекта. \en Local coordinate system of the object. + std::vector planeItems; ///< \ru Множество двумерных объектов эскиза. \en A set of two-dimensional sketch objects. + +protected : + /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. + explicit MbPlaneInstance( const MbPlaneInstance &, MbRegDuplicate * ); +public : + /// \ru Конструктор по локальной системе координат без двумерных объектов. \en Constructor by a local coordinate system without two-dimensional objects. + MbPlaneInstance( const MbPlacement3D & ); + /// \ru Конструктор по двумерному объекту (используется оригинал) и локальной системе координат. \en Constructor by two-dimensional object (original is used) and a local coordinate system. + MbPlaneInstance( const MbPlaneItem &, const MbPlacement3D & ); + /// \ru Конструктор по двумерным объектам (используются оригиналы) и локальной системе координат. \en Constructor by two-dimensional object (originals are used) and a local coordinate system. + template + MbPlaneInstance( const MbPlacement3D &, const PlaneItems & ); + /// \ru Деструктор \en Destructor + virtual ~MbPlaneInstance(); + +public : + VISITING_CLASS( MbPlaneInstance ); + + // \ru Общие функции геометрического объекта. \en Common functions of a geometric object. + + virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en A type of an object. + virtual MbSpaceItem & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Создать копию. \en Create a copy. + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвинуть вдоль вектора. \en Translate along a vector. + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. + virtual bool IsSame ( const MbSpaceItem &, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Are the objects equal? + virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными? \en Are the objects similar? + virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать объекты равными. \en Make the objects equal. + virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. + virtual void AddYourGabaritTo( MbCube & ) const; // \ru Добавь свой габарит в куб. \en Add bounding box into a cube. + virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate bounding box in the local coordinate system. + virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. + + virtual MbProperty & CreateProperty( MbePrompt ) const; // \ru Создать собственное свойство. \en Create a custom property. + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. + virtual void GetBasisPoints( MbControlData & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + // \ru Получить локальную систему координат объекта. \en Get the local coordinate system of an object. + virtual bool GetPlacement( MbPlacement3D & ) const; + // \ru Установить локальную систему координат объекта. \en Set the local coordinate system of an object. + virtual bool SetPlacement( const MbPlacement3D & ); + + // \ru Создать полигональный объект - упрощенную копию данного объекта. \en Create a polygonal object - a simplified copy of the given object. + virtual MbItem * CreateMesh( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * iReg ) const; + // \ru Найти объект по геометрическому объекту (MbPlaneItem). \en Find the object by a geometric object (MbSpaceItem). + virtual const MbItem * FindItem( const MbPlaneItem * s, MbPath & path, MbMatrix3D & from ) const; + + /** \ru \name Общие функции вставки двумерного объекта. + \en \name Common functions of instance of two-dimensional object. + \{ */ + /// \ru Выдать число двумерных геометрических объектов. \en Get number of two-dimensional geometric objects. + size_t PlaneItemsCount() const; + /// \ru Выдать двумерный геометрический объект. \en Get two-dimensional geometric object. + const MbPlaneItem * GetPlaneItem( size_t ind = 0 ) const; + /// \ru Выдать двумерный геометрический объект для возможного редактирования. \en Get two-dimensional object for possible editing. + MbPlaneItem * SetPlaneItem( size_t ind = 0 ); + /// \ru Заменить двумерный геометрический объект. \en Replace two-dimensional geometric object. + bool SetPlaneItem( MbPlaneItem * init, size_t ind = 0 ); + /// \ru Добавить двумерный геометрический объект. \en Add two-dimensional geometric object. The method returns the index of added or existing object in MbPlaneInstance (the method returns SYS_MAX_T if the object is NULL). + size_t AddPlaneItem( MbPlaneItem * init ); + /// \ru Метод возвращает индекс двумерного геометрического объекта. \en The method returns the index of two-dimensional geometric object in MbPlaneInstance (the method returns SYS_MAX_T if the object was not finded). + size_t GetIndex( MbPlaneItem * init ); + /// \ru Выдать локальную систему координат объекта. \en Get the local coordinate system of an object. + const MbPlacement3D & GetPlacement() const { return place; } + /// \ru Выдать локальную систему координат объекта для редактирования. \en Get the local coordinate system of an object for editing. + MbPlacement3D & SetPlacement() { return place; } + + /// \ru Преобразовать двумерный объект согласно матрице. \en Transform two-dimensional object according to the matrix. + void Transform( const MbMatrix &, MbRegTransform * iReg = NULL ); + /// \ru Сдвинуть двумерный объект вдоль вектора. \en Translate two-dimensional object along a vector. + void Move ( const MbVector &, MbRegTransform * iReg = NULL ); + /// \ru Повернуть двумерный объект вокруг точки на заданный угол. \en Rotate two-dimensional object at a given angle around an axis. + void Rotate ( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * iReg = NULL ); + /// \ru Повернуть двумерный объект вокруг точки на заданный угол. \en Rotate two-dimensional object at a given angle around an axis. + void Rotate ( const MbCartPoint & pnt, double angle, MbRegTransform * iReg = NULL ); + + /// \ru Удалить все объекты эскиза. \en Delete all the sketch items. + void DeleteItems(); + /// \ru Удалить объект эскиза. \en Delete the sketch item by index. + bool DeleteItem( size_t ind ); + /// \ru Удалить объект эскиза. \en Delete the sketch item by index. + MbPlaneItem * DetachItem( size_t ind ); + + /** \brief \ru Заменить объект. + \en Replace an item. \~ + \details \ru Заменить объект новым. + \en Replace an item by a new one. \~ + \param[in] item - \ru Заменяемый объект. + \en An item to be replaced. \~ + \param[in] newItem - \ru Новый объект. + \en A new item. \~ + \return \ru Возвращает true, если замена была выполнена. + \en Returns true if the replacement has been performed. \~ + */ + bool ReplaceItem( const MbPlaneItem & item, MbPlaneItem & newItem ); + + /// \ru Выдать все объекты. \en Get all the items. + template + void GetItems( PlaneItems & ) const; + + /** \} */ + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbPlaneInstance ) +OBVIOUS_PRIVATE_COPY( MbPlaneInstance ) +}; + +IMPL_PERSISTENT_OPS( MbPlaneInstance ); + + +//------------------------------------------------------------------------------ +// Конструктор +// --- +template +inline MbPlaneInstance::MbPlaneInstance( const MbPlacement3D & p, const PlaneItems & inits ) + : MbItem ( ) + , place ( p ) + , planeItems( ) +{ + size_t addCnt = inits.size(); + planeItems.reserve( addCnt ); + for ( size_t k = 0; k < addCnt; ++k ) { + const MbPlaneItem * planeItem = inits[k]; + if ( planeItem != NULL ) { + planeItem->AddRef(); + planeItems.push_back( const_cast( planeItem ) ); + } + } + C3D_ASSERT( !planeItems.empty() ); +} + + +//------------------------------------------------------------------------------ +// дать все объекты +// --- +template +void MbPlaneInstance::GetItems( PlaneItems & items ) const +{ + size_t addCnt = planeItems.size(); + items.reserve( items.size() + addCnt ); + SPtr item_i; + for ( size_t i = 0; i < addCnt; ++i ) { + if ( planeItems[i] != NULL ) { + item_i = planeItems[i]; + items.push_back( item_i ); + } + } +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Набор ошибок - результат диагностики эскиза. + \en A set of errors is the result of a sketch diagnosis. \~ + \details \ru Набор ошибок и соответствующих объектов эскиза - результат диагностики эскиза. \n + \en A set of errors and corresponding sketch objects is the result of sketch diagnostics. \n \~ + \ingroup Data_Structures +*/ +// --- +class MATH_CLASS MbSketchErrors { +private: + std::multimap> errors; ///< \ru Набор ошибок ошибок. \en Set of errors. + +public: + /// \ru Конструктор. \en Constructor. + MbSketchErrors() {} + +public: + /// \ru Добавить к набору тип ошибки и соответствующие объекты эскиза. \en Add error type and corresponding sketch objects to the set. + void AddError( MbResultType errorType, std::vector & items ) { + errors.insert( std::pair< MbResultType, std::vector >( errorType, items ) ); + } + + /// \ru Есть ли ошибка типа errorType. \en Is there any error like errorType. + bool IsAnyError( MbResultType errorType ) const { return errors.find( errorType ) != errors.end(); } + + /// \ru Дать набор объектов по типу ошибки. \en Give a set of objects by type of error. + void GetItemsByErrorType( MbResultType errorType, std::vector & items ) const; + + /// \ru Дать набор пар пересекающихся контуров. \en Give a set of pairs of intersecting contours. + void GetIntersectedItemsPairs( std::vector> & items ) const; +}; + + +//------------------------------------------------------------------------------- +/** \brief \ru Параметры диагностики эскиза. + \en Sketch diagnostic parameters. \~ + \details \ru Параметры диагностики эскиза с набором необходимых проверок. По умолчанию проводятся все проверки. \n + Возможные проверки: \n + - проверка контуров на взаимное пересечение (rt_Intersection), \n + - самопересечение (rt_SelfIntersection), \n + - поиск разрывов (rt_ContourGapError), \n + - поиск наложений (rt_ContourSegmentsOverlapError), \n + - соответствие флага замкнутости и фактической замкнутости (rt_MustBeOpen, rt_MustBeClosed), \n + - проверка гладкой стыковки сегментов по касательной ( G1 ) с указанной угловой погрешностью (rt_ContourSegmentsNoTangentJoint). \n + \en Sketch diagnostic parameters with a set of necessary checks. By default, all checks are performed. \n + Possible checks: \n + - checking contours for intersections (rt_Intersection), \n + - self-intersections (rt_SelfIntersection), \n + - searching for gaps (rt_ContourGapError), \n + - searching overlaps (rt_ContourSegmentsOverlapError), \n + - correspondence of the flag of closure and actual closure (rt_MustBeOpen, rt_MustBeClosed), \n + - check of tangent smooth joining of segments ( G1 ) with the specified angular tolerance (rt_ContourSegmentsNoTangentJoint). \n \~ + \ingroup Data_Structures +*/ +// --- +class MATH_CLASS MbSketchCheckParameters { +private: + std::set checkedErrors; ///< \ru Набор проверок. При пустом контейнере выполняются все проверки. \en A set of checks. If the container is empty, all checks are performed. + double metricEpsilon; ///< \ru Метрическая погрешность. \en Metric tolerance. + double angleEpsilon; ///< \ru Угловая погрешность. \en Angular tolerance. + +public: + /// \ru Конструктор. \en Constructor. + MbSketchCheckParameters() + : metricEpsilon( Math::metricEpsilon ) + , angleEpsilon ( Math::angleEpsilon ) + {} + + /// \ru Можно ли задать этот тип проверки? \en Can I set this type of check? + bool CanSetThisCheckType( MbResultType errorType ) const { + return errorType == rt_InvalidType || + errorType == rt_SelfIntersection || + errorType == rt_Intersection || + errorType == rt_ContourGapError || + errorType == rt_ContourSegmentsOverlapError || + errorType == rt_MustBeOpen || + errorType == rt_MustBeClosed || + errorType == rt_ContourSegmentsNoTangentJoint; + } + + /// \ru Добавить тип проверки эскиза. \en Add a sketch verification type. + void AddCheckType( MbResultType errorType ) { + if ( CanSetThisCheckType( errorType ) ) + checkedErrors.insert( errorType ); + } + + /// \ru Установить метрическую погрешность. \en Set metric tolerance. + void SetMetricEpsilon( double mEpsilon ) { metricEpsilon = mEpsilon; } + /// \ru Установить угловую погрешность. \en Set angular tolerance. + void SetAngleEpsilon( double aEpsilon ) { angleEpsilon = aEpsilon; } + + /// \ru Проверять ли на наличие ошибки типа errorType. \en Do I need to check for errors like errorType. + bool IsErrorTypeSetToCheck( MbResultType errorType ) const { + if ( checkedErrors.empty() ) + return CanSetThisCheckType( errorType ); + else + return std::find( checkedErrors.begin(), checkedErrors.end(), errorType ) != checkedErrors.end(); + } + + /// \ru Выдать метрическую погрешность. \en Get metric tolerance. + double GetMetricEpsilon() const { return metricEpsilon; } + /// \ru Выдать угловую погрешность. \en Get angular tolerance. + double GetAngleEpsilon() const { return angleEpsilon; } +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить эскиз. + \en Check sketch. \~ + \details \ru Диагностика эскиза. Проверка контуров на взаимное пересечение (rt_Intersection), + самопересечение (rt_SelfIntersection), + поиск разрывов (rt_ContourGapError) и наложений (rt_ContourSegmentsOverlapError), + соответствие флага замкнутости и фактической замкнутости (rt_MustBeOpen, rt_MustBeClosed). + \en Sketch diagnostics. Checking contours for intersections (rt_Intersection), + self-intersections (rt_SelfIntersection), + searching for gaps (rt_ContourGapError) and overlaps (rt_ContourSegmentsOverlapError), + correspondence of the flag of closure and actual closure (rt_MustBeOpen, rt_MustBeClosed). \~ + \param[in] planeInstance - \ru Эскиз, содержащий набор контуров. + \en A sketch containing a set of contours. \~ + \param[in] parameters - \ru Параметры проверки. + \en Parameters of check. \~ + \param[out] errors - \ru Набор ошибок. + \en Errors set. \~ +*/ +// --- +MATH_FUNC( void ) CheckSketch( const MbPlaneInstance & planeInstance, + const MbSketchCheckParameters & parameters, + MbSketchErrors & errors ); + + +#endif // __PLANE_INSTANCE_H diff --git a/C3d/Include/plane_item.h b/C3d/Include/plane_item.h index 4286009..a4e3fb7 100644 --- a/C3d/Include/plane_item.h +++ b/C3d/Include/plane_item.h @@ -390,7 +390,7 @@ public : The function sets a flag that allow to write the object once and to use the references to the recorded instance in the other records. Reading is performed once too, in other cases of reading the address of the already read object is used. \~ */ - void PrepareWrite() { SetRegistrable( (GetUseCount() > 1) ? registrable : noRegistrable ); } + void PrepareWrite() const { SetRegistrable( (GetUseCount() > 1) ? registrable : noRegistrable ); } DECLARE_PERSISTENT_CLASS( MbPlaneItem ) OBVIOUS_PRIVATE_COPY( MbPlaneItem ) diff --git a/C3d/Include/point3d.h b/C3d/Include/point3d.h index 6f37724..0f0479a 100644 --- a/C3d/Include/point3d.h +++ b/C3d/Include/point3d.h @@ -1,101 +1,101 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Пространственная точка со свойствами геометрического объекта. - \en Spatial point with properties of geometric object. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __POINT3D_H -#define __POINT3D_H - - -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Трёхмерная точка со свойствами геометрического объекта. - \en Three-dimensional point with properties of geometric object. \~ - \details \ru Точка cодержит трехмерную точку MbCartPoint3D и является - наследником геометрического объекта в пространстве.\n - \en The point contains three-dimensional point MbCartPoint3D and also is - the inheritor of a geometric object in space.\n \~ - \ingroup Point_3D -*/ -// --- -class MATH_CLASS MbPoint3D : public MbSpaceItem { -protected: - MbCartPoint3D point; ///< \ru Трехмерная точка. \en Three-dimensional point. - -private: - /// \ru Конструктор копирования. \en Copy-constructor. - MbPoint3D ( const MbPoint3D & ); -public: - /// \ru Конструктор. \en Constructor. - MbPoint3D (); - /// \ru Конструктор по точке. \en Constructor by point. - MbPoint3D ( const MbCartPoint3D & ); - /// \ru Конструктор по координатам. \en Constructor by coordinates. - MbPoint3D ( double x, double y, double z ); - /// \ru Деструктор. \en Destructor. - virtual ~MbPoint3D (); - -public: - - // \ru Общие функции геометрического объекта. \en Common functions of a geometric object. - - virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en A type of an object. - virtual MbeSpaceType Type() const; // \ru Групповой тип объекта. \en Group type of object. - virtual MbeSpaceType Family() const; // \ru Семейство объекта. \en Family of object. - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Создать копию. \en Create a copy. - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвинуть вдоль вектора. \en Translate along a vector. - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Are the objects equal? - virtual bool SetEqual ( const MbSpaceItem & init ); // \ru Сделать объекты равным. \en Make the objects equal. - virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. - virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Добавь свой габарит в куб. \en Add bounding box into a cube. - virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate bounding box in the local coordinate system. - virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. - - virtual MbProperty & CreateProperty( MbePrompt n ) const; // \ru Создать собственное свойство. \en Create a custom property. - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & properties ); // \ru Установить свойства объекта. \en Set properties of the object. - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - /** \ru \name Функции точки. - \en \name Functions of point. - \{ */ - /// \ru Выдать декартову точку. \en Get Cartesian point. - void GetCartPoint( MbCartPoint3D & ) const; - /// \ru Выдать декартову точку. \en Get Cartesian point. - const MbCartPoint3D & GetCartPoint() const { return point; } - /// \ru Выдать декартову точку для возможного редактирования. \en Get Cartesian point for possible editing. - MbCartPoint3D & SetCartPoint() { return point; } - /// \ru Проверить равенство с другой точкой. \en Check for equality with another point. - bool operator == ( const MbPoint3D & ) const; - /// \ru Проверить на неравенство с другой точкой. \en Check for inequality with another point. - bool operator != ( const MbPoint3D & ) const; - /// \ru Инициализировать точку по другой точке. \en Initialize point by another point. - void Init( const MbPoint3D & init ) { point.Init(init.point); } - /// \ru Инициализировать точку по другой точке. \en Initialize point by another point. - void Init( const MbCartPoint3D & init ) { point.Init(init); } - /// \ru Инициализировать точку по координатам. \en Initialize point by coordinates. - void Init( double xx, double yy, double zz ) { point.Init(xx,yy,zz); } - /// \ru Обнуление координат. \en Set coordinates to zero. - void SetZero() { point.SetZero(); } - /** \} */ - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. - void operator = ( const MbPoint3D & ); - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbPoint3D ) -}; // MbPoint3D - -IMPL_PERSISTENT_OPS( MbPoint3D ) - -#endif // __POINT3D_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Пространственная точка со свойствами геометрического объекта. + \en Spatial point with properties of geometric object. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __POINT3D_H +#define __POINT3D_H + + +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Трёхмерная точка со свойствами геометрического объекта. + \en Three-dimensional point with properties of geometric object. \~ + \details \ru Точка cодержит трехмерную точку MbCartPoint3D и является + наследником геометрического объекта в пространстве.\n + \en The point contains three-dimensional point MbCartPoint3D and also is + the inheritor of a geometric object in space.\n \~ + \ingroup Point_3D +*/ +// --- +class MATH_CLASS MbPoint3D : public MbSpaceItem { +protected: + MbCartPoint3D point; ///< \ru Трехмерная точка. \en Three-dimensional point. + +private: + /// \ru Конструктор копирования. \en Copy-constructor. + MbPoint3D ( const MbPoint3D & ); +public: + /// \ru Конструктор. \en Constructor. + MbPoint3D (); + /// \ru Конструктор по точке. \en Constructor by point. + MbPoint3D ( const MbCartPoint3D & ); + /// \ru Конструктор по координатам. \en Constructor by coordinates. + MbPoint3D ( double x, double y, double z ); + /// \ru Деструктор. \en Destructor. + virtual ~MbPoint3D (); + +public: + + // \ru Общие функции геометрического объекта. \en Common functions of a geometric object. + + virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en A type of an object. + virtual MbeSpaceType Type() const; // \ru Групповой тип объекта. \en Group type of object. + virtual MbeSpaceType Family() const; // \ru Семейство объекта. \en Family of object. + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Создать копию. \en Create a copy. + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвинуть вдоль вектора. \en Translate along a vector. + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Are the objects equal? + virtual bool SetEqual ( const MbSpaceItem & init ); // \ru Сделать объекты равным. \en Make the objects equal. + virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. + virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Добавь свой габарит в куб. \en Add bounding box into a cube. + virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate bounding box in the local coordinate system. + virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. + + virtual MbProperty & CreateProperty( MbePrompt n ) const; // \ru Создать собственное свойство. \en Create a custom property. + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & properties ); // \ru Установить свойства объекта. \en Set properties of the object. + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + /** \ru \name Функции точки. + \en \name Functions of point. + \{ */ + /// \ru Выдать декартову точку. \en Get Cartesian point. + void GetCartPoint( MbCartPoint3D & ) const; + /// \ru Выдать декартову точку. \en Get Cartesian point. + const MbCartPoint3D & GetCartPoint() const { return point; } + /// \ru Выдать декартову точку для возможного редактирования. \en Get Cartesian point for possible editing. + MbCartPoint3D & SetCartPoint() { return point; } + /// \ru Проверить равенство с другой точкой. \en Check for equality with another point. + bool operator == ( const MbPoint3D & ) const; + /// \ru Проверить на неравенство с другой точкой. \en Check for inequality with another point. + bool operator != ( const MbPoint3D & ) const; + /// \ru Инициализировать точку по другой точке. \en Initialize point by another point. + void Init( const MbPoint3D & init ) { point.Init(init.point); } + /// \ru Инициализировать точку по другой точке. \en Initialize point by another point. + void Init( const MbCartPoint3D & init ) { point.Init(init); } + /// \ru Инициализировать точку по координатам. \en Initialize point by coordinates. + void Init( double xx, double yy, double zz ) { point.Init(xx,yy,zz); } + /// \ru Обнуление координат. \en Set coordinates to zero. + void SetZero() { point.SetZero(); } + /** \} */ + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. + void operator = ( const MbPoint3D & ); + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbPoint3D ) +}; // MbPoint3D + +IMPL_PERSISTENT_OPS( MbPoint3D ) + +#endif // __POINT3D_H diff --git a/C3d/Include/point_frame.h b/C3d/Include/point_frame.h index 4d9894e..e930442 100644 --- a/C3d/Include/point_frame.h +++ b/C3d/Include/point_frame.h @@ -21,6 +21,7 @@ #include class MATH_CLASS MbPointFrame; +class MATH_CLASS MbPoint3D; namespace c3d // namespace C3D @@ -58,6 +59,8 @@ public: explicit MbPointFrame( const MbVertex &, bool same ); /// \ru Конструктор по точке. \en Constructor by point. explicit MbPointFrame( const MbCartPoint3D & ); + /// \ru Конструктор по точке. \en Constructor by point. + explicit MbPointFrame( const MbPoint3D & ); /// \ru Конструктор по координатам. \en Constructor by coordinates. MbPointFrame( double x, double y, double z ); /// \ru Конструктор по массиву вершин и флагу использования этих объектов, а не их копий. \en Constructor by array of vertices and by flag of use of these objects instead of their copies. @@ -108,7 +111,7 @@ public: virtual MbProperty & CreateProperty( MbePrompt n ) const; // \ru Создать собственное свойство. \en Create a custom property. virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. virtual void SetProperties( const MbProperties & ); // \ru Установить свойства объекта. \en Set properties of the object. - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты. \en Get the basis objects. + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты. \en Get the basis objects. virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. @@ -126,12 +129,43 @@ public: size_t GetVerticesCount() const { return vertices.size(); } /// \ru Получить вершину по индексу. \en Get vertex by an index. const MbVertex * GetVertex( size_t k ) const { return ((k < vertices.size()) ? vertices[k] : NULL ); } - /// \ru Получить вершину по индексу для модификации. \en Get vertex by an index for modification. - MbVertex * GetVertex( size_t k ) { return ((k < vertices.size()) ? vertices[k] : NULL ); } + /// \ru Получить вершину по индексу для возможного редактирования. \en Get vertex by an index for the possible editing. + MbVertex * SetVertex( size_t k ) { return ((k < vertices.size()) ? vertices[k] : NULL ); } + + /// \ru Получить вершины. \en Get vertices. + template + void GetVertices( VerticesVector & dstVertices ) const + { + if ( !vertices.empty() ) { + size_t addCnt = vertices.size(); + dstVertices.reserve( dstVertices.size() + addCnt ); + c3d::ConstVertexSPtr vertex; + for ( size_t k = 0; k < addCnt; ++k ) { + vertex = vertices[k]; + dstVertices.push_back( vertex ); + } + } + } + /// \ru Получить вершины для возможного редактирования. \en Get vertices for the possible editing. + template + void SetVertices( VerticesVector & dstVertices ) + { + if ( !vertices.empty() ) { + size_t addCnt = vertices.size(); + dstVertices.reserve( dstVertices.size() + addCnt ); + c3d::VertexSPtr vertex; + for ( size_t k = 0; k < addCnt; ++k ) { + vertex = vertices[k]; + dstVertices.push_back( vertex ); + } + } + } /// \ru Добавить вершину по точке. \en Add vertex by point. void AddVertex( const MbCartPoint3D & ); - /// \ru Добавить вершину или ее копию, что определяется флагом использования самого объекта. \en Add vertex or its copy (defined by flag of use of object). - void AddVertex( const MbVertex &, bool same ); + /// \ru Добавить вершину или ее копию, что определяется флагом. \en Add vertex or its copy (defined by flag). + void AddVertex( const MbVertex &, bool sameVertex ); + /// \ru Добавить вершины или их копию, что определяется флагом. \en Add vertices or theirs copies (defined by flag). + bool AddVertices( const MbPointFrame &, bool sameVertices ); /// \ru Вставить вершину по номеру и точке. \en Insert vertex by index and point. void InsertVertex( size_t k, const MbCartPoint3D & ); /// \ru Вставить по номеру вершину или ее копию, что определяется флагом использования самого объекта. \en Insert vertex or its copy (defined by flag of use of object) by index. diff --git a/C3d/Include/position_data.h b/C3d/Include/position_data.h index efd0f69..9e729a0 100644 --- a/C3d/Include/position_data.h +++ b/C3d/Include/position_data.h @@ -1,188 +1,188 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Данные для размеров операции. - \en Data for operation dimensions. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __POSITION_DATA_H -#define __POSITION_DATA_H - - -#include -#include -#include -#include - - -class MATH_CLASS MbCurveEdge; - - -//------------------------------------------------------------------------------ -/** \brief \ru Данные для размеров операции. - \en Data for operation dimensions. \~ - \details \ru Данные для позиционирования размеров операции скругления и фаски рёбер. \n - \en Data for positioning dimensions of operations of fillet and chamfer of edges. \n \~ - \ingroup Data_Structures -*/ -// --- -struct MATH_CLASS MbPositionData { -public: - MbCartPoint3D point1; ///< \ru Опорная точка размера. \en Support point of dimension. - MbCartPoint3D point2; ///< \ru Опорная точка размера. \en Support point of dimension. - MbCartPoint3D origin; ///< \ru Начальна точка или центр. \en Start point or center. - MbVector3D normal; ///< \ru Нормаль плоскости размера. \en Plane normal of dimension. - double param; ///< \ru Положение размера в процентах длины ребра. \en Position of dimension in percentage of edge length. - MbName itemName; ///< \ru Имя объекта. \en A name of an object. - -public: - /// \ru Пустой конструктор. \en Empty constructor. - MbPositionData() - : point1() - , point2() - , origin() - , normal( 0.0, 0.0, 1.0 ) - , param (0.5) - , itemName() - {} - /// \ru Конструктор по толщинам и замкнутости. \en Constructor by thicknesses and closedness. - MbPositionData( const MbCartPoint3D & p1, const MbCartPoint3D & p2, const MbCartPoint3D & _or, - const MbVector3D & nor, double t, const MbName & n ) - : point1( p1 ) - , point2( p2 ) - , origin( _or ) - , normal( nor ) - , param ( t ) - , itemName( n ) - {} - /// \ru Конструктор копирования. \en Copy-constructor. - MbPositionData( const MbPositionData & other ) - : point1( other.point1 ) - , point2( other.point2 ) - , origin( other.origin ) - , normal( other.normal ) - , param ( other.param ) - , itemName( other.itemName ) - {} - /// \ru Деструктор. \en Destructor. - ~MbPositionData() {} - /// \ru Функция копирования данных. \en Copy function of data. - void Init( const MbPositionData & other ) { - point1 = other.point1; - point2 = other.point2; - origin = other.origin; - normal = other.normal; - param = other.param; - itemName.SetName( other.itemName ); - } - - /// \ru Первая контрольная точка. \en The first control point. - const MbCartPoint3D & GetPoint1() const { return point1; } - /// \ru Вторая контрольная точка. \en The second control point. - const MbCartPoint3D & GetPoint2() const { return point2; } - /// \ru Начальная точка. \en The starting point. - const MbCartPoint3D & GetOrigin() const { return origin; } - /// \ru Нормаль плоскости размера. \en Plane normal of dimension. - const MbVector3D & Normal() const { return normal; } - /// \ru Положение размера в процентах длины ребра. \en Position of dimension in percentage of edge length. - const double & GetParam() const { return param; } - /// \ru Имя объекта. \en A name of an object. - const MbName & GetName() const { return itemName; } - /// \ru Оператор присваивания. \en Assignment operator. - void operator = ( const MbPositionData & other ) { - point1 = other.point1; - point2 = other.point2; - origin = other.origin; - normal = other.normal; - param = other.param; - itemName.SetName( other.itemName ); - } - - KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbPositionData, MATH_FUNC_EX ); - KNOWN_OBJECTS_RW_PTR_OPERATORS_EX( MbPositionData, MATH_FUNC_EX ); - DECLARE_NEW_DELETE_CLASS( MbPositionData ) - DECLARE_NEW_DELETE_CLASS_EX( MbPositionData ) -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Последовательность рёбер. - \en Sequence of edges. \~ - \details \ru Последовательность гладко стыкующихся рёбер, скругляемых одновременно. \n - \en Sequence of smooth mating edges rounded at the same time. \n \~ - \ingroup Data_Structures -*/ -// --- -struct MATH_CLASS MbEdgeSequence { -public: - RPArray edges; ///< \ru Рёбра последовательности. \en Edges of sequence. - SArray sense; ///< \ru Направленность рёбер в последовательности. \en Direction of edges in the sequence. - bool closed; ///< \ru Замкнутость последовательности. \en Closedness of sequence. - -public: - /// \ru Пустой конструктор. \en Empty constructor. - MbEdgeSequence() - : edges( 0, 1 ) - , sense( 0, 1 ) - , closed( false ) - {} - /// \ru Конструктор по толщинам и замкнутости. \en Constructor by thicknesses and closedness. - MbEdgeSequence( const MbCurveEdge & edge, bool s, bool c ) - : edges( 1, 1 ) - , sense( 1, 1 ) - , closed( c ) - { - edges.Add( &edge ); - sense.Add( s ); - } - /// \ru Конструктор копирования. \en Copy-constructor. - MbEdgeSequence( const MbEdgeSequence & other ) - : edges( other.edges.Count(), 1 ) - , sense( other.sense ) - , closed( other.closed ) - { - edges.AddArray( other.edges ); - } - /// \ru Деструктор. \en Destructor. - ~MbEdgeSequence() {} - /// \ru Функция копирования данных. \en Copy function of data. - void Init( const MbEdgeSequence & other ) { - edges.DetachAll(); - sense.Flush(); - edges.AddArray( other.edges ); - sense = other.sense; - closed = other.closed; - } - /// \ru Зарезервировать место под столько элементов. \en Reserve space for a given count of elements. - void Reserve( size_t count ) { - edges.Reserve( count ); - sense.Reserve( count ); - } - /// \ru Добавить ребро. \en Add an edge. - void AddEdge( const MbCurveEdge & edge, bool s ) { - edges.Add( &edge ); - sense.Add( s ); - } - /// \ru Добавить в последовательность все гладко стыкующиеся с ней ребра. \en Add all smooth mating edges to a sequence. - bool CollectEdges( double epsilon ); - /// \ru Установить замкнутость. \en Set closedness. - void SetClosed( bool c ) { closed = c; } - - const MbCurveEdge * Edge ( size_t i ) const { return ( i < edges.Count() ) ? edges[i] : NULL; } - const bool Sense( size_t i ) const { return ( i < sense.Count() ) ? sense[i] : false; } - size_t Count() const { return edges.Count(); } - /// \ru Оператор присваивания. \en Assignment operator. - void operator = ( const MbEdgeSequence & other ) { - Init( other ); - } - - KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbEdgeSequence, MATH_FUNC_EX ); - KNOWN_OBJECTS_RW_PTR_OPERATORS_EX( MbEdgeSequence, MATH_FUNC_EX ); - DECLARE_NEW_DELETE_CLASS( MbEdgeSequence ) - DECLARE_NEW_DELETE_CLASS_EX( MbEdgeSequence ) -}; - - -#endif // __POSITION_DATA_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Данные для размеров операции. + \en Data for operation dimensions. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __POSITION_DATA_H +#define __POSITION_DATA_H + + +#include +#include +#include +#include + + +class MATH_CLASS MbCurveEdge; + + +//------------------------------------------------------------------------------ +/** \brief \ru Данные для размеров операции. + \en Data for operation dimensions. \~ + \details \ru Данные для позиционирования размеров операции скругления и фаски рёбер. \n + \en Data for positioning dimensions of operations of fillet and chamfer of edges. \n \~ + \ingroup Data_Structures +*/ +// --- +struct MATH_CLASS MbPositionData { +public: + MbCartPoint3D point1; ///< \ru Опорная точка размера. \en Support point of dimension. + MbCartPoint3D point2; ///< \ru Опорная точка размера. \en Support point of dimension. + MbCartPoint3D origin; ///< \ru Начальна точка или центр. \en Start point or center. + MbVector3D normal; ///< \ru Нормаль плоскости размера. \en Plane normal of dimension. + double param; ///< \ru Положение размера в процентах длины ребра. \en Position of dimension in percentage of edge length. + MbName itemName; ///< \ru Имя объекта. \en A name of an object. + +public: + /// \ru Пустой конструктор. \en Empty constructor. + MbPositionData() + : point1() + , point2() + , origin() + , normal( 0.0, 0.0, 1.0 ) + , param (0.5) + , itemName() + {} + /// \ru Конструктор по толщинам и замкнутости. \en Constructor by thicknesses and closedness. + MbPositionData( const MbCartPoint3D & p1, const MbCartPoint3D & p2, const MbCartPoint3D & _or, + const MbVector3D & nor, double t, const MbName & n ) + : point1( p1 ) + , point2( p2 ) + , origin( _or ) + , normal( nor ) + , param ( t ) + , itemName( n ) + {} + /// \ru Конструктор копирования. \en Copy-constructor. + MbPositionData( const MbPositionData & other ) + : point1( other.point1 ) + , point2( other.point2 ) + , origin( other.origin ) + , normal( other.normal ) + , param ( other.param ) + , itemName( other.itemName ) + {} + /// \ru Деструктор. \en Destructor. + ~MbPositionData() {} + /// \ru Функция копирования данных. \en Copy function of data. + void Init( const MbPositionData & other ) { + point1 = other.point1; + point2 = other.point2; + origin = other.origin; + normal = other.normal; + param = other.param; + itemName.SetName( other.itemName ); + } + + /// \ru Первая контрольная точка. \en The first control point. + const MbCartPoint3D & GetPoint1() const { return point1; } + /// \ru Вторая контрольная точка. \en The second control point. + const MbCartPoint3D & GetPoint2() const { return point2; } + /// \ru Начальная точка. \en The starting point. + const MbCartPoint3D & GetOrigin() const { return origin; } + /// \ru Нормаль плоскости размера. \en Plane normal of dimension. + const MbVector3D & Normal() const { return normal; } + /// \ru Положение размера в процентах длины ребра. \en Position of dimension in percentage of edge length. + const double & GetParam() const { return param; } + /// \ru Имя объекта. \en A name of an object. + const MbName & GetName() const { return itemName; } + /// \ru Оператор присваивания. \en Assignment operator. + void operator = ( const MbPositionData & other ) { + point1 = other.point1; + point2 = other.point2; + origin = other.origin; + normal = other.normal; + param = other.param; + itemName.SetName( other.itemName ); + } + + KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbPositionData, MATH_FUNC_EX ); + KNOWN_OBJECTS_RW_PTR_OPERATORS_EX( MbPositionData, MATH_FUNC_EX ); + DECLARE_NEW_DELETE_CLASS( MbPositionData ) + DECLARE_NEW_DELETE_CLASS_EX( MbPositionData ) +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Последовательность рёбер. + \en Sequence of edges. \~ + \details \ru Последовательность гладко стыкующихся рёбер, скругляемых одновременно. \n + \en Sequence of smooth mating edges rounded at the same time. \n \~ + \ingroup Data_Structures +*/ +// --- +struct MATH_CLASS MbEdgeSequence { +public: + RPArray edges; ///< \ru Рёбра последовательности. \en Edges of sequence. + SArray sense; ///< \ru Направленность рёбер в последовательности. \en Direction of edges in the sequence. + bool closed; ///< \ru Замкнутость последовательности. \en Closedness of sequence. + +public: + /// \ru Пустой конструктор. \en Empty constructor. + MbEdgeSequence() + : edges( 0, 1 ) + , sense( 0, 1 ) + , closed( false ) + {} + /// \ru Конструктор по толщинам и замкнутости. \en Constructor by thicknesses and closedness. + MbEdgeSequence( const MbCurveEdge & edge, bool s, bool c ) + : edges( 1, 1 ) + , sense( 1, 1 ) + , closed( c ) + { + edges.Add( &edge ); + sense.Add( s ); + } + /// \ru Конструктор копирования. \en Copy-constructor. + MbEdgeSequence( const MbEdgeSequence & other ) + : edges( other.edges.Count(), 1 ) + , sense( other.sense ) + , closed( other.closed ) + { + edges.AddArray( other.edges ); + } + /// \ru Деструктор. \en Destructor. + ~MbEdgeSequence() {} + /// \ru Функция копирования данных. \en Copy function of data. + void Init( const MbEdgeSequence & other ) { + edges.DetachAll(); + sense.Flush(); + edges.AddArray( other.edges ); + sense = other.sense; + closed = other.closed; + } + /// \ru Зарезервировать место под столько элементов. \en Reserve space for a given count of elements. + void Reserve( size_t count ) { + edges.Reserve( count ); + sense.Reserve( count ); + } + /// \ru Добавить ребро. \en Add an edge. + void AddEdge( const MbCurveEdge & edge, bool s ) { + edges.Add( &edge ); + sense.Add( s ); + } + /// \ru Добавить в последовательность все гладко стыкующиеся с ней ребра. \en Add all smooth mating edges to a sequence. + bool CollectEdges( double epsilon ); + /// \ru Установить замкнутость. \en Set closedness. + void SetClosed( bool c ) { closed = c; } + + const MbCurveEdge * Edge ( size_t i ) const { return ( i < edges.Count() ) ? edges[i] : NULL; } + const bool Sense( size_t i ) const { return ( i < sense.Count() ) ? sense[i] : false; } + size_t Count() const { return edges.Count(); } + /// \ru Оператор присваивания. \en Assignment operator. + void operator = ( const MbEdgeSequence & other ) { + Init( other ); + } + + KNOWN_OBJECTS_RW_REF_OPERATORS_EX( MbEdgeSequence, MATH_FUNC_EX ); + KNOWN_OBJECTS_RW_PTR_OPERATORS_EX( MbEdgeSequence, MATH_FUNC_EX ); + DECLARE_NEW_DELETE_CLASS( MbEdgeSequence ) + DECLARE_NEW_DELETE_CLASS_EX( MbEdgeSequence ) +}; + + +#endif // __POSITION_DATA_H diff --git a/C3d/Include/reference_item.h b/C3d/Include/reference_item.h index 9dbe037..f8f459c 100644 --- a/C3d/Include/reference_item.h +++ b/C3d/Include/reference_item.h @@ -1,1021 +1,1021 @@ -////////////////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Счетчик ссылок (владельцев объекта). - \en Reference counter (of an object owners). \~ - -*/ -////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef __REFERENCE_ITEM_H -#define __REFERENCE_ITEM_H - - -#include -#include -#include -#include -#include -#include -#include -//#include -#include - - -//---------------------------------------------------------------------------------------- -/** \brief \ru Типы объекта со счетчиком ссылок. - \en Types of object with reference counter. \~ - \details \ru Тип несёт информацию об объекте-наследнике. \n - \en Type has an information of inheritor object. \n \~ - \ingroup Geometric_Items -*/ -// --- -enum MbeRefType -{ - rt_RefItem = 0, ///< \ru Некоторый объект. \en Some object. - rt_PlaneItem, ///< \ru Двумерный геометрически объект. \en Two-dimensional geometric object. - rt_SpaceItem, ///< \ru Трехмерный геометрический объект. \en Three-dimensional geometric object. - rt_TopItem, ///< \ru Топологический объект. \en A topological object. - rt_Creator, ///< \ru Строитель объекта. \en Object constructor - rt_Attribute, ///< \ru Атрибут объекта. \en Attribute of an object. - rt_Primitive, ///< \ru Элемент полигонального объекта. \en Element of polygonal object. - // \ru В конец можно добавлять новые нужные \en It is possible to add new necessary ones to the end -}; - - -class MATH_CLASS MbRefItem; -namespace c3d // namespace C3D -{ -typedef SPtr RefItemSPtr; -typedef SPtr ConstRefItemSPtr; - -typedef std::vector RefItemsVector; -typedef std::vector ConstRefItemsVector; - -typedef std::vector RefItemsSPtrVector; -typedef std::vector ConstRefItemsSPtrVector; -} - -//---------------------------------------------------------------------------------------- -/** \brief \ru Объект с подсчетом ссылок. - \en Reference-counted object. \~ - \details \ru Объект, считающий количество своих владельцев. \n - Используется в качестве одного из родительских классов геометрических объектов. \n - Если наследник данного класса захватывается другим объектом или алгоритмом,то другой - объект или алгоритм должен увеличить счетчик ссылок на единицу методом AddRef(). - При отказе от использования наследника данного класса другим объектом (например, при деструктурировании) - или алгоритмом другой объект или алгоритм должны уменьшить счетчик ссылок на единицу - методом Release(). Такое правило позволяет использовать одного и того же наследника - данного класса несколькими другим объектами или алгоритмами одновременно и гарантирует, - что объект будет удалён, когда он станет никому не нужен.\n - - \en Object counting number of its owners. \n - Is used as one of parent classes of geometric objects. \n - If inheritor of current class is captured by other object or algorithm, then the other - object or algorithm has to increase reference counter by one by AddRef() method. - At refusal of use of the successor of this class by other object (for example at destruction) - or by algorithm, the other object or algorithm has to decrease reference counter - by one by Release() method. Such rule allows to use the same inheritor of current - class simultaneously by several other objects or algorithms and guarantees that the - object will be removed when it becomes unnecessary.\n \~ - - \note \ru Рекомендуется применение автоматических указателей типа SPtr к экземплярам - данного класса. Это упростит работу с кодом, где нужно позаботится об автоматической - сборке мусора. - \en It is recommended to use smart pointers of type SPtr to instances of - this class. This will simplify the work with the code where you want to take care of - the automatic garbage collection. \~ - \sa #SPtr - \ingroup Geometric_Items -*/ -// --- -class MATH_CLASS MbRefItem { -private: - mutable use_count_type useCount; ///< \ru Счетчик ссылок на объект, изменяемый владельцами объекта. \en A counter of references to an object modifiable by owners of object. -public: - /// \ru Конструктор без параметров. \en Constructor without parameters. - MbRefItem(); -protected: - virtual ~MbRefItem(); - -public: - /** \ru \name Функции регистрации ссылок на геометрический объект владельцами объекта. - \en \name Functions for registration of references to geometric object by owners of object. - \{ */ - /// \ru Выдать количество ссылок (выдать количество владельцев объекта). \en Get count of references (get count of owners of an object). - refcount_t GetUseCount() const; - /// \ru Увеличить количество ссылок на единицу. \en Increase count of references by one. - refcount_t AddRef() const; - /// \ru Уменьшить количество ссылок на единицу. \en Decrease count of references by one. - refcount_t DecRef() const; - /// \ru Уменьшить количество ссылок на единицу и, если количество ссылок стало равным нулю, удалить себя. \en Decrease count of references by one and if count of references became zero, then remove itself. - refcount_t Release() const; - /** \} */ -public: - /// \ru Регистрационный тип (для копирования, дублирования). \en Registration type (for copying, duplication). - virtual MbeRefType RefType() const; - -OBVIOUS_PRIVATE_COPY( MbRefItem ) -}; - - -//---------------------------------------------------------------------------------------- -/** \brief \ru Шаблон класса сериализации (порядковой нумерации создаваемых объектов). - \en Serialization class template (ordinal numbering of created objects). \~ - \details \ru Шаблон класса сериализации (порядковой нумерации создаваемых объектов). \n - Применение: наследовать от данного класса. - Аргумент шаблона позволяет иметь несколько сериализаций одновременно: \n - если нужна сериализация объектов класса T, можно записать class T: virtual public MbSerialItem \n - если объекты U должны иметь ту же сериализацию, можно записать class U: virtual public MbSerialItem \n - или же создать пустой класс для сериализации типа class MbBasicSerializer {} - и наследоваться от MbSerialItem. \n - - \en Serialization class template (ordinal numbering of created objects). \n - How to use: inherit from this class. \n - The template argument allows multiple serializations at the same time: \n - if you need serialization of objects of class T, you can write class T: virtual public MbSerialItem \n - if U objects must have the same serialization, you can write a class U: virtual public MbSerialItem \n - or you can create an empty class to serialize the class type MbBasicSerializer \n - and inherit it from MbSerialItem . \n \~ - - \ingroup Geometric_Items -*/ -// --- -template class MbSerialItem { -private: - static serial_type serialLast; ///< \ru Индекс последнего созданного объекта (0 в случае отсутствия объектов). \en Index of the last object created (0 if there are no objects). - static serial_type serialCount; ///< \ru Счетчик объектов. \en Counter of objects. - serial_type serialThis; ///< \ru Уникальный порядковый номер объекта, выдается при создании. В дальнейшем не меняется. \en The unique sequence number of the object, issued at creation. In the future it is not changed. -public: - MbSerialItem(); ///< \ru Конструктор. \en Constructor. -protected: - virtual ~MbSerialItem(); ///< \ru Деструктор. \en Destructor. -public: - size_t GetSerial() const { return SerialTypeValue( serialThis ); } ///< \ru Выдать порядковый номер объекта. \en Get object serial identifier. - -OBVIOUS_PRIVATE_COPY( MbSerialItem ) -}; - - -// \ru Инициализация статических переменных шаблона класса сериализации. \en Initializing static variables of the serialization class template. -template serial_type MbSerialItem::serialLast = 0; -template serial_type MbSerialItem::serialCount = 0; - - -//----------------------------------------------------------------------------- -// \ru Конструктор. \en Constructor. -// --- -template MbSerialItem::MbSerialItem() -{ // Set the serial number and promote the counters - PRECONDITION( serialLast != SYS_MAX_T ); // number overflow - serialThis = ++serialLast; - ++serialCount; -} - - -//----------------------------------------------------------------------------- -// \ru Деструктор. \en Destructor. -// --- -template MbSerialItem::~MbSerialItem() -{ - PRECONDITION( SerialTypeValue( serialCount ) != 0 ); // If it equals 0 then it means that the counter is damaged. - if ( --serialCount == 0 ) // The case when the last of the instantiated objects is deleted - serialLast = 0; // Set serialization to zero - at the subsequent creation of objects the numbering will start again (serially since 1). -} - - - -//------------------------------------------------------------------------------ -/// \ru Удалить объект без ссылок. \en Delete an object without references. -//--- -template -inline void DeleteMatItem( Type *& item ) -{ - if ( item != NULL ) { - delete item; - item = NULL; - } -} - - -//------------------------------------------------------------------------------ -/// \ru Удалить объекты без ссылок. \en Delete objects without references. -// --- -template -void DeleteMatItems( Vector & items ) -{ - for ( size_t k = 0, itemsCnt = items.size(); k < itemsCnt; ++k ) - ::DeleteMatItem( items[k] ); - items.clear(); -} - - -//------------------------------------------------------------------------------ -/// \ru Сделать копию, если объект используется, иначе вернуть оригинал. \en Create a copy if object is used, otherwise return original. -// --- -template -Type & DuplicateIfUsed( Type & item ) -{ - Type * resItem = &item; - if ( item.GetUseCount() > 0 ) // \ru Если оригинал, то делаем копию. \en If there is original, then make a copy. - resItem = static_cast( &item.Duplicate() ); - - return *resItem; -} - - -//------------------------------------------------------------------------------ -/// \ru Сделать копию, если объект используется, иначе вернуть оригинал. \en Create a copy if object is used, otherwise return original. -// --- -template -Type & DuplicateIfUsed( Type & item, RegType * iReg ) -{ - Type * resItem = &item; - if ( item.GetUseCount() > 0 ) // \ru Если оригинал, то делаем копию. \en If there is original, then make a copy. - resItem = static_cast( &item.Duplicate( iReg ) ); - - return *resItem; -} - - -//------------------------------------------------------------------------------ -/// \ru Сделать копию, если объект используется, иначе вернуть оригинал. \en Create a copy if object is used, otherwise return original. -// --- -template -Type * DuplicateIfUsed( SPtr & item ) -{ - if ( item == NULL ) - return NULL; - Type * resItem = item.get(); - if ( item->GetUseCount() > 1 ) // \ru Если оригинал, то делаем копию. \en If there is original, then make a copy. - resItem = static_cast( &item->Duplicate() ); - - return resItem; -} - - -//------------------------------------------------------------------------------ -/// \ru Сделать копию, если объект используется, иначе вернуть оригинал. \en Create a copy if object is used, otherwise return original. -// --- -template -Type * DuplicateIfUsed( SPtr & item, RegType * iReg ) -{ - if ( item == NULL ) - return NULL; - Type * resItem = item.get(); - if ( item->GetUseCount() > 1 ) // \ru Если оригинал, то делаем копию. \en If there is original, then make a copy. - resItem = static_cast( &item->Duplicate( iReg ) ); - - return resItem; -} - -//------------------------------------------------------------------------------ -/// \ru Удалить объект, если он больше никому не нужен. \en Delete an object if it is unnecessary. -// --- -template -void DeleteItem( Type *& item ) -{ - if ( item != NULL ) { - if ( item->GetUseCount() < 1 ) - delete item; - item = NULL; - } -} - -//------------------------------------------------------------------------------ -/// \ru Освободить ссылку на объект. \en Release the reference to object. -// --- -template -void ReleaseItem( Type *& item ) -{ - if ( item != NULL ) { - item->Release(); - item = NULL; - } -} - -//------------------------------------------------------------------------------ -/// \ru Захватить объект. \en Catch an object. -// --- -template -void AddRefItem( const Type * item ) -{ - if ( item != NULL ) - item->AddRef(); -} - -//------------------------------------------------------------------------------ -/// \ru Отпустить объект без удаления. \en Detach an object without removing. -// --- -template -void DecRefItem( const Type * item ) -{ - if ( item != NULL ) - item->DecRef(); -} - - -//------------------------------------------------------------------------------ -/// \ru Захватить объекты. \en Catch objects. -// --- -template -void AddRefItems( const Vector & items ) -{ - for ( size_t k = 0, itemsCnt = items.size(); k < itemsCnt; ++k ) { - if ( items[k] != NULL ) - items[k]->AddRef(); - } -} - - -//------------------------------------------------------------------------------ -/// \ru Отпустить объекты без удаления. \en Detach objects without removing. -// --- -template -void DecRefItems( const Vector & items ) -{ - for ( size_t k = 0, itemsCnt = items.size(); k < itemsCnt; ++k ) { - if ( items[k] != NULL ) - items[k]->DecRef(); - } -} - - -//------------------------------------------------------------------------------ -/// \ru Удалить никому не нужные объекты. \en Remove unnecessary objects. -// --- -template -void DeleteItems( Vector & items ) -{ - for ( size_t k = 0, itemsCnt = items.size(); k < itemsCnt; ++k ) - ::DeleteItem( items[k] ); - items.clear(); -} - - -//------------------------------------------------------------------------------ -/// \ru Отпустить объекты с возможным удалением. \en Detach objects with possible removing. -// --- -template -void ReleaseItems( Vector & items ) -{ - for ( size_t k = 0, itemsCnt = items.size(); k < itemsCnt; ++k ) - ::ReleaseItem( items[k] ); - items.clear(); -} - - -//------------------------------------------------------------------------------ -/// \ru Удалить никому не нужные объекты. \en Remove unnecessary objects. -// --- -template -void DeleteItems( Vector & items, SArray & coItems ) -{ - size_t itemsCnt = items.size(); - if ( itemsCnt > 0 ) { - for ( size_t k = 0; k < itemsCnt; ++k ) - ::DeleteItem( items[k] ); - items.clear(); - coItems.clear(); - } -} - - -//------------------------------------------------------------------------------ -/// \ru Отпустить объекты с возможным удалением. \en Detach objects with possible removing. -// --- -template -void ReleaseItems( Vector & items, SArray & coItems ) -{ - size_t itemsCnt = items.size(); - if ( itemsCnt > 0 ) { - for ( size_t k = 0; k < itemsCnt; ++k ) - ::ReleaseItem( items[k] ); - items.clear(); - coItems.clear(); - } -} - - -//------------------------------------------------------------------------------ -/// \ru Переложить элементы с захватом и возможным копированием. \en Put elements with capturing and with possible copying. -// --- -template -void AddRefItems( const TypeVector & srcItems, bool same, RPArray & dstItems ) -{ - if ( (srcItems.size() > 0) && reinterpret_cast( &srcItems ) != reinterpret_cast( &dstItems ) ) { - dstItems.reserve( dstItems.size() + srcItems.size() ); - for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { - if ( srcItems[k] != NULL ) { - Type * srcItem = &const_cast(*srcItems[k]); - Type * dstItem = same ? srcItem : static_cast( &srcItem->Duplicate() ); - dstItem->AddRef(); - dstItems.push_back( dstItem ); - } - } - } -} - - -//------------------------------------------------------------------------------ -/// \ru Переложить элементы с захватом и возможным копированием. \en Put elements with capturing and with possible copying. -// --- -template -void AddRefItems( const TypeVector & srcItems, bool same, std::vector< SPtr > & dstItems ) -{ - if ( (srcItems.size() > 0) && reinterpret_cast(&srcItems) != reinterpret_cast(&dstItems) ) { - dstItems.reserve( dstItems.size() + srcItems.size() ); - for ( size_t k = 0, cnt = srcItems.size(); k < cnt; k++ ) { - if ( srcItems[k] != NULL ) { - Type * srcItem = &const_cast(*srcItems[k]); - SPtr dstItem; - dstItem = same ? srcItem : static_cast( &srcItem->Duplicate() ); - dstItems.push_back( dstItem ); - } - } - } -} - - -//------------------------------------------------------------------------------ -/// \ru Переложить элементы с захватом и возможным копированием. \en Put elements with capturing and with possible copying. -// --- -template -void AddRefItems( const TypeVector & srcItems, bool same, std::vector & dstItems ) -{ - if ( (srcItems.size() > 0) && reinterpret_cast( &srcItems ) != reinterpret_cast( &dstItems ) ) { - dstItems.reserve( dstItems.size() + srcItems.size() ); - for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { - if ( srcItems[k] != NULL ) { - Type * srcItem = &const_cast(*srcItems[k]); - Type * dstItem = same ? srcItem : static_cast( &srcItem->Duplicate() ); - dstItem->AddRef(); - dstItems.push_back( dstItem ); - } - } - } -} - - -//------------------------------------------------------------------------------ -/// \ru Переложить элементы с захватом и возможным копированием. \en Put elements with capturing and with possible copying. -// --- -template -void AddRefRegItems( const TypeVector & srcItems, bool same, RPArray & dstItems, RegType * iReg ) -{ - if ( (srcItems.size() > 0) && reinterpret_cast(&srcItems) != reinterpret_cast(&dstItems) ) { - dstItems.reserve( dstItems.size() + srcItems.size() ); - for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { - if ( srcItems[k] != NULL ) { - Type * srcItem = &const_cast(*srcItems[k]); - Type * dstItem = same ? srcItem : static_cast( &srcItem->Duplicate( iReg ) ); - dstItem->AddRef(); - dstItems.push_back( dstItem ); - } - } - } -} - - -//------------------------------------------------------------------------------ -/// \ru Переложить элементы с захватом и возможным копированием. \en Put elements with capturing and with possible copying. -// --- -template -void AddRefRegItems( const TypeVector & srcItems, bool same, std::vector< SPtr > & dstItems, RegType * iReg ) -{ - if ( (srcItems.size() > 0) && reinterpret_cast(&srcItems) != reinterpret_cast(&dstItems) ) { - dstItems.reserve( dstItems.size() + srcItems.size() ); - for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { - if ( srcItems[k] != NULL ) { - Type * srcItem = &const_cast(*srcItems[k]); - SPtr dstItem; - dstItem = same ? srcItem : static_cast( &srcItem->Duplicate( iReg ) ); - dstItems.push_back( dstItem ); - } - } - } -} - - -//------------------------------------------------------------------------------ -/// \ru Переложить элементы с захватом и возможным копированием. \en Put elements with capturing and with possible copying. -// --- -template -void AddRefRegItems( const TypeVector & srcItems, bool same, std::vector & dstItems, RegType * iReg ) -{ - if ( (srcItems.size() > 0) && reinterpret_cast(&srcItems) != reinterpret_cast(&dstItems) ) { - dstItems.reserve( dstItems.size() + srcItems.size() ); - for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { - if ( srcItems[k] != NULL ) { - Type * srcItem = &const_cast(*srcItems[k]); - Type * dstItem = same ? srcItem : static_cast( &srcItem->Duplicate( iReg ) ); - dstItem->AddRef(); - dstItems.push_back( dstItem ); - } - } - } -} - - -//------------------------------------------------------------------------------ -/// \ru Являются ли объекты подобными. \en Determine whether the objects are similar. -// --- -template -bool IsItemSame( const Item * item1, const Item * item2, double accuracy ) -{ - if ( (item1 == NULL) && (item2 == NULL) ) - return true; - else if ( (item1 != NULL) && (item2 != NULL) && item1->IsSame( *item2, accuracy ) ) - return true; - return false; -} - - -//------------------------------------------------------------------------------ -/// \ru Являются ли объекты подобными. \en Determine whether the objects are similar. -// --- -template -bool AreItemsSame( const Vector & items1, const Vector & items2, double accuracy ) -{ - bool areEqual = false; - - const size_t cnt = items1.size(); - if ( cnt == items2.size() ) { - areEqual = true; - for ( size_t k = 0; k < cnt; ++k ) { - if ( (items1[k] == NULL) || (items2[k] == NULL) || !items1[k]->IsSame( *items2[k], accuracy ) ) { - areEqual = false; - break; - } - } - } - - return areEqual; -} - - -//------------------------------------------------------------------------------ -/// \ru Являются ли объекты подобными. \en Determine whether the objects are similar. -// --- -template -bool AreObjectsSame( const Vector & items1, const Vector & items2, double accuracy ) -{ - bool areEqual = false; - - const size_t cnt = items1.size(); - if ( cnt == items2.size() ) { - areEqual = true; - for ( size_t k = 0; k < cnt; ++k ) { - if ( !items1[k].IsSame( items2[k], accuracy ) ) { - areEqual = false; - break; - } - } - } - - return areEqual; -} - - -//------------------------------------------------------------------------------ -/// \ru Являются ли объекты подобными. \en Determine whether the objects are similar. -// --- -template -bool AreItemsSimilar( const Vector & items1, const Vector & items2 ) -{ - bool areEqual = false; - - const size_t cnt = items1.size(); - if ( cnt == items2.size() ) { - areEqual = true; - for ( size_t k = 0; k < cnt; ++k ) { - if ( (items1[k] == NULL) || (items2[k] == NULL) || !items1[k]->IsSimilar( *items2[k] ) ) { - areEqual = false; - break; - } - } - } - - return areEqual; -} - - -//------------------------------------------------------------------------------ -/// \ru Сделать равными. \en Make equal. -// --- -template -bool SetItemsEqual( const Vector & srcItems, Vector & dstItems ) -{ - bool setEqual = false; - - size_t cnt = srcItems.size(); - if ( cnt == dstItems.size() ) { - setEqual = ::AreItemsSimilar( srcItems, dstItems ); - - if ( setEqual ) { - for ( size_t k = 0; k < cnt; ++k ) { - if ( srcItems[k] == NULL || dstItems[k] == NULL || !dstItems[k]->SetEqual( *srcItems[k] ) ) { - setEqual = false; - break; - } - } - } - } - - return setEqual; -} - - -//------------------------------------------------------------------------------ -/// \ru Дублировать c регистратором (опционально переложить оригиналы). \en Duplicate with registrator (optionally put originals). -// --- -template -void DuplicateItems( const TypeVector & srcItems, RegType * iReg, bool same, RPArray & dstItems ) -{ - C3D_ASSERT( dstItems.size() < 1 ); - dstItems.Reserve( srcItems.size() ); - for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { - Type * srcItem = srcItems[k]; - if ( srcItem != NULL ) { - Type * dstItem = same ? srcItem : static_cast( &srcItem->Duplicate( iReg ) ); - dstItems.push_back( dstItem ); - } - } -} - - -//------------------------------------------------------------------------------ -/// \ru Дублировать c регистратором (опционально переложить оригиналы). \en Duplicate with registrator (optionally put originals). -// --- -template -void DuplicateItems( const TypeVector & srcItems, RegType * iReg, bool same, std::vector< SPtr > & dstItems ) -{ - C3D_ASSERT( dstItems.size() < 1 ); - dstItems.reserve( dstItems.size() + srcItems.size() ); - for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { - Type * srcItem = srcItems[k]; - if ( srcItem != NULL ) { - SPtr dstItem; - dstItem = same ? srcItem : static_cast( &srcItem->Duplicate( iReg ) ); - dstItems.push_back( dstItem ); - } - } -} - - -//------------------------------------------------------------------------------ -/// \ru Дублировать c регистратором (опционально переложить оригиналы). \en Duplicate with registrator (optionally put originals). -// --- -template -void DuplicateItems( const TypeVector & srcItems, RegType * iReg, bool same, std::vector & dstItems ) -{ - C3D_ASSERT( dstItems.size() < 1 ); - dstItems.reserve( dstItems.size() + srcItems.size() ); - for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { - Type * srcItem = srcItems[k]; - if ( srcItem != NULL ) { - Type * dstItem = same ? srcItem : static_cast( &srcItem->Duplicate( iReg ) ); - dstItems.push_back( dstItem ); - } - } -} - - -//------------------------------------------------------------------------------ -/// \ru Преобразовать элементы согласно матрице. \en Transform elements according to the matrix. -// --- -template -void TransformItems( Array & items, const Matrix & matr, RegType * iReg ) -{ - for ( size_t k = 0, cnt = items.size(); k < cnt; ++k ) { - if ( items[k] != NULL ) - items[k]->Transform( matr, iReg ); - } -} - -//------------------------------------------------------------------------------ -/// \ru Преобразовать элементы согласно матрице. \en Transform elements according to the matrix. -// --- -template -void TransformObjects( Array & objects, const Matrix & matr ) -{ - for ( size_t k = 0, cnt = objects.size(); k < cnt; ++k ) - objects[k].Transform( matr ); -} - - -//------------------------------------------------------------------------------ -/// \ru Сдвинуть вдоль объекты вектора. \en Translate objects along a vector. -// --- -template -void MoveItems( Array & items, const Vector & to, RegType * iReg ) -{ - for ( size_t k = 0, cnt = items.size(); k < cnt; ++k ) { - if ( items[k] != NULL ) - items[k]->Move( to, iReg ); - } -} - -//------------------------------------------------------------------------------ -/// \ru Сдвинуть вдоль объекты вектора. \en Translate objects along a vector. -// --- -template -void MoveObjects( Array & objects, const Vector & to ) -{ - for ( size_t k = 0, cnt = objects.size(); k < cnt; ++k ) - objects[k].Move( to ); -} - - -//------------------------------------------------------------------------------ -/// \ru Повернуть вокруг оси. \en Rotate about an axis. -// --- -template -void RotateItems( Array & items, const Axis & axis, double angle, RegType * iReg ) -{ - for ( size_t k = 0, cnt = items.size(); k < cnt; ++k ) { - if ( items[k] != NULL ) - items[k]->Rotate( axis, angle, iReg ); - } -} - -//------------------------------------------------------------------------------ -/// \ru Повернуть вокруг оси. \en Rotate about an axis. -// --- -template -void RotateObjects( Array & objects, const Axis & axis, double angle ) -{ - for ( size_t k = 0, cnt = objects.size(); k < cnt; ++k ) - objects[k].Rotate( axis, angle ); -} - - -//------------------------------------------------------------------------------ -/// \ru Запись объектов в поток. \en Write objects to the stream. -// --- -template -void WriteRefItems( const Vector & items, Writer & out ) -{ - size_t k, cnt = items.size(); - - for ( k = 0; k < cnt; ++k ) { - if ( items[k] == NULL ) - cnt--; - } - - WriteCOUNT( out, cnt ); - - if ( out.good() ) { - for ( k = 0; k < cnt; ++k ) { - if ( items[k] != NULL ) { - items[k]->PrepareWrite(); - out << &(*items[k]); - } - } - } -} - - -//------------------------------------------------------------------------------ -/// \ru Запись объекты в поток. \en Write objects to the stream. -// --- -template -void WriteRefItems( const std::vector< SPtr > & items, Writer & out ) -{ - size_t k, cnt = items.size(); - - for ( k = 0; k < cnt; ++k ) { - if ( items[k] == NULL ) - cnt--; - } - - WriteCOUNT( out, cnt ); - - if ( out.good() ) { - for ( k = 0; k < cnt; ++k ) { - if ( items[k] != NULL ) { - items[k]->PrepareWrite(); - out << items[k].get(); - } - } - } -} - - -//------------------------------------------------------------------------------ -/// \ru Чтение массива объектов из потока с захватом. \en Read an array of objects with capturing from the stream. -// --- -template -void ReadRefItems( Reader & in, RPArray & items ) -{ - size_t cnt = ReadCOUNT( in ); - - if ( in.good() && cnt > 0 ) { - items.reserve( items.size() + cnt ); - - for ( size_t i = 0; i < cnt; ++i ) { - Type * item = NULL; - in >> item; - if ( item != NULL ) { - items.push_back( item ); - item->AddRef(); - } - } - } -} - - -//------------------------------------------------------------------------------ -/// \ru Чтение массива объектов из потока с захватом. \en Read an array of objects with capturing from the stream. -// --- -template -void ReadRefItems( Reader & in, std::vector & items ) -{ - size_t cnt = ReadCOUNT( in ); - - if ( in.good() && cnt > 0 ) { - for ( size_t i = 0; i < cnt; ++i ) { - Type * item = NULL; - in >> item; - if ( item != NULL ) { - items.push_back( item ); - item->AddRef(); - } - } - } -} - - -//------------------------------------------------------------------------------ -/// \ru Чтение массива объектов из потока с захватом. \en Read an array of objects with capturing from the stream. -// --- -template -void ReadRefItems( Reader & in, std::vector< SPtr > & items ) -{ - size_t cnt = ReadCOUNT( in ); - - if ( in.good() && cnt > 0 ) { - for ( size_t i = 0; i < cnt; ++i ) { - Type * item = NULL; - in >> item; - if ( item != NULL ) - items.push_back( SPtr(item) ); - } - } -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Отцепить от массива и уменьшить счетчик ссылок. - \en Detach from array and decrease reference counter. \~ - \details \ru Отцепить от массива и уменьшить счетчик ссылок без проверок.\n - \en Detach from array and decrease reference counter without checks.\n \~ - \param[in,out] items - \ru Множество элементов. - \en An array of elements. \~ - \param[in] index - \ru Номер элемента. Не проверяется на корректность. - \en Index of element. Isn't checked for correctness. \~ -*/ -// --- -template -void ReleaseAndDetachItem_( Vector & items, size_t index ) -{ - items[index]->Release(); - items.erase( items.begin() + index ); -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Добавить объект и увеличить счетчик ссылок. - \en Add an object and increase reference counter. \~ - \details \ru Добавить объект и увеличить счетчик ссылок без проверок.\n - \en Add an object and increase reference counter without checks.\n \~ - \param[in,out] items - \ru Множество элементов. - \en An array of elements. \~ - \param[in] newItem - \ru Новый элемент. - \en New element. \~ -*/ -// --- -template -void AddRefAndAddItem_( Vector & items, Type * newItem ) -{ - newItem->AddRef(); - items.push_back( newItem ); -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Добавить объект и увеличить счетчик ссылок. - \en Add an object and increase reference counter. \~ - \details \ru Добавить объект и увеличить счетчик ссылок без проверок.\n - \en Add an object and increase reference counter without checks.\n \~ - \param[in,out] items - \ru Множество элементов. - \en An array of elements. \~ - \param[in] newItem - \ru Новый элемент. - \en New element. \~ - \param[in] index - \ru Номер элемента. Не проверяется на корректность. - \en Index of element. Isn't checked for correctness. \~ -*/ -// --- -template -void AddRefAndAddAtItem_( Vector & items, Type * newItem, size_t index ) -{ - newItem->AddRef(); - items.insert( index, newItem ); -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Отцепить от массива и уменьшить счетчик ссылок. - \en Detach from array and decrease reference counter. \~ - \details \ru Отцепить от массива и уменьшить счетчик ссылок без проверок.\n - \en Detach from array and decrease reference counter without checks.\n \~ - \param[in,out] items - \ru Множество элементов. - \en An array of elements. \~ - \param[in] index - \ru Номер элемента. Не проверяется на корректность. - \en Index of element. Isn't checked for correctness. \~ -*/ -// --- -template -void ReleaseAndDetachItem_( std::vector > & items, size_t index ) -{ - items[index].reset(); - items.erase( items.begin() + index ); -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Добавить объект и увеличить счетчик ссылок. - \en Add an object and increase reference counter. \~ - \details \ru Добавить объект и увеличить счетчик ссылок без проверок.\n - \en Add an object and increase reference counter without checks.\n \~ - \param[in,out] items - \ru Множество элементов. - \en An array of elements. \~ - \param[in] newItem - \ru Новый элемент. - \en New element. \~ -*/ -// --- -template -void AddRefAndAddItem_( std::vector > & items, Type * newItem ) -{ - items.push_back( SPtr(newItem) ); -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Добавить объект и увеличить счетчик ссылок. - \en Add an object and increase reference counter. \~ - \details \ru Добавить объект и увеличить счетчик ссылок без проверок.\n - \en Add an object and increase reference counter without checks.\n \~ - \param[in,out] items - \ru Множество элементов. - \en An array of elements. \~ - \param[in] newItem - \ru Новый элемент. - \en New element. \~ - \param[in] index - \ru Номер элемента. Не проверяется на корректность. - \en Index of element. Isn't checked for correctness. \~ -*/ -// --- -template -void AddRefAndAddAtItem_( std::vector > & items, Type * newItem, size_t index ) -{ - items.insert( items.begin() + index, SPtr(newItem) ); -} - - -//------------------------------------------------------------------------------ -/// \ru Отцепить объект из владеющего указателя. \en Detach object from owning pointer. -// --- -template -inline Type * DetachItem( SPtr & itemOwner ) -{ - return itemOwner.detach(); -} - - -//------------------------------------------------------------------------------ -/// \ru Заменить объект на копию. \en Replace object by copy. -// --- -template -void ReplaceByCopy( Type *& item ) -{ - if ( item != NULL ) { - Type * temp = (Type *)&item->Duplicate(); - ::DeleteItem( item ); - item = temp; - } -} - - -//------------------------------------------------------------------------------ -/// \ru Включить габариты объектов массива в общий габарит. \en Include bounding boxes of an array of objects in a common bounding box. -//--- -template -void AddYourGabaritTo( Objects & objects, Gab & gab ) -{ - for ( size_t k = 0, cnt = objects.size(); k < cnt; ++k ) { - if ( objects[k] ) - objects[k]->AddYourGabaritTo( gab ); - } -} - - -#endif // __REFERENCE_ITEM_H +////////////////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Счетчик ссылок (владельцев объекта). + \en Reference counter (of an object owners). \~ + +*/ +////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef __REFERENCE_ITEM_H +#define __REFERENCE_ITEM_H + + +#include +#include +#include +#include +#include +#include +#include +//#include +#include + + +//---------------------------------------------------------------------------------------- +/** \brief \ru Типы объекта со счетчиком ссылок. + \en Types of object with reference counter. \~ + \details \ru Тип несёт информацию об объекте-наследнике. \n + \en Type has an information of inheritor object. \n \~ + \ingroup Geometric_Items +*/ +// --- +enum MbeRefType +{ + rt_RefItem = 0, ///< \ru Некоторый объект. \en Some object. + rt_PlaneItem, ///< \ru Двумерный геометрически объект. \en Two-dimensional geometric object. + rt_SpaceItem, ///< \ru Трехмерный геометрический объект. \en Three-dimensional geometric object. + rt_TopItem, ///< \ru Топологический объект. \en A topological object. + rt_Creator, ///< \ru Строитель объекта. \en Object constructor + rt_Attribute, ///< \ru Атрибут объекта. \en Attribute of an object. + rt_Primitive, ///< \ru Элемент полигонального объекта. \en Element of polygonal object. + // \ru В конец можно добавлять новые нужные \en It is possible to add new necessary ones to the end +}; + + +class MATH_CLASS MbRefItem; +namespace c3d // namespace C3D +{ +typedef SPtr RefItemSPtr; +typedef SPtr ConstRefItemSPtr; + +typedef std::vector RefItemsVector; +typedef std::vector ConstRefItemsVector; + +typedef std::vector RefItemsSPtrVector; +typedef std::vector ConstRefItemsSPtrVector; +} + +//---------------------------------------------------------------------------------------- +/** \brief \ru Объект с подсчетом ссылок. + \en Reference-counted object. \~ + \details \ru Объект, считающий количество своих владельцев. \n + Используется в качестве одного из родительских классов геометрических объектов. \n + Если наследник данного класса захватывается другим объектом или алгоритмом,то другой + объект или алгоритм должен увеличить счетчик ссылок на единицу методом AddRef(). + При отказе от использования наследника данного класса другим объектом (например, при деструктурировании) + или алгоритмом другой объект или алгоритм должны уменьшить счетчик ссылок на единицу + методом Release(). Такое правило позволяет использовать одного и того же наследника + данного класса несколькими другим объектами или алгоритмами одновременно и гарантирует, + что объект будет удалён, когда он станет никому не нужен.\n + + \en Object counting number of its owners. \n + Is used as one of parent classes of geometric objects. \n + If inheritor of current class is captured by other object or algorithm, then the other + object or algorithm has to increase reference counter by one by AddRef() method. + At refusal of use of the successor of this class by other object (for example at destruction) + or by algorithm, the other object or algorithm has to decrease reference counter + by one by Release() method. Such rule allows to use the same inheritor of current + class simultaneously by several other objects or algorithms and guarantees that the + object will be removed when it becomes unnecessary.\n \~ + + \note \ru Рекомендуется применение автоматических указателей типа SPtr к экземплярам + данного класса. Это упростит работу с кодом, где нужно позаботится об автоматической + сборке мусора. + \en It is recommended to use smart pointers of type SPtr to instances of + this class. This will simplify the work with the code where you want to take care of + the automatic garbage collection. \~ + \sa #SPtr + \ingroup Geometric_Items +*/ +// --- +class MATH_CLASS MbRefItem { +private: + mutable use_count_type useCount; ///< \ru Счетчик ссылок на объект, изменяемый владельцами объекта. \en A counter of references to an object modifiable by owners of object. +public: + /// \ru Конструктор без параметров. \en Constructor without parameters. + MbRefItem(); +protected: + virtual ~MbRefItem(); + +public: + /** \ru \name Функции регистрации ссылок на геометрический объект владельцами объекта. + \en \name Functions for registration of references to geometric object by owners of object. + \{ */ + /// \ru Выдать количество ссылок (выдать количество владельцев объекта). \en Get count of references (get count of owners of an object). + refcount_t GetUseCount() const; + /// \ru Увеличить количество ссылок на единицу. \en Increase count of references by one. + refcount_t AddRef() const; + /// \ru Уменьшить количество ссылок на единицу. \en Decrease count of references by one. + refcount_t DecRef() const; + /// \ru Уменьшить количество ссылок на единицу и, если количество ссылок стало равным нулю, удалить себя. \en Decrease count of references by one and if count of references became zero, then remove itself. + refcount_t Release() const; + /** \} */ +public: + /// \ru Регистрационный тип (для копирования, дублирования). \en Registration type (for copying, duplication). + virtual MbeRefType RefType() const; + +OBVIOUS_PRIVATE_COPY( MbRefItem ) +}; + + +//---------------------------------------------------------------------------------------- +/** \brief \ru Шаблон класса сериализации (порядковой нумерации создаваемых объектов). + \en Serialization class template (ordinal numbering of created objects). \~ + \details \ru Шаблон класса сериализации (порядковой нумерации создаваемых объектов). \n + Применение: наследовать от данного класса. + Аргумент шаблона позволяет иметь несколько сериализаций одновременно: \n + если нужна сериализация объектов класса T, можно записать class T: virtual public MbSerialItem \n + если объекты U должны иметь ту же сериализацию, можно записать class U: virtual public MbSerialItem \n + или же создать пустой класс для сериализации типа class MbBasicSerializer {} + и наследоваться от MbSerialItem. \n + + \en Serialization class template (ordinal numbering of created objects). \n + How to use: inherit from this class. \n + The template argument allows multiple serializations at the same time: \n + if you need serialization of objects of class T, you can write class T: virtual public MbSerialItem \n + if U objects must have the same serialization, you can write a class U: virtual public MbSerialItem \n + or you can create an empty class to serialize the class type MbBasicSerializer \n + and inherit it from MbSerialItem . \n \~ + + \ingroup Geometric_Items +*/ +// --- +template class MbSerialItem { +private: + static serial_type serialLast; ///< \ru Индекс последнего созданного объекта (0 в случае отсутствия объектов). \en Index of the last object created (0 if there are no objects). + static serial_type serialCount; ///< \ru Счетчик объектов. \en Counter of objects. + serial_type serialThis; ///< \ru Уникальный порядковый номер объекта, выдается при создании. В дальнейшем не меняется. \en The unique sequence number of the object, issued at creation. In the future it is not changed. +public: + MbSerialItem(); ///< \ru Конструктор. \en Constructor. +protected: + virtual ~MbSerialItem(); ///< \ru Деструктор. \en Destructor. +public: + size_t GetSerial() const { return SerialTypeValue( serialThis ); } ///< \ru Выдать порядковый номер объекта. \en Get object serial identifier. + +OBVIOUS_PRIVATE_COPY( MbSerialItem ) +}; + + +// \ru Инициализация статических переменных шаблона класса сериализации. \en Initializing static variables of the serialization class template. +template serial_type MbSerialItem::serialLast( 0 ); +template serial_type MbSerialItem::serialCount( 0 ); + + +//----------------------------------------------------------------------------- +// \ru Конструктор. \en Constructor. +// --- +template MbSerialItem::MbSerialItem() +{ // Set the serial number and promote the counters + PRECONDITION( serialLast != SYS_MAX_T ); // number overflow + serialThis = ++serialLast; + ++serialCount; +} + + +//----------------------------------------------------------------------------- +// \ru Деструктор. \en Destructor. +// --- +template MbSerialItem::~MbSerialItem() +{ + PRECONDITION( SerialTypeValue( serialCount ) != 0 ); // If it equals 0 then it means that the counter is damaged. + if ( --serialCount == 0 ) // The case when the last of the instantiated objects is deleted + serialLast = 0; // Set serialization to zero - at the subsequent creation of objects the numbering will start again (serially since 1). +} + + + +//------------------------------------------------------------------------------ +/// \ru Удалить объект без ссылок. \en Delete an object without references. +//--- +template +inline void DeleteMatItem( Type *& item ) +{ + if ( item != NULL ) { + delete item; + item = NULL; + } +} + + +//------------------------------------------------------------------------------ +/// \ru Удалить объекты без ссылок. \en Delete objects without references. +// --- +template +void DeleteMatItems( Vector & items ) +{ + for ( size_t k = 0, itemsCnt = items.size(); k < itemsCnt; ++k ) + ::DeleteMatItem( items[k] ); + items.clear(); +} + + +//------------------------------------------------------------------------------ +/// \ru Сделать копию, если объект используется, иначе вернуть оригинал. \en Create a copy if object is used, otherwise return original. +// --- +template +Type & DuplicateIfUsed( Type & item ) +{ + Type * resItem = &item; + if ( item.GetUseCount() > 0 ) // \ru Если оригинал, то делаем копию. \en If there is original, then make a copy. + resItem = static_cast( &item.Duplicate() ); + + return *resItem; +} + + +//------------------------------------------------------------------------------ +/// \ru Сделать копию, если объект используется, иначе вернуть оригинал. \en Create a copy if object is used, otherwise return original. +// --- +template +Type & DuplicateIfUsed( Type & item, RegType * iReg ) +{ + Type * resItem = &item; + if ( item.GetUseCount() > 0 ) // \ru Если оригинал, то делаем копию. \en If there is original, then make a copy. + resItem = static_cast( &item.Duplicate( iReg ) ); + + return *resItem; +} + + +//------------------------------------------------------------------------------ +/// \ru Сделать копию, если объект используется, иначе вернуть оригинал. \en Create a copy if object is used, otherwise return original. +// --- +template +Type * DuplicateIfUsed( SPtr & item ) +{ + if ( item == NULL ) + return NULL; + Type * resItem = item.get(); + if ( item->GetUseCount() > 1 ) // \ru Если оригинал, то делаем копию. \en If there is original, then make a copy. + resItem = static_cast( &item->Duplicate() ); + + return resItem; +} + + +//------------------------------------------------------------------------------ +/// \ru Сделать копию, если объект используется, иначе вернуть оригинал. \en Create a copy if object is used, otherwise return original. +// --- +template +Type * DuplicateIfUsed( SPtr & item, RegType * iReg ) +{ + if ( item == NULL ) + return NULL; + Type * resItem = item.get(); + if ( item->GetUseCount() > 1 ) // \ru Если оригинал, то делаем копию. \en If there is original, then make a copy. + resItem = static_cast( &item->Duplicate( iReg ) ); + + return resItem; +} + +//------------------------------------------------------------------------------ +/// \ru Удалить объект, если он больше никому не нужен. \en Delete an object if it is unnecessary. +// --- +template +void DeleteItem( Type *& item ) +{ + if ( item != NULL ) { + if ( item->GetUseCount() < 1 ) + delete item; + item = NULL; + } +} + +//------------------------------------------------------------------------------ +/// \ru Освободить ссылку на объект. \en Release the reference to object. +// --- +template +void ReleaseItem( Type *& item ) +{ + if ( item != NULL ) { + item->Release(); + item = NULL; + } +} + +//------------------------------------------------------------------------------ +/// \ru Захватить объект. \en Catch an object. +// --- +template +void AddRefItem( const Type * item ) +{ + if ( item != NULL ) + item->AddRef(); +} + +//------------------------------------------------------------------------------ +/// \ru Отпустить объект без удаления. \en Detach an object without removing. +// --- +template +void DecRefItem( const Type * item ) +{ + if ( item != NULL ) + item->DecRef(); +} + + +//------------------------------------------------------------------------------ +/// \ru Захватить объекты. \en Catch objects. +// --- +template +void AddRefItems( const Vector & items ) +{ + for ( size_t k = 0, itemsCnt = items.size(); k < itemsCnt; ++k ) { + if ( items[k] != NULL ) + items[k]->AddRef(); + } +} + + +//------------------------------------------------------------------------------ +/// \ru Отпустить объекты без удаления. \en Detach objects without removing. +// --- +template +void DecRefItems( const Vector & items ) +{ + for ( size_t k = 0, itemsCnt = items.size(); k < itemsCnt; ++k ) { + if ( items[k] != NULL ) + items[k]->DecRef(); + } +} + + +//------------------------------------------------------------------------------ +/// \ru Удалить никому не нужные объекты. \en Remove unnecessary objects. +// --- +template +void DeleteItems( Vector & items ) +{ + for ( size_t k = 0, itemsCnt = items.size(); k < itemsCnt; ++k ) + ::DeleteItem( items[k] ); + items.clear(); +} + + +//------------------------------------------------------------------------------ +/// \ru Отпустить объекты с возможным удалением. \en Detach objects with possible removing. +// --- +template +void ReleaseItems( Vector & items ) +{ + for ( size_t k = 0, itemsCnt = items.size(); k < itemsCnt; ++k ) + ::ReleaseItem( items[k] ); + items.clear(); +} + + +//------------------------------------------------------------------------------ +/// \ru Удалить никому не нужные объекты. \en Remove unnecessary objects. +// --- +template +void DeleteItems( Vector & items, SArray & coItems ) +{ + size_t itemsCnt = items.size(); + if ( itemsCnt > 0 ) { + for ( size_t k = 0; k < itemsCnt; ++k ) + ::DeleteItem( items[k] ); + items.clear(); + coItems.clear(); + } +} + + +//------------------------------------------------------------------------------ +/// \ru Отпустить объекты с возможным удалением. \en Detach objects with possible removing. +// --- +template +void ReleaseItems( Vector & items, SArray & coItems ) +{ + size_t itemsCnt = items.size(); + if ( itemsCnt > 0 ) { + for ( size_t k = 0; k < itemsCnt; ++k ) + ::ReleaseItem( items[k] ); + items.clear(); + coItems.clear(); + } +} + + +//------------------------------------------------------------------------------ +/// \ru Переложить элементы с захватом и возможным копированием. \en Put elements with capturing and with possible copying. +// --- +template +void AddRefItems( const TypeVector & srcItems, bool same, RPArray & dstItems ) +{ + if ( (srcItems.size() > 0) && reinterpret_cast( &srcItems ) != reinterpret_cast( &dstItems ) ) { + dstItems.reserve( dstItems.size() + srcItems.size() ); + for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { + if ( srcItems[k] != NULL ) { + Type * srcItem = &const_cast(*srcItems[k]); + Type * dstItem = same ? srcItem : static_cast( &srcItem->Duplicate() ); + dstItem->AddRef(); + dstItems.push_back( dstItem ); + } + } + } +} + + +//------------------------------------------------------------------------------ +/// \ru Переложить элементы с захватом и возможным копированием. \en Put elements with capturing and with possible copying. +// --- +template +void AddRefItems( const TypeVector & srcItems, bool same, std::vector< SPtr > & dstItems ) +{ + if ( (srcItems.size() > 0) && reinterpret_cast(&srcItems) != reinterpret_cast(&dstItems) ) { + dstItems.reserve( dstItems.size() + srcItems.size() ); + for ( size_t k = 0, cnt = srcItems.size(); k < cnt; k++ ) { + if ( srcItems[k] != NULL ) { + Type * srcItem = &const_cast(*srcItems[k]); + SPtr dstItem; + dstItem = same ? srcItem : static_cast( &srcItem->Duplicate() ); + dstItems.push_back( dstItem ); + } + } + } +} + + +//------------------------------------------------------------------------------ +/// \ru Переложить элементы с захватом и возможным копированием. \en Put elements with capturing and with possible copying. +// --- +template +void AddRefItems( const TypeVector & srcItems, bool same, std::vector & dstItems ) +{ + if ( (srcItems.size() > 0) && reinterpret_cast( &srcItems ) != reinterpret_cast( &dstItems ) ) { + dstItems.reserve( dstItems.size() + srcItems.size() ); + for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { + if ( srcItems[k] != NULL ) { + Type * srcItem = &const_cast(*srcItems[k]); + Type * dstItem = same ? srcItem : static_cast( &srcItem->Duplicate() ); + dstItem->AddRef(); + dstItems.push_back( dstItem ); + } + } + } +} + + +//------------------------------------------------------------------------------ +/// \ru Переложить элементы с захватом и возможным копированием. \en Put elements with capturing and with possible copying. +// --- +template +void AddRefRegItems( const TypeVector & srcItems, bool same, RPArray & dstItems, RegType * iReg ) +{ + if ( (srcItems.size() > 0) && reinterpret_cast(&srcItems) != reinterpret_cast(&dstItems) ) { + dstItems.reserve( dstItems.size() + srcItems.size() ); + for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { + if ( srcItems[k] != NULL ) { + Type * srcItem = &const_cast(*srcItems[k]); + Type * dstItem = same ? srcItem : static_cast( &srcItem->Duplicate( iReg ) ); + dstItem->AddRef(); + dstItems.push_back( dstItem ); + } + } + } +} + + +//------------------------------------------------------------------------------ +/// \ru Переложить элементы с захватом и возможным копированием. \en Put elements with capturing and with possible copying. +// --- +template +void AddRefRegItems( const TypeVector & srcItems, bool same, std::vector< SPtr > & dstItems, RegType * iReg ) +{ + if ( (srcItems.size() > 0) && reinterpret_cast(&srcItems) != reinterpret_cast(&dstItems) ) { + dstItems.reserve( dstItems.size() + srcItems.size() ); + for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { + if ( srcItems[k] != NULL ) { + Type * srcItem = &const_cast(*srcItems[k]); + SPtr dstItem; + dstItem = same ? srcItem : static_cast( &srcItem->Duplicate( iReg ) ); + dstItems.push_back( dstItem ); + } + } + } +} + + +//------------------------------------------------------------------------------ +/// \ru Переложить элементы с захватом и возможным копированием. \en Put elements with capturing and with possible copying. +// --- +template +void AddRefRegItems( const TypeVector & srcItems, bool same, std::vector & dstItems, RegType * iReg ) +{ + if ( (srcItems.size() > 0) && reinterpret_cast(&srcItems) != reinterpret_cast(&dstItems) ) { + dstItems.reserve( dstItems.size() + srcItems.size() ); + for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { + if ( srcItems[k] != NULL ) { + Type * srcItem = &const_cast(*srcItems[k]); + Type * dstItem = same ? srcItem : static_cast( &srcItem->Duplicate( iReg ) ); + dstItem->AddRef(); + dstItems.push_back( dstItem ); + } + } + } +} + + +//------------------------------------------------------------------------------ +/// \ru Являются ли объекты подобными. \en Determine whether the objects are similar. +// --- +template +bool IsItemSame( const Item * item1, const Item * item2, double accuracy ) +{ + if ( (item1 == NULL) && (item2 == NULL) ) + return true; + else if ( (item1 != NULL) && (item2 != NULL) && item1->IsSame( *item2, accuracy ) ) + return true; + return false; +} + + +//------------------------------------------------------------------------------ +/// \ru Являются ли объекты подобными. \en Determine whether the objects are similar. +// --- +template +bool AreItemsSame( const Vector & items1, const Vector & items2, double accuracy ) +{ + bool areEqual = false; + + const size_t cnt = items1.size(); + if ( cnt == items2.size() ) { + areEqual = true; + for ( size_t k = 0; k < cnt; ++k ) { + if ( (items1[k] == NULL) || (items2[k] == NULL) || !items1[k]->IsSame( *items2[k], accuracy ) ) { + areEqual = false; + break; + } + } + } + + return areEqual; +} + + +//------------------------------------------------------------------------------ +/// \ru Являются ли объекты подобными. \en Determine whether the objects are similar. +// --- +template +bool AreObjectsSame( const Vector & items1, const Vector & items2, double accuracy ) +{ + bool areEqual = false; + + const size_t cnt = items1.size(); + if ( cnt == items2.size() ) { + areEqual = true; + for ( size_t k = 0; k < cnt; ++k ) { + if ( !items1[k].IsSame( items2[k], accuracy ) ) { + areEqual = false; + break; + } + } + } + + return areEqual; +} + + +//------------------------------------------------------------------------------ +/// \ru Являются ли объекты подобными. \en Determine whether the objects are similar. +// --- +template +bool AreItemsSimilar( const Vector & items1, const Vector & items2 ) +{ + bool areEqual = false; + + const size_t cnt = items1.size(); + if ( cnt == items2.size() ) { + areEqual = true; + for ( size_t k = 0; k < cnt; ++k ) { + if ( (items1[k] == NULL) || (items2[k] == NULL) || !items1[k]->IsSimilar( *items2[k] ) ) { + areEqual = false; + break; + } + } + } + + return areEqual; +} + + +//------------------------------------------------------------------------------ +/// \ru Сделать равными. \en Make equal. +// --- +template +bool SetItemsEqual( const Vector & srcItems, Vector & dstItems ) +{ + bool setEqual = false; + + size_t cnt = srcItems.size(); + if ( cnt == dstItems.size() ) { + setEqual = ::AreItemsSimilar( srcItems, dstItems ); + + if ( setEqual ) { + for ( size_t k = 0; k < cnt; ++k ) { + if ( srcItems[k] == NULL || dstItems[k] == NULL || !dstItems[k]->SetEqual( *srcItems[k] ) ) { + setEqual = false; + break; + } + } + } + } + + return setEqual; +} + + +//------------------------------------------------------------------------------ +/// \ru Дублировать c регистратором (опционально переложить оригиналы). \en Duplicate with registrator (optionally put originals). +// --- +template +void DuplicateItems( const TypeVector & srcItems, RegType * iReg, bool same, RPArray & dstItems ) +{ + C3D_ASSERT( dstItems.size() < 1 ); + dstItems.Reserve( srcItems.size() ); + for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { + Type * srcItem = srcItems[k]; + if ( srcItem != NULL ) { + Type * dstItem = same ? srcItem : static_cast( &srcItem->Duplicate( iReg ) ); + dstItems.push_back( dstItem ); + } + } +} + + +//------------------------------------------------------------------------------ +/// \ru Дублировать c регистратором (опционально переложить оригиналы). \en Duplicate with registrator (optionally put originals). +// --- +template +void DuplicateItems( const TypeVector & srcItems, RegType * iReg, bool same, std::vector< SPtr > & dstItems ) +{ + C3D_ASSERT( dstItems.size() < 1 ); + dstItems.reserve( dstItems.size() + srcItems.size() ); + for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { + Type * srcItem = srcItems[k]; + if ( srcItem != NULL ) { + SPtr dstItem; + dstItem = same ? srcItem : static_cast( &srcItem->Duplicate( iReg ) ); + dstItems.push_back( dstItem ); + } + } +} + + +//------------------------------------------------------------------------------ +/// \ru Дублировать c регистратором (опционально переложить оригиналы). \en Duplicate with registrator (optionally put originals). +// --- +template +void DuplicateItems( const TypeVector & srcItems, RegType * iReg, bool same, std::vector & dstItems ) +{ + C3D_ASSERT( dstItems.size() < 1 ); + dstItems.reserve( dstItems.size() + srcItems.size() ); + for ( size_t k = 0, cnt = srcItems.size(); k < cnt; ++k ) { + Type * srcItem = srcItems[k]; + if ( srcItem != NULL ) { + Type * dstItem = same ? srcItem : static_cast( &srcItem->Duplicate( iReg ) ); + dstItems.push_back( dstItem ); + } + } +} + + +//------------------------------------------------------------------------------ +/// \ru Преобразовать элементы согласно матрице. \en Transform elements according to the matrix. +// --- +template +void TransformItems( Array & items, const Matrix & matr, RegType * iReg ) +{ + for ( size_t k = 0, cnt = items.size(); k < cnt; ++k ) { + if ( items[k] != NULL ) + items[k]->Transform( matr, iReg ); + } +} + +//------------------------------------------------------------------------------ +/// \ru Преобразовать элементы согласно матрице. \en Transform elements according to the matrix. +// --- +template +void TransformObjects( Array & objects, const Matrix & matr ) +{ + for ( size_t k = 0, cnt = objects.size(); k < cnt; ++k ) + objects[k].Transform( matr ); +} + + +//------------------------------------------------------------------------------ +/// \ru Сдвинуть вдоль объекты вектора. \en Translate objects along a vector. +// --- +template +void MoveItems( Array & items, const Vector & to, RegType * iReg ) +{ + for ( size_t k = 0, cnt = items.size(); k < cnt; ++k ) { + if ( items[k] != NULL ) + items[k]->Move( to, iReg ); + } +} + +//------------------------------------------------------------------------------ +/// \ru Сдвинуть вдоль объекты вектора. \en Translate objects along a vector. +// --- +template +void MoveObjects( Array & objects, const Vector & to ) +{ + for ( size_t k = 0, cnt = objects.size(); k < cnt; ++k ) + objects[k].Move( to ); +} + + +//------------------------------------------------------------------------------ +/// \ru Повернуть вокруг оси. \en Rotate about an axis. +// --- +template +void RotateItems( Array & items, const Axis & axis, double angle, RegType * iReg ) +{ + for ( size_t k = 0, cnt = items.size(); k < cnt; ++k ) { + if ( items[k] != NULL ) + items[k]->Rotate( axis, angle, iReg ); + } +} + +//------------------------------------------------------------------------------ +/// \ru Повернуть вокруг оси. \en Rotate about an axis. +// --- +template +void RotateObjects( Array & objects, const Axis & axis, double angle ) +{ + for ( size_t k = 0, cnt = objects.size(); k < cnt; ++k ) + objects[k].Rotate( axis, angle ); +} + + +//------------------------------------------------------------------------------ +/// \ru Запись объектов в поток. \en Write objects to the stream. +// --- +template +void WriteRefItems( const Vector & items, Writer & out ) +{ + size_t k, cnt = items.size(); + + for ( k = 0; k < cnt; ++k ) { + if ( items[k] == NULL ) + cnt--; + } + + WriteCOUNT( out, cnt ); + + if ( out.good() ) { + for ( k = 0; k < cnt; ++k ) { + if ( items[k] != NULL ) { + items[k]->PrepareWrite(); + out << &(*items[k]); + } + } + } +} + + +//------------------------------------------------------------------------------ +/// \ru Запись объекты в поток. \en Write objects to the stream. +// --- +template +void WriteRefItems( const std::vector< SPtr > & items, Writer & out ) +{ + size_t k, cnt = items.size(); + + for ( k = 0; k < cnt; ++k ) { + if ( items[k] == NULL ) + cnt--; + } + + WriteCOUNT( out, cnt ); + + if ( out.good() ) { + for ( k = 0; k < cnt; ++k ) { + if ( items[k] != NULL ) { + items[k]->PrepareWrite(); + out << items[k].get(); + } + } + } +} + + +//------------------------------------------------------------------------------ +/// \ru Чтение массива объектов из потока с захватом. \en Read an array of objects with capturing from the stream. +// --- +template +void ReadRefItems( Reader & in, RPArray & items ) +{ + size_t cnt = ReadCOUNT( in ); + + if ( in.good() && cnt > 0 ) { + items.reserve( items.size() + cnt ); + + for ( size_t i = 0; i < cnt; ++i ) { + Type * item = NULL; + in >> item; + if ( item != NULL ) { + items.push_back( item ); + item->AddRef(); + } + } + } +} + + +//------------------------------------------------------------------------------ +/// \ru Чтение массива объектов из потока с захватом. \en Read an array of objects with capturing from the stream. +// --- +template +void ReadRefItems( Reader & in, std::vector & items ) +{ + size_t cnt = ReadCOUNT( in ); + + if ( in.good() && cnt > 0 ) { + for ( size_t i = 0; i < cnt; ++i ) { + Type * item = NULL; + in >> item; + if ( item != NULL ) { + items.push_back( item ); + item->AddRef(); + } + } + } +} + + +//------------------------------------------------------------------------------ +/// \ru Чтение массива объектов из потока с захватом. \en Read an array of objects with capturing from the stream. +// --- +template +void ReadRefItems( Reader & in, std::vector< SPtr > & items ) +{ + size_t cnt = ReadCOUNT( in ); + + if ( in.good() && cnt > 0 ) { + for ( size_t i = 0; i < cnt; ++i ) { + Type * item = NULL; + in >> item; + if ( item != NULL ) + items.push_back( SPtr(item) ); + } + } +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Отцепить от массива и уменьшить счетчик ссылок. + \en Detach from array and decrease reference counter. \~ + \details \ru Отцепить от массива и уменьшить счетчик ссылок без проверок.\n + \en Detach from array and decrease reference counter without checks.\n \~ + \param[in,out] items - \ru Множество элементов. + \en An array of elements. \~ + \param[in] index - \ru Номер элемента. Не проверяется на корректность. + \en Index of element. Isn't checked for correctness. \~ +*/ +// --- +template +void ReleaseAndDetachItem_( Vector & items, size_t index ) +{ + items[index]->Release(); + items.erase( items.begin() + index ); +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Добавить объект и увеличить счетчик ссылок. + \en Add an object and increase reference counter. \~ + \details \ru Добавить объект и увеличить счетчик ссылок без проверок.\n + \en Add an object and increase reference counter without checks.\n \~ + \param[in,out] items - \ru Множество элементов. + \en An array of elements. \~ + \param[in] newItem - \ru Новый элемент. + \en New element. \~ +*/ +// --- +template +void AddRefAndAddItem_( Vector & items, Type * newItem ) +{ + newItem->AddRef(); + items.push_back( newItem ); +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Добавить объект и увеличить счетчик ссылок. + \en Add an object and increase reference counter. \~ + \details \ru Добавить объект и увеличить счетчик ссылок без проверок.\n + \en Add an object and increase reference counter without checks.\n \~ + \param[in,out] items - \ru Множество элементов. + \en An array of elements. \~ + \param[in] newItem - \ru Новый элемент. + \en New element. \~ + \param[in] index - \ru Номер элемента. Не проверяется на корректность. + \en Index of element. Isn't checked for correctness. \~ +*/ +// --- +template +void AddRefAndAddAtItem_( Vector & items, Type * newItem, size_t index ) +{ + newItem->AddRef(); + items.insert( index, newItem ); +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Отцепить от массива и уменьшить счетчик ссылок. + \en Detach from array and decrease reference counter. \~ + \details \ru Отцепить от массива и уменьшить счетчик ссылок без проверок.\n + \en Detach from array and decrease reference counter without checks.\n \~ + \param[in,out] items - \ru Множество элементов. + \en An array of elements. \~ + \param[in] index - \ru Номер элемента. Не проверяется на корректность. + \en Index of element. Isn't checked for correctness. \~ +*/ +// --- +template +void ReleaseAndDetachItem_( std::vector > & items, size_t index ) +{ + items[index].reset(); + items.erase( items.begin() + index ); +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Добавить объект и увеличить счетчик ссылок. + \en Add an object and increase reference counter. \~ + \details \ru Добавить объект и увеличить счетчик ссылок без проверок.\n + \en Add an object and increase reference counter without checks.\n \~ + \param[in,out] items - \ru Множество элементов. + \en An array of elements. \~ + \param[in] newItem - \ru Новый элемент. + \en New element. \~ +*/ +// --- +template +void AddRefAndAddItem_( std::vector > & items, Type * newItem ) +{ + items.push_back( SPtr(newItem) ); +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Добавить объект и увеличить счетчик ссылок. + \en Add an object and increase reference counter. \~ + \details \ru Добавить объект и увеличить счетчик ссылок без проверок.\n + \en Add an object and increase reference counter without checks.\n \~ + \param[in,out] items - \ru Множество элементов. + \en An array of elements. \~ + \param[in] newItem - \ru Новый элемент. + \en New element. \~ + \param[in] index - \ru Номер элемента. Не проверяется на корректность. + \en Index of element. Isn't checked for correctness. \~ +*/ +// --- +template +void AddRefAndAddAtItem_( std::vector > & items, Type * newItem, size_t index ) +{ + items.insert( items.begin() + index, SPtr(newItem) ); +} + + +//------------------------------------------------------------------------------ +/// \ru Отцепить объект из владеющего указателя. \en Detach object from owning pointer. +// --- +template +inline Type * DetachItem( SPtr & itemOwner ) +{ + return itemOwner.detach(); +} + + +//------------------------------------------------------------------------------ +/// \ru Заменить объект на копию. \en Replace object by copy. +// --- +template +void ReplaceByCopy( Type *& item ) +{ + if ( item != NULL ) { + Type * temp = (Type *)&item->Duplicate(); + ::DeleteItem( item ); + item = temp; + } +} + + +//------------------------------------------------------------------------------ +/// \ru Включить габариты объектов массива в общий габарит. \en Include bounding boxes of an array of objects in a common bounding box. +//--- +template +void AddYourGabaritTo( Objects & objects, Gab & gab ) +{ + for ( size_t k = 0, cnt = objects.size(); k < cnt; ++k ) { + if ( objects[k] ) + objects[k]->AddYourGabaritTo( gab ); + } +} + + +#endif // __REFERENCE_ITEM_H diff --git a/C3d/Include/region.h b/C3d/Include/region.h index 8cde1e6..cc0c05c 100644 --- a/C3d/Include/region.h +++ b/C3d/Include/region.h @@ -64,19 +64,19 @@ public: virtual MbePlaneType Type() const; // \ru Групповой тип объекта. \en Group type of object. virtual MbePlaneType Family() const; // \ru Семейство объекта. \en Family of object. virtual MbPlaneItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию. \en Create a copy - virtual void Transform( const MbMatrix & matr, MbRegTransform * iReg = NULL, const MbSurface * newSurface = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. - virtual void Move ( const MbVector & to, MbRegTransform * iReg = NULL, const MbSurface * newSurface = NULL ); // \ru Сдвинуть вдоль вектора. \en Move along a vector. - virtual void Rotate( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * iReg = NULL, const MbSurface * newSurface = NULL ); - virtual bool IsSame ( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Determine whether objects are equal. - virtual bool SetEqual ( const MbPlaneItem & item ); // \ru Сделать объекты равным. \en Make objects equal. - virtual void AddYourGabaritTo( MbRect & r ) const; // \ru Добавить свой габарит в присланный габарит. \en Add your own bounding box into the sent bounding box. - virtual bool IsVisibleInRect( const MbRect & r, bool exact = false ) const; // \ru Виден ли объект в заданном прямоугольнике. \en Whether the object is visible in the given rectangle - virtual double DistanceToPoint( const MbCartPoint & to ) const; // \ru Вычислить расстояние до точки to. \en Calculate the distance to a point 'to'. - virtual bool DistanceToPointIfLess( const MbCartPoint & to, - double & distance ) const; // \ru Вычислить расстояние до точки to, если оно меньше d. \en Calculate the distance to the point 'to' if it is less than d. - virtual void Refresh(); // \ru Сбросить все временные данные. \en Reset all temporary data. + virtual void Transform( const MbMatrix &, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. + virtual void Move ( const MbVector &, MbRegTransform * = NULL, const MbSurface * newSurface = NULL ); // \ru Сдвинуть вдоль вектора. \en Move along a vector. + virtual void Rotate ( const MbCartPoint &, const MbDirection & angle, MbRegTransform * iReg = NULL, const MbSurface * newSurface = NULL ); + virtual bool IsSame ( const MbPlaneItem &, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Determine whether objects are equal. + virtual bool SetEqual ( const MbPlaneItem & ); // \ru Сделать объекты равным. \en Make objects equal. + virtual void AddYourGabaritTo( MbRect & ) const; // \ru Добавить свой габарит в присланный габарит. \en Add your own bounding box into the sent bounding box. + virtual bool IsVisibleInRect( const MbRect &, bool exact = false ) const; // \ru Виден ли объект в заданном прямоугольнике. \en Whether the object is visible in the given rectangle + virtual double DistanceToPoint( const MbCartPoint & ) const; // \ru Вычислить расстояние до точки to. \en Calculate the distance to a point 'to'. + virtual bool DistanceToPointIfLess( const MbCartPoint &, + double & distance ) const; // \ru Вычислить расстояние до точки to, если оно меньше d. \en Calculate the distance to the point 'to' if it is less than d. + virtual void Refresh(); // \ru Сбросить все временные данные. \en Reset all temporary data. - virtual MbProperty & CreateProperty( MbePrompt name ) const; // \ru Создать собственное свойство. \en Create a custom property. + virtual MbProperty & CreateProperty( MbePrompt ) const; // \ru Создать собственное свойство. \en Create a custom property. virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта. \en Set properties of the object. virtual void GetBasisPoints( MbControlData & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. @@ -107,7 +107,21 @@ public: /// \ru Отсоединить используемые контуры и удалить остальные. \en Detach used contours and delete other. void DeleteContours(); /// \ru Отцепить все контуры региона без удаления. \en Detach all region contours without deletion. - void DetachContours( RPArray & ); + template + void DetachContours( ContoursVector & dstContours ) + { + size_t addCnt = contours.size(); + dstContours.reserve( dstContours.size() + addCnt ); + c3d::PlaneContourSPtr contour; + for ( size_t k = 0; k < addCnt; ++k ) { + ::DecRefItem( contours[k] ); + contour = contours[k]; + dstContours.push_back( contour ); + ::DetachItem( contour ); + } + contours.clear(); + setCorrect = ts_neutral; + } /// \ru Сделать регион корректным (если это нужно и возможно). \en Make region correct (if this is possible). bool SetCorrect(); diff --git a/C3d/Include/sheet_metal_param.h b/C3d/Include/sheet_metal_param.h index da57f9b..3ee36ae 100644 --- a/C3d/Include/sheet_metal_param.h +++ b/C3d/Include/sheet_metal_param.h @@ -111,6 +111,11 @@ public: return isSame; } + ///\ru Трансформировать по матрице. \en To transform by the matrix. + void Transform( const MbMatrix3D & matr ) { + matr.TransformLength( radius ); + } + KNOWN_OBJECTS_RW_REF_OPERATORS( MbBendValues ) // \ru Для работы со ссылками и объектами класса \en For treatment of references and objects of the class }; @@ -268,6 +273,12 @@ public: return isSame; } + void Transform( const MbMatrix3D & matr ) { + matr.TransformLength( width ); + matr.TransformLength( depth ); + matr.TransformLength( radius ); + } + KNOWN_OBJECTS_RW_REF_OPERATORS( MbSlotValues ) // \ru Для работы со ссылками и объектами класса \en For treatment of references and objects of the class }; @@ -553,6 +564,13 @@ struct MATH_CLASS MbClosedCornerValues { return isSame; } + ///\ru Трансформировать по матрице. \en To transform by the matrix. + void Transform( const MbMatrix3D & matr ) { + matr.TransformLength( gap ); + matr.TransformLength( diameter ); + matr.TransformLength( shift ); + } + KNOWN_OBJECTS_RW_REF_OPERATORS( MbClosedCornerValues ) // \ru Для работы со ссылками и объектами класса \en For treatment of references and objects of the class }; @@ -663,6 +681,14 @@ public: return isSame; } + + ///\ru Трансформировать по матрице. \en To transform by the matrix. + void Transform( const MbMatrix3D & matr ) { + matr.TransformLength( distance ); + matr.TransformLength( widening ); + matr.TransformLength( length ); + } + }; // \ru Параметры одного края сгиба \en Parameters of one boundary of bend public: @@ -806,6 +832,18 @@ public: return isSame; } + ///\ru Трансформировать по матрице. \en To transform by the matrix. + void Transform( const MbMatrix3D & matr ) { + MbBendValues::Transform( matr ); + matr.TransformLength( deepness ); + sideLeft.Transform( matr ); + sideRight.Transform( matr ); + miterBegin.Transform( matr ); + miterEnd.Transform( matr ); + miterMiddle.Transform( matr ); + slot.Transform( matr ); + } + KNOWN_OBJECTS_RW_REF_OPERATORS( MbBendByEdgeValues ) // \ru Для работы со ссылками и объектами класса \en For treatment of references and objects of the class }; @@ -1645,9 +1683,9 @@ public: /// \ru Конструктор по умолчанию. \en Default constructor. MbSMBendNames() : MbBendValues(), - segName1 ( SimpleName(SIMPLENAME_MAX) ), - segName2 ( SimpleName(SIMPLENAME_MAX) ), - extraName( SimpleName(SIMPLENAME_MAX) ), + segName1 ( SimpleName(c3d::SIMPLENAME_MAX) ), + segName2 ( SimpleName(c3d::SIMPLENAME_MAX) ), + extraName( SimpleName(c3d::SIMPLENAME_MAX) ), groupNumber( SYS_MAX_UINT ), innerFaceName(), outerFaceName() {} @@ -1857,51 +1895,127 @@ private: //------------------------------------------------------------------------------ /** \brief \ru Параметры штамповки телом-инструментом. \en The parameters of stamping by a tool solid. \~ - \details \ru Параметры шатмповки телом-инструментом определяют толщину формованной части и радиус скругления основания.\n - \en The parameters of stamping by a tool solid is specified a thickness of a stamped part and fillet radius of stamping base.\n \~ + \details \ru Параметры шатмповки телом-инструментом определяют толщину формованной части и радиусы скругления основания.\n + \en The parameters of stamping by a tool solid is specified a thickness of a stamped part and fillet radiuses of stamping base.\n \~ \ingroup Build_Parameters */ // --- -struct MATH_CLASS MbUserStampingValues { - double baseFilletRadius; ///< \ru Радиус скругления основания (отрицательное значение запрещает скругление). \en Fillet radius of base (negative value prohibits fillet). - double stampThickness; ///< \ru Толщина формованной части. \en Thickness of a stamped part. +struct MATH_CLASS MbToolStampingValues { + double punchFilletRadius; ///< \ru Радиус скругления основания со стороны пуансона (отрицательное значение запрещает скругление). \en Punch fillet radius of base (negative value prohibits fillet). + double dieFilletRadius; ///< \ru Радиус скругления основания со стороны матрицы (отрицательное значение запрещает скругление). \en Die fillet radius of base (negative value prohibits fillet). + double stampThickness; ///< \ru Толщина формованной части. \en Thickness of a stamped part. + bool constantThickness; ///< \ru Флаг постоянной толщины на штамповке. \en Constant thickness flag. /// \ru Конструктор по умолчанию. \en Default constructor. - MbUserStampingValues() : - baseFilletRadius ( 0.0 ), - stampThickness ( 0.0 ) + MbToolStampingValues() : + punchFilletRadius( 0.0 ), + dieFilletRadius ( 0.0 ), + stampThickness ( 0.0 ), + constantThickness( true ) {} /// \ru Конструктор копирования. \en Copy-constructor. - MbUserStampingValues( const MbUserStampingValues & other ) : - baseFilletRadius ( other.baseFilletRadius ), - stampThickness ( other.stampThickness ) + MbToolStampingValues( const MbToolStampingValues & other ) : + punchFilletRadius( other.punchFilletRadius ), + dieFilletRadius ( other.dieFilletRadius ), + stampThickness ( other.stampThickness ), + constantThickness( other.constantThickness ) {} /// \ru Конструктор по конкретным параметрам. \en Constructor by specific parameters. - MbUserStampingValues( double baseRad, double thick ) : - baseFilletRadius ( baseRad ), - stampThickness ( thick ) + MbToolStampingValues( double punchRad, double dieRad, double thick, bool constThick ) : + punchFilletRadius( punchRad ), + dieFilletRadius ( dieRad ), + stampThickness ( thick ), + constantThickness( constThick ) {} /// \ru Оператор присваивания. \en Assignment operator. - MbUserStampingValues & operator = ( const MbUserStampingValues &other ) { Init( other ); return *this; } + MbToolStampingValues & operator = ( const MbToolStampingValues &other ) { Init( other ); return *this; } /// \ru Инициализация по другому объекту. \en Initialization by another object. - void Init( const MbUserStampingValues & other ) { - baseFilletRadius = other.baseFilletRadius; + void Init( const MbToolStampingValues & other ) { + punchFilletRadius = other.punchFilletRadius; + dieFilletRadius = other.dieFilletRadius; stampThickness = other.stampThickness; + constantThickness = other.constantThickness; } ///\ru Являются ли объекты равными? \en Determine whether an object is equal? - bool IsSame( const MbUserStampingValues & other, double accuracy ) const { + bool IsSame( const MbToolStampingValues & other, double accuracy ) const { bool isSame = false; - if ( ::fabs(baseFilletRadius - other.baseFilletRadius) < accuracy && - ::fabs(stampThickness - other.stampThickness) < accuracy ) + if ( ::fabs(punchFilletRadius - other.punchFilletRadius) < accuracy && + ::fabs(dieFilletRadius - other.dieFilletRadius) < accuracy && + ::fabs(stampThickness - other.stampThickness) < accuracy && + constantThickness == other.constantThickness ) isSame = true; return isSame; } - KNOWN_OBJECTS_RW_REF_OPERATORS( MbUserStampingValues ) // \ru Для работы со ссылками и объектами класса \en For treatment of references and objects of the class + KNOWN_OBJECTS_RW_REF_OPERATORS( MbToolStampingValues ) // \ru Для работы со ссылками и объектами класса \en For treatment of references and objects of the class }; -#endif // __SHEET_METAL_PARAM_H \ No newline at end of file + +//------------------------------------------------------------------------------ +/** \brief \ru Параметры построения листового тела по произвольному телу. + \en The parameters of sheet metal solid building based on an arbitrary solid. \~ + \details \ru Параметры построения оболочки из листового материала на основе граней и ребер произвольного тела.\n + \en The parameters of the sheet metal shell based on faces and edges of an arbitrary solid.\n \~ + \ingroup Build_Parameters + \warning \ru В разработке. + \en Under development. \~ +*/ +// --- +struct MATH_CLASS MbSolidToSheetMetalValues { +public: + double k; ///< \ru Коэффициент, определяющий положение нейтрального слоя. \en Coefficient determining the position of the neutral layer. + double sheetThickness; ///< \ru Толщина листового тела. \en Thickness of a sheet solid. + double bendRadius; ///< \ru Внутренний радиус сгиба. \en The internal radius of the bend. + SArray bendEdgesIndices; ///< \ru Индексы ребер сгиба. \en Indicies of bend edges. + SArray cutEdgesIndices; ///< \ru Индексы ребер разреза. \en Indicies of cut edges. + + /// \ru Конструктор по умолчанию. \en Default constructor. + MbSolidToSheetMetalValues() : + sheetThickness ( 1.0 ), + bendRadius ( 0.0 ), + bendEdgesIndices( ), + cutEdgesIndices ( ) + {} + /// \ru Конструктор копирования. \en Copy-constructor. + MbSolidToSheetMetalValues( const MbSolidToSheetMetalValues & other ) : + sheetThickness ( other.sheetThickness ), + bendRadius ( other.bendRadius ), + bendEdgesIndices( other.bendEdgesIndices ), + cutEdgesIndices ( other.cutEdgesIndices ) + {} + /// \ru Конструктор по конкретным параметрам. \en Constructor by specific parameters. + MbSolidToSheetMetalValues( double thick, double rad, SArray & bIndicies, SArray & cIndicies ) : + sheetThickness ( thick ), + bendRadius ( rad ), + bendEdgesIndices( bIndicies ), + cutEdgesIndices ( cIndicies ) + {} + + /// \ru Оператор присваивания. \en Assignment operator. + MbSolidToSheetMetalValues & operator = ( const MbSolidToSheetMetalValues &other ) { Init( other ); return *this; } + /// \ru Инициализация по другому объекту. \en Initialization by another object. + void Init( const MbSolidToSheetMetalValues & other ) { + sheetThickness = other.sheetThickness; + bendRadius = other.bendRadius; + bendEdgesIndices = other.bendEdgesIndices; + cutEdgesIndices = other.cutEdgesIndices; + } + + ///\ru Являются ли объекты равными? \en Determine whether an object is equal? + bool IsSame( const MbSolidToSheetMetalValues & other, double accuracy ) const { + bool isSame = false; + if ( ::fabs(sheetThickness - other.sheetThickness) < accuracy && + ::fabs(bendRadius - other.bendRadius) < accuracy ) + isSame = true; + + return isSame; + } + + KNOWN_OBJECTS_RW_REF_OPERATORS( MbSolidToSheetMetalValues ) // \ru Для работы со ссылками и объектами класса \en For treatment of references and objects of the class +}; + +#endif // __SHEET_METAL_PARAM_H diff --git a/C3d/Include/shell_history.h b/C3d/Include/shell_history.h index 5fddebc..1f117b6 100644 --- a/C3d/Include/shell_history.h +++ b/C3d/Include/shell_history.h @@ -52,12 +52,8 @@ public: RPArray & SetCopyFaces() { return copyFaces; } /// \ru Заменить в shell неизменённые copy-объекты на origin-объекты. \en Replace in 'shell' the unchanged 'copy'-objects by the 'origin'-objects. void SetOrigins ( MbFaceShell & shell ); - -private: - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. - MbShellHistory( const MbShellHistory & ); // \ru Не реализовано \en Not implemented - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - void operator = ( const MbShellHistory & ); // \ru Не реализовано \en Not implemented + +OBVIOUS_PRIVATE_COPY( MbShellHistory ) }; diff --git a/C3d/Include/solid.h b/C3d/Include/solid.h index 2ceecc6..2aaafcb 100644 --- a/C3d/Include/solid.h +++ b/C3d/Include/solid.h @@ -143,7 +143,7 @@ public : virtual void Transform( const MbMatrix3D &, MbRegTransform * iReg = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. virtual void Move ( const MbVector3D &, MbRegTransform * iReg = NULL ); // \ru Сдвинуть вдоль вектора. \en Move along a vector. virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * iReg = NULL ); // \ru Повернуть вокруг оси. \en Rotate around an axis. - virtual bool IsSame ( const MbSpaceItem &, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Determine whether objects are equal. + virtual bool IsSame ( const MbSpaceItem &, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Determine whether objects are equal. virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными? \en Determine whether objects are similar. virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать объекты равными. \en Make the objects equal. virtual double DistanceToPoint( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. @@ -396,7 +396,7 @@ public : MbFace * FindFaceByName ( const MbName & ); /// \ru Создать именователь тела. \en Create name-maker of solid. - MbSNameMaker GetYourName() const; + MbSNameMaker GetYourNameMaker() const; /// \ru Установить заданный флаг измененности для всех граней, рёбер и вершин. \en Set flag of changes for all the faces, edges and vertices. void SetOwnChangedThrough( MbeChangedType ); diff --git a/C3d/Include/space_instance.h b/C3d/Include/space_instance.h index cf887f5..63ba5d0 100644 --- a/C3d/Include/space_instance.h +++ b/C3d/Include/space_instance.h @@ -1,121 +1,121 @@ -////////////////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Вставка трёхмерного объекта. - \en Instance of three-dimensional object. \~ - -*/ -////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef __SPACE_INSTANCE_H -#define __SPACE_INSTANCE_H - - -#include -#include -#include -#include - - -class MATH_CLASS MbLegend; -class MATH_CLASS MbPoint3D; -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbSurface; - - -class MATH_CLASS MbSpaceInstance; -namespace c3d // namespace C3D -{ -typedef SPtr SInstanceSPtr; -typedef SPtr ConstSInstanceSPtr; - -typedef std::vector SInstancesVector; -typedef std::vector ConstSInstancesVector; - -typedef std::vector SInstancesSPtrVector; -typedef std::vector ConstSInstancesSPtrVector; -} - - -//---------------------------------------------------------------------------------------- -/** \brief \ru Вставка трёхмерного объекта. - \en Instance of three-dimensional object. \~ - \details \ru Вставка позволяет работать с трёхмерным геометрическим объектом, как с - объектом геометричекой модели. Вставка позволяет использовать в геометричекой - модели любые другие объекты MbSpaceItem, например, резьбу и условные обозначения. - \en Instance allows to deal with three-dimensional object as with object of - geometric model. Instance allows to use any objects inherited from MbSpaceItem - such as thread and conventional notations in geometric model. \~ - \ingroup Model_Items -*/ -// --- -class MATH_CLASS MbSpaceInstance : public MbItem { -protected : - MbSpaceItem * spaceItem; ///< \ru Трёхмерный геометрический объект. \en Three-dimensional geometric object. - -protected : - /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. - explicit MbSpaceInstance( const MbSpaceInstance &, MbRegDuplicate * ); -public : - /// \ru Конструктор по вспомогательному объекту. \en Constructor by auxiliary item. - MbSpaceInstance( MbLegend & ); - /// \ru Конструктор по точке. \en Constructor by point. - MbSpaceInstance( MbPoint3D & ); - /// \ru Конструктор по кривой. \en Constructor by curve. - MbSpaceInstance( MbCurve3D & ); - /// \ru Конструктор по поверхности. \en Constructor by surface. - MbSpaceInstance( MbSurface & ); - /// \ru Деструктор. \en Destructor. - virtual ~MbSpaceInstance(); - -public : - VISITING_CLASS( MbSpaceInstance ); - - // \ru Общие функции геометрического объекта. \en Common functions of a geometric object. - virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en A type of an object. - virtual MbSpaceItem & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Создать копию. \en Create a copy. - virtual void Transform( const MbMatrix3D &, MbRegTransform * iReg = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. - virtual void Move ( const MbVector3D &, MbRegTransform * iReg = NULL ); // \ru Сдвинуть вдоль вектора. \en Translate along a vector. - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * iReg = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Are the objects equal? - virtual bool IsSimilar( const MbSpaceItem & init ) const; // \ru Являются ли объекты подобными? \en Are the objects similar? - virtual bool SetEqual ( const MbSpaceItem & init ); // \ru Сделать объекты равными. \en Make the objects equal. - virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. - virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Добавь свой габарит в куб. \en Add bounding box into a cube. - virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate bounding box in the local coordinate system. - virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. - - virtual MbProperty & CreateProperty( MbePrompt n ) const; // \ru Создать собственное свойство. \en Create a custom property. - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & properties ); // \ru Установить свойства объекта. \en Set properties of the object. - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - // \ru Создать полигональный объект - упрощенную копию данного объекта. \en Create a polygonal object - a simplified copy of the given object. - virtual MbItem * CreateMesh( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * iReg ) const; - // \ru Найти объект по геометрическому объекту (MbSpaceItem). \en Find the object by a geometric object (MbSpaceItem). - virtual const MbItem * FindItem( const MbSpaceItem * s, MbPath & path, MbMatrix3D & from ) const; - // \ru Дать все объекты указанного типа. \en Get all objects by type. \~ - virtual bool GetItems( MbeSpaceType type, const MbMatrix3D & from, - RPArray & items, SArray & matrs ); - // \ru Дать все уникальные объекты указанного типа. \en Get all unique objects by type . \~ - virtual bool GetUniqItems( MbeSpaceType type, CSSArray & items ) const; - - /** \ru \name Общие функции вставки трёхмерного объекта. - \en \name Common functions of instance of three-dimensional object. - \{ */ - /// \ru Выдать трёхмерный геометрический объект. \en Get three-dimensional geometric object. - const MbSpaceItem * GetSpaceItem() const; - /// \ru Выдать трёхмерный геометрический объект для модификации. \en Get three-dimensional geometric object for modification. - MbSpaceItem * SetSpaceItem(); - /// \ru Заменить геометрический объект. \en Replace geometric object. - void SetSpaceItem( MbSpaceItem * init ); - /** \} */ - -DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSpaceInstance ) -OBVIOUS_PRIVATE_COPY( MbSpaceInstance ) -}; - -IMPL_PERSISTENT_OPS( MbSpaceInstance ) - -#endif // __SPACE_INSTANCE_H +////////////////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Вставка трёхмерного объекта. + \en Instance of three-dimensional object. \~ + +*/ +////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef __SPACE_INSTANCE_H +#define __SPACE_INSTANCE_H + + +#include +#include +#include +#include + + +class MATH_CLASS MbLegend; +class MATH_CLASS MbPoint3D; +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbSurface; + + +class MATH_CLASS MbSpaceInstance; +namespace c3d // namespace C3D +{ +typedef SPtr SInstanceSPtr; +typedef SPtr ConstSInstanceSPtr; + +typedef std::vector SInstancesVector; +typedef std::vector ConstSInstancesVector; + +typedef std::vector SInstancesSPtrVector; +typedef std::vector ConstSInstancesSPtrVector; +} + + +//---------------------------------------------------------------------------------------- +/** \brief \ru Вставка трёхмерного объекта. + \en Instance of three-dimensional object. \~ + \details \ru Вставка позволяет работать с трёхмерным геометрическим объектом, как с + объектом геометричекой модели. Вставка позволяет использовать в геометричекой + модели любые другие объекты MbSpaceItem, например, резьбу и условные обозначения. + \en Instance allows to deal with three-dimensional object as with object of + geometric model. Instance allows to use any objects inherited from MbSpaceItem + such as thread and conventional notations in geometric model. \~ + \ingroup Model_Items +*/ +// --- +class MATH_CLASS MbSpaceInstance : public MbItem { +protected : + MbSpaceItem * spaceItem; ///< \ru Трёхмерный геометрический объект. \en Three-dimensional geometric object. + +protected : + /// \ru Конструктор копирования с регистратором. \en Copy-constructor with the registrator. + explicit MbSpaceInstance( const MbSpaceInstance &, MbRegDuplicate * ); +public : + /// \ru Конструктор по вспомогательному объекту. \en Constructor by auxiliary item. + MbSpaceInstance( MbLegend & ); + /// \ru Конструктор по точке. \en Constructor by point. + MbSpaceInstance( MbPoint3D & ); + /// \ru Конструктор по кривой. \en Constructor by curve. + MbSpaceInstance( MbCurve3D & ); + /// \ru Конструктор по поверхности. \en Constructor by surface. + MbSpaceInstance( MbSurface & ); + /// \ru Деструктор. \en Destructor. + virtual ~MbSpaceInstance(); + +public : + VISITING_CLASS( MbSpaceInstance ); + + // \ru Общие функции геометрического объекта. \en Common functions of a geometric object. + virtual MbeSpaceType IsA() const; // \ru Тип объекта. \en A type of an object. + virtual MbSpaceItem & Duplicate( MbRegDuplicate * iReg = NULL ) const; // \ru Создать копию. \en Create a copy. + virtual void Transform( const MbMatrix3D &, MbRegTransform * iReg = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. + virtual void Move ( const MbVector3D &, MbRegTransform * iReg = NULL ); // \ru Сдвинуть вдоль вектора. \en Translate along a vector. + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * iReg = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Are the objects equal? + virtual bool IsSimilar( const MbSpaceItem & init ) const; // \ru Являются ли объекты подобными? \en Are the objects similar? + virtual bool SetEqual ( const MbSpaceItem & init ); // \ru Сделать объекты равными. \en Make the objects equal. + virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. + virtual void AddYourGabaritTo( MbCube & r ) const; // \ru Добавь свой габарит в куб. \en Add bounding box into a cube. + virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; // \ru Рассчитать габарит в локальной системы координат. \en Calculate bounding box in the local coordinate system. + virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. + + virtual MbProperty & CreateProperty( MbePrompt n ) const; // \ru Создать собственное свойство. \en Create a custom property. + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & properties ); // \ru Установить свойства объекта. \en Set properties of the object. + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + // \ru Создать полигональный объект - упрощенную копию данного объекта. \en Create a polygonal object - a simplified copy of the given object. + virtual MbItem * CreateMesh( const MbStepData & stepData, const MbFormNote & note, MbRegDuplicate * iReg ) const; + // \ru Найти объект по геометрическому объекту (MbSpaceItem). \en Find the object by a geometric object (MbSpaceItem). + virtual const MbItem * FindItem( const MbSpaceItem * s, MbPath & path, MbMatrix3D & from ) const; + // \ru Дать все объекты указанного типа. \en Get all objects by type. \~ + virtual bool GetItems( MbeSpaceType type, const MbMatrix3D & from, + RPArray & items, SArray & matrs ); + // \ru Дать все уникальные объекты указанного типа. \en Get all unique objects by type . \~ + virtual bool GetUniqItems( MbeSpaceType type, CSSArray & items ) const; + + /** \ru \name Общие функции вставки трёхмерного объекта. + \en \name Common functions of instance of three-dimensional object. + \{ */ + /// \ru Выдать трёхмерный геометрический объект. \en Get three-dimensional geometric object. + const MbSpaceItem * GetSpaceItem() const; + /// \ru Выдать трёхмерный геометрический объект для модификации. \en Get three-dimensional geometric object for modification. + MbSpaceItem * SetSpaceItem(); + /// \ru Заменить геометрический объект. \en Replace geometric object. + void SetSpaceItem( MbSpaceItem * init ); + /** \} */ + +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSpaceInstance ) +OBVIOUS_PRIVATE_COPY( MbSpaceInstance ) +}; + +IMPL_PERSISTENT_OPS( MbSpaceInstance ) + +#endif // __SPACE_INSTANCE_H diff --git a/C3d/Include/space_item.h b/C3d/Include/space_item.h index a89a3ab..3b014ad 100644 --- a/C3d/Include/space_item.h +++ b/C3d/Include/space_item.h @@ -148,6 +148,7 @@ enum MbeSpaceType { st_ConicUnbendedSurface = 345, ///< \ru Поверхность, полученная коническим разгибом. \en Surface constructed by conical unbending. st_GregoryRibbonPatchSurface= 346, ///< \ru Поверхность Грегори с граничными условиями. \en Gregory patch surface with ribbons. st_ExplorationSurface = 347, ///< \ru Поверхность заметания с масштабированием и поворотом образующей кривой. \en Swept surface with scaling and winding of generating curve. + st_SectionSurface = 348, ///< \ru Поверхность заметания переменного сечения. \en The swept mutable section surface. st_FreeSurface = 400, ///< \ru Тип для поверхностей, созданных пользователем. \en Type for the user-defined surfaces. \n // \ru Типы вспомогательных объектов. \en Helper object types. @@ -451,7 +452,7 @@ public : and make a reference to the written instance of the other records. Reading is performed once, and there is substitution of already read object for all the remaining reading cases. */ - void PrepareWrite() { SetRegistrable( (GetUseCount() > 1) ? registrable : noRegistrable ); } + void PrepareWrite() const { SetRegistrable( (GetUseCount() > 1) ? registrable : noRegistrable ); } DECLARE_PERSISTENT_CLASS( MbSpaceItem ) OBVIOUS_PRIVATE_COPY( MbSpaceItem ) diff --git a/C3d/Include/surf_chamfer_surface.h b/C3d/Include/surf_chamfer_surface.h index c7c09a0..07fe82a 100644 --- a/C3d/Include/surf_chamfer_surface.h +++ b/C3d/Include/surf_chamfer_surface.h @@ -184,6 +184,9 @@ public: \{ */ virtual double CurvatureV ( double u, double v ) const; // \ru Kривизна вдоль v. \en Curvature along v. + virtual void CalculateGabarit( MbCube & ) const; // \ru Выдать габарит. \en Get the bounding box. + virtual void CalculateLocalGabarit( const MbMatrix3D &, MbCube & ) const; // \ru Рассчитать габарит относительно л.с.к.. \en Calculate bounding box relative to the local coordinate system. + virtual MbSurface * Offset( double d, bool same ) const; // \ru Создание эквидистантной поверхности. \en Creation of an offset surface. virtual MbSplineSurface * NurbsSurface( double, double, double, double, bool bmatch = false ) const; // \ru Построить NURBS копию поверхности. \en Construct a NURBS copy of the surface. diff --git a/C3d/Include/surf_channel_surface.h b/C3d/Include/surf_channel_surface.h index 27c8a1c..1cc3bad 100644 --- a/C3d/Include/surf_channel_surface.h +++ b/C3d/Include/surf_channel_surface.h @@ -1,275 +1,273 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Поверхность скругления с переменным радиусом обычная или с сохранением кромки. - \en Fillet surface with variable radius is normal or with preservation of edges. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __SURF_CHANNEL_SURFACE_H -#define __SURF_CHANNEL_SURFACE_H - - -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Поверхность скругления с переменным радиусом обычная или с сохранением кромки. - \en Fillet surface with variable radius is normal or with preservation of edges. \~ - \details \ru Поверхность скругления с переменным радиусом является NURBS-поверхностью, - построенной по трём кривым: curve1, curve0, curve2. - Первый параметр поверхности совпадает с параметром кривых curve1, curve0, curve2. - Второй параметр изменяется от нуля (точки совпадают с curve1) до единицы (точки совпадают с curve2). - Функция function определяет изменение радиуса и равна отношению текущего радиуса к заданному для поверхности радиусу. - Параметр функции радиуса совпадает с параметром кривых curve1, curve0, curve2. - Если коэффициент формы conic = _ARC_ ( 0 ), то вес каждой точки кривой curve0 задаётся функцией weights0 и - вычислен так, что сечение поверхности вдоль её второго параметра будет дугой окружности, - то есть при любом параметре u три точки curve1(u), curve0(u), curve2(u) определяют NURBS-кривую в форме дуги окружности. - Если коэффициент формы conic != _ARC_, то вес каждой точки кривой curve0 равен conic / ( 1.0 - conic ). - При conic = 0.5 сечение поверхности вдоль её второго параметра будет параболой. \n - \en Fillet surface with variable radius is NURBS-surface - constructed on three curves: curve1, curve0, curve2. - The first surface parameter coincides with the parameter of curve1, curve0, curve2 curves. - The second parameter is changed from zero (points coincide with curve1) to unit (points coincide with curve2). - Function "function" determines the change of the radius and equals the ratio of the current radius to given for surface. - The parameter of radius function coincides with the parameter of curves curve1, curve0, curve2. - If coefficient of shape conic = _ARC_ ( 0 ), then the weight of each point of the curve0 curve determined by the function weights0 and - calculated so that the section of surface along its second parameter is a circular arc - i.e. for any parameter u three points of curve1(u), curve0(u), curve2(u) determine the NURBS-curve with the shape of a circular arc. - If coefficient of shape conic != _ARC_, then the weight of each point of the curve0 curve is equal to conic / ( 1.0 - conic ). - If conic = 0.5, then the surface section along its second parameter is parabola. \n \~ - \ingroup Surfaces -*/ -// --- -class MATH_CLASS MbChannelSurface : public MbFilletSurface { -private: - MbFunction * function; ///< \ru Функция изменения радиуса (переменный коэффициент). \en Function of change of the radius (variable coefficient). - -public: - - /** \brief \ru Конструктор по двум кривым и типу сопряжения. - \en Constructor by two curves and type of mate. \~ - \details \ru Конструктор по двум кривым и типу сопряжения. - \en Constructor by two curves and type of mate. \~ - \param[in] curv1 - \ru Опорная кривая на первой поверхности - \en Support curve on the first surface \~ - \param[in] curv2 - \ru Опорная кривая на второй поверхности - \en Support curve on the second surface \~ - \param[in] d1 - \ru Радиус скругления со знаком для поверхности кривой crve1 - \en Fillet radius with sign for surface of crve1 curve \~ - \param[in] d2 - \ru Радиус скругления со знаком для поверхности кривой crve2 - \en Fillet radius with sign for surface of crve2 curve \~ - \param[in] fm - \ru Тип сопряжения: \n - st_Span - скругление с заданной хордой \n - st_Fillet - скругление с заданными радиусами - \en Mate type: \n - st_Span - fillet with a given chord \n - st_Fillet - fillet with given radii. \~ - \param[in] cn - \ru Коэффициент формы, изменяется от 0.05 до 0.95 (при 0.5 - дуга окружности) - \en Coefficient of shape is changed from 0.05 to 0.95 (if 0.5 - circular arc) \~ - \param[in] func - \ru Функция изменения радиуса - \en Function of change of the radius \~ - \param[in] ev - \ru Равномерная параметризация по дуге или нет - \en Uniform parametrization by arc or not \~ - */ - MbChannelSurface( MbSurfaceCurve & curv1, MbSurfaceCurve & curv2, - double d1, double d2, MbeSmoothForm fm, double cn, MbFunction & func, bool ev ); - - /** \brief \ru Конструктор по двум кривым и типу сопряжения. - \en Constructor by two curves and type of mate. \~ - \details \ru Конструктор поверхности с сохранением кромки по двум кривым и типу сопряжения. - \en Constructor of surface with preservation of edges by two curves and mate type. \~ - \param[in] curv1 - \ru Опорная кривая на первой поверхности - \en Support curve on the first surface \~ - \param[in] curv2 - \ru Опорная кривая на второй поверхности - \en Support curve on the second surface \~ - \param[in] d1 - \ru Радиус скругления со знаком для поверхности кривой crve1 - \en Fillet radius with sign for surface of crve1 curve \~ - \param[in] d2 - \ru Радиус скругления со знаком для поверхности кривой crve2 - \en Fillet radius with sign for surface of crve2 curve \~ - \param[in] fm - \ru Тип сопряжения: \n - st_Span - скругление с заданной хордой \n - st_Fillet - скругление с заданными радиусами - \en Mate type: \n - st_Span - fillet with a given chord \n - st_Fillet - fillet with given radii. \~ - \param[in] cn - \ru Коэффициент формы, изменяется от 0.05 до 0.95 (при 0.5 - дуга окружности) - \en Coefficient of shape is changed from 0.05 to 0.95 (if 0.5 - circular arc) \~ - \param[in] func - \ru Функция изменения радиуса - \en Function of change of the radius \~ - \param[in] byFirst - \ru true - кривая curve2 является кромкой, false - кривая curve1 является кромкой - \en True - curve2 curve is edge, false - curve1 curve is edge \~ - \param[in] ev - \ru Равномерная параметризация по дуге или нет - \en Uniform parametrization by arc or not \~ - */ - MbChannelSurface( MbSurfaceCurve & curv1, MbSurfaceCurve & curv2, - double d1, double d2, MbeSmoothForm fm, double cn, MbFunction & func, bool byFirst, bool ev ); - - /** \brief \ru Конструктор по двум кривым и типу сопряжения. - \en Constructor by two curves and type of mate. \~ - \details \ru Конструктор поверхности с сохранением кромки по двум кривым и типу сопряжения. - \en Constructor of surface with preservation of edges by two curves and mate type. \~ - \param[in] surf1 - \ru Первая поверхность - \en First surface \~ - \param[in] curv1 - \ru Опорная кривая в параметрах первой поверхности - \en Support curve at parameters of the first surface \~ - \param[in] surf2 - \ru Вторая поверхность - \en Second surface \~ - \param[in] curv2 - \ru Опорная кривая в параметрах второй поверхности - \en Support curve at parameters of the second surface \~ - \param[in] curv0 - \ru Кривая пересеченния касательных к поверхностям - \en Intersection curve of tangents to surfaces \~ - \param[in] weig0 - \ru Функция изменения веса. - \en Function of change of the weights. \~ - \param[in] d1 - \ru Радиус скругления со знаком для поверхности кривой crve1 - \en Fillet radius with sign for surface of crve1 curve \~ - \param[in] d2 - \ru Радиус скругления со знаком для поверхности кривой crve2 - \en Fillet radius with sign for surface of crve2 curve \~ - \param[in] fm - \ru Тип сопряжения: \n - st_Span - скругление с заданной хордой \n - st_Fillet - скругление с заданными радиусами - \en Mate type: \n - st_Span - fillet with a given chord \n - st_Fillet - fillet with given radii. \~ - \param[in] cn - \ru Коэффициент формы, изменяется от 0.05 до 0.95 (при 0.5 - дуга окружности) - \en Coefficient of shape is changed from 0.05 to 0.95 (if 0.5 - circular arc) \~ - \param[in] func - \ru Функция изменения радиуса. - \en Function of change of the radius. \~ - \param[in] byFirst - \ru true - кривая curve2 является кромкой, false - кривая curve1 является кромкой - \en True - curve2 curve is edge, false - curve1 curve is edge \~ - \param[in] ev - \ru Равномерная параметризация по дуге или нет - \en Uniform parametrization by arc or not \~ - */ - MbChannelSurface( MbSurface & surf1, MbCurve & curv1, - MbSurface & surf2, MbCurve & curv2, - MbCurve3D & curv0, MbFunction & weig0, - double d1, double d2, MbeSmoothForm fm, double cn, MbFunction & func, bool ev ); - -protected: - MbChannelSurface( const MbChannelSurface &, MbRegDuplicate * ); - MbChannelSurface( const MbChannelSurface * ); // \ru Конструктор копирования с теми же опорными поверхностями для CurvesDuplicate() \en Copy constructor with the same support surfaces for CurvesDuplicate() - -private: - MbChannelSurface( const MbChannelSurface & ); // \ru Не реализовано. \en Not implemented. - -public: - virtual ~MbChannelSurface (); - -public: - VISITING_CLASS( MbChannelSurface ); - - /** \ru \name Общие функции геометрического объекта - \en \name Common functions of a geometric object - \{ */ - virtual MbeSpaceType IsA() const; // \ru Тип элемента. \en A type of element. - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли объект копией. \en Whether the object is a copy. - virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным. \en Make equal. - virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными. \en Determine whether the objects are similar. - - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. - /** \} */ - /** \ru \name Общие функции поверхности - \en \name Common functions of surface - \{ */ - virtual MbSurface * Offset( double d, bool same ) const; // \ru Создание эквидистантной поверхности. \en Creation of an offset surface. - virtual double GetFilletRadius( const MbCartPoint3D & p ) const; - /** \} */ - /** \ru \name Функции поверхности сопряжения - \en \name Functions of smooth surface - \{ */ - virtual MbSmoothSurface & CurvesDuplicate() const; // \ru Копия с теми же опорными поверхностями. \en Copy with the same support surfaces. - virtual double GetSmoothRadius() const; // \ru Дать радиус. \en Get radius. - virtual double DistanceRatio( bool firstCurve, MbCartPoint3D & p, double distance ) const; - /** \} */ - /** \ru \name Функции поверхности скругления с переменным радиусом обычная или с сохранением кромки - \en \name Functions of fillet surface with variable radius is normal or with preservation of edges - \{ */ - - /** \brief \ru Добавить точку в опорные кривые границы. - \en Add a point to the support curves of the boundary. \~ - \details \ru Добавить точку в опорные кривые границы.\n - Точка будет добавлена в кривую, если она имеет тип pt_LineSegment, pt_CubicSpline или pt_Hermit. - \en Add a point to the support curves of the boundary.\n - A point will be added into a curve if it has a type pt_LineSegment, pt_CubicSpline or pt_Hermit. \~ - \param[out] t1 - \ru Параметр точки на первой кривой (если add1 = true) - \en Parameter of a point on the first curve (if add1 equals true) \~ - \param[in] p1 - \ru Точка на первой кривой - \en Point on the first curve \~ - \param[in] add1 - \ru Нужно ли добавлять точку в первую кривую - \en Whether to add a point to the first curve \~ - \param[out] t2 - \ru Параметр точки на второй кривой (если add2 = true) - \en Parameter of a point on the second curve (if add2 equals true) \~ - \param[in] p2 - \ru Точка на второй кривой - \en Point on the second curve \~ - \param[in] add2 - \ru Нужно ли добавлять точку во вторую кривую - \en Whether to add a point to the second curve \~ - */ - virtual bool InsertPoints( double & t1, const MbCartPoint & p1, bool add1, - double & t2, const MbCartPoint & p2, bool add2 ); - - /** \brief \ru Проверить наличие полюса. - \en Check pole availability. \~ - \details \ru Проверить наличие полюса. - \en Check pole availability. \~ - \param[in] u - \ru Начальное приближение параметра по U для поиска полюса - \en Initial approximation of parameter U to search pole \~ - \param[in] bModify - \ru Флаг модификации поверхности \n - если true, то поверхность корректирует свои параметры по U - и соответственно им опорные кривые curve1 и curve2 - \en Flag of surface modification \n - if true, then the surface corrects its parameters along U - and according to them the support curves curve1 and curve2 \~ - \return \ru true - если нашли полюс - \en True - if pole has been found \~ - */ - bool CheckPole( double & u, bool bModify = true ); - /// \ru Получить функцию изменения радиуса. \en Get a function of radius changing. - const MbFunction & GetFunction() const { return *function; } - /// \ru Получить функцию изменения радиуса. \en Get a function of radius changing. - MbFunction & SetFunction() { return *function; } - /// \ru Заменить функцию изменения радиуса. \en Set a function of radius changing. - void SetFunction( MbFunction & funcNew ); // \ru (новая функция должна быть корректна) \en (new function must be correct) - - /** \} */ -protected: - void CalculateCurves( bool insertPoints ); - -private: - // \ru Дать коэффициент для радиуса \en Get coefficient for radius - virtual double FunctionValue( double u ) const; - void CheckPole(); // \ru Проверить полюса \en Check poles - // \ru Добавить точку в опорные кривые границы поверхности с постоянной хордой. \en Add a point to the support curves of the boundary of surface with constant chord. \~ - bool InsertForSpan( double & t1, const MbCartPoint & p1, bool add1, - double & t2, const MbCartPoint & p2, bool add2 ); - void operator = ( const MbChannelSurface & ); // \ru Не реализовано. \en Not implemented. - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbChannelSurface ) -}; - -IMPL_PERSISTENT_OPS( MbChannelSurface ) - -//------------------------------------------------------------------------------ -// \ru Создать поверхность переменного радиуса \en Create surface with variable radius -// --- -MbSmoothSurface * CreateChannelSurface( const MbSurface & surface1, SArray & points1, - const MbSurface & surface2, SArray & points2, - MbeSmoothForm form, double distance1, double distance2, double conic, - SArray & dFactor, SArray & dTendency, - bool even ); - - -//------------------------------------------------------------------------------ -// \ru Создать поверхность переменного радиуса с сохранением кромки \en Create surface with variable radius with preservation of edges -// --- -MbSmoothSurface * CreateKerbChannelSurface( const MbSurface & surface1, SArray & points1, - const MbSurface & surface2, SArray & points2, - MbeSmoothForm form, double distance1, double distance2, double conic, - const MbSurfaceIntersectionCurve & guideCurve, SArray & params, - SArray & dFactor, - bool byFirstSurface, bool even ); - - -#endif // __SURF_CHANNEL_SURFACE_H - +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Поверхность скругления с переменным радиусом обычная или с сохранением кромки. + \en Fillet surface with variable radius is normal or with preservation of edges. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __SURF_CHANNEL_SURFACE_H +#define __SURF_CHANNEL_SURFACE_H + + +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Поверхность скругления с переменным радиусом обычная или с сохранением кромки. + \en Fillet surface with variable radius is normal or with preservation of edges. \~ + \details \ru Поверхность скругления с переменным радиусом является NURBS-поверхностью, + построенной по трём кривым: curve1, curve0, curve2. + Первый параметр поверхности совпадает с параметром кривых curve1, curve0, curve2. + Второй параметр изменяется от нуля (точки совпадают с curve1) до единицы (точки совпадают с curve2). + Функция function определяет изменение радиуса и равна отношению текущего радиуса к заданному для поверхности радиусу. + Параметр функции радиуса совпадает с параметром кривых curve1, curve0, curve2. + Если коэффициент формы conic = _ARC_ ( 0 ), то вес каждой точки кривой curve0 задаётся функцией weights0 и + вычислен так, что сечение поверхности вдоль её второго параметра будет дугой окружности, + то есть при любом параметре u три точки curve1(u), curve0(u), curve2(u) определяют NURBS-кривую в форме дуги окружности. + Если коэффициент формы conic != _ARC_, то вес каждой точки кривой curve0 равен conic / ( 1.0 - conic ). + При conic = 0.5 сечение поверхности вдоль её второго параметра будет параболой. \n + \en Fillet surface with variable radius is NURBS-surface + constructed on three curves: curve1, curve0, curve2. + The first surface parameter coincides with the parameter of curve1, curve0, curve2 curves. + The second parameter is changed from zero (points coincide with curve1) to unit (points coincide with curve2). + Function "function" determines the change of the radius and equals the ratio of the current radius to given for surface. + The parameter of radius function coincides with the parameter of curves curve1, curve0, curve2. + If coefficient of shape conic = _ARC_ ( 0 ), then the weight of each point of the curve0 curve determined by the function weights0 and + calculated so that the section of surface along its second parameter is a circular arc + i.e. for any parameter u three points of curve1(u), curve0(u), curve2(u) determine the NURBS-curve with the shape of a circular arc. + If coefficient of shape conic != _ARC_, then the weight of each point of the curve0 curve is equal to conic / ( 1.0 - conic ). + If conic = 0.5, then the surface section along its second parameter is parabola. \n \~ + \ingroup Surfaces +*/ +// --- +class MATH_CLASS MbChannelSurface : public MbFilletSurface { +private: + MbFunction * function; ///< \ru Функция изменения радиуса (переменный коэффициент). \en Function of change of the radius (variable coefficient). + +public: + + /** \brief \ru Конструктор по двум кривым и типу сопряжения. + \en Constructor by two curves and type of mate. \~ + \details \ru Конструктор по двум кривым и типу сопряжения. + \en Constructor by two curves and type of mate. \~ + \param[in] curv1 - \ru Опорная кривая на первой поверхности + \en Support curve on the first surface \~ + \param[in] curv2 - \ru Опорная кривая на второй поверхности + \en Support curve on the second surface \~ + \param[in] d1 - \ru Радиус скругления со знаком для поверхности кривой crve1 + \en Fillet radius with sign for surface of crve1 curve \~ + \param[in] d2 - \ru Радиус скругления со знаком для поверхности кривой crve2 + \en Fillet radius with sign for surface of crve2 curve \~ + \param[in] fm - \ru Тип сопряжения: \n + st_Span - скругление с заданной хордой \n + st_Fillet - скругление с заданными радиусами + \en Mate type: \n + st_Span - fillet with a given chord \n + st_Fillet - fillet with given radii. \~ + \param[in] cn - \ru Коэффициент формы, изменяется от 0.05 до 0.95 (при 0.5 - дуга окружности) + \en Coefficient of shape is changed from 0.05 to 0.95 (if 0.5 - circular arc) \~ + \param[in] func - \ru Функция изменения радиуса + \en Function of change of the radius \~ + \param[in] ev - \ru Равномерная параметризация по дуге или нет + \en Uniform parametrization by arc or not \~ + */ + MbChannelSurface( MbSurfaceCurve & curv1, MbSurfaceCurve & curv2, + double d1, double d2, MbeSmoothForm fm, double cn, MbFunction & func, bool ev ); + + /** \brief \ru Конструктор по двум кривым и типу сопряжения. + \en Constructor by two curves and type of mate. \~ + \details \ru Конструктор поверхности с сохранением кромки по двум кривым и типу сопряжения. + \en Constructor of surface with preservation of edges by two curves and mate type. \~ + \param[in] curv1 - \ru Опорная кривая на первой поверхности + \en Support curve on the first surface \~ + \param[in] curv2 - \ru Опорная кривая на второй поверхности + \en Support curve on the second surface \~ + \param[in] d1 - \ru Радиус скругления со знаком для поверхности кривой crve1 + \en Fillet radius with sign for surface of crve1 curve \~ + \param[in] d2 - \ru Радиус скругления со знаком для поверхности кривой crve2 + \en Fillet radius with sign for surface of crve2 curve \~ + \param[in] fm - \ru Тип сопряжения: \n + st_Span - скругление с заданной хордой \n + st_Fillet - скругление с заданными радиусами + \en Mate type: \n + st_Span - fillet with a given chord \n + st_Fillet - fillet with given radii. \~ + \param[in] cn - \ru Коэффициент формы, изменяется от 0.05 до 0.95 (при 0.5 - дуга окружности) + \en Coefficient of shape is changed from 0.05 to 0.95 (if 0.5 - circular arc) \~ + \param[in] func - \ru Функция изменения радиуса + \en Function of change of the radius \~ + \param[in] byFirst - \ru true - кривая curve2 является кромкой, false - кривая curve1 является кромкой + \en True - curve2 curve is edge, false - curve1 curve is edge \~ + \param[in] ev - \ru Равномерная параметризация по дуге или нет + \en Uniform parametrization by arc or not \~ + */ + MbChannelSurface( MbSurfaceCurve & curv1, MbSurfaceCurve & curv2, + double d1, double d2, MbeSmoothForm fm, double cn, MbFunction & func, bool byFirst, bool ev ); + + /** \brief \ru Конструктор по двум кривым и типу сопряжения. + \en Constructor by two curves and type of mate. \~ + \details \ru Конструктор поверхности с сохранением кромки по двум кривым и типу сопряжения. + \en Constructor of surface with preservation of edges by two curves and mate type. \~ + \param[in] surf1 - \ru Первая поверхность + \en First surface \~ + \param[in] curv1 - \ru Опорная кривая в параметрах первой поверхности + \en Support curve at parameters of the first surface \~ + \param[in] surf2 - \ru Вторая поверхность + \en Second surface \~ + \param[in] curv2 - \ru Опорная кривая в параметрах второй поверхности + \en Support curve at parameters of the second surface \~ + \param[in] curv0 - \ru Кривая пересеченния касательных к поверхностям + \en Intersection curve of tangents to surfaces \~ + \param[in] weig0 - \ru Функция изменения веса. + \en Function of change of the weights. \~ + \param[in] d1 - \ru Радиус скругления со знаком для поверхности кривой crve1 + \en Fillet radius with sign for surface of crve1 curve \~ + \param[in] d2 - \ru Радиус скругления со знаком для поверхности кривой crve2 + \en Fillet radius with sign for surface of crve2 curve \~ + \param[in] fm - \ru Тип сопряжения: \n + st_Span - скругление с заданной хордой \n + st_Fillet - скругление с заданными радиусами + \en Mate type: \n + st_Span - fillet with a given chord \n + st_Fillet - fillet with given radii. \~ + \param[in] cn - \ru Коэффициент формы, изменяется от 0.05 до 0.95 (при 0.5 - дуга окружности) + \en Coefficient of shape is changed from 0.05 to 0.95 (if 0.5 - circular arc) \~ + \param[in] func - \ru Функция изменения радиуса. + \en Function of change of the radius. \~ + \param[in] byFirst - \ru true - кривая curve2 является кромкой, false - кривая curve1 является кромкой + \en True - curve2 curve is edge, false - curve1 curve is edge \~ + \param[in] ev - \ru Равномерная параметризация по дуге или нет + \en Uniform parametrization by arc or not \~ + */ + MbChannelSurface( MbSurface & surf1, MbCurve & curv1, + MbSurface & surf2, MbCurve & curv2, + MbCurve3D & curv0, MbFunction & weig0, + double d1, double d2, MbeSmoothForm fm, double cn, MbFunction & func, bool ev ); + +protected: + MbChannelSurface( const MbChannelSurface &, MbRegDuplicate * ); + MbChannelSurface( const MbChannelSurface * ); // \ru Конструктор копирования с теми же опорными поверхностями для CurvesDuplicate() \en Copy constructor with the same support surfaces for CurvesDuplicate() + +private: + MbChannelSurface( const MbChannelSurface & ); // \ru Не реализовано. \en Not implemented. + +public: + virtual ~MbChannelSurface (); + +public: + VISITING_CLASS( MbChannelSurface ); + + /** \ru \name Общие функции геометрического объекта + \en \name Common functions of a geometric object + \{ */ + virtual MbeSpaceType IsA() const; // \ru Тип элемента. \en A type of element. + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли объект копией. \en Whether the object is a copy. + virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным. \en Make equal. + virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными. \en Determine whether the objects are similar. + + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. + /** \} */ + /** \ru \name Общие функции поверхности + \en \name Common functions of surface + \{ */ + virtual MbSurface * Offset( double d, bool same ) const; // \ru Создание эквидистантной поверхности. \en Creation of an offset surface. + virtual double GetFilletRadius( const MbCartPoint3D & p ) const; + virtual double GetFilletRadius( double u ) const; + /** \} */ + /** \ru \name Функции поверхности сопряжения + \en \name Functions of smooth surface + \{ */ + virtual MbSmoothSurface & CurvesDuplicate() const; // \ru Копия с теми же опорными поверхностями. \en Copy with the same support surfaces. + virtual double GetSmoothRadius() const; // \ru Дать радиус. \en Get radius. + virtual double DistanceRatio( bool firstCurve, MbCartPoint3D & p, double distance ) const; + /** \} */ + /** \ru \name Функции поверхности скругления с переменным радиусом обычная или с сохранением кромки + \en \name Functions of fillet surface with variable radius is normal or with preservation of edges + \{ */ + + /** \brief \ru Добавить точку в опорные кривые границы. + \en Add a point to the support curves of the boundary. \~ + \details \ru Добавить точку в опорные кривые границы.\n + Точка будет добавлена в кривую, если она имеет тип pt_LineSegment, pt_CubicSpline или pt_Hermit. + \en Add a point to the support curves of the boundary.\n + A point will be added into a curve if it has a type pt_LineSegment, pt_CubicSpline or pt_Hermit. \~ + \param[out] t1 - \ru Параметр точки на первой кривой (если add1 = true) + \en Parameter of a point on the first curve (if add1 equals true) \~ + \param[in] p1 - \ru Точка на первой кривой + \en Point on the first curve \~ + \param[in] add1 - \ru Нужно ли добавлять точку в первую кривую + \en Whether to add a point to the first curve \~ + \param[out] t2 - \ru Параметр точки на второй кривой (если add2 = true) + \en Parameter of a point on the second curve (if add2 equals true) \~ + \param[in] p2 - \ru Точка на второй кривой + \en Point on the second curve \~ + \param[in] add2 - \ru Нужно ли добавлять точку во вторую кривую + \en Whether to add a point to the second curve \~ + */ + virtual bool InsertPoints( double & t1, const MbCartPoint & p1, bool add1, + double & t2, const MbCartPoint & p2, bool add2 ); + + /** \brief \ru Проверить наличие полюса. + \en Check pole availability. \~ + \details \ru Проверить наличие полюса. + \en Check pole availability. \~ + \param[in] u - \ru Начальное приближение параметра по U для поиска полюса + \en Initial approximation of parameter U to search pole \~ + \param[in] bModify - \ru Флаг модификации поверхности \n + если true, то поверхность корректирует свои параметры по U + и соответственно им опорные кривые curve1 и curve2 + \en Flag of surface modification \n + if true, then the surface corrects its parameters along U + and according to them the support curves curve1 and curve2 \~ + \return \ru true - если нашли полюс + \en True - if pole has been found \~ + */ + bool CheckPole( double & u, bool bModify = true ); + /// \ru Получить функцию изменения радиуса. \en Get a function of radius changing. + const MbFunction & GetFunction() const { return *function; } + /// \ru Получить функцию изменения радиуса. \en Get a function of radius changing. + MbFunction & SetFunction() { return *function; } + /// \ru Заменить функцию изменения радиуса. \en Set a function of radius changing. + void SetFunction( MbFunction & funcNew ); // \ru (новая функция должна быть корректна) \en (new function must be correct) + + /** \} */ +private: + // \ru Дать коэффициент для радиуса \en Get coefficient for radius + virtual double FunctionValue( double u ) const; + void CheckPole(); // \ru Проверить полюса \en Check poles + // \ru Добавить точку в опорные кривые границы поверхности с постоянной хордой. \en Add a point to the support curves of the boundary of surface with constant chord. \~ + bool InsertForSpan( double & t1, const MbCartPoint & p1, bool add1, + double & t2, const MbCartPoint & p2, bool add2 ); + void operator = ( const MbChannelSurface & ); // \ru Не реализовано. \en Not implemented. + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbChannelSurface ) +}; + +IMPL_PERSISTENT_OPS( MbChannelSurface ) + +//------------------------------------------------------------------------------ +// \ru Создать поверхность переменного радиуса \en Create surface with variable radius +// --- +MbSmoothSurface * CreateChannelSurface( const MbSurface & surface1, SArray & points1, + const MbSurface & surface2, SArray & points2, + MbeSmoothForm form, double distance1, double distance2, double conic, + SArray & dFactor, SArray & dTendency, + bool even ); + + +//------------------------------------------------------------------------------ +// \ru Создать поверхность переменного радиуса с сохранением кромки \en Create surface with variable radius with preservation of edges +// --- +MbSmoothSurface * CreateKerbChannelSurface( const MbSurface & surface1, SArray & points1, + const MbSurface & surface2, SArray & points2, + MbeSmoothForm form, double distance1, double distance2, double conic, + const MbSurfaceIntersectionCurve & guideCurve, SArray & params, + SArray & dFactor, + bool byFirstSurface, bool even ); + + +#endif // __SURF_CHANNEL_SURFACE_H + diff --git a/C3d/Include/surf_coons_surface.h b/C3d/Include/surf_coons_surface.h index 97e72f4..c5d2fd0 100644 --- a/C3d/Include/surf_coons_surface.h +++ b/C3d/Include/surf_coons_surface.h @@ -1,523 +1,529 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Бикубическая поверхность Кунса на четырех кривых и их поперечных производных. - \en Bicubic Coons surface on four curves and its transverse derivatives. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __SURF_COONS_SURFACE_H -#define __SURF_COONS_SURFACE_H - - -#include -#include -#include - - -class MATH_CLASS MbCurve; - - -#define COONS_COUNT 4 ///< \ru Число кривых, используемых для построения поверхности Кунса \en Count of curves used to construct Coons surface. - - -//------------------------------------------------------------------------------ -/** \brief \ru Способ расчёта поверхности Кунса. -\en Type of calculation of Coons surface. \~ -\details \ru Способ расчёта поверхности Кунса. \n -\en Type of calculation of Coons surface. \n \~ -\ingroup Surfaces -*/ -// --- -enum MbeCoonsSurfaceCalcType { - cst_DefaultType = 0, ///< \ru Способ по умолчанию. \en Default type. - cst_SurfaceType, ///< \ru Точный способ по кривым на поверхностях. \en Exact type by Curves on surfaces. -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Поверхность Кунса на четырех кривых. - \en Coons surface on four curves. \~ - \details \ru Бикубическая поверхность Кунса определяется четырьмя кривыми и - производными поверхности на этих кривых в поперечном к кривым направлениях. - Поверхность проходит через определяющие её кривые и - имеет заданные производные на этих кривых в поперечном к кривым направлениях. \n - \en Bicubic Coons surface is determined by four curves and - surface derivatives on these curves in transverse directions to curves. - Surface passes through its determining curves and - has specified derivatives on this curves in transverse directions to curves. \n \~ - \ingroup Surfaces -*/ -// --- -class MATH_CLASS MbCoonsPatchSurface : public MbSurface { - -// curve2V -// t2min curve2 t2max -// P3 ______________________ P2 -// t3max | | t1max -// | | -// | | -// curve3 | | curve1 -// curve3U | | curve1U -// t0=t0min*(1-u)+t0max*u | | -// t1=t1min*(1-v)+t1max*v | | -// t2=t2min*(1-u)+t2max*u t3min |______________________| t1min -// t3=t3min*(1-v)+t3max*v P0 P1 -// t0min curve0 t0max -// curve0V -// \ru Не переименовывать в MbCoonsSurface - хэш совпал с существующим объектом (BUG_60351). \en No renaming to MbCoonsSurface - hash was coincided with existing object (BUG_60351). - -private: - MbCurve3D * curve0; ///< \ru Кривая 0. \en Curve 0. - MbCurve3D * curve1; ///< \ru Кривая 1. \en Curve 1. - MbCurve3D * curve2; ///< \ru Кривая 2. \en Curve 2. - MbCurve3D * curve3; ///< \ru Кривая 3. \en Curve 3. - MbCurve3D * curve0V; ///< \ru Производная по v вдоль кривой 0. \en Derivative by v along curve 0. - MbCurve3D * curve1U; ///< \ru Производная по u вдоль кривой 1. \en Derivative by u along curve 1. - MbCurve3D * curve2V; ///< \ru Производная по v вдоль кривой 2. \en Derivative by v along curve 2. - MbCurve3D * curve3U; ///< \ru Производная по u вдоль кривой 3. \en Derivative by u along curve 3. - MbCartPoint3D vertex[COONS_COUNT]; ///< \ru Вершины. \en Vertices. - MbCartPoint3D vertexU[COONS_COUNT]; ///< \ru Производная по u в вершинах. \en Derivative by u at vertices. - MbCartPoint3D vertexV[COONS_COUNT]; ///< \ru Производная по v в вершинах. \en Derivative by v at vertices. - MbCartPoint3D vertexUV[COONS_COUNT]; ///< \ru Производная по uv в вершинах. \en Derivative by uv at vertices. - double t0min; ///< \ru Минимальное значение параметра на кривой 0. \en Minimal value of parameter on curve 0. - double t0max; ///< \ru Максимальное значение параметра на кривой 0. \en Maximal value of parameter on curve 0. - double t1min; ///< \ru Минимальное значение параметра на кривой 1. \en Minimal value of parameter on curve 1. - double t1max; ///< \ru Максимальное значение параметра на кривой 1. \en Maximal value of parameter on curve 1. - double t2min; ///< \ru Минимальное значение параметра на кривой 2. \en Minimal value of parameter on curve 2. - double t2max; ///< \ru Максимальное значение параметра на кривой 2. \en Maximal value of parameter on curve 2. - double t3min; ///< \ru Минимальное значение параметра на кривой 3. \en Minimal value of parameter on curve 3. - double t3max; ///< \ru Максимальное значение параметра на кривой 3. \en Maximal value of parameter on curve 3. - bool uclosed; ///< \ru Замкнутость по u. \en Closeness by u. - bool vclosed; ///< \ru Замкнутость по v. \en Closeness by v. - bool poleUMin; ///< \ru Полюс в начале. \en Pole at the beginning. - bool poleUMax; ///< \ru Полюс в конце. \en Pole at the end. - bool poleVMin; ///< \ru Полюс в начале. \en Pole at the beginning. - bool poleVMax; ///< \ru Полюс в конце. \en Pole at the end. - MbeCoonsSurfaceCalcType calcType; ///< \ru Версия реализации определяет способ расчёта поверхности. \en Version of implementation determines a type of calculation of surface. - -protected: - /** \brief \ru Конструктор поверхности Кунса. - \en Constructor of Coons surface. \~ - \details \ru Конструктор поверхности Кунса по набору кривых и производных вдоль кривых. - \en Constructor of Coons surface by set of curves and derivatives along curves. \~ - \param[in] initCurve0 - \ru Кривая 0. - \en Curve 0. \~ - \param[in] initCurve1 - \ru Кривая 1. - \en Curve 1. \~ - \param[in] initCurve2 - \ru Кривая 2. - \en Curve 2. \~ - \param[in] initCurve3 - \ru Кривая 3. - \en Curve 3. \~ - \param[in] derVCurve0 - \ru Производная по v вдоль кривой 0. - \en Derivative by v along curve 0. \~ - \param[in] derUCurve1 - \ru Производная по u вдоль кривой 1. - \en Derivative by u along curve 1. \~ - \param[in] derVCurve2 - \ru Производная по v вдоль кривой 2. - \en Derivative by v along curve 2. \~ - \param[in] derUCurve3 - \ru Производная по u вдоль кривой 3. - \en Derivative by u along curve 3. \~ - */ - MbCoonsPatchSurface ( MbCurve3D & initCurve0, MbCurve3D & initCurve1, MbCurve3D & initCurve2, MbCurve3D & initCurve3, - MbCurve3D & derVCurve0, MbCurve3D & derUCurve1, MbCurve3D & derVCurve2, MbCurve3D & derUCurve3, - double w0min, double w0max, double w1min, double w1max, double w2min, double w2max, double w3min, double w3max, - MbeCoonsSurfaceCalcType calcType = cst_DefaultType ); -private: - MbCoonsPatchSurface( const MbCoonsPatchSurface & ); // \ru Не реализовано. \en Not implemented. - MbCoonsPatchSurface( const MbCoonsPatchSurface &, MbRegDuplicate * ); ///< \ru Конструктор копирования. \en Copy-constructor. -public: - virtual ~MbCoonsPatchSurface( void ); - -public: - VISITING_CLASS( MbCoonsPatchSurface ); - - /// \ru Создание поверхности Кунса заданным кривым на поверхностях. \en Creation of Coons surface by curves on surfaces. - static MbCoonsPatchSurface * Create( const MbCurve3D & curve0, - const MbCurve3D & curve1, - const MbCurve3D & curve2, - const MbCurve3D & curve3, - MbResultType & resType ); - - /// \ru Инициализация поверхности Кунса заданной поверхностью Кунса. \en Initialization of Coons surface by specified Coons surface. - void Init( const MbCoonsPatchSurface & ); - - /** \ru \name Общие функции геометрического объекта - \en \name Common functions of a geometric object - \{ */ - virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Cделать копию элемента \en Make a copy of element - virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли объект копией. \en Whether the object is a copy. - virtual bool SetEqual( const MbSpaceItem & init ); // \ru Сделать равным \en Make equal - virtual bool IsSimilar( const MbSpaceItem & init ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar - virtual void Transform( const MbMatrix3D & matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move( const MbVector3D & to, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate( const MbAxis3D & axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisItems ( RPArray &s ); // \ru Дать базовые объекты \en Get the base objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - /** \} */ - - /** \ru \name Функции описания области определения поверхности - \en \name Functions for surface domain description - \{ */ - virtual double GetUMin() const; - virtual double GetVMin() const; - virtual double GetUMax() const; - virtual double GetVMax() const; - virtual bool IsUClosed() const; // \ru Замкнута ли поверхность по параметру u. \en Whether the surface is closed by parameter u. - virtual bool IsVClosed() const; // \ru Замкнута ли поверхность по параметру v. \en Whether the surface is closed by parameter v. - virtual bool GetPoleUMin() const; - virtual bool GetPoleUMax() const; - virtual bool GetPoleVMin() const; - virtual bool GetPoleVMax() const; - virtual bool IsPole( double u, double v ) const; // \ru Является ли точка особенной \en Whether the point is special - /** \} */ - - /** \ru \name Функции для работы в области определения поверхности - Функции PointOn, Derive... поверхностей корректируют параметры - при выходе их за пределы прямоугольной области определения параметров.\n - \en \name Functions for working at surface domain - Functions PointOn, Derive... of surfaces correct parameters - when they are out of bounds of rectangular domain of parameters.\n - \{ */ - virtual void PointOn ( double & u, double & v, MbCartPoint3D & ) const; // \ru Точка на поверхности \en Point on the surface - virtual void DeriveU ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по u \en First derivative with respect to u - virtual void DeriveV ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по v \en First derivative with respect to v - virtual void DeriveUU ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по u \en Second derivative with respect to u - virtual void DeriveVV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по v \en Second derivative with respect to v - virtual void DeriveUV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv - virtual void DeriveUUU( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative - virtual void DeriveUUV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative - virtual void DeriveUVV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative - virtual void DeriveVVV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative - virtual void Normal ( double & u, double & v, MbVector3D & ) const; // \ru Нормаль \en Normal - /** \} */ - - /** \ru \name Функции для работы внутри и вне области определения поверхности - функции _PointOn, _Derive... поверхностей не корректируют - параметры при выходе их за пределы прямоугольной области определения параметров. - \en \name Functions for working inside and outside the surface's domain - functions _PointOn, _Derive... of surfaces don't correct - parameters when they are out of bounds of rectangular domain of parameters. - \{ */ - virtual void _PointOn ( double u, double v, MbCartPoint3D & ) const; // \ru Точка на расширенной поверхности \en Point on the extended surface - virtual void _DeriveU ( double u, double v, MbVector3D & ) const; // \ru Первая производная по u \en First derivative with respect to u - virtual void _DeriveV ( double u, double v, MbVector3D & ) const; // \ru Первая производная по v \en First derivative with respect to v - virtual void _DeriveUU ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по u \en Second derivative with respect to u - virtual void _DeriveVV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по v \en Second derivative with respect to v - virtual void _DeriveUV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv - virtual void _DeriveUUU( double u, double v, MbVector3D & ) const; // \ru Третья производная \en Third derivative - virtual void _DeriveUUV( double u, double v, MbVector3D & ) const; // \ru Третья производная \en Third derivative - virtual void _DeriveUVV( double u, double v, MbVector3D & ) const; // \ru Третья производная \en Third derivative - virtual void _DeriveVVV( double u, double v, MbVector3D & ) const; // \ru Третья производная \en Third derivative - virtual void _Normal ( double u, double v, MbVector3D & ) const; // \ru Нормаль \en Normal - /** \} */ - - /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. - \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. - \{ */ - virtual void Explore( double & u, double & v, bool ext, - MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, - MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; - /** \} */ - - /** \ru \name Функции движения по поверхности - \en \name Functions of moving along the surface - \{ */ - virtual double StepU( double u, double v, double sag ) const; // \ru Вычисление шага параметра u по по величине прогиба \en Calculation of parameter u step by the value of sag - virtual double StepV( double u, double v, double sag ) const; // \ru Вычисление шага параметра v по по величине прогиба \en Calculation of parameter v step by the value of sag - virtual double DeviationStepU( double u, double v, double ang ) const; // \ru Вычисление шага параметра u по углу отклонения нормали \en Calculation of parameter u step by the angle of deviation of normal - virtual double DeviationStepV( double u, double v, double ang ) const; // \ru Вычисление шага параметра v по углу отклонения нормали \en Calculation of parameter v step by the angle of deviation of normal - virtual size_t GetUCount() const; - virtual size_t GetVCount() const; - /** \} */ - - /** \ru \name Общие функции поверхности - \en \name Common functions of surface - \{ */ - virtual MbCurve3D * CurveU( double v, MbRect1D * pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии v = const \en Spatial copy of 'v = const'-line - virtual MbCurve3D * CurveV( double u, MbRect1D * pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии u = const \en Spatial copy of 'u = const'-line - - virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменение носителя \en Changing of carrier - // \ru Существует ли полюс на границе параметрической области \en Whether there is pole on boundary of parametric region - - virtual size_t GetUMeshCount() const; // \ru Выдать количество полигонов по u \en Get the count of polygons by u - virtual size_t GetVMeshCount() const; // \ru Выдать количество полигонов по v \en Get the count of polygons by v - - /// \ru Получить кривую 0. \en Get curve 0. - const MbCurve3D & GetCurve0() const { return *curve0; } - /// \ru Получить кривую 1. \en Get curve 1. - const MbCurve3D & GetCurve1() const { return *curve1; } - /// \ru Получить кривую 2. \en Get curve 2. - const MbCurve3D & GetCurve2() const { return *curve2; } - /// \ru Получить кривую 3. \en Get curve 3. - const MbCurve3D & GetCurve3() const { return *curve3; } - /// \ru Получить кривую производной в трансверсальном направлении к кривой 0. \en Get derivative curve transversal to curve 0. - const MbCurve3D & GetDerCurve0() const { return *curve0V; } - /// \ru Получить кривую производной в трансверсальном направлении к кривой 1. \en Get derivative curve transversal to curve 1. - const MbCurve3D & GetDerCurve1() const { return *curve1U; } - /// \ru Получить кривую производной в трансверсальном направлении к кривой 2. \en Get derivative curve transversal to curve 2. - const MbCurve3D & GetDerCurve2() const { return *curve2V; } - /// \ru Получить кривую производной в трансверсальном направлении к кривой 3. \en Get derivative curve transversal to curve 3. - const MbCurve3D & GetDerCurve3() const { return *curve3U; } - /// \ru Получить кривую по индексу. \en Get curve by an index. - const MbCurve3D * GetCurve( size_t ind ) const; - /// \ru Получить количество кривых. \en Get count of curves. - size_t GetCurvesCount() const { return COONS_COUNT; } //-V112 - const MbCartPoint3D * GetVertex() const { return vertex; } ///< \ru Выдать вершины P0, P1, P2. \en Get vertices P0, P1, P2. - /** \} */ - double GetT0Min() const { return t0min; } ///< \ru Минимальное значение параметра на кривой 0. \en Minimal value of parameter on curve 0. - double GetT0Max() const { return t0max; } ///< \ru Максимальное значение параметра на кривой 0. \en Maximal value of parameter on curve 0. - double GetT1Min() const { return t1min; } ///< \ru Минимальное значение параметра на кривой 1. \en Minimal value of parameter on curve 1. - double GetT1Max() const { return t1max; } ///< \ru Максимальное значение параметра на кривой 1. \en Maximal value of parameter on curve 1. - double GetT2Min() const { return t2min; } ///< \ru Минимальное значение параметра на кривой 2. \en Minimal value of parameter on curve 2. - double GetT2Max() const { return t2max; } ///< \ru Максимальное значение параметра на кривой 2. \en Maximal value of parameter on curve 2. - double GetT3Min() const { return t3min; } ///< \ru Минимальное значение параметра на кривой 3. \en Minimal value of parameter on curve 3. - double GetT3Max() const { return t3max; } ///< \ru Максимальное значение параметра на кривой 3. \en Maximal value of parameter on curve 3. - - /** \brief \ru Получить образующую кривую по индексу, если она точно совпадает с соответствующим краем поверхности. - \en Get exact curve by index, if it coincides with the corresponding border of the surface. \~ - \details \ru Совпадение кривой с краем поверхности определяется по крайним точкам кривой. - \en Coincidence of the curve with the border of the surface is determined by the end points of the curve. \~ - \param[in] k - \ru Индекс кривой. - \en Index of the curve. \~ - \param[out] sense - \ru Флаг совпадения направленности кривой с рисунком, приведенным выше. - \en Flag that indicates the coincidence of the curve with the picture shown above.\~ - \return - \ru Указатель на кривую или NULL. - \en Pointer to the curve or NULL. \~ - */ - const MbCurve3D * GetExactCurve( size_t k, bool & sense ) const; - - /** \brief \ru Проверка полюсов на кривых. - \en Check poles on curves. \~ - \details \ru Определяет, есть ли полюс на границе области определения по длине кривой, определяющей границу.\n - Результат вычислений можно получить с помощью функций GetPoleUMin, GetPoleUMax, GetPoleVMin, GetPoleVMax. - \en Determines whether the pole at domain boundary by curve length determining boundary.\n - Result of calculations can be obtained with help of GetPoleUMin, GetPoleUMax, GetPoleVMin, GetPoleVMax functions. \~ - */ - void CheckPole(); - -private: - void operator = ( const MbCoonsPatchSurface & ); // \ru Не реализовано. \en Not implemented. - void Setup(); - void CheckParams( double & u, double & v ) const; // \ru Проверить и изменить при необходимости параметры. \en Check and correct parameters. - // \ru Определение местных координат. \en Determination of local coordinates. - void CalculateCoordinate( double & u, double & v, - double & t0, double & t1, double & t2, double & t3 ) const; - void CalculatePoint ( double & u, double & v, - MbCartPoint3D * point, MbCartPoint3D * pointUV ) const; - void CalculateFirst ( double & u, double & v, - MbCartPoint3D * point, MbVector3D * first, - MbCartPoint3D * pointUV, MbVector3D * firstUV ) const; - void CalculateThird ( double & u, double & v, - MbCartPoint3D * point, MbVector3D * third, - MbCartPoint3D * pointUV, MbVector3D * thirdUV ) const; - void CalculateExplore( double & u, double & v, - MbCartPoint3D * point, MbVector3D * first, MbVector3D * second, - MbCartPoint3D * pointUV, MbVector3D * firstUV, MbVector3D * secondUV ) const; - // \ru Производные. \en Derivatives with respect to u and to v. - void Derivatives( double & u, double & v, MbVector3D & uDer, MbVector3D & vDer ) const; - // \ru Нормаль. \en Calculate surface normal with refinement on borders. - void Normal( double u, double v, MbVector3D & derU, MbVector3D & derV, MbVector3D & norm ) const; - inline void ParamPoint ( double w, double * t ) const; - inline void ParamFirst ( double w, double * t ) const; - inline void ParamSecond( double w, double * t ) const; - inline void ParamThird ( double w, double * t ) const; - // \ru Добавить матрицу поверхности. \en Add the matrix of the surface. - inline void AddMatrix ( double * uu, double * vv, MbVector3D & p ) const; - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCoonsPatchSurface ) -}; // MbCoonsSurface - - -IMPL_PERSISTENT_OPS( MbCoonsPatchSurface ) - - -//------------------------------------------------------------------------------ -// \ru Определение массива степеней параметра точки \en Determination of array of degrees of point parameter -// --- -inline void MbCoonsPatchSurface::ParamPoint ( double w, double * t ) const { - t[0] = 1.0 - 3.0 * w * w + 2.0 * w * w * w; //*/ 1.0 - 10.0 * w * w * w + 15.0 * w * w * w * w - 6.0 * w * w * w * w * w; - t[1] = 3.0 * w * w - 2.0 * w * w * w; //*/ 10.0 * w * w * w - 15.0 * w * w * w * w + 6.0 * w * w * w * w * w; - t[2] = w - 2.0 * w * w + w * w * w; //*/ 0.0; - t[3] = - w * w + w * w * w; //*/ 0.0; -} - - -//------------------------------------------------------------------------------ -// \ru Определение массива степеней параметра производной \en Determination of array of degrees of derivative parameter -// --- -inline void MbCoonsPatchSurface::ParamFirst ( double w, double * t ) const { - t[0] = - 6.0 * w + 6.0 * w * w; //*/ -30.0 * w * w + 60.0 * w * w * w - 30.0 * w * w * w * w; - t[1] = 6.0 * w - 6.0 * w * w; //*/ 30.0 * w * w - 60.0 * w * w * w + 30.0 * w * w * w * w; - t[2] = 1.0 - 4.0 * w + 3.0 * w * w; //*/ 0.0; - t[3] = - 2.0 * w + 3.0 * w * w; //*/ 0.0; -} - - -//------------------------------------------------------------------------------ -// \ru Определение массива степеней параметра второй производной \en Determination of array of degrees of second derivative parameter -// --- -inline void MbCoonsPatchSurface::ParamSecond( double w, double * t ) const { - t[0] = - 6.0 + 12.0 * w; //*/ -60.0 * w + 180.0 * w * w - 120.0 * w * w * w; - t[1] = 6.0 - 12.0 * w; //*/ 60.0 * w - 180.0 * w * w + 120.0 * w * w * w; - t[2] = - 4.0 + 6.0 * w; //*/ 0.0; - t[3] = - 2.0 + 6.0 * w; //*/ 0.0; -} - - -//------------------------------------------------------------------------------ -// \ru Определение массива степеней параметра третьей производной \en Determination of array of degrees of third derivative parameter -// --- -inline void MbCoonsPatchSurface::ParamThird ( double /*w*/, double * t ) const { - t[0] = 12.0; //*/ -60.0 + 360.0 * w - 360.0 * w * w; - t[1] = -12.0; //*/ 60.0 - 360.0 * w + 360.0 * w * w; - t[2] = 6.0; //*/ 0.0; - t[3] = 6.0; //*/ 0.0; -} - - -//------------------------------------------------------------------------------ -// \ru Добавить матрицу поверхности. \en Add the matrix of the surface. -// --- -inline void MbCoonsPatchSurface::AddMatrix( double * uu, double * vv, MbVector3D & p ) const { - p.Add( vertex[0], -uu[0]*vv[0], vertex[1], -uu[1]*vv[0], vertex[2], -uu[1]*vv[1], vertex[3], -uu[0]*vv[1] ); - p.Add( vertexU[0], -uu[2]*vv[0], vertexU[1], -uu[3]*vv[0], vertexU[2], -uu[3]*vv[1], vertexU[3], -uu[2]*vv[1] ); - p.Add( vertexV[0], -uu[0]*vv[2], vertexV[1], -uu[1]*vv[2], vertexV[2], -uu[1]*vv[3], vertexV[3], -uu[0]*vv[3] ); - p.Add( vertexUV[0], -uu[2]*vv[2], vertexUV[1], -uu[3]*vv[2], vertexUV[2], -uu[3]*vv[3], vertexUV[3], -uu[2]*vv[3] ); -} - - -//------------------------------------------------------------------------------ -// \ru Получить кривую по индексу \en Get curve by an index -// --- -inline const MbCurve3D * MbCoonsPatchSurface::GetCurve( size_t ind ) const -{ - if ( ind >= COONS_COUNT ) - ind = ind % COONS_COUNT; - switch ( ind ) { - case 0 : { return curve0; } - case 1 : { return curve1; } - case 2 : { return curve2; } - case 3 : { return curve3; } - } - return NULL; -} - - -//////////////////////////////////////////////////////////////////////////////// -// -// Вспомогательные объекты бикубической поверхности Кунса. -// Auxiliary objects for bicubic Coons surface. -// -//////////////////////////////////////////////////////////////////////////////// - - -//------------------------------------------------------------------------------ -// \ru Кривая производных, обслуживающаяя точную бикубическую поверхность Кунса, построенная кривой на поверхности. -// \en The curve of derivetives serving the exact bicubic Coons surface, constructed by a curve on the surface. \~ -// --- -class MATH_CLASS MbCoonsDerivative : public MbCurve3D { -protected : - MbSurfaceCurve * curve; ///< \ru Кривая на поверхности (всегда не NULL). \en Curve on surface (always not NULL). - double param1; ///< \ru Параметр первой точки кривой. \en The first point parameter of curve. - double param2; ///< \ru Параметр второй точки кривой. \en The second point parameter of curve. - MbVector rail1; ///< \ru Вектор для вычисления поперечной производной в первой точке кривой. \en The vector for calculatiob of the transverse derivative in first point of curve. - MbVector rail2; ///< \ru Вектор для вычисления поперечной производной во второй точке кривой. \en The vector for calculatiob of the transverse derivative in second point of curve. - double turner; ///< \ru Угол поворота векторов на единицу изменения параметра. \en The angle of rotation of vectors per unit of parameter change. - -public : - /// \ru Конструктор кривой на поверхности. \en Constructor of curve on surface. - MbCoonsDerivative( MbSurfaceCurve & c, double t1, const MbVector & r1, double t2, const MbVector & r2 ); -protected: - /// \ru Конструктор копирования. \en Copy-constructor. - MbCoonsDerivative( const MbCoonsDerivative &, MbRegDuplicate * ); -private: - MbCoonsDerivative( const MbCoonsDerivative & ); // \ru Не реализовано!!! \en Not implemented!!! - -public : - virtual ~MbCoonsDerivative(); - -public: - /// \ru Реализация функции, инициирующей посещение объекта. \en Implementation of a function initializing a visit of an object. - VISITING_CLASS( MbCoonsDerivative ); - - /** \ru \name Общие функции геометрического объекта. - \en \name Common functions of a geometric object. - \{ */ - - virtual MbeSpaceType IsA() const; // \ru Дать тип элемента. \en Get element type. - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Определить, являются ли объекты одинаковыми. \en Determine whether objects are equal. - virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным. \en Make equal. - virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Определить, являются ли объекты подобными. \en Determine whether the objects are similar. - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвинуть вдоль вектора. \en Move along a vector. - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта. \en Set properties of the object. - - /** \} */ - /** \ru \name Общие функции кривой. - \en \name Common functions of curve. - \{ */ - - virtual double GetTMin() const; // \ru Вернуть минимальное значение параметра. \en Get the minimum value of parameter. - virtual double GetTMax() const; // \ru Вернуть максимальное значение параметра. \en Get the maximum value of parameter. - virtual bool IsClosed() const; // \ru Проверить замкнутость кривой. \en Check for curve closedness. - virtual double GetPeriod() const; // \ru Вернуть период периодической кривой. \en Get period of a periodic curve. - - // \ru Функции для работы в области определения. \en Functions for working in the definition domain. - virtual void PointOn ( double & t, MbCartPoint3D & ) const; // \ru Вычислить точку на кривой. \en Calculate a point on the curve. - virtual void FirstDer ( double & t, MbVector3D & ) const; // \ru Вычислить первую производную. \en Calculate the first derivative. - virtual void SecondDer( double & t, MbVector3D & ) const; // \ru Вычислить вторую производную. \en Calculate the second derivative. - virtual void ThirdDer ( double & t, MbVector3D & ) const; // \ru Вычислить третью производную по t. \en Calculate the third derivative by t. - // \ru Функции для работы вне области определения. \en Functions for working outside of definition domain. - virtual void _PointOn ( double t, MbCartPoint3D & ) const; // \ru Вычислить точку на расширенной кривой. \en Calculate a point on the extended curve. - virtual void _FirstDer ( double t, MbVector3D & ) const; // \ru Вычислить первую производную. \en Calculate the first derivative. - virtual void _SecondDer( double t, MbVector3D & ) const; // \ru Вычислить вторую производную. \en Calculate the second derivative. - virtual void _ThirdDer ( double t, MbVector3D & ) const; // \ru Вычислить третью производную по t. \en Calculate the third derivative by t. - // \ru Вычислить значения точки и производных для заданного параметра. \en Calculate point and derivatives of object for given parameter. \~ - virtual void Explore ( double & t, bool ext, - MbCartPoint3D & pnt, MbVector3D & fir, MbVector3D * sec, MbVector3D * thir ) const; - - virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменить направление. \en Change the direction. - - virtual double Step ( double t, double sag ) const; // \ru Вычисление шага по стрелке прогиба. \en Calculation of parameter step by the sag. - virtual double DeviationStep( double t, double angle ) const; // \ru Вычисление шага по углу отклонения нормали. \en Calculation of parameter step by the deviation angle. - - virtual void ChangeCarrier ( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменить носитель. \en Change the carrier. - virtual bool ChangeCarrierBorne( const MbSpaceItem &, MbSpaceItem &, const MbMatrix & matr ); // \ru Изменить носимые элементы. \en Change a carrier elements. - - /** \} */ - - /// \ru Вычислить нормаль к поверхности. \en Calculate surface normal. - void SurfaceNormal( double & t, MbVector3D & n ) const { curve->SurfaceNormal( t, n ); } - /// \ru Заменить кривую. \en Replace curve. - bool ChangeCurve( MbSurfaceCurve & ); - /// \ru Дать кривую. \en Get curve. - const MbSurfaceCurve * GetSurfaceCurve() const { return curve; } - /// \ru Дать кривую. \en Get curve. - MbSurfaceCurve * SetSurfaceCurve() { return curve; } - -protected: - void CheckParam ( double & t ) const; // \ru Проверить и изменить при необходимости параметр. \en Check and correct parameter. - -private: - // \ru Объявить оператор приравнивания по ссылке. \en Declare operator of assignment by reference. - void operator = ( const MbCoonsDerivative & ); // \ru Не реализовано!!! \en Not implemented!!! - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCoonsDerivative ) - -}; - -IMPL_PERSISTENT_OPS( MbCoonsDerivative ) - - -#endif // __SURF_COONS_SURFACE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Бикубическая поверхность Кунса на четырех кривых и их поперечных производных. + \en Bicubic Coons surface on four curves and its transverse derivatives. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __SURF_COONS_SURFACE_H +#define __SURF_COONS_SURFACE_H + + +#include +#include +#include + + +class MATH_CLASS MbCurve; + + +#define COONS_COUNT 4 ///< \ru Число кривых, используемых для построения поверхности Кунса \en Count of curves used to construct Coons surface. + + +//------------------------------------------------------------------------------ +/** \brief \ru Способ расчёта поверхности Кунса. +\en Type of calculation of Coons surface. \~ +\details \ru Способ расчёта поверхности Кунса. \n +\en Type of calculation of Coons surface. \n \~ +\ingroup Surfaces +*/ +// --- +enum MbeCoonsSurfaceCalcType { + cst_DefaultType = 0, ///< \ru Способ по умолчанию. \en Default type. + cst_SurfaceType, ///< \ru Точный способ по кривым на поверхностях. \en Exact type by Curves on surfaces. +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Поверхность Кунса на четырех кривых. + \en Coons surface on four curves. \~ + \details \ru Бикубическая поверхность Кунса определяется четырьмя кривыми и + производными поверхности на этих кривых в поперечном к кривым направлениях. + Поверхность проходит через определяющие её кривые и + имеет заданные производные на этих кривых в поперечном к кривым направлениях. \n + \en Bicubic Coons surface is determined by four curves and + surface derivatives on these curves in transverse directions to curves. + Surface passes through its determining curves and + has specified derivatives on this curves in transverse directions to curves. \n \~ + \ingroup Surfaces +*/ +// --- +class MATH_CLASS MbCoonsPatchSurface : public MbSurface { + +// curve2V +// t2min curve2 t2max +// P3 ______________________ P2 +// t3max | | t1max +// | | +// | | +// curve3 | | curve1 +// curve3U | | curve1U +// t0=t0min*(1-u)+t0max*u | | +// t1=t1min*(1-v)+t1max*v | | +// t2=t2min*(1-u)+t2max*u t3min |______________________| t1min +// t3=t3min*(1-v)+t3max*v P0 P1 +// t0min curve0 t0max +// curve0V +// \ru Не переименовывать в MbCoonsSurface - хэш совпал с существующим объектом (BUG_60351). \en No renaming to MbCoonsSurface - hash was coincided with existing object (BUG_60351). + +private: + MbCurve3D * curve0; ///< \ru Кривая 0. \en Curve 0. + MbCurve3D * curve1; ///< \ru Кривая 1. \en Curve 1. + MbCurve3D * curve2; ///< \ru Кривая 2. \en Curve 2. + MbCurve3D * curve3; ///< \ru Кривая 3. \en Curve 3. + MbCurve3D * curve0V; ///< \ru Производная по v вдоль кривой 0. \en Derivative by v along curve 0. + MbCurve3D * curve1U; ///< \ru Производная по u вдоль кривой 1. \en Derivative by u along curve 1. + MbCurve3D * curve2V; ///< \ru Производная по v вдоль кривой 2. \en Derivative by v along curve 2. + MbCurve3D * curve3U; ///< \ru Производная по u вдоль кривой 3. \en Derivative by u along curve 3. + MbCartPoint3D vertex[COONS_COUNT]; ///< \ru Вершины. \en Vertices. + MbCartPoint3D vertexU[COONS_COUNT]; ///< \ru Производная по u в вершинах. \en Derivative by u at vertices. + MbCartPoint3D vertexV[COONS_COUNT]; ///< \ru Производная по v в вершинах. \en Derivative by v at vertices. + MbCartPoint3D vertexUV[COONS_COUNT]; ///< \ru Производная по uv в вершинах. \en Derivative by uv at vertices. + double t0min; ///< \ru Минимальное значение параметра на кривой 0. \en Minimal value of parameter on curve 0. + double t0max; ///< \ru Максимальное значение параметра на кривой 0. \en Maximal value of parameter on curve 0. + double t1min; ///< \ru Минимальное значение параметра на кривой 1. \en Minimal value of parameter on curve 1. + double t1max; ///< \ru Максимальное значение параметра на кривой 1. \en Maximal value of parameter on curve 1. + double t2min; ///< \ru Минимальное значение параметра на кривой 2. \en Minimal value of parameter on curve 2. + double t2max; ///< \ru Максимальное значение параметра на кривой 2. \en Maximal value of parameter on curve 2. + double t3min; ///< \ru Минимальное значение параметра на кривой 3. \en Minimal value of parameter on curve 3. + double t3max; ///< \ru Максимальное значение параметра на кривой 3. \en Maximal value of parameter on curve 3. + bool uclosed; ///< \ru Замкнутость по u. \en Closeness by u. + bool vclosed; ///< \ru Замкнутость по v. \en Closeness by v. + bool poleUMin; ///< \ru Полюс в начале. \en Pole at the beginning. + bool poleUMax; ///< \ru Полюс в конце. \en Pole at the end. + bool poleVMin; ///< \ru Полюс в начале. \en Pole at the beginning. + bool poleVMax; ///< \ru Полюс в конце. \en Pole at the end. + MbeCoonsSurfaceCalcType calcType; ///< \ru Версия реализации определяет способ расчёта поверхности. \en Version of implementation determines a type of calculation of surface. + +protected: + /** \brief \ru Конструктор поверхности Кунса. + \en Constructor of Coons surface. \~ + \details \ru Конструктор поверхности Кунса по набору кривых и производных вдоль кривых. + \en Constructor of Coons surface by set of curves and derivatives along curves. \~ + \param[in] initCurve0 - \ru Кривая 0. + \en Curve 0. \~ + \param[in] initCurve1 - \ru Кривая 1. + \en Curve 1. \~ + \param[in] initCurve2 - \ru Кривая 2. + \en Curve 2. \~ + \param[in] initCurve3 - \ru Кривая 3. + \en Curve 3. \~ + \param[in] derVCurve0 - \ru Производная по v вдоль кривой 0. + \en Derivative by v along curve 0. \~ + \param[in] derUCurve1 - \ru Производная по u вдоль кривой 1. + \en Derivative by u along curve 1. \~ + \param[in] derVCurve2 - \ru Производная по v вдоль кривой 2. + \en Derivative by v along curve 2. \~ + \param[in] derUCurve3 - \ru Производная по u вдоль кривой 3. + \en Derivative by u along curve 3. \~ + */ + MbCoonsPatchSurface ( MbCurve3D & initCurve0, MbCurve3D & initCurve1, MbCurve3D & initCurve2, MbCurve3D & initCurve3, + MbCurve3D & derVCurve0, MbCurve3D & derUCurve1, MbCurve3D & derVCurve2, MbCurve3D & derUCurve3, + double w0min, double w0max, double w1min, double w1max, double w2min, double w2max, double w3min, double w3max, + MbeCoonsSurfaceCalcType calcType = cst_DefaultType ); +private: + MbCoonsPatchSurface( const MbCoonsPatchSurface & ); // \ru Не реализовано. \en Not implemented. + MbCoonsPatchSurface( const MbCoonsPatchSurface &, MbRegDuplicate * ); ///< \ru Конструктор копирования. \en Copy-constructor. +public: + virtual ~MbCoonsPatchSurface( void ); + +public: + VISITING_CLASS( MbCoonsPatchSurface ); + + /// \ru Создание поверхности Кунса по заданным кривым на поверхностях. \en Creation of Coons surface by curves on surfaces. + static MbCoonsPatchSurface * Create( const MbCurve3D & curve0, + const MbCurve3D & curve1, + const MbCurve3D & curve2, + const MbCurve3D & curve3, + MbResultType & resType ); + /// \ru Создание поверхности Кунса по заданным кривым на поверхностях. \en Creation of Coons surface by curves on surfaces. + static MbCoonsPatchSurface * Create( const MbCurve3D & curve0, bool tangent0, + const MbCurve3D & curve1, bool tangent1, + const MbCurve3D & curve2, bool tangent2, + const MbCurve3D & curve3, bool tangent3, + MbResultType & resType ); + + /// \ru Инициализация поверхности Кунса заданной поверхностью Кунса. \en Initialization of Coons surface by specified Coons surface. + void Init( const MbCoonsPatchSurface & ); + + /** \ru \name Общие функции геометрического объекта + \en \name Common functions of a geometric object + \{ */ + virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Cделать копию элемента \en Make a copy of element + virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли объект копией. \en Whether the object is a copy. + virtual bool SetEqual( const MbSpaceItem & init ); // \ru Сделать равным \en Make equal + virtual bool IsSimilar( const MbSpaceItem & init ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar + virtual void Transform( const MbMatrix3D & matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move( const MbVector3D & to, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate( const MbAxis3D & axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisItems ( RPArray &s ); // \ru Дать базовые объекты \en Get the base objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + /** \} */ + + /** \ru \name Функции описания области определения поверхности + \en \name Functions for surface domain description + \{ */ + virtual double GetUMin() const; + virtual double GetVMin() const; + virtual double GetUMax() const; + virtual double GetVMax() const; + virtual bool IsUClosed() const; // \ru Замкнута ли поверхность по параметру u. \en Whether the surface is closed by parameter u. + virtual bool IsVClosed() const; // \ru Замкнута ли поверхность по параметру v. \en Whether the surface is closed by parameter v. + virtual bool GetPoleUMin() const; + virtual bool GetPoleUMax() const; + virtual bool GetPoleVMin() const; + virtual bool GetPoleVMax() const; + virtual bool IsPole( double u, double v ) const; // \ru Является ли точка особенной \en Whether the point is special + /** \} */ + + /** \ru \name Функции для работы в области определения поверхности + Функции PointOn, Derive... поверхностей корректируют параметры + при выходе их за пределы прямоугольной области определения параметров.\n + \en \name Functions for working at surface domain + Functions PointOn, Derive... of surfaces correct parameters + when they are out of bounds of rectangular domain of parameters.\n + \{ */ + virtual void PointOn ( double & u, double & v, MbCartPoint3D & ) const; // \ru Точка на поверхности \en Point on the surface + virtual void DeriveU ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по u \en First derivative with respect to u + virtual void DeriveV ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по v \en First derivative with respect to v + virtual void DeriveUU ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по u \en Second derivative with respect to u + virtual void DeriveVV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по v \en Second derivative with respect to v + virtual void DeriveUV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv + virtual void DeriveUUU( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative + virtual void DeriveUUV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative + virtual void DeriveUVV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative + virtual void DeriveVVV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative + virtual void Normal ( double & u, double & v, MbVector3D & ) const; // \ru Нормаль \en Normal + /** \} */ + + /** \ru \name Функции для работы внутри и вне области определения поверхности + функции _PointOn, _Derive... поверхностей не корректируют + параметры при выходе их за пределы прямоугольной области определения параметров. + \en \name Functions for working inside and outside the surface's domain + functions _PointOn, _Derive... of surfaces don't correct + parameters when they are out of bounds of rectangular domain of parameters. + \{ */ + virtual void _PointOn ( double u, double v, MbCartPoint3D & ) const; // \ru Точка на расширенной поверхности \en Point on the extended surface + virtual void _DeriveU ( double u, double v, MbVector3D & ) const; // \ru Первая производная по u \en First derivative with respect to u + virtual void _DeriveV ( double u, double v, MbVector3D & ) const; // \ru Первая производная по v \en First derivative with respect to v + virtual void _DeriveUU ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по u \en Second derivative with respect to u + virtual void _DeriveVV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по v \en Second derivative with respect to v + virtual void _DeriveUV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv + virtual void _DeriveUUU( double u, double v, MbVector3D & ) const; // \ru Третья производная \en Third derivative + virtual void _DeriveUUV( double u, double v, MbVector3D & ) const; // \ru Третья производная \en Third derivative + virtual void _DeriveUVV( double u, double v, MbVector3D & ) const; // \ru Третья производная \en Third derivative + virtual void _DeriveVVV( double u, double v, MbVector3D & ) const; // \ru Третья производная \en Third derivative + virtual void _Normal ( double u, double v, MbVector3D & ) const; // \ru Нормаль \en Normal + /** \} */ + + /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. + \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. + \{ */ + virtual void Explore( double & u, double & v, bool ext, + MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, + MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; + /** \} */ + + /** \ru \name Функции движения по поверхности + \en \name Functions of moving along the surface + \{ */ + virtual double StepU( double u, double v, double sag ) const; // \ru Вычисление шага параметра u по по величине прогиба \en Calculation of parameter u step by the value of sag + virtual double StepV( double u, double v, double sag ) const; // \ru Вычисление шага параметра v по по величине прогиба \en Calculation of parameter v step by the value of sag + virtual double DeviationStepU( double u, double v, double ang ) const; // \ru Вычисление шага параметра u по углу отклонения нормали \en Calculation of parameter u step by the angle of deviation of normal + virtual double DeviationStepV( double u, double v, double ang ) const; // \ru Вычисление шага параметра v по углу отклонения нормали \en Calculation of parameter v step by the angle of deviation of normal + virtual size_t GetUCount() const; + virtual size_t GetVCount() const; + /** \} */ + + /** \ru \name Общие функции поверхности + \en \name Common functions of surface + \{ */ + virtual MbCurve3D * CurveU( double v, MbRect1D * pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии v = const \en Spatial copy of 'v = const'-line + virtual MbCurve3D * CurveV( double u, MbRect1D * pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии u = const \en Spatial copy of 'u = const'-line + + virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменение носителя \en Changing of carrier + // \ru Существует ли полюс на границе параметрической области \en Whether there is pole on boundary of parametric region + + virtual size_t GetUMeshCount() const; // \ru Выдать количество полигонов по u \en Get the count of polygons by u + virtual size_t GetVMeshCount() const; // \ru Выдать количество полигонов по v \en Get the count of polygons by v + + /// \ru Получить кривую 0. \en Get curve 0. + const MbCurve3D & GetCurve0() const { return *curve0; } + /// \ru Получить кривую 1. \en Get curve 1. + const MbCurve3D & GetCurve1() const { return *curve1; } + /// \ru Получить кривую 2. \en Get curve 2. + const MbCurve3D & GetCurve2() const { return *curve2; } + /// \ru Получить кривую 3. \en Get curve 3. + const MbCurve3D & GetCurve3() const { return *curve3; } + /// \ru Получить кривую производной в трансверсальном направлении к кривой 0. \en Get derivative curve transversal to curve 0. + const MbCurve3D & GetDerCurve0() const { return *curve0V; } + /// \ru Получить кривую производной в трансверсальном направлении к кривой 1. \en Get derivative curve transversal to curve 1. + const MbCurve3D & GetDerCurve1() const { return *curve1U; } + /// \ru Получить кривую производной в трансверсальном направлении к кривой 2. \en Get derivative curve transversal to curve 2. + const MbCurve3D & GetDerCurve2() const { return *curve2V; } + /// \ru Получить кривую производной в трансверсальном направлении к кривой 3. \en Get derivative curve transversal to curve 3. + const MbCurve3D & GetDerCurve3() const { return *curve3U; } + /// \ru Получить кривую по индексу. \en Get curve by an index. + const MbCurve3D * GetCurve( size_t ind ) const; + /// \ru Получить количество кривых. \en Get count of curves. + size_t GetCurvesCount() const { return COONS_COUNT; } //-V112 + const MbCartPoint3D * GetVertex() const { return vertex; } ///< \ru Выдать вершины P0, P1, P2. \en Get vertices P0, P1, P2. + /** \} */ + double GetT0Min() const { return t0min; } ///< \ru Минимальное значение параметра на кривой 0. \en Minimal value of parameter on curve 0. + double GetT0Max() const { return t0max; } ///< \ru Максимальное значение параметра на кривой 0. \en Maximal value of parameter on curve 0. + double GetT1Min() const { return t1min; } ///< \ru Минимальное значение параметра на кривой 1. \en Minimal value of parameter on curve 1. + double GetT1Max() const { return t1max; } ///< \ru Максимальное значение параметра на кривой 1. \en Maximal value of parameter on curve 1. + double GetT2Min() const { return t2min; } ///< \ru Минимальное значение параметра на кривой 2. \en Minimal value of parameter on curve 2. + double GetT2Max() const { return t2max; } ///< \ru Максимальное значение параметра на кривой 2. \en Maximal value of parameter on curve 2. + double GetT3Min() const { return t3min; } ///< \ru Минимальное значение параметра на кривой 3. \en Minimal value of parameter on curve 3. + double GetT3Max() const { return t3max; } ///< \ru Максимальное значение параметра на кривой 3. \en Maximal value of parameter on curve 3. + + /** \brief \ru Получить образующую кривую по индексу, если она точно совпадает с соответствующим краем поверхности. + \en Get exact curve by index, if it coincides with the corresponding border of the surface. \~ + \details \ru Совпадение кривой с краем поверхности определяется по крайним точкам кривой. + \en Coincidence of the curve with the border of the surface is determined by the end points of the curve. \~ + \param[in] k - \ru Индекс кривой. + \en Index of the curve. \~ + \param[out] sense - \ru Флаг совпадения направленности кривой с рисунком, приведенным выше. + \en Flag that indicates the coincidence of the curve with the picture shown above.\~ + \return - \ru Указатель на кривую или NULL. + \en Pointer to the curve or NULL. \~ + */ + const MbCurve3D * GetExactCurve( size_t k, bool & sense ) const; + + /** \brief \ru Проверка полюсов на кривых. + \en Check poles on curves. \~ + \details \ru Определяет, есть ли полюс на границе области определения по длине кривой, определяющей границу.\n + Результат вычислений можно получить с помощью функций GetPoleUMin, GetPoleUMax, GetPoleVMin, GetPoleVMax. + \en Determines whether the pole at domain boundary by curve length determining boundary.\n + Result of calculations can be obtained with help of GetPoleUMin, GetPoleUMax, GetPoleVMin, GetPoleVMax functions. \~ + */ + void CheckPole(); + +private: + void operator = ( const MbCoonsPatchSurface & ); // \ru Не реализовано. \en Not implemented. + void Setup(); + void CheckParams( double & u, double & v, bool ext = false ) const; // \ru Проверить и изменить при необходимости параметры. \en Check and correct parameters. + // \ru Определение местных координат. \en Determination of local coordinates. + void CalculateCoordinate( double & u, double & v, + double & t0, double & t1, double & t2, double & t3 ) const; + void CalculatePoint ( double & u, double & v, + MbCartPoint3D * point, MbCartPoint3D * pointUV ) const; + void CalculateFirst ( double & u, double & v, + MbCartPoint3D * point, MbVector3D * first, + MbCartPoint3D * pointUV, MbVector3D * firstUV ) const; + void CalculateThird ( double & u, double & v, + MbCartPoint3D * point, MbVector3D * third, + MbCartPoint3D * pointUV, MbVector3D * thirdUV ) const; + void CalculateExplore( double & u, double & v, + MbCartPoint3D * point, MbVector3D * first, MbVector3D * second, + MbCartPoint3D * pointUV, MbVector3D * firstUV, MbVector3D * secondUV ) const; + // \ru Производные. \en Derivatives with respect to u and to v. + void Derivatives( double & u, double & v, MbVector3D & uDer, MbVector3D & vDer ) const; + // \ru Нормаль. \en Calculate surface normal with refinement on borders. + void Normal( double u, double v, MbVector3D & derU, MbVector3D & derV, MbVector3D & norm ) const; + inline void ParamPoint ( double w, double * t ) const; + inline void ParamFirst ( double w, double * t ) const; + inline void ParamSecond( double w, double * t ) const; + inline void ParamThird ( double w, double * t ) const; + // \ru Добавить матрицу поверхности. \en Add the matrix of the surface. + inline void AddMatrix ( double * uu, double * vv, MbVector3D & p ) const; + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCoonsPatchSurface ) +}; // MbCoonsSurface + + +IMPL_PERSISTENT_OPS( MbCoonsPatchSurface ) + + +//------------------------------------------------------------------------------ +// \ru Определение массива степеней параметра точки \en Determination of array of degrees of point parameter +// --- +inline void MbCoonsPatchSurface::ParamPoint ( double w, double * t ) const { + t[0] = 1.0 - 3.0 * w * w + 2.0 * w * w * w; //*/ 1.0 - 10.0 * w * w * w + 15.0 * w * w * w * w - 6.0 * w * w * w * w * w; + t[1] = 3.0 * w * w - 2.0 * w * w * w; //*/ 10.0 * w * w * w - 15.0 * w * w * w * w + 6.0 * w * w * w * w * w; + t[2] = w - 2.0 * w * w + w * w * w; //*/ 0.0; + t[3] = - w * w + w * w * w; //*/ 0.0; +} + + +//------------------------------------------------------------------------------ +// \ru Определение массива степеней параметра производной \en Determination of array of degrees of derivative parameter +// --- +inline void MbCoonsPatchSurface::ParamFirst ( double w, double * t ) const { + t[0] = - 6.0 * w + 6.0 * w * w; //*/ -30.0 * w * w + 60.0 * w * w * w - 30.0 * w * w * w * w; + t[1] = 6.0 * w - 6.0 * w * w; //*/ 30.0 * w * w - 60.0 * w * w * w + 30.0 * w * w * w * w; + t[2] = 1.0 - 4.0 * w + 3.0 * w * w; //*/ 0.0; + t[3] = - 2.0 * w + 3.0 * w * w; //*/ 0.0; +} + + +//------------------------------------------------------------------------------ +// \ru Определение массива степеней параметра второй производной \en Determination of array of degrees of second derivative parameter +// --- +inline void MbCoonsPatchSurface::ParamSecond( double w, double * t ) const { + t[0] = - 6.0 + 12.0 * w; //*/ -60.0 * w + 180.0 * w * w - 120.0 * w * w * w; + t[1] = 6.0 - 12.0 * w; //*/ 60.0 * w - 180.0 * w * w + 120.0 * w * w * w; + t[2] = - 4.0 + 6.0 * w; //*/ 0.0; + t[3] = - 2.0 + 6.0 * w; //*/ 0.0; +} + + +//------------------------------------------------------------------------------ +// \ru Определение массива степеней параметра третьей производной \en Determination of array of degrees of third derivative parameter +// --- +inline void MbCoonsPatchSurface::ParamThird ( double /*w*/, double * t ) const { + t[0] = 12.0; //*/ -60.0 + 360.0 * w - 360.0 * w * w; + t[1] = -12.0; //*/ 60.0 - 360.0 * w + 360.0 * w * w; + t[2] = 6.0; //*/ 0.0; + t[3] = 6.0; //*/ 0.0; +} + + +//------------------------------------------------------------------------------ +// \ru Добавить матрицу поверхности. \en Add the matrix of the surface. +// --- +inline void MbCoonsPatchSurface::AddMatrix( double * uu, double * vv, MbVector3D & p ) const { + p.Add( vertex[0], -uu[0]*vv[0], vertex[1], -uu[1]*vv[0], vertex[2], -uu[1]*vv[1], vertex[3], -uu[0]*vv[1] ); + p.Add( vertexU[0], -uu[2]*vv[0], vertexU[1], -uu[3]*vv[0], vertexU[2], -uu[3]*vv[1], vertexU[3], -uu[2]*vv[1] ); + p.Add( vertexV[0], -uu[0]*vv[2], vertexV[1], -uu[1]*vv[2], vertexV[2], -uu[1]*vv[3], vertexV[3], -uu[0]*vv[3] ); + p.Add( vertexUV[0], -uu[2]*vv[2], vertexUV[1], -uu[3]*vv[2], vertexUV[2], -uu[3]*vv[3], vertexUV[3], -uu[2]*vv[3] ); +} + + +//------------------------------------------------------------------------------ +// \ru Получить кривую по индексу \en Get curve by an index +// --- +inline const MbCurve3D * MbCoonsPatchSurface::GetCurve( size_t ind ) const +{ + if ( ind >= COONS_COUNT ) + ind = ind % COONS_COUNT; + switch ( ind ) { + case 0 : { return curve0; } + case 1 : { return curve1; } + case 2 : { return curve2; } + case 3 : { return curve3; } + } + return NULL; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Вспомогательные объекты бикубической поверхности Кунса. +// Auxiliary objects for bicubic Coons surface. +// +//////////////////////////////////////////////////////////////////////////////// + + +//------------------------------------------------------------------------------ +// \ru Кривая производных, обслуживающая точную бикубическую поверхность Кунса, построенная кривой на поверхности. +// \en The curve of derivatives serving the exact bicubic Coons surface, constructed by a curve on the surface. \~ +// --- +class MATH_CLASS MbCoonsDerivative : public MbCurve3D { +protected : + MbSurfaceCurve * curve; ///< \ru Кривая на поверхности (всегда не NULL). \en Curve on surface (always not NULL). + double param1; ///< \ru Параметр первой точки кривой. \en The first point parameter of curve. + double param2; ///< \ru Параметр второй точки кривой. \en The second point parameter of curve. + MbVector rail1; ///< \ru Вектор для вычисления поперечной производной в первой точке кривой. \en The vector for calculation of the transverse derivative in first point of curve. + MbVector rail2; ///< \ru Вектор для вычисления поперечной производной во второй точке кривой. \en The vector for calculation of the transverse derivative in second point of curve. + double turner; ///< \ru Угол поворота векторов на единицу изменения параметра. \en The angle of rotation of vectors per unit of parameter change. + +public : + /// \ru Конструктор кривой на поверхности. \en Constructor of curve on surface. + MbCoonsDerivative( MbSurfaceCurve & c, double t1, const MbVector & r1, double t2, const MbVector & r2 ); +protected: + /// \ru Конструктор копирования. \en Copy-constructor. + MbCoonsDerivative( const MbCoonsDerivative &, MbRegDuplicate * ); +private: + MbCoonsDerivative( const MbCoonsDerivative & ); // \ru Не реализовано!!! \en Not implemented!!! + +public : + virtual ~MbCoonsDerivative(); + +public: + /// \ru Реализация функции, инициирующей посещение объекта. \en Implementation of a function initializing a visit of an object. + VISITING_CLASS( MbCoonsDerivative ); + + /** \ru \name Общие функции геометрического объекта. + \en \name Common functions of a geometric object. + \{ */ + + virtual MbeSpaceType IsA() const; // \ru Дать тип элемента. \en Get element type. + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Определить, являются ли объекты одинаковыми. \en Determine whether objects are equal. + virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным. \en Make equal. + virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Определить, являются ли объекты подобными. \en Determine whether the objects are similar. + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвинуть вдоль вектора. \en Move along a vector. + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта. \en Set properties of the object. + + /** \} */ + /** \ru \name Общие функции кривой. + \en \name Common functions of curve. + \{ */ + + virtual double GetTMin() const; // \ru Вернуть минимальное значение параметра. \en Get the minimum value of parameter. + virtual double GetTMax() const; // \ru Вернуть максимальное значение параметра. \en Get the maximum value of parameter. + virtual bool IsClosed() const; // \ru Проверить замкнутость кривой. \en Check for curve closedness. + virtual double GetPeriod() const; // \ru Вернуть период периодической кривой. \en Get period of a periodic curve. + + // \ru Функции для работы в области определения. \en Functions for working in the definition domain. + virtual void PointOn ( double & t, MbCartPoint3D & ) const; // \ru Вычислить точку на кривой. \en Calculate a point on the curve. + virtual void FirstDer ( double & t, MbVector3D & ) const; // \ru Вычислить первую производную. \en Calculate the first derivative. + virtual void SecondDer( double & t, MbVector3D & ) const; // \ru Вычислить вторую производную. \en Calculate the second derivative. + virtual void ThirdDer ( double & t, MbVector3D & ) const; // \ru Вычислить третью производную по t. \en Calculate the third derivative by t. + // \ru Функции для работы вне области определения. \en Functions for working outside of definition domain. + virtual void _PointOn ( double t, MbCartPoint3D & ) const; // \ru Вычислить точку на расширенной кривой. \en Calculate a point on the extended curve. + virtual void _FirstDer ( double t, MbVector3D & ) const; // \ru Вычислить первую производную. \en Calculate the first derivative. + virtual void _SecondDer( double t, MbVector3D & ) const; // \ru Вычислить вторую производную. \en Calculate the second derivative. + virtual void _ThirdDer ( double t, MbVector3D & ) const; // \ru Вычислить третью производную по t. \en Calculate the third derivative by t. + // \ru Вычислить значения точки и производных для заданного параметра. \en Calculate point and derivatives of object for given parameter. \~ + virtual void Explore ( double & t, bool ext, + MbCartPoint3D & pnt, MbVector3D & fir, MbVector3D * sec, MbVector3D * thir ) const; + + virtual void Inverse( MbRegTransform * iReg = NULL ); // \ru Изменить направление. \en Change the direction. + + virtual double Step ( double t, double sag ) const; // \ru Вычисление шага по стрелке прогиба. \en Calculation of parameter step by the sag. + virtual double DeviationStep( double t, double angle ) const; // \ru Вычисление шага по углу отклонения нормали. \en Calculation of parameter step by the deviation angle. + + virtual void ChangeCarrier ( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменить носитель. \en Change the carrier. + virtual bool ChangeCarrierBorne( const MbSpaceItem &, MbSpaceItem &, const MbMatrix & matr ); // \ru Изменить носимые элементы. \en Change a carrier elements. + + /** \} */ + + /// \ru Вычислить нормаль к поверхности. \en Calculate surface normal. + void SurfaceNormal( double & t, MbVector3D & n ) const { curve->SurfaceNormal( t, n ); } + /// \ru Заменить кривую. \en Replace curve. + bool ChangeCurve( MbSurfaceCurve & ); + /// \ru Дать кривую. \en Get curve. + const MbSurfaceCurve * GetSurfaceCurve() const { return curve; } + /// \ru Дать кривую. \en Get curve. + MbSurfaceCurve * SetSurfaceCurve() { return curve; } + +protected: + void CheckParam ( double & t ) const; // \ru Проверить и изменить при необходимости параметр. \en Check and correct parameter. + +private: + // \ru Объявить оператор приравнивания по ссылке. \en Declare operator of assignment by reference. + void operator = ( const MbCoonsDerivative & ); // \ru Не реализовано!!! \en Not implemented!!! + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCoonsDerivative ) + +}; + +IMPL_PERSISTENT_OPS( MbCoonsDerivative ) + + +#endif // __SURF_COONS_SURFACE_H diff --git a/C3d/Include/surf_corner_surface.h b/C3d/Include/surf_corner_surface.h index c456ccf..33b2518 100644 --- a/C3d/Include/surf_corner_surface.h +++ b/C3d/Include/surf_corner_surface.h @@ -1,346 +1,346 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Треугольная поверхность на сетке из трех кривых. - \en Triangular surface on grid of three curves. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __SURF_CORNER_SURFACE_H -#define __SURF_CORNER_SURFACE_H - - -#include - - -#define CORNER_COUNT 3 ///< \ru Число кривых, нужных для построения треугольной поверхности. \en Count of curves used to construct triangular surface. -#define VECT_CNT CORNER_COUNT - - -class MATH_CLASS MbContour3D; - - -//------------------------------------------------------------------------------ -/** \brief \ru Треугольная поверхность на кривых. - \en Triangular surface on curves. \~ - \details \ru Треугольная поверхность на сетке из трех кривых. - Кривые должны попарно пересекаться или иметь точки скрещения. - Если кривые попарно пересекаются, то поверхность проходит через определяющиее её кривые. \n - \en Triangular surface on grid of three curves. - Curves have to be intersected pairwise or have crossing points. - If curves are intersected pairwise then surface passes through its determining curves. \n \~ - \ingroup Surfaces -*/ -// --- -class MATH_CLASS MbCornerSurface : public MbSurface { -// P2 -// t1max ^* t0min -// R(s0,s1,s2) = / * -// (curve2(1-s1)+curve1(s2)-P0)*s0+ / * -// (curve0(1-s2)+curve2(s0)-P1)*s1+ / * -// (curve1(1-s0)+curve0(s1)-P2)*s2 curve1 / * curve0 -// / R * -// s0(u,v) + s1(u,v) + s2(u,v) = 1 / * -// R(0,s1,s2) = curve0(s1) / * -// R(s0,0,s2) = curve1(s2) t1min <----------------v t0max -// R(s0,s1,0) = curve2(s0) P0 P1 -// t2max curve2 t2min -// -// \ru s0,s1,s2 - Барицентрические (треугольные) координаты, \en S0,s1,s2 - Baricentric (triangular) coordinates. -// \ru P0 - полюс \en P0 - pole -private: - MbCurve3D * curve0; ///< \ru Кривая 0. \en Curve 0. - MbCurve3D * curve1; ///< \ru Кривая 1. \en Curve 1. - MbCurve3D * curve2; ///< \ru Кривая 2. \en Curve 2. - MbCartPoint3D vertex[CORNER_COUNT]; ///< \ru Вершины P0, P1, P2. \en Vertices P0, P1, P2. - double t0min; ///< \ru Минимальное значение параметра на кривой 0. \en Minimal value of parameter on curve 0. - double t0max; ///< \ru Максимальное значение параметра на кривой 0. \en Maximal value of parameter on curve 0. - double t1min; ///< \ru Минимальное значение параметра на кривой 1. \en Minimal value of parameter on curve 1. - double t1max; ///< \ru Максимальное значение параметра на кривой 1. \en Maximal value of parameter on curve 1. - double t2min; ///< \ru Минимальное значение параметра на кривой 2. \en Minimal value of parameter on curve 2. - double t2max; ///< \ru Максимальное значение параметра на кривой 2. \en Maximal value of parameter on curve 2. - -public: - /** \brief \ru Конструктор треугольной поверхности. - \en Constructor of triangular surface. \~ - \details \ru Конструктор треугольной поверхности по набору кривых. - \en Constructor of triangular surface by set of curves. \~ - \param[in] initCurve0 - \ru Кривая 0. - \en Curve 0. \~ - \param[in] initCurve1 - \ru Кривая 1. - \en Curve 1. \~ - \param[in] initCurve2 - \ru Кривая 2. - \en Curve 2. \~ - */ - MbCornerSurface ( const MbCurve3D & initCurve0, const MbCurve3D & initCurve1, const MbCurve3D & initCurve2 ); -private: - MbCornerSurface( const MbCornerSurface & ); // \ru Не реализовано. \en Not implemented. - MbCornerSurface( const MbCornerSurface & init, MbRegDuplicate * ); -public: - virtual ~MbCornerSurface( void ); - -public: - VISITING_CLASS( MbCornerSurface ); - - /// \ru Инициализация треугольной поверхности заданной треугольной поверхностью. \en Initialization of triangular surface by given triangular surface. - void Init( const MbCornerSurface &init ); - - /** \ru \name Общие функции геометрического объекта - \en \name Common functions of a geometric object - \{ */ - virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Cделать копию элемента \en Make a copy of element - virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли объект копией. \en Whether the object is a copy. - virtual bool SetEqual( const MbSpaceItem & init ); // \ru Сделать равным \en Make equal - virtual bool IsSimilar( const MbSpaceItem & init ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar - virtual void Transform( const MbMatrix3D & matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move( const MbVector3D & to, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate( const MbAxis3D & axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis - - virtual void CalculateSurfaceWire( const MbStepData & stepData, size_t beg, MbMesh & mesh, - size_t uMeshCount = c3d::WIRE_MAX, size_t vMeshCount = c3d::WIRE_MAX ) const; - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the base objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - /** \} */ - /** \ru \name Функции описания области определения поверхности - \en \name Functions for surface domain description - \{ */ - virtual double GetUMin() const; - virtual double GetVMin() const; - virtual double GetUMax() const; - virtual double GetVMax() const; - virtual bool IsUClosed() const; // \ru Замкнута ли поверхность по параметру u. \en Whether the surface is closed by parameter u. - virtual bool IsVClosed() const; // \ru Замкнута ли поверхность по параметру v. \en Whether the surface is closed by parameter v. - virtual bool GetPoleUMin() const; - virtual bool GetPoleUMax() const; - virtual bool GetPoleVMin() const; - virtual bool GetPoleVMax() const; - virtual bool IsPole( double u, double v ) const; // \ru Является ли точка особенной \en Whether the point is special - /** \} */ - - /** \ru \name Функции для работы в области определения поверхности - Функции PointOn, Derive... поверхностей корректируют параметры - при выходе их за пределы прямоугольной области определения параметров.\n - \en \name Functions for working at surface domain - Functions PointOn, Derive... of surfaces correct parameters - when they are out of bounds of rectangular domain of parameters.\n - \{ */ - virtual void PointOn ( double & u, double & v, MbCartPoint3D & p ) const; // \ru Точка на поверхности \en Point on the surface - virtual void DeriveU ( double & u, double & v, MbVector3D & p ) const; // \ru Первая производная по u \en First derivative with respect to u - virtual void DeriveV ( double & u, double & v, MbVector3D & p ) const; // \ru Первая производная по v \en First derivative with respect to v - virtual void DeriveUU ( double & u, double & v, MbVector3D & p ) const; // \ru Вторая производная по u \en Second derivative with respect to u - virtual void DeriveVV ( double & u, double & v, MbVector3D & p ) const; // \ru Вторая производная по v \en Second derivative with respect to v - virtual void DeriveUV ( double & u, double & v, MbVector3D & p ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv - virtual void DeriveUUU( double & u, double & v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative - virtual void DeriveUUV( double & u, double & v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative - virtual void DeriveUVV( double & u, double & v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative - virtual void DeriveVVV( double & u, double & v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative - virtual void TangentV ( double & u, double & v, MbVector3D & p ) const; - virtual void Normal ( double & u, double & v, MbVector3D & p ) const; // \ru Нормаль \en Normal - /** \} */ - - /** \ru \name Функции для работы внутри и вне области определения поверхности - функции _PointOn, _Derive... поверхностей не корректируют - параметры при выходе их за пределы прямоугольной области определения параметров. - \en \name Functions for working inside and outside the surface's domain - functions _PointOn, _Derive... of surfaces don't correct - parameters when they are out of bounds of rectangular domain of parameters. - \{ */ - virtual void _PointOn ( double u, double v, MbCartPoint3D & p ) const; // \ru Точка на расширенной поверхности \en Point on the extended surface - virtual void _DeriveU ( double u, double v, MbVector3D & p ) const; // \ru Первая производная по u \en First derivative with respect to u - virtual void _DeriveV ( double u, double v, MbVector3D & p ) const; // \ru Первая производная по v \en First derivative with respect to v - virtual void _DeriveUU ( double u, double v, MbVector3D & p ) const; // \ru Вторая производная по u \en Second derivative with respect to u - virtual void _DeriveVV ( double u, double v, MbVector3D & p ) const; // \ru Вторая производная по v \en Second derivative with respect to v - virtual void _DeriveUV ( double u, double v, MbVector3D & p ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv - virtual void _DeriveUUU( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative - virtual void _DeriveUUV( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative - virtual void _DeriveUVV( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative - virtual void _DeriveVVV( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative - virtual void _TangentV ( double u, double v, MbVector3D & p ) const; - virtual void _Normal ( double u, double v, MbVector3D & p ) const; // \ru Нормаль \en Normal - virtual void _NormalV ( double u, double v, MbVector3D & p ) const; // \ru Производная нормали \en Derivative of normal - /** \} */ - - /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. - \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. - \{ */ - virtual void Explore( double & u, double & v, bool ext, - MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, - MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; - /** \} */ - - /** \ru \name Функции движения по поверхности - \en \name Functions of moving along the surface - \{ */ - virtual double StepU( double u, double v, double sag ) const; // \ru Вычисление шага аппроксимации с учетом радиуса кривизны по U \en Calculation of the approximation step with consideration of the curvature radius by U - virtual double StepV( double u, double v, double sag ) const; // \ru Вычисление шага аппроксимации с учетом радиуса кривизны по V \en Calculation of the approximation step with consideration of the curvature radius by V - virtual double DeviationStepU( double u, double v, double ang ) const; ///< \ru Вычисление шага параметра u по углу отклонения нормали \en Calculation of parameter u step by the angle of deviation of normal - virtual double DeviationStepV( double u, double v, double ang ) const; ///< \ru Вычисление шага параметра v по углу отклонения нормали \en Calculation of parameter v step by the angle of deviation of normal - - virtual size_t GetUCount() const; - virtual size_t GetVCount() const; - /** \} */ - - /** \ru \name Общие функции поверхности - \en \name Common functions of surface - \{ */ - virtual MbCurve3D * CurveU( double v, MbRect1D * pRgn, bool doApprox = true ) const; // \ru Пространственная копия линии v = const \en Spatial copy of 'v = const'-line - virtual MbCurve3D * CurveV( double u, MbRect1D * pRgn, bool doApprox = true ) const; // \ru Пространственная копия линии u = const \en Spatial copy of 'u = const'-line - - virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменение носителя \en Changing of carrier - // \ru Существует ли полюс на границе параметрической области \en Whether there is pole on boundary of parametric region - - virtual size_t GetUMeshCount() const; // \ru Выдать количество полигонов по u \en Get the count of polygons by u - virtual size_t GetVMeshCount() const; // \ru Выдать количество полигонов по v \en Get the count of polygons by v - - /// \ru Получить кривую 0. \en Get curve 0. - const MbCurve3D & GetCurve0() const { return *curve0; } - /// \ru Получить кривую 1. \en Get curve 1. - const MbCurve3D & GetCurve1() const { return *curve1; } - /// \ru Получить кривую 2. \en Get curve 2. - const MbCurve3D & GetCurve2() const { return *curve2; } - /// \ru Получить кривую по индексу. \en Get curve by an index. - const MbCurve3D * GetCurve( size_t ind ) const; - /// \ru Получить количество кривых. \en Get count of curves. - size_t GetCurvesCount() const { return 3; } //-V112 - const MbCartPoint3D * GetVertex() const { return vertex; } ///< \ru Выдать вершины P0, P1, P2. \en Get vertices P0, P1, P2. - double GetT0Min() const { return t0min; } ///< \ru Минимальное значение параметра на кривой 0. \en Minimal value of parameter on curve 0. - double GetT0Max() const { return t0max; } ///< \ru Максимальное значение параметра на кривой 0. \en Maximal value of parameter on curve 0. - double GetT1Min() const { return t1min; } ///< \ru Минимальное значение параметра на кривой 1. \en Minimal value of parameter on curve 1. - double GetT1Max() const { return t1max; } ///< \ru Максимальное значение параметра на кривой 1. \en Maximal value of parameter on curve 1. - double GetT2Min() const { return t2min; } ///< \ru Минимальное значение параметра на кривой 2. \en Minimal value of parameter on curve 2. - double GetT2Max() const { return t2max; } ///< \ru Максимальное значение параметра на кривой 2. \en Maximal value of parameter on curve 2. - double GetTMin( size_t ind ) const; ///< \ru Минимальное значение параметра на кривой с индексом ind. \en Get The minimal value of parameter on curve by index. - double GetTMax( size_t ind ) const; ///< \ru Максимальное значение параметра на кривой с индексом ind. \en Get The maximal value of parameter on curve by index. - /** \} */ - -private: - void Init(); - inline void CalculateCoordinate( double & u, double & v, bool ext, - double & s0, double & s1, double & s2, - double & c0, double & c1, double & c2, - double & t0, double & t1, double & t2 ) const; - void CalculatePoint ( double & u, double & v, bool ext, - MbCartPoint3D & point ) const; - void CalculateFirst ( double & u, double & v, bool ext, - MbVector3D * first ) const; - void CalculateSecond( double & u, double & v, bool ext, - MbVector3D * second ) const; - void CalculateThird ( double & u, double & v, bool ext, - MbVector3D * second, MbVector3D * third ) const; - void CalculateExplore( double & u, double & v, bool ext, - MbCartPoint3D * point, MbVector3D * first, MbVector3D * second ) const; - bool GetNormalFactor( MbVector3D & norm ) const; // \ru Нормаль в точке с параметрами u=0. \en Normal at u=0. - void Derivatives( double u, double v, bool ext, MbVector3D & uDer, MbVector3D & vDer ) const; // \ru Ппроизводные. \en Derivatives with respect to u and to v. - void operator = ( const MbCornerSurface & ); // \ru Не реализовано. \en Not implemented. - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCornerSurface ) -}; // MbCornerSurface - -IMPL_PERSISTENT_OPS( MbCornerSurface ) - -//------------------------------------------------------------------------------ -// \ru Определение местных координат \en Determination of local coordinates -// --- -inline void MbCornerSurface::CalculateCoordinate( double & u, double & v, bool ext, - double & s0, double & s1, double & s2, - double & c0, double & c1, double & c2, - double & t0, double & t1, double & t2 ) const -{ - if ( !ext ) { - if ( v <-1.0 ) v =-1.0; - if ( u > 1.0 ) u = 1.0; - if ( v > 1.0 ) v = 1.0; - } - if (u < 0.0) - u = 0.0; // \ru Нельзя заходить за полюс. \en Is impossible to go behind a pole - - s0 = 1.0 - u; - s1 = 0.5 * (u - u*v); - s2 = 0.5 * (u + u*v); -// \ru Пересчет параметров u,v в параметры a,b,c \en Recalculation of u,v parameters to a,b,c parameters -// -// v c=1 -// +1 | /| -// | / | -// curve1 / | curve0 -// |/ | -// a=1+----+--> u -// |\ |1 -// curve2 \ | -// | \ | -// -1 | \| -// | b=1 -// - c0 = t0min*(1.0-s1) + t0max*s1; - c1 = t1min*(1.0-s2) + t1max*s2; - c2 = t2min*(1.0-s0) + t2max*s0; - t0 = t0min*s2 + t0max*(1.0-s2); - t1 = t1min*s0 + t1max*(1.0-s0); - t2 = t2min*s1 + t2max*(1.0-s1); - - //if ( u < PARAM_EPSILON ) { - // if ( vc> PARAM_EPSILON && v> PARAM_EPSILON ) - // v = 1.0; - // else - // if ( vc<-PARAM_EPSILON && v<-PARAM_EPSILON ) - // v =-1.0; - //} -} - - -//------------------------------------------------------------------------------ -// \ru Получить кривую по индексу \en Get curve by an index -// --- -inline const MbCurve3D * MbCornerSurface::GetCurve( size_t ind ) const -{ - if ( ind >= CORNER_COUNT ) - ind = ind % CORNER_COUNT; - switch ( ind ) { - case 0 : { return curve0; } - case 1 : { return curve1; } - case 2 : { return curve2; } - } - return NULL; -} - - -//------------------------------------------------------------------------------ -// \ru Минимальное значение параметра на кривой с индексом ind. \en Get The minimal value of parameter on curve by index. -// --- -inline double MbCornerSurface::GetTMin( size_t ind ) const -{ - if ( ind >= CORNER_COUNT ) - ind = ind % CORNER_COUNT; - switch ( ind ) { - case 0 : { return t0min; } - case 1 : { return t1min; } - case 2 : { return t2min; } - } - return UNDEFINED_DBL; -} - - -//------------------------------------------------------------------------------ -// \ru Максимальное значение параметра на кривой с индексом ind. \en Get The maximal value of parameter on curve by index. -// --- -inline double MbCornerSurface::GetTMax( size_t ind ) const -{ - if ( ind >= CORNER_COUNT ) - ind = ind % CORNER_COUNT; - switch ( ind ) { - case 0 : { return t0max; } - case 1 : { return t1max; } - case 2 : { return t2max; } - } - return UNDEFINED_DBL; -} - - -#endif // __SURF_CORNER_SURFACE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Треугольная поверхность на сетке из трех кривых. + \en Triangular surface on grid of three curves. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __SURF_CORNER_SURFACE_H +#define __SURF_CORNER_SURFACE_H + + +#include + + +#define CORNER_COUNT 3 ///< \ru Число кривых, нужных для построения треугольной поверхности. \en Count of curves used to construct triangular surface. +#define VECT_CNT CORNER_COUNT + + +class MATH_CLASS MbContour3D; + + +//------------------------------------------------------------------------------ +/** \brief \ru Треугольная поверхность на кривых. + \en Triangular surface on curves. \~ + \details \ru Треугольная поверхность на сетке из трех кривых. + Кривые должны попарно пересекаться или иметь точки скрещения. + Если кривые попарно пересекаются, то поверхность проходит через определяющиее её кривые. \n + \en Triangular surface on grid of three curves. + Curves have to be intersected pairwise or have crossing points. + If curves are intersected pairwise then surface passes through its determining curves. \n \~ + \ingroup Surfaces +*/ +// --- +class MATH_CLASS MbCornerSurface : public MbSurface { +// P2 +// t1max ^* t0min +// R(s0,s1,s2) = / * +// (curve2(1-s1)+curve1(s2)-P0)*s0+ / * +// (curve0(1-s2)+curve2(s0)-P1)*s1+ / * +// (curve1(1-s0)+curve0(s1)-P2)*s2 curve1 / * curve0 +// / R * +// s0(u,v) + s1(u,v) + s2(u,v) = 1 / * +// R(0,s1,s2) = curve0(s1) / * +// R(s0,0,s2) = curve1(s2) t1min <----------------v t0max +// R(s0,s1,0) = curve2(s0) P0 P1 +// t2max curve2 t2min +// +// \ru s0,s1,s2 - Барицентрические (треугольные) координаты, \en S0,s1,s2 - Baricentric (triangular) coordinates. +// \ru P0 - полюс \en P0 - pole +private: + MbCurve3D * curve0; ///< \ru Кривая 0. \en Curve 0. + MbCurve3D * curve1; ///< \ru Кривая 1. \en Curve 1. + MbCurve3D * curve2; ///< \ru Кривая 2. \en Curve 2. + MbCartPoint3D vertex[CORNER_COUNT]; ///< \ru Вершины P0, P1, P2. \en Vertices P0, P1, P2. + double t0min; ///< \ru Минимальное значение параметра на кривой 0. \en Minimal value of parameter on curve 0. + double t0max; ///< \ru Максимальное значение параметра на кривой 0. \en Maximal value of parameter on curve 0. + double t1min; ///< \ru Минимальное значение параметра на кривой 1. \en Minimal value of parameter on curve 1. + double t1max; ///< \ru Максимальное значение параметра на кривой 1. \en Maximal value of parameter on curve 1. + double t2min; ///< \ru Минимальное значение параметра на кривой 2. \en Minimal value of parameter on curve 2. + double t2max; ///< \ru Максимальное значение параметра на кривой 2. \en Maximal value of parameter on curve 2. + +public: + /** \brief \ru Конструктор треугольной поверхности. + \en Constructor of triangular surface. \~ + \details \ru Конструктор треугольной поверхности по набору кривых. + \en Constructor of triangular surface by set of curves. \~ + \param[in] initCurve0 - \ru Кривая 0. + \en Curve 0. \~ + \param[in] initCurve1 - \ru Кривая 1. + \en Curve 1. \~ + \param[in] initCurve2 - \ru Кривая 2. + \en Curve 2. \~ + */ + MbCornerSurface ( const MbCurve3D & initCurve0, const MbCurve3D & initCurve1, const MbCurve3D & initCurve2 ); +private: + MbCornerSurface( const MbCornerSurface & ); // \ru Не реализовано. \en Not implemented. + MbCornerSurface( const MbCornerSurface & init, MbRegDuplicate * ); +public: + virtual ~MbCornerSurface( void ); + +public: + VISITING_CLASS( MbCornerSurface ); + + /// \ru Инициализация треугольной поверхности заданной треугольной поверхностью. \en Initialization of triangular surface by given triangular surface. + void Init( const MbCornerSurface &init ); + + /** \ru \name Общие функции геометрического объекта + \en \name Common functions of a geometric object + \{ */ + virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Cделать копию элемента \en Make a copy of element + virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Является ли объект копией. \en Whether the object is a copy. + virtual bool SetEqual( const MbSpaceItem & init ); // \ru Сделать равным \en Make equal + virtual bool IsSimilar( const MbSpaceItem & init ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar + virtual void Transform( const MbMatrix3D & matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move( const MbVector3D & to, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate( const MbAxis3D & axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis + + virtual void CalculateSurfaceWire( const MbStepData & stepData, size_t beg, MbMesh & mesh, + size_t uMeshCount = c3d::WIRE_MAX, size_t vMeshCount = c3d::WIRE_MAX ) const; + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the base objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + /** \} */ + /** \ru \name Функции описания области определения поверхности + \en \name Functions for surface domain description + \{ */ + virtual double GetUMin() const; + virtual double GetVMin() const; + virtual double GetUMax() const; + virtual double GetVMax() const; + virtual bool IsUClosed() const; // \ru Замкнута ли поверхность по параметру u. \en Whether the surface is closed by parameter u. + virtual bool IsVClosed() const; // \ru Замкнута ли поверхность по параметру v. \en Whether the surface is closed by parameter v. + virtual bool GetPoleUMin() const; + virtual bool GetPoleUMax() const; + virtual bool GetPoleVMin() const; + virtual bool GetPoleVMax() const; + virtual bool IsPole( double u, double v ) const; // \ru Является ли точка особенной \en Whether the point is special + /** \} */ + + /** \ru \name Функции для работы в области определения поверхности + Функции PointOn, Derive... поверхностей корректируют параметры + при выходе их за пределы прямоугольной области определения параметров.\n + \en \name Functions for working at surface domain + Functions PointOn, Derive... of surfaces correct parameters + when they are out of bounds of rectangular domain of parameters.\n + \{ */ + virtual void PointOn ( double & u, double & v, MbCartPoint3D & p ) const; // \ru Точка на поверхности \en Point on the surface + virtual void DeriveU ( double & u, double & v, MbVector3D & p ) const; // \ru Первая производная по u \en First derivative with respect to u + virtual void DeriveV ( double & u, double & v, MbVector3D & p ) const; // \ru Первая производная по v \en First derivative with respect to v + virtual void DeriveUU ( double & u, double & v, MbVector3D & p ) const; // \ru Вторая производная по u \en Second derivative with respect to u + virtual void DeriveVV ( double & u, double & v, MbVector3D & p ) const; // \ru Вторая производная по v \en Second derivative with respect to v + virtual void DeriveUV ( double & u, double & v, MbVector3D & p ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv + virtual void DeriveUUU( double & u, double & v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative + virtual void DeriveUUV( double & u, double & v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative + virtual void DeriveUVV( double & u, double & v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative + virtual void DeriveVVV( double & u, double & v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative + virtual void TangentV ( double & u, double & v, MbVector3D & p ) const; + virtual void Normal ( double & u, double & v, MbVector3D & p ) const; // \ru Нормаль \en Normal + /** \} */ + + /** \ru \name Функции для работы внутри и вне области определения поверхности + функции _PointOn, _Derive... поверхностей не корректируют + параметры при выходе их за пределы прямоугольной области определения параметров. + \en \name Functions for working inside and outside the surface's domain + functions _PointOn, _Derive... of surfaces don't correct + parameters when they are out of bounds of rectangular domain of parameters. + \{ */ + virtual void _PointOn ( double u, double v, MbCartPoint3D & p ) const; // \ru Точка на расширенной поверхности \en Point on the extended surface + virtual void _DeriveU ( double u, double v, MbVector3D & p ) const; // \ru Первая производная по u \en First derivative with respect to u + virtual void _DeriveV ( double u, double v, MbVector3D & p ) const; // \ru Первая производная по v \en First derivative with respect to v + virtual void _DeriveUU ( double u, double v, MbVector3D & p ) const; // \ru Вторая производная по u \en Second derivative with respect to u + virtual void _DeriveVV ( double u, double v, MbVector3D & p ) const; // \ru Вторая производная по v \en Second derivative with respect to v + virtual void _DeriveUV ( double u, double v, MbVector3D & p ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv + virtual void _DeriveUUU( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative + virtual void _DeriveUUV( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative + virtual void _DeriveUVV( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative + virtual void _DeriveVVV( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative + virtual void _TangentV ( double u, double v, MbVector3D & p ) const; + virtual void _Normal ( double u, double v, MbVector3D & p ) const; // \ru Нормаль \en Normal + virtual void _NormalV ( double u, double v, MbVector3D & p ) const; // \ru Производная нормали \en Derivative of normal + /** \} */ + + /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. + \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. + \{ */ + virtual void Explore( double & u, double & v, bool ext, + MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, + MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; + /** \} */ + + /** \ru \name Функции движения по поверхности + \en \name Functions of moving along the surface + \{ */ + virtual double StepU( double u, double v, double sag ) const; // \ru Вычисление шага аппроксимации с учетом радиуса кривизны по U \en Calculation of the approximation step with consideration of the curvature radius by U + virtual double StepV( double u, double v, double sag ) const; // \ru Вычисление шага аппроксимации с учетом радиуса кривизны по V \en Calculation of the approximation step with consideration of the curvature radius by V + virtual double DeviationStepU( double u, double v, double ang ) const; ///< \ru Вычисление шага параметра u по углу отклонения нормали \en Calculation of parameter u step by the angle of deviation of normal + virtual double DeviationStepV( double u, double v, double ang ) const; ///< \ru Вычисление шага параметра v по углу отклонения нормали \en Calculation of parameter v step by the angle of deviation of normal + + virtual size_t GetUCount() const; + virtual size_t GetVCount() const; + /** \} */ + + /** \ru \name Общие функции поверхности + \en \name Common functions of surface + \{ */ + virtual MbCurve3D * CurveU( double v, MbRect1D * pRgn, bool doApprox = true ) const; // \ru Пространственная копия линии v = const \en Spatial copy of 'v = const'-line + virtual MbCurve3D * CurveV( double u, MbRect1D * pRgn, bool doApprox = true ) const; // \ru Пространственная копия линии u = const \en Spatial copy of 'u = const'-line + + virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменение носителя \en Changing of carrier + // \ru Существует ли полюс на границе параметрической области \en Whether there is pole on boundary of parametric region + + virtual size_t GetUMeshCount() const; // \ru Выдать количество полигонов по u \en Get the count of polygons by u + virtual size_t GetVMeshCount() const; // \ru Выдать количество полигонов по v \en Get the count of polygons by v + + /// \ru Получить кривую 0. \en Get curve 0. + const MbCurve3D & GetCurve0() const { return *curve0; } + /// \ru Получить кривую 1. \en Get curve 1. + const MbCurve3D & GetCurve1() const { return *curve1; } + /// \ru Получить кривую 2. \en Get curve 2. + const MbCurve3D & GetCurve2() const { return *curve2; } + /// \ru Получить кривую по индексу. \en Get curve by an index. + const MbCurve3D * GetCurve( size_t ind ) const; + /// \ru Получить количество кривых. \en Get count of curves. + size_t GetCurvesCount() const { return 3; } //-V112 + const MbCartPoint3D * GetVertex() const { return vertex; } ///< \ru Выдать вершины P0, P1, P2. \en Get vertices P0, P1, P2. + double GetT0Min() const { return t0min; } ///< \ru Минимальное значение параметра на кривой 0. \en Minimal value of parameter on curve 0. + double GetT0Max() const { return t0max; } ///< \ru Максимальное значение параметра на кривой 0. \en Maximal value of parameter on curve 0. + double GetT1Min() const { return t1min; } ///< \ru Минимальное значение параметра на кривой 1. \en Minimal value of parameter on curve 1. + double GetT1Max() const { return t1max; } ///< \ru Максимальное значение параметра на кривой 1. \en Maximal value of parameter on curve 1. + double GetT2Min() const { return t2min; } ///< \ru Минимальное значение параметра на кривой 2. \en Minimal value of parameter on curve 2. + double GetT2Max() const { return t2max; } ///< \ru Максимальное значение параметра на кривой 2. \en Maximal value of parameter on curve 2. + double GetTMin( size_t ind ) const; ///< \ru Минимальное значение параметра на кривой с индексом ind. \en Get The minimal value of parameter on curve by index. + double GetTMax( size_t ind ) const; ///< \ru Максимальное значение параметра на кривой с индексом ind. \en Get The maximal value of parameter on curve by index. + /** \} */ + +private: + void Init(); + inline void CalculateCoordinate( double & u, double & v, bool ext, + double & s0, double & s1, double & s2, + double & c0, double & c1, double & c2, + double & t0, double & t1, double & t2 ) const; + void CalculatePoint ( double & u, double & v, bool ext, + MbCartPoint3D & point ) const; + void CalculateFirst ( double & u, double & v, bool ext, + MbVector3D * first ) const; + void CalculateSecond( double & u, double & v, bool ext, + MbVector3D * second ) const; + void CalculateThird ( double & u, double & v, bool ext, + MbVector3D * second, MbVector3D * third ) const; + void CalculateExplore( double & u, double & v, bool ext, + MbCartPoint3D * point, MbVector3D * first, MbVector3D * second ) const; + bool GetNormalFactor( MbVector3D & norm ) const; // \ru Нормаль в точке с параметрами u=0. \en Normal at u=0. + void Derivatives( double u, double v, bool ext, MbVector3D & uDer, MbVector3D & vDer ) const; // \ru Ппроизводные. \en Derivatives with respect to u and to v. + void operator = ( const MbCornerSurface & ); // \ru Не реализовано. \en Not implemented. + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCornerSurface ) +}; // MbCornerSurface + +IMPL_PERSISTENT_OPS( MbCornerSurface ) + +//------------------------------------------------------------------------------ +// \ru Определение местных координат \en Determination of local coordinates +// --- +inline void MbCornerSurface::CalculateCoordinate( double & u, double & v, bool ext, + double & s0, double & s1, double & s2, + double & c0, double & c1, double & c2, + double & t0, double & t1, double & t2 ) const +{ + if ( !ext ) { + if ( v <-1.0 ) v =-1.0; + if ( u > 1.0 ) u = 1.0; + if ( v > 1.0 ) v = 1.0; + } + if (u < 0.0) + u = 0.0; // \ru Нельзя заходить за полюс. \en Is impossible to go behind a pole + + s0 = 1.0 - u; + s1 = 0.5 * (u - u*v); + s2 = 0.5 * (u + u*v); +// \ru Пересчет параметров u,v в параметры a,b,c \en Recalculation of u,v parameters to a,b,c parameters +// +// v c=1 +// +1 | /| +// | / | +// curve1 / | curve0 +// |/ | +// a=1+----+--> u +// |\ |1 +// curve2 \ | +// | \ | +// -1 | \| +// | b=1 +// + c0 = t0min*(1.0-s1) + t0max*s1; + c1 = t1min*(1.0-s2) + t1max*s2; + c2 = t2min*(1.0-s0) + t2max*s0; + t0 = t0min*s2 + t0max*(1.0-s2); + t1 = t1min*s0 + t1max*(1.0-s0); + t2 = t2min*s1 + t2max*(1.0-s1); + + //if ( u < PARAM_EPSILON ) { + // if ( vc> PARAM_EPSILON && v> PARAM_EPSILON ) + // v = 1.0; + // else + // if ( vc<-PARAM_EPSILON && v<-PARAM_EPSILON ) + // v =-1.0; + //} +} + + +//------------------------------------------------------------------------------ +// \ru Получить кривую по индексу \en Get curve by an index +// --- +inline const MbCurve3D * MbCornerSurface::GetCurve( size_t ind ) const +{ + if ( ind >= CORNER_COUNT ) + ind = ind % CORNER_COUNT; + switch ( ind ) { + case 0 : { return curve0; } + case 1 : { return curve1; } + case 2 : { return curve2; } + } + return NULL; +} + + +//------------------------------------------------------------------------------ +// \ru Минимальное значение параметра на кривой с индексом ind. \en Get The minimal value of parameter on curve by index. +// --- +inline double MbCornerSurface::GetTMin( size_t ind ) const +{ + if ( ind >= CORNER_COUNT ) + ind = ind % CORNER_COUNT; + switch ( ind ) { + case 0 : { return t0min; } + case 1 : { return t1min; } + case 2 : { return t2min; } + } + return UNDEFINED_DBL; +} + + +//------------------------------------------------------------------------------ +// \ru Максимальное значение параметра на кривой с индексом ind. \en Get The maximal value of parameter on curve by index. +// --- +inline double MbCornerSurface::GetTMax( size_t ind ) const +{ + if ( ind >= CORNER_COUNT ) + ind = ind % CORNER_COUNT; + switch ( ind ) { + case 0 : { return t0max; } + case 1 : { return t1max; } + case 2 : { return t2max; } + } + return UNDEFINED_DBL; +} + + +#endif // __SURF_CORNER_SURFACE_H diff --git a/C3d/Include/surf_cover_surface.h b/C3d/Include/surf_cover_surface.h index 9d19032..94b3e23 100644 --- a/C3d/Include/surf_cover_surface.h +++ b/C3d/Include/surf_cover_surface.h @@ -196,6 +196,12 @@ public: virtual MbCurve3D * CurveV( double u, MbRect1D * pRgn, bool doApprox = true ) const; // \ru Пространственная копия линии u = const \en Spatial copy of 'u = const'-line virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменение носителя \en Changing of carrier + // \ru Подобные ли поверхности для объединения (слива). \en Whether the surfaces to union (joining) are similar. + virtual bool IsSimilarToSurface( const MbSurface & surf, VERSION version, double precision = METRIC_PRECISION ) const; + // \ru Дать двумерную матрицу преобразования из своей параметрической области в параметрическую область surf. \en Get two-dimensional transformation matrix from own parametric region to parametric region of 'surf'. + virtual bool GetMatrixToSurface( const MbSurface & surf, MbMatrix & matr, VERSION version, double precision = METRIC_PRECISION ) const; + // \ru Подобные ли поверхности для объединения (слива) \en Whether the surfaces to union (joining) are similar + virtual bool IsSpecialSimilarToSurface( const MbSurface & surf, VERSION version, double precision = METRIC_PRECISION ) const; // \ru Специальный случай \en Special case virtual size_t GetUMeshCount() const; // \ru Выдать количество полигонов по u \en Get the count of polygons by u virtual size_t GetVMeshCount() const; // \ru Выдать количество полигонов по v \en Get the count of polygons by v diff --git a/C3d/Include/surf_curve_bounded_surface.h b/C3d/Include/surf_curve_bounded_surface.h index 3d72a12..2b7bb35 100644 --- a/C3d/Include/surf_curve_bounded_surface.h +++ b/C3d/Include/surf_curve_bounded_surface.h @@ -88,7 +88,7 @@ class MbContoursSearchTree; \ingroup Surfaces */ // --- -class MATH_CLASS MbCurveBoundedSurface : public MbSurface, public MbNestSyncItem { +class MATH_CLASS MbCurveBoundedSurface : public MbSurface { private: MbSurface * basisSurface; ///< \ru Базовая поверхность. \en Base surface. RPArray curves; ///< \ru Множество контуров на поверхности, определяющих её края. \en Set of contours on surface determining surface boundary. @@ -114,9 +114,11 @@ public : /// \ru Конструктор с массивом контуров на плоскости (двумерных контуров). \en Constructor with array of contours on plane (two-dimensional contours). MbCurveBoundedSurface( MbSurface & initSurface, RPArray & initCurves, bool sameContours ); /// \ru Конструктор с массивом контуров на плоскости (двумерных контуров). \en Constructor with array of contours on plane (two-dimensional contours). - MbCurveBoundedSurface( MbSurface & initSurface, std::vector< SPtr > & initCurves, bool sameContours ); + MbCurveBoundedSurface( MbSurface & initSurface, c3d::PlaneContoursSPtrVector & initCurves, bool sameContours ); /// \ru Конструктор для поверхности c габаритом при чтении грани. \en Constructor for surface with bounding box at face reading. MbCurveBoundedSurface( MbSurface & initSurface, RPArray & initCurves, MbCube & gab ); + /// \ru Конструктор для поверхности c габаритом при чтении грани. \en Constructor for surface with bounding box at face reading. + MbCurveBoundedSurface( MbSurface & initSurface, c3d::PlaneContoursSPtrVector & initCurves, MbCube & gab ); /// \ru Конструктор по контурам, берет за базовую поверхность поверхность первого контура. \en Constructor by contours, uses the surface of first contour as base surface. MbCurveBoundedSurface( MbContourOnSurface & init1, MbContourOnSurface * init2 = NULL ); /// \ru Конструктор-копия на новую базовую поверхность. \en Copy-constructor for new base surface. @@ -430,7 +432,7 @@ public : */ bool ChangeContour( size_t index, MbContourOnSurface * cntr ); /// \ru Заменить базовую поверхность. \en Replace base surface. - bool ChangeSurface( MbSurface & newsurf ); + bool ChangeSurface( const MbSurface & newsurf ); /// \ru Заменить базовую поверхность на ее копию. \en Replace base surface with its copy. void NewBasisSurface(); /// \ru Вычислить параметрические границы поверхности без сброса габарита. \en Calculate parametric bounds of surface without resetting the bounding box. @@ -458,7 +460,7 @@ public : /** \brief \ru Ориентировать ограничивающие контуры. \en Orient bounding contours. \~ - \details \ru Ориентирует внешний контур против часовой стрелки, внутренние контуры - по часовой стрелки. + \details \ru Ориентирует внешний контур против часовой стрелки, внутренние контуры - по часовой стрелке. \en External contour is oriented counterclockwise, internal contours - clockwise. \~ \return \ru Возвращает площадь параметрической области поверхности. \en Returns area of parametric region of surface. \~ diff --git a/C3d/Include/surf_cylinder_surface.h b/C3d/Include/surf_cylinder_surface.h index a1b6c92..a77edfb 100644 --- a/C3d/Include/surf_cylinder_surface.h +++ b/C3d/Include/surf_cylinder_surface.h @@ -54,7 +54,7 @@ class MATH_CLASS MbPlane; \ingroup Surfaces */ // --- -class MATH_CLASS MbCylinderSurface : public MbElementarySurface { +class MATH_CLASS MbCylinderSurface : public MbElementarySurface { private: double radius; ///< \ru Радиус цилиндра. \en Radius of cylinder. double height; ///< \ru Высота цилиндра. \en Height of cylinder. diff --git a/C3d/Include/surf_elementary_surface.h b/C3d/Include/surf_elementary_surface.h index 9cf446f..cc51887 100644 --- a/C3d/Include/surf_elementary_surface.h +++ b/C3d/Include/surf_elementary_surface.h @@ -15,9 +15,9 @@ #include -#define CIRC_COUNT 32 -#define HIDE_COUNT 16 -#define LINE_COUNT 10 +const_expr size_t CIRC_COUNT = 32; +const_expr size_t HIDE_COUNT = 16; +const_expr size_t LINE_COUNT = 10; //------------------------------------------------------------------------------ diff --git a/C3d/Include/surf_expansion_surface.h b/C3d/Include/surf_expansion_surface.h index 11b8643..199e813 100644 --- a/C3d/Include/surf_expansion_surface.h +++ b/C3d/Include/surf_expansion_surface.h @@ -20,14 +20,14 @@ \en Motion surface (plane-parallel swept surface). \~ \details \ru Поверхность плоскопараллельного движения получается путем движения образующей кривой curve по направляющей кривой spine->curve параллельно самой себе: - Радиус-вектор поверхности описывается векторной функцией \n + Радиус-вектор поверхности описывается векторной функцией \n r(u,v) = curve(u) + spine(v) - origin. \n Направляющая плоскопараллельной поверхности должна быть незамкнутой монотонной кривой. Первый параметр поверхности совпадает с параметром образующей кривой. Второй параметр поверхности совпадает с параметром направляющей кривой. \en Expansion surface is obtained by moving 'curve' generating curve along 'spine->curve" guide curve parallel to itself: - Radius-vector of surface is described by the vector function \n + Radius-vector of surface is described by the vector function \n r(u,v) = curve(u) + spine(v) - origin. \n Guide curve of plane-parallel surface should be open monotonous curve. First parameter of surface coincides with parameter of generating curve. diff --git a/C3d/Include/surf_exploration_surface.h b/C3d/Include/surf_exploration_surface.h index 07f018d..a102f9e 100644 --- a/C3d/Include/surf_exploration_surface.h +++ b/C3d/Include/surf_exploration_surface.h @@ -1,194 +1,194 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Поверхность заметания с масштабированием и поворотом образующей кривой. - \en The swept surface with scaling and winding of generation curve. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __SURF_EXTENSION_SURFACE_H -#define __SURF_EXTENSION_SURFACE_H - - -#include -#include - - -class MATH_CLASS MbFunction; - - -//------------------------------------------------------------------------------ -/** \brief \ru Поверхность заметания с масштабированием и поворотом образующей кривой. - \en The swept surface with scaling and winding of generation curve. \~ - \details \ru Кинематическая поверхность образуется путем движения образующей кривой curve по направляющей кривой spine->curve. - В процессе движения вдоль направляющей кривой образующая кривая сохраняет своё положение в движущейся локальной системе координат, - начало которой совпадает с текущей точкой базовой кривой. - Одна из осей движущейся локальной системы координат всегда совпадает с касательной направляющей кривой, - а две другие оси ортогональны ей. - Первый параметр поверхности совпадает с параметром образующей кривой. - Второй параметр поверхности совпадает с параметром направляющей кривой. - \en Sweep with guide curve surface is formed by moving the 'curve' generating curve along spine->curve guide curve. - While moving along a guide curve the generating curve keeps its position in the moving local coordinate system, - which origin coincides with the current point of the base curve. - One of the axes of the moving local coordinate system is always coincident to the tangent of the guide curve, - and the other two axes are orthogonal to it. - First parameter of surface coincides with parameter of generating curve. - Second parameter of surface coincides with parameter of guide curve. \~ - \ingroup Surfaces -*/ -// --- -class MATH_CLASS MbExplorationSurface : public MbEvolutionSurface { -protected: - MbFunction * scaling; ///< \ru Функция второго параметра (v) масштабирования образующей кривой. \en The function of curve scale by second parameter (v). - MbFunction * winding; ///< \ru Функция второго параметра (v) вращения образующей кривой. \en The function of curve rotation by second parameter (v). - MbAxis3D axis; ///< \ru Не пишется. \en The axis are not writing. - -protected: - /** \brief \ru Конструктор по образующей и направляющей. - \en Constructor by generating curve and guide curve. \~ - \details \ru Конструктор по образующей и направляющей. - \en Constructor by generating curve and guide curve. \~ - \param[in] c - \ru Образующая - \en Generating curve \~ - \param[in] s - \ru Направляющая - \en Guide curve \~ - \param[in] sameCurve - \ru Признак использования оригинала образующей, а не копии - \en Attribute of usage of original of generating curve, not a copy \~ - \param[in] sameSpine - \ru Признак использования оригинала направляющей, а не копии - \en Attribute of usage of original of guide curve, not a copy \~ - */ - MbExplorationSurface( const MbCurve3D & c, const MbSpine & s, bool sameCurve, bool sameSpine, - MbFunction & _scaling, MbFunction & _winding ); - -protected: - MbExplorationSurface( const MbExplorationSurface &, MbRegDuplicate * ); -private: - MbExplorationSurface( const MbExplorationSurface & ); // \ru Не реализовано. \en Not implemented. -public: - virtual ~MbExplorationSurface(); - -public: - VISITING_CLASS( MbExplorationSurface ); - - /** \ru \name Общие функции геометрического объекта - \en \name Common functions of a geometric object - \{ */ - virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Равны ли объекты. \en Whether the objects are equal. - virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным. \en Make equal. - virtual bool IsSimilar ( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными. \en Determine whether the objects are similar. - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг. \en Move. - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate around an axis. - - virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта. \en Set properties of the object. - - /** \} */ - /** \ru \name Функции для работы в области определения поверхности - Функции PointOn, Derive... поверхностей корректируют параметры - при выходе их за пределы прямоугольной области определения параметров.\n - \en \name Functions for working at surface domain - Functions PointOn, Derive... of surfaces correct parameters - when they are out of bounds of rectangular domain of parameters.\n - \{ */ - virtual void PointOn ( double & u, double & v, MbCartPoint3D & ) const; // \ru Точка на поверхности. \en The point on the surface. - virtual void DeriveU ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по u. \en The first derivative with respect to u. - virtual void DeriveV ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по v. \en The first derivative with respect to v. - virtual void DeriveUU ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по u. \en The second derivative with respect to u. - virtual void DeriveVV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по v. \en The second derivative with respect to v. - virtual void DeriveUV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по uv. \en The second derivative with respect to uv. - virtual void DeriveUUU( double & u, double & v, MbVector3D & ) const; - virtual void DeriveUUV( double & u, double & v, MbVector3D & ) const; - virtual void DeriveUVV( double & u, double & v, MbVector3D & ) const; - virtual void DeriveVVV( double & u, double & v, MbVector3D & ) const; - /** \} */ - /** \ru \name Функции для работы внутри и вне области определения поверхности - функции _PointOn, _Derive... поверхностей не корректируют - параметры при выходе их за пределы прямоугольной области определения параметров. - \en \name Functions for working inside and outside the surface's domain - functions _PointOn, _Derive... of surfaces don't correct - parameters when they are out of bounds of rectangular domain of parameters. - \{ */ - virtual void _PointOn ( double u, double v, MbCartPoint3D & ) const; // \ru Точка на расширенной поверхности. \en The point on the extended surface. - virtual void _DeriveU ( double u, double v, MbVector3D & ) const; // \ru Первая производная по u. \en The first derivative with respect to u. - virtual void _DeriveV ( double u, double v, MbVector3D & ) const; // \ru Первая производная по v. \en The first derivative with respect to v. - virtual void _DeriveUU ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по u. \en The second derivative with respect to u. - virtual void _DeriveVV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по v. \en The second derivative with respect to v. - virtual void _DeriveUV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по uv. \en The second derivative with respect to uv. - virtual void _DeriveUUU( double u, double v, MbVector3D & ) const; - virtual void _DeriveUUV( double u, double v, MbVector3D & ) const; - virtual void _DeriveUVV( double u, double v, MbVector3D & ) const; - virtual void _DeriveVVV( double u, double v, MbVector3D & ) const; - /** \} */ - /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. - \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. - \{ */ - virtual void Explore( double & u, double & v, bool ext, - MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, - MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; - /** \} */ - /** \ru \name Функции движения по поверхности - \en \name Functions of moving along the surface - \{ */ - virtual double StepU ( double u, double v, double sag ) const; // \ru Вычисление шага по u по заданной стрелке прогиба. \en Calculation of the parameter step in u direction by the sag. - virtual double StepV ( double u, double v, double sag ) const; // \ru Вычисление шага по v по заданной стрелке прогиба. \en Calculation of the parameter step in v direction by the sag. - virtual double DeviationStepU( double u, double v, double angle ) const; // \ru Вычисление шага по u по заданному углу отклонения. \en Calculation of the parameter step in u direction by the deviation angle. - virtual double DeviationStepV( double u, double v, double angle ) const; // \ru Вычисление шага по v по заданному углу отклонения. \en Calculation of the parameter step in v direction by the deviation angle. - /** \} */ - /** \ru \name Общие функции поверхности - \en \name Common functions of surface - \{ */ - virtual MbSplineSurface * NurbsSurface( double, double, double, double, bool bmatch = false ) const; // \ru NURBS копия поверхности. \en NURBS copy of a surface. - virtual MbSurface * Offset( double d, bool same ) const; // \ru Создание эквидистантной поверхности. \en Create an offset surface. - - virtual MbCurve3D * CurveU( double v, MbRect1D *pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии v = const. \en Spatial copy of 'v = const'-line. - virtual MbCurve3D * CurveV( double u, MbRect1D *pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии u = const. \en Spatial copy of 'u = const'-line. - - // \ru Определение разбивки параметрической области поверхности вертикалями и горизонталями. \en Determine splitting of parametric region of surface by vertical and horizontal lines. - virtual void GetTesselation( const MbStepData & stepData, - double u1, double u2, double v1, double v2, - SArray & uu, SArray & vv ) const; - - virtual size_t GetUCount() const; // \ru Количество разбиений по параметру u для проверки событий. \en The number of splittings by u-parameter for a check of events. - virtual size_t GetVCount() const; // \ru Количество разбиений по параметру v для проверки событий. \en The number of splittings by v-parameter for a check of events. - - virtual size_t GetUMeshCount() const; // \ru Выдать количество полигонов по u. \en Get the count of polygons by u. - virtual size_t GetVMeshCount() const; // \ru Выдать количество полигонов по v. \en Get the count of polygons by v. - - //------------------------------------------------------------------------------ - /** \brief \ru Создать кинематическую поверхность. - \en Create an evolution surface. \~ - \details \ru Создать кинематическую поверхность. - \en Create an evolution surface. \~ - \param[in] curve - \ru Образующая кривая - \en Generating curve \~ - \param[in] spine - \ru Направляющая кривая - \en Guide curve \~ - \param[in] samec - \ru Признак использования оригинала образующей кривой, а не копии - \en Attribute of usage of original of generating curve, not a copy \~ - \param[in] sFunc - \ru Функция масштабирования образующей кривой. - \en The function of curve scaling. \~ - \param[in] rFunc - \ru Функция вращения образующей кривой. - \en The function of curve rotation. \~ - \return \ru Возвращает созданную поверхность. - \en Return the created surface. \~ - \ingroup Surface_Modeling - */ - // --- - static MbSurface * Create( const MbCurve3D & curve, const MbSpine & spine, bool samec, bool sames, - MbFunction & _scaling, MbFunction & _winding ); - -protected : - void Init(); - -private: - void operator = ( const MbExplorationSurface & ); // \ru Не реализовано. \en Not implemented. - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbExplorationSurface ) -}; - - -#endif // __SURF_EXTENSION_SURFACE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Поверхность заметания с масштабированием и поворотом образующей кривой. + \en The swept surface with scaling and winding of generation curve. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __SURF_EXTENSION_SURFACE_H +#define __SURF_EXTENSION_SURFACE_H + + +#include +#include + + +class MATH_CLASS MbFunction; + + +//------------------------------------------------------------------------------ +/** \brief \ru Поверхность заметания с масштабированием и поворотом образующей кривой. + \en The swept surface with scaling and winding of generation curve. \~ + \details \ru Кинематическая поверхность образуется путем движения образующей кривой curve по направляющей кривой spine->curve. + В процессе движения вдоль направляющей кривой образующая кривая сохраняет своё положение в движущейся локальной системе координат, + начало которой совпадает с текущей точкой базовой кривой. + Одна из осей движущейся локальной системы координат всегда совпадает с касательной направляющей кривой, + а две другие оси ортогональны ей. + Первый параметр поверхности совпадает с параметром образующей кривой. + Второй параметр поверхности совпадает с параметром направляющей кривой. + \en Sweep with guide curve surface is formed by moving the 'curve' generating curve along spine->curve guide curve. + While moving along a guide curve the generating curve keeps its position in the moving local coordinate system, + which origin coincides with the current point of the base curve. + One of the axes of the moving local coordinate system is always coincident to the tangent of the guide curve, + and the other two axes are orthogonal to it. + First parameter of surface coincides with parameter of generating curve. + Second parameter of surface coincides with parameter of guide curve. \~ + \ingroup Surfaces +*/ +// --- +class MATH_CLASS MbExplorationSurface : public MbEvolutionSurface { +protected: + MbFunction * scaling; ///< \ru Функция второго параметра (v) масштабирования образующей кривой. \en The function of curve scale by second parameter (v). + MbFunction * winding; ///< \ru Функция второго параметра (v) вращения образующей кривой. \en The function of curve rotation by second parameter (v). + MbAxis3D axis; ///< \ru Не пишется. \en The axis are not writing. + +protected: + /** \brief \ru Конструктор по образующей и направляющей. + \en Constructor by generating curve and guide curve. \~ + \details \ru Конструктор по образующей и направляющей. + \en Constructor by generating curve and guide curve. \~ + \param[in] c - \ru Образующая + \en Generating curve \~ + \param[in] s - \ru Направляющая + \en Guide curve \~ + \param[in] sameCurve - \ru Признак использования оригинала образующей, а не копии + \en Attribute of usage of original of generating curve, not a copy \~ + \param[in] sameSpine - \ru Признак использования оригинала направляющей, а не копии + \en Attribute of usage of original of guide curve, not a copy \~ + */ + MbExplorationSurface( const MbCurve3D & c, const MbSpine & s, bool sameCurve, bool sameSpine, + MbFunction & _scaling, MbFunction & _winding ); + +protected: + MbExplorationSurface( const MbExplorationSurface &, MbRegDuplicate * ); +private: + MbExplorationSurface( const MbExplorationSurface & ); // \ru Не реализовано. \en Not implemented. +public: + virtual ~MbExplorationSurface(); + +public: + VISITING_CLASS( MbExplorationSurface ); + + /** \ru \name Общие функции геометрического объекта + \en \name Common functions of a geometric object + \{ */ + virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Равны ли объекты. \en Whether the objects are equal. + virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным. \en Make equal. + virtual bool IsSimilar ( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными. \en Determine whether the objects are similar. + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг. \en Move. + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate around an axis. + + virtual void GetProperties( MbProperties & ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & ); // \ru Записать свойства объекта. \en Set properties of the object. + + /** \} */ + /** \ru \name Функции для работы в области определения поверхности + Функции PointOn, Derive... поверхностей корректируют параметры + при выходе их за пределы прямоугольной области определения параметров.\n + \en \name Functions for working at surface domain + Functions PointOn, Derive... of surfaces correct parameters + when they are out of bounds of rectangular domain of parameters.\n + \{ */ + virtual void PointOn ( double & u, double & v, MbCartPoint3D & ) const; // \ru Точка на поверхности. \en The point on the surface. + virtual void DeriveU ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по u. \en The first derivative with respect to u. + virtual void DeriveV ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по v. \en The first derivative with respect to v. + virtual void DeriveUU ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по u. \en The second derivative with respect to u. + virtual void DeriveVV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по v. \en The second derivative with respect to v. + virtual void DeriveUV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по uv. \en The second derivative with respect to uv. + virtual void DeriveUUU( double & u, double & v, MbVector3D & ) const; + virtual void DeriveUUV( double & u, double & v, MbVector3D & ) const; + virtual void DeriveUVV( double & u, double & v, MbVector3D & ) const; + virtual void DeriveVVV( double & u, double & v, MbVector3D & ) const; + /** \} */ + /** \ru \name Функции для работы внутри и вне области определения поверхности + функции _PointOn, _Derive... поверхностей не корректируют + параметры при выходе их за пределы прямоугольной области определения параметров. + \en \name Functions for working inside and outside the surface's domain + functions _PointOn, _Derive... of surfaces don't correct + parameters when they are out of bounds of rectangular domain of parameters. + \{ */ + virtual void _PointOn ( double u, double v, MbCartPoint3D & ) const; // \ru Точка на расширенной поверхности. \en The point on the extended surface. + virtual void _DeriveU ( double u, double v, MbVector3D & ) const; // \ru Первая производная по u. \en The first derivative with respect to u. + virtual void _DeriveV ( double u, double v, MbVector3D & ) const; // \ru Первая производная по v. \en The first derivative with respect to v. + virtual void _DeriveUU ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по u. \en The second derivative with respect to u. + virtual void _DeriveVV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по v. \en The second derivative with respect to v. + virtual void _DeriveUV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по uv. \en The second derivative with respect to uv. + virtual void _DeriveUUU( double u, double v, MbVector3D & ) const; + virtual void _DeriveUUV( double u, double v, MbVector3D & ) const; + virtual void _DeriveUVV( double u, double v, MbVector3D & ) const; + virtual void _DeriveVVV( double u, double v, MbVector3D & ) const; + /** \} */ + /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. + \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. + \{ */ + virtual void Explore( double & u, double & v, bool ext, + MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, + MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; + /** \} */ + /** \ru \name Функции движения по поверхности + \en \name Functions of moving along the surface + \{ */ + virtual double StepU ( double u, double v, double sag ) const; // \ru Вычисление шага по u по заданной стрелке прогиба. \en Calculation of the parameter step in u direction by the sag. + virtual double StepV ( double u, double v, double sag ) const; // \ru Вычисление шага по v по заданной стрелке прогиба. \en Calculation of the parameter step in v direction by the sag. + virtual double DeviationStepU( double u, double v, double angle ) const; // \ru Вычисление шага по u по заданному углу отклонения. \en Calculation of the parameter step in u direction by the deviation angle. + virtual double DeviationStepV( double u, double v, double angle ) const; // \ru Вычисление шага по v по заданному углу отклонения. \en Calculation of the parameter step in v direction by the deviation angle. + /** \} */ + /** \ru \name Общие функции поверхности + \en \name Common functions of surface + \{ */ + virtual MbSplineSurface * NurbsSurface( double, double, double, double, bool bmatch = false ) const; // \ru NURBS копия поверхности. \en NURBS copy of a surface. + virtual MbSurface * Offset( double d, bool same ) const; // \ru Создание эквидистантной поверхности. \en Create an offset surface. + + virtual MbCurve3D * CurveU( double v, MbRect1D *pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии v = const. \en Spatial copy of 'v = const'-line. + virtual MbCurve3D * CurveV( double u, MbRect1D *pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии u = const. \en Spatial copy of 'u = const'-line. + + // \ru Определение разбивки параметрической области поверхности вертикалями и горизонталями. \en Determine splitting of parametric region of surface by vertical and horizontal lines. + virtual void GetTesselation( const MbStepData & stepData, + double u1, double u2, double v1, double v2, + SArray & uu, SArray & vv ) const; + + virtual size_t GetUCount() const; // \ru Количество разбиений по параметру u для проверки событий. \en The number of splittings by u-parameter for a check of events. + virtual size_t GetVCount() const; // \ru Количество разбиений по параметру v для проверки событий. \en The number of splittings by v-parameter for a check of events. + + virtual size_t GetUMeshCount() const; // \ru Выдать количество полигонов по u. \en Get the count of polygons by u. + virtual size_t GetVMeshCount() const; // \ru Выдать количество полигонов по v. \en Get the count of polygons by v. + + //------------------------------------------------------------------------------ + /** \brief \ru Создать кинематическую поверхность. + \en Create an evolution surface. \~ + \details \ru Создать кинематическую поверхность. + \en Create an evolution surface. \~ + \param[in] curve - \ru Образующая кривая + \en Generating curve \~ + \param[in] spine - \ru Направляющая кривая + \en Guide curve \~ + \param[in] samec - \ru Признак использования оригинала образующей кривой, а не копии + \en Attribute of usage of original of generating curve, not a copy \~ + \param[in] sFunc - \ru Функция масштабирования образующей кривой. + \en The function of curve scaling. \~ + \param[in] rFunc - \ru Функция вращения образующей кривой. + \en The function of curve rotation. \~ + \return \ru Возвращает созданную поверхность. + \en Return the created surface. \~ + \ingroup Surface_Modeling + */ + // --- + static MbSurface * Create( const MbCurve3D & curve, const MbSpine & spine, bool samec, bool sames, + MbFunction & _scaling, MbFunction & _winding ); + +protected : + void Init(); + +private: + void operator = ( const MbExplorationSurface & ); // \ru Не реализовано. \en Not implemented. + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbExplorationSurface ) +}; + + +#endif // __SURF_EXTENSION_SURFACE_H diff --git a/C3d/Include/surf_extrusion_surface.h b/C3d/Include/surf_extrusion_surface.h index 2cc76c0..89f83fa 100644 --- a/C3d/Include/surf_extrusion_surface.h +++ b/C3d/Include/surf_extrusion_surface.h @@ -22,11 +22,11 @@ class MATH_CLASS MbOffsetSurface; /** \brief \ru Поверхность выдавливания. \en Extrusion surface. \~ \details \ru Поверхность выдавливания является кинематической поверхностью с прямолинейной образующей. - Радиус-вектор поверхности описывается векторной функцией \n + Радиус-вектор поверхности описывается векторной функцией \n r(u,v) = curve(u) + (direction distance v). \n Первый параметр поверхности совпадает с параметром образующей кривой. \en Extrusion surface is swept surface with rectilinear generating curve. - Radius-vector of surface is described by the vector function \n + Radius-vector of surface is described by the vector function \n r(u,v) = curve(u) + (direction distance v). \n First parameter of surface coincides with parameter of generating curve. \~ \ingroup Surfaces diff --git a/C3d/Include/surf_fillet_surface.h b/C3d/Include/surf_fillet_surface.h index 3faa846..9b0e375 100644 --- a/C3d/Include/surf_fillet_surface.h +++ b/C3d/Include/surf_fillet_surface.h @@ -1,712 +1,713 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Поверхность скругления с постоянными радиусами обычная или с сохранением кромки. - \en Fillet surface of constant radii, ordinary or with preservation of fillet. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __SURF_FILLET_SURFACE_H -#define __SURF_FILLET_SURFACE_H - - -#include - - -class MATH_CLASS MbFunction; - - -//------------------------------------------------------------------------------ -/** \brief \ru Поверхность скругления с постоянными радиусами обычная или с сохранением кромки. - \en Fillet surface of constant radii, ordinary or with preservation of fillet. \~ - \details \ru Поверхность скругления является NURBS-поверхностью, - построенной по трём кривым: curve1, curve0, curve2. - Первый параметр поверхности совпадает с параметром кривых curve1, curve0, curve2. - Второй параметр изменяется от нуля (точки совпадают с curve1) до единицы (точки совпадают с curve2). - В отличие от других поверхностей Функции PointOn и Derive... поверхность скругления не корректирует - первый параметр при выходе его за пределы области определения. - Если коэффициент формы conic = _ARC_ ( 0 ), то вес каждой точки кривой curve0 задаётся функцией weights0 и - вычислен так, что сечение поверхности вдоль её второго параметра будет дугой окружности, - то есть при любом параметре u три точки curve1(u), curve0(u), curve2(u) определяют NURBS-кривую в форме дуги окружности. - Если коэффициент формы conic != _ARC_, то вес каждой точки кривой curve0 равен conic / ( 1.0 - conic ). - При conic = 0.5 сечение поверхности вдоль её второго параметра будет параболой. \n - \en Fillet surface is NURBS-surface - constructed by three curves: curve1, curve0, curve2. - First parameter of surface coincides with parameter of curve1, curve0, curve2 curves. - Second parameter is changed between 0 (points are coincident to curve1) and 1 (points are coincident to curve2). - For fillet surface in contrast to other surfaces PointOn and Derive... functions don't correct - first parameter when it is out of domain bounds. - If coefficient of shape conic = _ARC_ ( 0 ), then weight of each point of curve0 curve is given by weights0 function and - is calculated as that the section of surface along its second parameter will be a circular arc, - that is three points curve1(u), curve0(u), curve2(u) determine NURBS-curve with shape of circular arc at any u parameter. - If coefficient of shape conic != _ARC_, then weight of each point of curve0 curve is equal to conic / ( 1.0 - conic ). - If conic = 0.5, then section of surface along its second parameter will be a parabola. \n \~ - \ingroup Surfaces -*/// --- -class MATH_CLASS MbFilletSurface : public MbSmoothSurface { -protected: - MbCurve3D * curve0; ///< \ru Кривая пересечения касательных к поверхностям - всегда не NULL. \en Intersection curve of tangents to surfaces - always not NULL. - MbFunction * weights0; ///< \ru Функция веса точек средней кривой curve0. \en Function of weight of points of curve0 mid-curve. - double conic; ///< \ru Коэффициент формы, изменяется от 0.05 до 0.95, определяет вес точек кривой curve0. \en Coefficient of shape is changed between 0.05 and 0.95 and determines weight of points of curve0 curve. - bool even; ///< \ru Равномерная параметризация по дуге или нет. \en Whether arc length parameterization is uniform or not. - bool equable; ///< \ru true - обычная поверхность, false - curve1 или curve2 является кромкой. \en True - ordinary surface, false - curve1 or curve2 is fillet. - bool byCurve1; ///< \ru true - curve2 является кромкой, false - curve1 является кромкой. \en True - curve2 is fillet, false - curve1 is fillet. - MbCurve3D * spine; ///< \ru Кривая центров дуг окружности для случая равномерной параметризации. \en Curve of centers of circular arcs in case of uniform parameterization. - MbVector3D * spineDerUMin; // \ru Производные spine в точках uMin и uMax ( для случая равномерной параметризации ). \en Derivatives of spine at uMin and uMax points (in case of uniform parameterization). - MbVector3D * spineDerUMax; // \ru Производные spine в точках uMin и uMax ( для случая равномерной параметризации ). \en Derivatives of spine at uMin and uMax points (in case of uniform parameterization). - -public: - - /** \brief \ru Конструктор поверхности скругления. - \en Constructor of fillet surface. \~ - \details \ru Конструктор поверхности скругления. - \en Constructor of fillet surface. \~ - \param[in] curv1 - \ru Опорная кривая на первой поверхности - \en Support curve on the first surface \~ - \param[in] curv2 - \ru Опорная кривая на второй поверхности - \en Support curve on the second surface \~ - \param[in] d1 - \ru Радиус скругления со знаком для поверхности кривой curve1 - \en Fillet radius with sign for surface of crve1 curve \~ - \param[in] d2 - \ru Радиус скругления со знаком для поверхности кривой curve2 - \en Fillet radius with sign for surface of crve2 curve \~ - \param[in] fm - \ru Тип сопряжения: \n - st_Span - скругление с заданной хордой - st_Fillet - скругление с заданными радиусами - \en Conjugation type: \n - st_Span - fillet with a given chord - st_Fillet - fillet with given radii \~ - \param[in] cn - \ru Коэффициент формы, изменяется от 0.05 до 0.95 (при 0 - дуга окружности) - \en Coefficient of shape is changed between 0.05 and 0.95 (if 0 - circular arc) \~ - \param[in] ev - \ru Равномерная параметризация по дуге или нет - \en Whether arc length parameterization is uniform or not \~ - */ - MbFilletSurface( MbSurfaceCurve & curv1, MbSurfaceCurve & curv2, - double d1, double d2, MbeSmoothForm fm, double cn, bool ev ); - - /** \brief \ru Конструктор поверхности с сохранением кромки. - \en Constructor of surface with preservation of fillet. \~ - \details \ru Конструктор поверхности с сохранением кромки. - \en Constructor of surface with preservation of fillet. \~ - \param[in] curv1 - \ru Опорная кривая на первой поверхности - \en Support curve on the first surface \~ - \param[in] curv2 - \ru Опорная кривая на второй поверхности - \en Support curve on the second surface \~ - \param[in] d1 - \ru Радиус скругления со знаком для поверхности кривой crve1 - \en Fillet radius with sign for surface of crve1 curve \~ - \param[in] d2 - \ru Радиус скругления со знаком для поверхности кривой crve2 - \en Fillet radius with sign for surface of crve2 curve \~ - \param[in] fm - \ru Тип сопряжения: \n - st_Span - скругление с заданной хордой \n - st_Fillet - скругление с заданными радиусами - \en Conjugation type: \n - st_Span - fillet with a given chord\n - st_Fillet - fillet with given radii \~ - \param[in] cn - \ru Коэффициент формы, изменяется от 0.05 до 0.95 (при 0 - дуга окружности) - \en Coefficient of shape is changed between 0.05 and 0.95 (if 0 - circular arc) \~ - \param[in] byFirst - \ru true - кривая curve2 является кромкой, false - кривая curve1 является кромкой - \en True - curve2 curve is fillet, false - curve1 curve is fillet. \~ - \param[in] ev - \ru Равномерная параметризация по дуге или нет - \en Whether arc length parameterization is uniform or not \~ - */ - MbFilletSurface( MbSurfaceCurve & curv1, MbSurfaceCurve & curv2, - double d1, double d2, MbeSmoothForm fm, double cn, bool byFirst, bool ev ); - - /** \brief \ru Конструктор поверхности скругления. - \en Constructor of fillet surface. \~ - \details \ru Конструктор поверхности скругления c кривой пересечения касательных к поверхностям. - \en Constructor of fillet surface with intersection curve of tangents to surfaces. \~ - \param[in] surf1 - \ru Первая поверхность - \en First surface \~ - \param[in] curv1 - \ru Опорная кривая в параметрах первой поверхности - \en Support curve at parameters of the first surface \~ - \param[in] surf2 - \ru Вторая поверхность - \en Second surface \~ - \param[in] curv2 - \ru Опорная кривая в параметрах второй поверхности - \en Support curve at parameters of the second surface \~ - \param[in] curv0 - \ru Кривая пересечения касательных к поверхностям - \en Intersection curve of tangents to surfaces \~ - \param[in] d1 - \ru Радиус скругления со знаком для поверхности кривой curv1 - \en Fillet radius with sign for surface of curv1 curve \~ - \param[in] d2 - \ru Радиус скругления со знаком для поверхности кривой curv2 - \en Fillet radius with sign for surface of curv2 curve \~ - \param[in] fm - \ru Тип сопряжения: \n - st_Span - скругление с заданной хордой \n - st_Fillet - скругление с заданными радиусами - \en Conjugation type: \n - st_Span - fillet with a given chord\n - st_Fillet - fillet with given radii \~ - \param[in] cn - \ru Коэффициент формы, изменяется от 0.05 до 0.95 (при 0 - дуга окружности) - \en Coefficient of shape is changed between 0.05 and 0.95 (if 0 - circular arc) \~ - \param[in] ev - \ru Равномерная параметризация по дуге или нет - \en Whether arc length parameterization is uniform or not \~ - */ - MbFilletSurface( MbSurface & surf1, MbCurve & curv1, - MbSurface & surf2, MbCurve & curv2, - MbCurve3D & curv0, double d1, double d2, MbeSmoothForm fm, double cn, bool ev ); - -protected: - /// \ru Конструктор для наследников обычной поверхности скругления. \en Constructor for inheritors of ordinary fillet surface. - MbFilletSurface( MbSurfaceCurve & curv1, double d1, - MbSurfaceCurve & curv2, double d2, MbeSmoothForm fm, double cn, bool ev ); - /// \ru Конструктор для наследников поверхности с сохранением кромки. \en Constructor for inheritors of surface with preservation of fillet. - MbFilletSurface( MbSurfaceCurve & curv1, double d1, - MbSurfaceCurve & curv2, double d2, MbeSmoothForm fm, double cn, bool byFirst, bool ev ); - /// \ru Конструктор поверхности скругления c кривой пересечения касательных к поверхностям. \en Constructor of fillet surface with intersection curve of tangents to surfaces. \~ - MbFilletSurface( MbSurface & surf1, MbCurve & curv1, double d1, - MbSurface & surf2, MbCurve & curv2, double d2, - MbCurve3D & curv0, MbFunction & weig0, - MbeSmoothForm fm, double cn, bool ev ); - - /// \ru Конструктор копирования. \en Copy-constructor. - MbFilletSurface( const MbFilletSurface &, MbRegDuplicate * ); - /// \ru Конструктор копирования с теми же опорными поверхностями. \en Copy-constructor with the same support surfaces. - MbFilletSurface( const MbFilletSurface * ); - // \ru Для CurvesDuplicate() \en For CurvesDuplicate() -private: - MbFilletSurface( const MbFilletSurface & ); // \ru Не реализовано. \en Not implemented. -public: - virtual ~MbFilletSurface (); - -public: - VISITING_CLASS( MbFilletSurface ); - - /** \ru \name Функции инициализации - \en \name Initialization functions - \{ */ - // \ru Коррекция средней линии поверхности скругления. \en Correction of mid-line of fillet surface. - virtual void Init0( double wmin, double wmax, bool insertPoints = true ); - /** \} */ - /** \ru \name Общие функции геометрического объекта - \en \name Common functions of a geometric object - \{ */ - virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; - virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным. \en Make equal. - virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными. \en Determine whether the objects are similar. - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг. \en Move. - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate around an axis. - - virtual void GetProperties( MbProperties &properties ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties &properties ); // \ru Записать свойства объекта. \en Set properties of the object. - virtual void GetBasisItems( RPArray &s ); // \ru Дать базовые поверхности. \en Get base surfaces. - - /** \} */ - /** \ru \name Функции описания области определения поверхности - \en \name Functions for surface domain description - \{ */ - virtual double GetVPeriod() const; // \ru Период для замкнутой поверхности или 0. \en Period for closed surface or 0. - - /** \} */ - /** \ru \name Функции для работы в области определения поверхности - Функции PointOn и Derive... поверхностей сопряжения не корректируют - первый параметр при его выходе за пределы определения параметров. - \en \name Functions for working at surface domain - Functions PointOn, Derive... of smooth surfaces don't correct - first parameter when it is out of domain bounds. - \{ */ - virtual void PointOn ( double &u, double &v, MbCartPoint3D & ) const; // \ru Точка на поверхности. \en The point on the surface. - virtual void DeriveU ( double &u, double &v, MbVector3D & ) const; // \ru Первая производная по u. \en The first derivative with respect to u. - virtual void DeriveV ( double &u, double &v, MbVector3D & ) const; // \ru Первая производная по v. \en The first derivative with respect to v. - virtual void DeriveUU ( double &u, double &v, MbVector3D & ) const; // \ru Вторая производная по u. \en The second derivative with respect to u. - virtual void DeriveVV ( double &u, double &v, MbVector3D & ) const; // \ru Вторая производная по v. \en The second derivative with respect to v. - virtual void DeriveUV ( double &u, double &v, MbVector3D & ) const; // \ru Вторая производная по uv. \en The second derivative with respect to uv. - virtual void DeriveUUU( double &u, double &v, MbVector3D & ) const; - virtual void DeriveUUV( double &u, double &v, MbVector3D & ) const; - virtual void DeriveUVV( double &u, double &v, MbVector3D & ) const; - virtual void DeriveVVV( double &u, double &v, MbVector3D & ) const; - virtual void Normal ( double &u, double &v, MbVector3D & ) const; // \ru Нормаль. \en Normal. - virtual void NormalU ( double &u, double &v, MbVector3D & ) const; // \ru Производная нормали. \en The derivative of normal. - virtual void NormalV ( double &u, double &v, MbVector3D & ) const; // \ru Производная нормали. \en The derivative of normal. - /** \} */ - /** \ru \name Функции для работы внутри и вне области определения поверхности - функции _PointOn, _Derive... поверхностей не корректируют - параметры при выходе их за пределы прямоугольной области определения параметров. - \en \name Functions for working inside and outside the surface's domain - functions _PointOn, _Derive... of surfaces don't correct - parameters when they are out of bounds of rectangular domain of parameters. - \{ */ - virtual void _PointOn ( double u, double v, MbCartPoint3D & ) const; // \ru Точка на расширенной поверхности. \en The point on the extended surface. - virtual void _DeriveU ( double u, double v, MbVector3D & ) const; // \ru Первая производная по u. \en The first derivative with respect to u. - virtual void _DeriveV ( double u, double v, MbVector3D & ) const; // \ru Первая производная по v. \en The first derivative with respect to v. - virtual void _DeriveUU ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по u. \en The second derivative with respect to u. - virtual void _DeriveVV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по v. \en The second derivative with respect to v. - virtual void _DeriveUV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по uv. \en The second derivative with respect to uv. - virtual void _DeriveUUU( double u, double v, MbVector3D & ) const; - virtual void _DeriveUUV( double u, double v, MbVector3D & ) const; - virtual void _DeriveUVV( double u, double v, MbVector3D & ) const; - virtual void _DeriveVVV( double u, double v, MbVector3D & ) const; - virtual void _Normal ( double u, double v, MbVector3D & ) const; // \ru Нормаль. \en Normal. - virtual void _NormalU ( double u, double v, MbVector3D & ) const; // \ru Производная нормали. \en The derivative of normal. - virtual void _NormalV ( double u, double v, MbVector3D & ) const; // \ru Производная нормали. \en The derivative of normal. - /** \} */ - /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. - \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. - \{ */ - virtual void Explore( double & u, double & v, bool ext, - MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, - MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; - // \ru Вычислить значения всех производных в точке. \en Calculate all derivatives at point. \~ - virtual void _PointNormal( double u, double v, - MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, - MbVector3D & norm, MbVector3D & uNorm, MbVector3D & vNorm, - MbVector3D & uuDer, MbVector3D & vvDer, MbVector3D & uvDer ) const; - /** \} */ - /** \ru \name Функции движения по поверхности - \en \name Functions of moving along the surface - \{ */ - virtual double StepU ( double u, double v, double sag ) const; // \ru Вычисление шага по u по заданной стрелке прогиба. \en Calculation of the parameter step in u direction by the sag. - virtual double StepV ( double u, double v, double sag ) const; // \ru Вычисление шага по v по заданной стрелке прогиба. \en Calculation of the parameter step in v direction by the sag. - virtual double DeviationStepU( double u, double v, double angle ) const; // \ru Вычисление шага по u по заданному углу отклонения. \en Calculation of the parameter step in u direction by the deviation angle. - virtual double DeviationStepV( double u, double v, double angle ) const; // \ru Вычисление шага по v по заданному углу отклонения. \en Calculation of the parameter step in v direction by the deviation angle. - virtual double MetricStepV ( double u, double v, double length ) const; // \ru Вычисление шага по v по заданной длине. \en Calculation of the parameter step in v direction by the given length. - /** \} */ - /** \ru \name Общие функции поверхности - \en \name Common functions of surface - \{ */ - virtual double CurvatureV ( double u, double v ) const; // \ru Kривизна вдоль v. \en Curvature along v. - - virtual MbSplineSurface * NurbsSurface( double, double, double, double, bool bmatch = false ) const; // \ru Построить NURBS копию поверхности. \en Construct a NURBS copy of a surface. - virtual MbSurface * NurbsSurface( const MbNurbsParameters & uParam, const MbNurbsParameters & vParam ) const; // \ru Построить NURBS-копию поверхности. \en Construct a NURBS-copy of a surface. - virtual MbSurface * Offset( double d, bool same ) const; // \ru Создание эквидистантной поверхности. \en Create an offset surface. - - virtual MbCurve3D * CurveV( double u, MbRect1D *pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии u = const. \en Spatial copy of 'u = const'-line. - - virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменение носителя. \en Changing of carrier. - virtual bool ChangeCarrierBorne( const MbSurface & item, MbSurface & init, const MbMatrix & matr ); // \ru Изменение носимых элементов. \en Change a carrier elements. - // \ru Нахождениe точки касания поверхностей \en Searching of surfaces tangency point - virtual MbeNewtonResult SurfaceTangentNewton( const MbSurface &, MbeParamDir switchPar, double funcEpsilon, size_t iterLimit, - double &u0, double &v0, double &u1, double &v1, - bool ext0, bool ext1 ) const; - - // \ru Проекции точки на поверхность. \en The point projections onto the surface. - virtual MbeNewtonResult PointProjectionNewton( const MbCartPoint3D & p, size_t iterLimit, - double & u, double & v, bool ext ) const; // \ru Функция для нахождения проекции точки на поверхность. \en Function for searching the point projection onto the surface. - virtual bool NearPointProjection ( const MbCartPoint3D & p, double & u, double & v, bool ext, MbRect2D * uvRange = NULL ) const; // \ru Ближайшая проекция точки на поверхность. \en The nearest point projection onto the surface. - - virtual double GetFilletRadius( const MbCartPoint3D & p ) const; // \ru Является ли поверхность скруглением. \en Whether the surface is fillet. - // \ru Построить касательные и нормальные плейсменты конструктивных плоскостей. \en Construct tangent and normal placements of constructive planes. - virtual bool CreateNormalPlacements ( const MbVector3D & axisZ, double angle, SArray & places ) const; - virtual bool CreateTangentPlacements( const MbVector3D & axisZ, SArray & places ) const; - virtual bool GetCylinderAxis( MbAxis3D &axis ) const; // \ru Дать ось вращения для поверхности. \en Get a rotation axis of a surface. - // \ru Подобные ли поверхности для объединения (слива). \en Whether the surfaces to union (joining) are similar. - virtual bool IsSimilarToSurface( const MbSurface & surf, VERSION version, double precision = METRIC_PRECISION ) const; - // \ru Дать двумерную матрицу преобразования из своей параметрической области в параметрическую область surf. \en Get two-dimensional transformation matrix from own parametric region to parametric region of 'surf'. - virtual bool GetMatrixToSurface( const MbSurface & surf, MbMatrix & matr, VERSION version, double precision = METRIC_PRECISION ) const; - // \ru Подобные ли поверхности для объединения (слива) \en Whether the surfaces to union (joining) are similar - virtual bool IsSpecialSimilarToSurface( const MbSurface & surf, VERSION version, double precision = METRIC_PRECISION ) const; // \ru Специальный случай \en Special case - - virtual ThreeStates Salient() const; // \ru Выпуклая ли поверхность. \en Whether the surface is convex. - virtual double GetRadius() const; // \ru Дать физический радиус объекта или ноль, если это невозможно. \en Get the physical radius of the object or null if it impossible. - virtual size_t GetUMeshCount() const; // \ru Выдать количество полигонов по u. \en Get the count of polygons by u. - virtual size_t GetVMeshCount() const; // \ru Выдать количество полигонов по v. \en Get the count of polygons by v. - /** \} */ - /** \ru \name Функции поверхности сопряжения - \en \name Functions of smooth surface - \{ */ - virtual MbSmoothSurface & CurvesDuplicate() const; // \ru Копия с теми же опорными поверхностями. \en Copy with the same support surfaces. - virtual double GetSmoothRadius() const; // \ru Дать радиус. \en Get radius. - virtual void GetDistances( double u, double &d1, double &d2 ) const; // \ru Дать радиусы со знаком. \en Get radii with a sign. - virtual double GetDistance( bool s ) const; // \ru Дать радиус со знаком. \en Get radius with a sign. - // \ru Объединить поверхности путём включения поверхности init в данную поверхность. \en Unite surfaces by inclusion of 'init' surface into current surface. - virtual bool SurfacesCombine( const MbSurfaceIntersectionCurve & edge, - const MbSurface & init, bool add, MbMatrix & matr, - const MbSurfaceIntersectionCurve * seam ); - /// \ru Дать коэффициент для радиуса. \en Get coefficient for radius. - virtual double DistanceRatio( bool firstCurve, MbCartPoint3D & p, double distance ) const; - // \ru Определение разбивки параметрической области поверхности вертикалями и горизонталями. \en Determine splitting of parametric region of surface by vertical and horizontal lines. - virtual void GetTesselation( const MbStepData & stepData, - double u1, double u2, double v1, double v2, - SArray & uu, SArray & vv ) const; - - /** \} */ - /** \ru \name Функции поверхности скругления - \en \name Functions of fillet surface - \{ */ - - /** \brief \ru Веса точек средней кривой. - \en Weights of points of mid-curve. \~ - \details \ru Веса точек средней кривой. - \en Weights of points of mid-curve. \~ - \param[in] u - \ru Параметр на средней кривой (по направлению U) - \en Parameter on mid-curve (by U direction) \~ - */ - double GetWeight( double u ) const; - - /** \brief \ru Угол раствора дуги. - \en Arc opening angle. \~ - \details \ru Угол раствора дуги. - \en Arc opening angle. \~ - \param[in] u - \ru Параметр по направлению U - \en Parameter by U direction \~ - \return \ru Угол раствора - \en Angle of opening \~ - */ - double GetAngle( double u ) const; // \ru Дать угол раствора дуги v \en Get v arc opening angle - - /** \brief \ru Ось поверхности в данной точке. - \en Axis of surface at given point. \~ - \details \ru Ось поверхности в данной точке. - \en Axis of surface at given point. \~ - \param[in] u - \ru Параметр по направлению U - \en Parameter by U direction \~ - \param[out] axis - \ru Результат - ось вращения - \en Result - rotation axis \~ - */ - double GetLocalAxis ( double u, MbAxis3D & axis ) const; // \ru Дать ось поверхности в данной точке \en Get axis of surface at given point - - /** \brief \ru Дать точку на оси. - \en Get point on axis. \~ - \details \ru Дать точку на оси. - \en Get point on axis. \~ - \param[in] u - \ru Параметр по направлению U - \en Parameter by U direction \~ - \param[out] p1 - \ru Точка на первой опорной кривой по параметру U - \en Point on first support curve by U parameter \~ - \param[out] p2 - \ru Точка на второй опорной кривой по параметру U - \en Point on second support curve by U parameter \~ - \param[out] p0 - \ru Точка на кривой пересечения касательных к поверхностям по параметру u (точка на оси) - \en Point on intersection curve of tangents to surfaces by u parameter (point on axis) \~ - */ - void GetCentrePoint( double u, MbCartPoint3D &p1, MbCartPoint3D &p2, MbCartPoint3D &p0 ) const; - - /** \brief \ru Дать среднюю точку. - \en Get the mid-point. \~ - \details \ru Дать среднюю точку. - \en Get the mid-point. \~ - \param[in] u - \ru Параметр по направлению U - \en Parameter by U direction \~ - \param[out] p1 - \ru Точка на первой опорной кривой по параметру U - \en Point on first support curve by U parameter \~ - \param[out] p2 - \ru Точка на второй опорной кривой по параметру U - \en Point on second support curve by U parameter \~ - \param[out] p0 - \ru Средняя точка - \en Mid-point \~ - \param[out] w - \ru Вес полученной средней точки - \en Weight of obtained mid-point \~ - */ - bool GetMiddlePoint( double u, MbCartPoint3D &p1, MbCartPoint3D &p2, MbCartPoint3D &p0, double &w ) const; - - // \ru Если параметризация равномерная, то на продолжении по V замыкается и период зависит от U \en If parameterization is uniform, then it is closed on extension by V and period depends on U - /** \brief \ru Период по направлению V. - \en Period by direction V. \~ - \details \ru Период по направлению V.\n - Если параметризация поверхности равномерная, то период по направлению V зависит от параметра U. - \en Period by direction V.\n - If parameterization of surface is uniform, then period by direction V depends on U parameter. \~ - \param[in] u - \ru Параметр по направлению U - \en Parameter by U direction \~ - \return \ru Период для заданного параметра - \en Period for given parameter \~ - */ - double GetVPeriod( double u ) const; - - /// \ru Кривая пересечения касательных к поверхностям. \en Intersection curve of tangents to surfaces. - const MbCurve3D & GetCurve0() const { return *curve0; } - - /** \brief \ru Скругление не круговое. - \en Fillet isn't circular. \~ - \details \ru Скругление не круговое. - \en Fillet isn't circular. \~ - \return \ru true, если радиусы скруглений для поверхностей не равны - \en True if surfaces fillet radii aren't equal \~ - */ - bool IsEllipse() const { return (fabs(fabs(distance1) - fabs(distance2))>=LENGTH_EPSILON); } // \ru Не равные радиусы \en Not equal radii - - /// \ru Параметризация по дуге равномерная. \en Uniform arc length parameterization. - bool IsEven() const { return even; } - - /** \brief \ru Однородная ли поверхность скругления. - \en Whether the fillet surface is homogeneous. \~ - \details \ru Однородная ли поверхность скругления. Поверхность скругления без сохранения кромки. - \en Whether the fillet surface is homogeneous. Fillet surface without preservation of fillet. \~ - \return \ru false, если одна из кривых curve1 или curve2 является кромкой - \en False if one of curve1 or curve2 curves is fillet \~ - */ - bool IsFilletSurface() const { return equable; } - - /** \brief \ru Коническое сечение общего вида. - \en General conic section. \~ - \details \ru Коническое сечение общего вида. - \en General conic section. \~ - \return \ru false, если сечение поверхности скругления является дугой окружности - \en False if section of fillet surface is circular arc \~ - */ - bool IsConic() const { return ( ::fabs(conic - c3d::_ARC_) >= EPSILON ); } // \ru Коническое сечение общего вида \en General conic section - - /** \brief \ru Коэффициент формы. - \en Coefficient of shape. \~ - \details \ru Коэффициент формы сечения поверхности скругления.\n - Изменяется от 0.05 до 0.95, при 0 сечение является дугой окружности. - \en Coefficient of shape of section of fillet surface.\n - Is changed between 0.05 and 0.95, if 0, then section is circular arc. \~ - \return \ru Коэффициент - \en Coefficient \~ - */ - double Conic() const { return conic; } - - /** \brief \ru Поверхность скругления с сохранением кромки. - \en Fillet surface with preservation of fillet. \~ - \details \ru Поверхность скругления с сохранением кромки. - \en Fillet surface with preservation of fillet. \~ - \return \ru true, если одна из кривых curve1 или curve2 является кромкой - \en True if one of curve1 or curve2 curves is fillet \~ - */ - bool IsKerbSurface() const { return !equable; } - - /** \brief \ru Является ли первая кривая кромкой. - \en Whether the first curve is fillet. \~ - \details \ru Является ли первая кривая кромкой. - \en Whether the first curve is fillet. \~ - \return \ru true, если первая кривая является кромкой - \en True if the first curve is fillet. \~ - */ - bool ByFirstCurve() const { return byCurve1; } - - /** \brief \ru Установить поверхность скругления типа с сохранением кромки. - \en Set fillet surface with preservation of fillet. - Need to call this->Init0() after this method \~ - \details \ru Установить поверхность скругления с сохранением кромки и указать определяющую кривую на поверхности. - Далее нужно вызвать метод this->Init0(). - \en Set fillet surface with preservation of fillet. \~ - \param[in] bc1 - \ru Определяющая кривая на поверхности: curve1 (bc1 = true), curve2 (bc1 = false). - \en General curve on surface: curve1 (bc1 = true), curve2 (bc1 = false). \~ - */ - void SetKerbSurface( bool bc1 ) { if ( equable ) { equable = false; byCurve1 = bc1; } } - - // \ru Выдать функцию весов точек средней кривой curve0. \en Get weight function for points of mid-curve (curve0). - const MbFunction * GetWeights() const; - // \ru Установить функцию весов точек средней кривой curve0. \en Set weight function for points of mid-curve (curve0). - bool SetWeights( MbFunction & func ); - - MbCurve3D * GetSpine() const; - void SetSpine( MbCurve3D * ); - - /** \} */ -protected: - void WeightKoefficient( double & w ) const; // \ru Вычисление веса при заданном коэффициенте \en Calculation of weight at given coefficient - void InitFilletSurface ( const MbFilletSurface & init ); - void CalculateCurve( double wmin, double wmax, bool insertPoints ); - double CalculateVParam( const MbCartPoint3D & p, double u ) const; // \ru Нахождение параметра v проекции точки на вырожденную поверхность \en Searching of v parameter of point projection onto degenerate surface - -protected: - // \ru Вычисление точки \en Calculation of a point -// void CalculateSurface( double u ) const; - // \ru Дать коэффициент для радиуса \en Get coefficient for radius - virtual double FunctionValue( double u ) const; - void CalculateData ( double & u, double & v, - MbCartPoint3D & uPoint0, MbCartPoint3D & uPoint1, MbCartPoint3D & uPoint2, // \ru Точки на кривых curve0, curve1, curve2. \en Points on curve0, curve1, curve2. - MbVector3D * uFirst0, MbVector3D * uFirst1, MbVector3D * uFirst2, // \ru Производные кривых curve0, curve1, curve2. \en Derivatives of curve0, curve1, curve2. - MbVector3D * uSecond0, MbVector3D * uSecond1, MbVector3D * uSecond2, // \ru Производные кривых curve0, curve1, curve2. \en Derivatives of curve0, curve1, curve2. - double & uWeight, double * wFirst, double * wSecond, // \ru Вес и его производные средней точки uPoint0. \en The weight and it derivatives of the mid-point uPoint0. - double & uP0, double & uP1, double & uP2, double & uPw, // \ru Коэффициенты точек uPoint0, uPoint1, uPoint2. \en Coefficients of points uPoint0, uPoint1, uPoint2. - double & uF0, double & uF1, double & uF2, double & uFw ) const; // \ru Коэффициенты производных uFirst0, uFirst1, uFirst2. \en Coefficients of derivatives uFirst0, uFirst1, uFirst2. - void InitSpineDerives(); - void CalculatePointOn ( double & v, - const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, - const double & uWeight, - const double & uP0, const double & uP1, const double & uP2, const double & uPw, - MbCartPoint3D & ) const; // \ru Точка на поверхности. \en The point on the surface. - void CalculateDeriveU ( double & v, - const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, - const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, - const double & uWeight, const double & wFirst, - const double & uP0, const double & uP1, const double & uP2, const double & uPw, - MbVector3D & ) const; // \ru Первая производная по u. \en The first derivative with respect to u. - void CalculateDeriveV ( double & v, - const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, - const double & uWeight, - const double & uP0, const double & uP1, const double & uP2, const double & uPw, - const double & uF0, const double & uF1, const double & uF2, const double & uFw, - MbVector3D & ) const; // \ru Первая производная по v. \en The first derivative with respect to v. - void CalculateDeriveUU ( double & v, - const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, - const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, - const MbVector3D & uSecond0, const MbVector3D & uSecond1, MbVector3D &uSecond2, - const double & uWeight, const double & wFirst, const double & wSecond, - const double & uP0, const double & uP1, const double & uP2, const double & uPw, - MbVector3D & ) const; // \ru Вторая производная по u. \en The second derivative with respect to u. - void CalculateDeriveVV ( double & v, - const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, - const double & uWeight, - const double & uP0, const double & uP1, const double & uP2, const double & uPw, - const double & uF0, const double & uF1, const double & uF2, const double & uFw, - MbVector3D & ) const; // \ru Вторая производная по v. \en The second derivative with respect to v. - void CalculateDeriveUV ( double & v, - const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, - const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, - const double & uWeight, const double & wFirst, - const double & uP0, const double & uP1, const double & uP2, const double & uPw, - const double & uF0, const double & uF1, const double & uF2, const double & uFw, - MbVector3D & ) const; // \ru Вторая производная по uv. \en The second derivative with respect to uv. - void CalculateDeriveUUU( double & u, double & v, - const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, - const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, - const MbVector3D & uSecond0, const MbVector3D & uSecond1, MbVector3D &uSecond2, - const double & uWeight, const double & wFirst, const double & wSecond, - const double & uP0, const double & uP1, const double & uP2, const double & uPw, - MbVector3D & ) const; - void CalculateDeriveUUV( double & v, - const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, - const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, - const MbVector3D & uSecond0, const MbVector3D & uSecond1, MbVector3D &uSecond2, - const double & uWeight, const double & wFirst, const double & wSecond, - const double & uP0, const double & uP1, const double & uP2, const double & uPw, - const double & uF0, const double & uF1, const double & uF2, const double & uFw, - MbVector3D & ) const; - void CalculateDeriveUVV( double & v, - const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, - const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, - const double & uWeight, const double & wFirst, - const double & uP0, const double & uP1, const double & uP2, const double & uPw, - const double & uF0, const double & uF1, const double & uF2, const double & uFw, - MbVector3D & ) const; - void CalculateDeriveVVV( double & v, - const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, - const double & uWeight, - const double & uP0, const double & uP1, const double & uP2, const double & uPw, - const double & uF0, const double & uF1, const double & uF2, const double & uFw, - MbVector3D & ) const; - void CalculateNormal ( double & u, double & v, - const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, - const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, - const double & uWeight, const double & wFirst, - const double & uP0, const double & uP1, const double & uP2, const double & uPw, - const double & uF0, const double & uF1, const double & uF2, const double & uFw, - MbVector3D & ) const; // \ru Нормаль. \en Normal. - void CalculateNormalU ( double & u, double & v, - const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, - const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, - const MbVector3D & uSecond0, const MbVector3D & uSecond1, MbVector3D &uSecond2, - const double & uWeight, const double & wFirst, const double & wSecond, - const double & uP0, const double & uP1, const double & uP2, const double & uPw, - const double & uF0, const double & uF1, const double & uF2, const double & uFw, - MbVector3D & ) const; // \ru Производная нормали. \en The derivative of normal. - void CalculateNormalV ( double & u, double & v, - const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, - const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, - const double & uWeight, const double & wFirst, - const double & uP0, const double & uP1, const double & uP2, const double & uPw, - const double & uF0, const double & uF1, const double & uF2, const double & uFw, - MbVector3D & ) const; // \ru Производная нормали. \en The derivative of normal. - - // \ru Проверка параметров. \en Check parameters. - void CheckUParam( double & u ) const; - void CheckVParam( double & v ) const; - - void operator = ( const MbFilletSurface & ); // \ru Не реализовано. \en Not implemented. - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbFilletSurface ) -}; // MbFilletSurface - - -IMPL_PERSISTENT_OPS( MbFilletSurface ) - - -//------------------------------------------------------------------------------ -// \ru Проверка параметра u по отношению к полюсам и замкнутости \en Check u parameter against poles and closedness -// --- -inline void MbFilletSurface::CheckUParam( double & u ) const { - if ( uclosed ) { - if ( (u < umin) || (u > umax ) ) { - double tmp = umax - umin; - u -= ::floor((u - umin) / tmp) * tmp; - } - } - else { - if ( poleMin && uumax ) - u = umax; - } -} - - -//------------------------------------------------------------------------------ -// \ru Проверка параметра v \en Check v parameter -// --- -inline void MbFilletSurface::CheckVParam( double & v ) const { - if ( v < vmin ) - v = vmin; - else - if ( v > vmax ) - v = vmax; -} - - -//------------------------------------------------------------------------------ -// \ru Вычислить вес для эллиптической точки \en Calculate weight for elliptic point -// --- -inline bool FilletWeight( double cos_a, double d1, double d2, double & uW ) -{ - bool result = false; - // \ru cos_a - косинус угла между нормалями \en Cos_a - cosine of angle between normals - if ( ::fabs( cos_a ) > PARAM_PRECISION ) { // \ru Не прямой угол \en Angle not right - if ( (d1 * d2) < 0 ) - cos_a = -cos_a; - double cos2_a = cos_a * cos_a; - double sin2_a = ::fabs( 1.0 - cos2_a ); - double sin_a = ::sqrt( sin2_a ); // \ru Синус угла между нормалями \en Sine of angle between normals - bool first = ( ::fabs(d1) > ::fabs(d2) ); - double a = first ? ::fabs( d1 ) : ::fabs( d2 ); // \ru Большая полуось эллипса \en Major semi axis of ellipse - double b = first ? ::fabs( d2 ) : ::fabs( d1 ); // \ru Малая полуось эллипса \en Minor semi axis of ellipse - // \ru Эллипс наиболее удалённой от центра точкой касался одной из поверхностей \en Ellipse concerns one of the surfaces by the point most remote from the center - if ( sin_a > PARAM_PRECISION ) { // \ru Не малый угол \en Angle not small - double aa = a * a; - double bb = b * b; - double p = ::sqrt( (aa*cos2_a) + (bb*sin2_a) ); // \ru Знаменатель (расстояние от центра эллипса до касательной) \en Denominator (distance from center of ellipse to tangent) - if ( p > NULL_EPSILON ) { // \ru Всегда должно выполняться, если a!=0 и b!=0 \en Always hold, if a! =0 and b! =0 - double d = 1.0 / p; - double cos_t = a * cos_a * d; // \ru Косинус параметрического угла эллипса \en Cosine of metric angle of ellipse - uW = ::sqrt( (1.0 + cos_t) * 0.5 ); // \ru Половина косинуса параметрического угла эллипса \en Half of cosine of metric angle of ellipse - result = true; - } - } - } - return result; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Создать поверхность сопряжения. - \en Create the fillet surface. \~ - \details \ru Создать поверхность сопряжения с сохранением кромки грани. - \en Create the fillet surface with the edge. \~ - \param[in] surface1 - \ru Сопрягаемая поверхность. - \en The conjugate surface. \~ - \param[in] points1 - \ru Точки для опорной кривой на сопрягаемой поверхности. - \en Points for curve on the conjugated surface. \~ - \param[in] surface2 - \ru Сопрягаемая поверхность. - \en The conjugate surface. \~ - \param[in] points2 - \ru Точки для опорной кривой на сопрягаемой поверхности. - \en Points for curve on the conjugated surface. \~ - \param[in] form - \ru Тип повержности сопряжения. - \en The surface type \~ - \param[in] distance1 - \ru Радиус скругления со знаком для поверхности кривой crve1 - \en Fillet radius with sign for surface of crve1 curve \~ - \param[in] distance2 - \ru Радиус скругления со знаком для поверхности кривой crve2 - \en Fillet radius with sign for surface of crve2 curve \~ - \param[in] conic - \ru Коэффициент формы, изменяется от 0.05 до 0.95 (при 0.5 - дуга окружности) - \en Coefficient of shape is changed from 0.05 to 0.95 (if 0.5 - circular arc) \~ - \param[in] curve - \ru Кривая опорной кромки. - \en The edge curve \~ - \param[in] params - \ru Параметры поверхности вдоль первого напрвыления (u). - \en The parameters of new the surface by first direction (u) \~ - \param[in] byFirstSurface - \ru Пурвая или вторая поверхность сопрягается гладко с новой поверхностью. - \en Is the first or second conjugate surface smooth with the new surface. \~ - \param[in] even - \ru Равномерная параметризация по дуге (v) или нет - \en Uniform parametrization by arc (v) or not \~ - \return \ru Возвращает созданную поверхность. - \en Return the created surface. \~ - \ingroup Surface_Modeling -*/ -MbSmoothSurface * CreateKerbSurface( const MbSurface &surface1, SArray & points1, - const MbSurface &surface2, SArray & points2, - MbeSmoothForm form, double distance1, double distance2, double conic, - const MbSurfaceIntersectionCurve & curve, SArray & params, - bool byFirstSurface, bool even, VERSION version ); - - -#endif // __SURF_FILLET_SURFACE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Поверхность скругления с постоянными радиусами обычная или с сохранением кромки. + \en Fillet surface of constant radii, ordinary or with preservation of fillet. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __SURF_FILLET_SURFACE_H +#define __SURF_FILLET_SURFACE_H + + +#include + + +class MATH_CLASS MbFunction; + + +//------------------------------------------------------------------------------ +/** \brief \ru Поверхность скругления с постоянными радиусами обычная или с сохранением кромки. + \en Fillet surface of constant radii, ordinary or with preservation of fillet. \~ + \details \ru Поверхность скругления является NURBS-поверхностью, + построенной по трём кривым: curve1, curve0, curve2. + Первый параметр поверхности совпадает с параметром кривых curve1, curve0, curve2. + Второй параметр изменяется от нуля (точки совпадают с curve1) до единицы (точки совпадают с curve2). + В отличие от других поверхностей Функции PointOn и Derive... поверхность скругления не корректирует + первый параметр при выходе его за пределы области определения. + Если коэффициент формы conic = _ARC_ ( 0 ), то вес каждой точки кривой curve0 задаётся функцией weights0 и + вычислен так, что сечение поверхности вдоль её второго параметра будет дугой окружности, + то есть при любом параметре u три точки curve1(u), curve0(u), curve2(u) определяют NURBS-кривую в форме дуги окружности. + Если коэффициент формы conic != _ARC_, то вес каждой точки кривой curve0 равен conic / ( 1.0 - conic ). + При conic = 0.5 сечение поверхности вдоль её второго параметра будет параболой. \n + \en Fillet surface is NURBS-surface + constructed by three curves: curve1, curve0, curve2. + First parameter of surface coincides with parameter of curve1, curve0, curve2 curves. + Second parameter is changed between 0 (points are coincident to curve1) and 1 (points are coincident to curve2). + For fillet surface in contrast to other surfaces PointOn and Derive... functions don't correct + first parameter when it is out of domain bounds. + If coefficient of shape conic = _ARC_ ( 0 ), then weight of each point of curve0 curve is given by weights0 function and + is calculated as that the section of surface along its second parameter will be a circular arc, + that is three points curve1(u), curve0(u), curve2(u) determine NURBS-curve with shape of circular arc at any u parameter. + If coefficient of shape conic != _ARC_, then weight of each point of curve0 curve is equal to conic / ( 1.0 - conic ). + If conic = 0.5, then section of surface along its second parameter will be a parabola. \n \~ + \ingroup Surfaces +*/// --- +class MATH_CLASS MbFilletSurface : public MbSmoothSurface { +protected: + MbCurve3D * curve0; ///< \ru Кривая пересечения касательных к поверхностям - всегда не NULL. \en Intersection curve of tangents to surfaces - always not NULL. + MbFunction * weights0; ///< \ru Функция веса точек средней кривой curve0. \en Function of weight of points of curve0 mid-curve. + double conic; ///< \ru Коэффициент формы, изменяется от 0.05 до 0.95, определяет вес точек кривой curve0. \en Coefficient of shape is changed between 0.05 and 0.95 and determines weight of points of curve0 curve. + bool even; ///< \ru Равномерная параметризация по дуге или нет. \en Whether arc length parameterization is uniform or not. + bool equable; ///< \ru true - обычная поверхность, false - curve1 или curve2 является кромкой. \en True - ordinary surface, false - curve1 or curve2 is fillet. + bool byCurve1; ///< \ru true - curve2 является кромкой, false - curve1 является кромкой. \en True - curve2 is fillet, false - curve1 is fillet. + MbCurve3D * spine; ///< \ru Кривая центров дуг окружности для случая равномерной параметризации. \en Curve of centers of circular arcs in case of uniform parameterization. + MbVector3D * spineDerUMin; // \ru Производные spine в точках uMin и uMax ( для случая равномерной параметризации ). \en Derivatives of spine at uMin and uMax points (in case of uniform parameterization). + MbVector3D * spineDerUMax; // \ru Производные spine в точках uMin и uMax ( для случая равномерной параметризации ). \en Derivatives of spine at uMin and uMax points (in case of uniform parameterization). + +public: + + /** \brief \ru Конструктор поверхности скругления. + \en Constructor of fillet surface. \~ + \details \ru Конструктор поверхности скругления. + \en Constructor of fillet surface. \~ + \param[in] curv1 - \ru Опорная кривая на первой поверхности + \en Support curve on the first surface \~ + \param[in] curv2 - \ru Опорная кривая на второй поверхности + \en Support curve on the second surface \~ + \param[in] d1 - \ru Радиус скругления со знаком для поверхности кривой curve1 + \en Fillet radius with sign for surface of crve1 curve \~ + \param[in] d2 - \ru Радиус скругления со знаком для поверхности кривой curve2 + \en Fillet radius with sign for surface of crve2 curve \~ + \param[in] fm - \ru Тип сопряжения: \n + st_Span - скругление с заданной хордой + st_Fillet - скругление с заданными радиусами + \en Conjugation type: \n + st_Span - fillet with a given chord + st_Fillet - fillet with given radii \~ + \param[in] cn - \ru Коэффициент формы, изменяется от 0.05 до 0.95 (при 0 - дуга окружности) + \en Coefficient of shape is changed between 0.05 and 0.95 (if 0 - circular arc) \~ + \param[in] ev - \ru Равномерная параметризация по дуге или нет + \en Whether arc length parameterization is uniform or not \~ + */ + MbFilletSurface( MbSurfaceCurve & curv1, MbSurfaceCurve & curv2, + double d1, double d2, MbeSmoothForm fm, double cn, bool ev ); + + /** \brief \ru Конструктор поверхности с сохранением кромки. + \en Constructor of surface with preservation of fillet. \~ + \details \ru Конструктор поверхности с сохранением кромки. + \en Constructor of surface with preservation of fillet. \~ + \param[in] curv1 - \ru Опорная кривая на первой поверхности + \en Support curve on the first surface \~ + \param[in] curv2 - \ru Опорная кривая на второй поверхности + \en Support curve on the second surface \~ + \param[in] d1 - \ru Радиус скругления со знаком для поверхности кривой crve1 + \en Fillet radius with sign for surface of crve1 curve \~ + \param[in] d2 - \ru Радиус скругления со знаком для поверхности кривой crve2 + \en Fillet radius with sign for surface of crve2 curve \~ + \param[in] fm - \ru Тип сопряжения: \n + st_Span - скругление с заданной хордой \n + st_Fillet - скругление с заданными радиусами + \en Conjugation type: \n + st_Span - fillet with a given chord\n + st_Fillet - fillet with given radii \~ + \param[in] cn - \ru Коэффициент формы, изменяется от 0.05 до 0.95 (при 0 - дуга окружности) + \en Coefficient of shape is changed between 0.05 and 0.95 (if 0 - circular arc) \~ + \param[in] byFirst - \ru true - кривая curve2 является кромкой, false - кривая curve1 является кромкой + \en True - curve2 curve is fillet, false - curve1 curve is fillet. \~ + \param[in] ev - \ru Равномерная параметризация по дуге или нет + \en Whether arc length parameterization is uniform or not \~ + */ + MbFilletSurface( MbSurfaceCurve & curv1, MbSurfaceCurve & curv2, + double d1, double d2, MbeSmoothForm fm, double cn, bool byFirst, bool ev ); + + /** \brief \ru Конструктор поверхности скругления. + \en Constructor of fillet surface. \~ + \details \ru Конструктор поверхности скругления c кривой пересечения касательных к поверхностям. + \en Constructor of fillet surface with intersection curve of tangents to surfaces. \~ + \param[in] surf1 - \ru Первая поверхность + \en First surface \~ + \param[in] curv1 - \ru Опорная кривая в параметрах первой поверхности + \en Support curve at parameters of the first surface \~ + \param[in] surf2 - \ru Вторая поверхность + \en Second surface \~ + \param[in] curv2 - \ru Опорная кривая в параметрах второй поверхности + \en Support curve at parameters of the second surface \~ + \param[in] curv0 - \ru Кривая пересечения касательных к поверхностям + \en Intersection curve of tangents to surfaces \~ + \param[in] d1 - \ru Радиус скругления со знаком для поверхности кривой curv1 + \en Fillet radius with sign for surface of curv1 curve \~ + \param[in] d2 - \ru Радиус скругления со знаком для поверхности кривой curv2 + \en Fillet radius with sign for surface of curv2 curve \~ + \param[in] fm - \ru Тип сопряжения: \n + st_Span - скругление с заданной хордой \n + st_Fillet - скругление с заданными радиусами + \en Conjugation type: \n + st_Span - fillet with a given chord\n + st_Fillet - fillet with given radii \~ + \param[in] cn - \ru Коэффициент формы, изменяется от 0.05 до 0.95 (при 0 - дуга окружности) + \en Coefficient of shape is changed between 0.05 and 0.95 (if 0 - circular arc) \~ + \param[in] ev - \ru Равномерная параметризация по дуге или нет + \en Whether arc length parameterization is uniform or not \~ + */ + MbFilletSurface( MbSurface & surf1, MbCurve & curv1, + MbSurface & surf2, MbCurve & curv2, + MbCurve3D & curv0, double d1, double d2, MbeSmoothForm fm, double cn, bool ev ); + +protected: + /// \ru Конструктор для наследников обычной поверхности скругления. \en Constructor for inheritors of ordinary fillet surface. + MbFilletSurface( MbSurfaceCurve & curv1, double d1, + MbSurfaceCurve & curv2, double d2, MbeSmoothForm fm, double cn, bool ev ); + /// \ru Конструктор для наследников поверхности с сохранением кромки. \en Constructor for inheritors of surface with preservation of fillet. + MbFilletSurface( MbSurfaceCurve & curv1, double d1, + MbSurfaceCurve & curv2, double d2, MbeSmoothForm fm, double cn, bool byFirst, bool ev ); + /// \ru Конструктор поверхности скругления c кривой пересечения касательных к поверхностям. \en Constructor of fillet surface with intersection curve of tangents to surfaces. \~ + MbFilletSurface( MbSurface & surf1, MbCurve & curv1, double d1, + MbSurface & surf2, MbCurve & curv2, double d2, + MbCurve3D & curv0, MbFunction & weig0, + MbeSmoothForm fm, double cn, bool ev ); + + /// \ru Конструктор копирования. \en Copy-constructor. + MbFilletSurface( const MbFilletSurface &, MbRegDuplicate * ); + /// \ru Конструктор копирования с теми же опорными поверхностями. \en Copy-constructor with the same support surfaces. + MbFilletSurface( const MbFilletSurface * ); + // \ru Для CurvesDuplicate() \en For CurvesDuplicate() +private: + MbFilletSurface( const MbFilletSurface & ); // \ru Не реализовано. \en Not implemented. +public: + virtual ~MbFilletSurface (); + +public: + VISITING_CLASS( MbFilletSurface ); + + /** \ru \name Функции инициализации + \en \name Initialization functions + \{ */ + // \ru Коррекция средней линии поверхности скругления. \en Correction of mid-line of fillet surface. + virtual void Init0( double wmin, double wmax, bool insertPoints = true ); + /** \} */ + /** \ru \name Общие функции геометрического объекта + \en \name Common functions of a geometric object + \{ */ + virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; + virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным. \en Make equal. + virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными. \en Determine whether the objects are similar. + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг. \en Move. + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate around an axis. + + virtual void GetProperties( MbProperties &properties ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties &properties ); // \ru Записать свойства объекта. \en Set properties of the object. + virtual void GetBasisItems( RPArray &s ); // \ru Дать базовые поверхности. \en Get base surfaces. + + /** \} */ + /** \ru \name Функции описания области определения поверхности + \en \name Functions for surface domain description + \{ */ + virtual double GetVPeriod() const; // \ru Период для замкнутой поверхности или 0. \en Period for closed surface or 0. + + /** \} */ + /** \ru \name Функции для работы в области определения поверхности + Функции PointOn и Derive... поверхностей сопряжения не корректируют + первый параметр при его выходе за пределы определения параметров. + \en \name Functions for working at surface domain + Functions PointOn, Derive... of smooth surfaces don't correct + first parameter when it is out of domain bounds. + \{ */ + virtual void PointOn ( double &u, double &v, MbCartPoint3D & ) const; // \ru Точка на поверхности. \en The point on the surface. + virtual void DeriveU ( double &u, double &v, MbVector3D & ) const; // \ru Первая производная по u. \en The first derivative with respect to u. + virtual void DeriveV ( double &u, double &v, MbVector3D & ) const; // \ru Первая производная по v. \en The first derivative with respect to v. + virtual void DeriveUU ( double &u, double &v, MbVector3D & ) const; // \ru Вторая производная по u. \en The second derivative with respect to u. + virtual void DeriveVV ( double &u, double &v, MbVector3D & ) const; // \ru Вторая производная по v. \en The second derivative with respect to v. + virtual void DeriveUV ( double &u, double &v, MbVector3D & ) const; // \ru Вторая производная по uv. \en The second derivative with respect to uv. + virtual void DeriveUUU( double &u, double &v, MbVector3D & ) const; + virtual void DeriveUUV( double &u, double &v, MbVector3D & ) const; + virtual void DeriveUVV( double &u, double &v, MbVector3D & ) const; + virtual void DeriveVVV( double &u, double &v, MbVector3D & ) const; + virtual void Normal ( double &u, double &v, MbVector3D & ) const; // \ru Нормаль. \en Normal. + virtual void NormalU ( double &u, double &v, MbVector3D & ) const; // \ru Производная нормали. \en The derivative of normal. + virtual void NormalV ( double &u, double &v, MbVector3D & ) const; // \ru Производная нормали. \en The derivative of normal. + /** \} */ + /** \ru \name Функции для работы внутри и вне области определения поверхности + функции _PointOn, _Derive... поверхностей не корректируют + параметры при выходе их за пределы прямоугольной области определения параметров. + \en \name Functions for working inside and outside the surface's domain + functions _PointOn, _Derive... of surfaces don't correct + parameters when they are out of bounds of rectangular domain of parameters. + \{ */ + virtual void _PointOn ( double u, double v, MbCartPoint3D & ) const; // \ru Точка на расширенной поверхности. \en The point on the extended surface. + virtual void _DeriveU ( double u, double v, MbVector3D & ) const; // \ru Первая производная по u. \en The first derivative with respect to u. + virtual void _DeriveV ( double u, double v, MbVector3D & ) const; // \ru Первая производная по v. \en The first derivative with respect to v. + virtual void _DeriveUU ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по u. \en The second derivative with respect to u. + virtual void _DeriveVV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по v. \en The second derivative with respect to v. + virtual void _DeriveUV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по uv. \en The second derivative with respect to uv. + virtual void _DeriveUUU( double u, double v, MbVector3D & ) const; + virtual void _DeriveUUV( double u, double v, MbVector3D & ) const; + virtual void _DeriveUVV( double u, double v, MbVector3D & ) const; + virtual void _DeriveVVV( double u, double v, MbVector3D & ) const; + virtual void _Normal ( double u, double v, MbVector3D & ) const; // \ru Нормаль. \en Normal. + virtual void _NormalU ( double u, double v, MbVector3D & ) const; // \ru Производная нормали. \en The derivative of normal. + virtual void _NormalV ( double u, double v, MbVector3D & ) const; // \ru Производная нормали. \en The derivative of normal. + /** \} */ + /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. + \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. + \{ */ + virtual void Explore( double & u, double & v, bool ext, + MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, + MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; + // \ru Вычислить значения всех производных в точке. \en Calculate all derivatives at point. \~ + virtual void _PointNormal( double u, double v, + MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, + MbVector3D & norm, MbVector3D & uNorm, MbVector3D & vNorm, + MbVector3D & uuDer, MbVector3D & vvDer, MbVector3D & uvDer ) const; + /** \} */ + /** \ru \name Функции движения по поверхности + \en \name Functions of moving along the surface + \{ */ + virtual double StepU ( double u, double v, double sag ) const; // \ru Вычисление шага по u по заданной стрелке прогиба. \en Calculation of the parameter step in u direction by the sag. + virtual double StepV ( double u, double v, double sag ) const; // \ru Вычисление шага по v по заданной стрелке прогиба. \en Calculation of the parameter step in v direction by the sag. + virtual double DeviationStepU( double u, double v, double angle ) const; // \ru Вычисление шага по u по заданному углу отклонения. \en Calculation of the parameter step in u direction by the deviation angle. + virtual double DeviationStepV( double u, double v, double angle ) const; // \ru Вычисление шага по v по заданному углу отклонения. \en Calculation of the parameter step in v direction by the deviation angle. + virtual double MetricStepV ( double u, double v, double length ) const; // \ru Вычисление шага по v по заданной длине. \en Calculation of the parameter step in v direction by the given length. + /** \} */ + /** \ru \name Общие функции поверхности + \en \name Common functions of surface + \{ */ + virtual double CurvatureV ( double u, double v ) const; // \ru Kривизна вдоль v. \en Curvature along v. + + virtual MbSplineSurface * NurbsSurface( double, double, double, double, bool bmatch = false ) const; // \ru Построить NURBS копию поверхности. \en Construct a NURBS copy of a surface. + virtual MbSurface * NurbsSurface( const MbNurbsParameters & uParam, const MbNurbsParameters & vParam ) const; // \ru Построить NURBS-копию поверхности. \en Construct a NURBS-copy of a surface. + virtual MbSurface * Offset( double d, bool same ) const; // \ru Создание эквидистантной поверхности. \en Create an offset surface. + + virtual MbCurve3D * CurveV( double u, MbRect1D *pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии u = const. \en Spatial copy of 'u = const'-line. + + virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменение носителя. \en Changing of carrier. + virtual bool ChangeCarrierBorne( const MbSurface & item, MbSurface & init, const MbMatrix & matr ); // \ru Изменение носимых элементов. \en Change a carrier elements. + // \ru Нахождениe точки касания поверхностей \en Searching of surfaces tangency point + virtual MbeNewtonResult SurfaceTangentNewton( const MbSurface &, MbeParamDir switchPar, double funcEpsilon, size_t iterLimit, + double &u0, double &v0, double &u1, double &v1, + bool ext0, bool ext1 ) const; + + // \ru Проекции точки на поверхность. \en The point projections onto the surface. + virtual MbeNewtonResult PointProjectionNewton( const MbCartPoint3D & p, size_t iterLimit, + double & u, double & v, bool ext ) const; // \ru Функция для нахождения проекции точки на поверхность. \en Function for searching the point projection onto the surface. + virtual bool NearPointProjection ( const MbCartPoint3D & p, double & u, double & v, bool ext, MbRect2D * uvRange = NULL ) const; // \ru Ближайшая проекция точки на поверхность. \en The nearest point projection onto the surface. + + virtual double GetFilletRadius( const MbCartPoint3D & p ) const; // \ru Является ли поверхность скруглением. \en Whether the surface is fillet. + virtual double GetFilletRadius( double u ) const; // \ru Дать радиус скругления по первому параметру. \en Get fillet radius if the surface is fillet. + // \ru Построить касательные и нормальные плейсменты конструктивных плоскостей. \en Construct tangent and normal placements of constructive planes. + virtual bool CreateNormalPlacements ( const MbVector3D & axisZ, double angle, SArray & places ) const; + virtual bool CreateTangentPlacements( const MbVector3D & axisZ, SArray & places ) const; + virtual bool GetCylinderAxis( MbAxis3D &axis ) const; // \ru Дать ось вращения для поверхности. \en Get a rotation axis of a surface. + // \ru Подобные ли поверхности для объединения (слива). \en Whether the surfaces to union (joining) are similar. + virtual bool IsSimilarToSurface( const MbSurface & surf, VERSION version, double precision = METRIC_PRECISION ) const; + // \ru Дать двумерную матрицу преобразования из своей параметрической области в параметрическую область surf. \en Get two-dimensional transformation matrix from own parametric region to parametric region of 'surf'. + virtual bool GetMatrixToSurface( const MbSurface & surf, MbMatrix & matr, VERSION version, double precision = METRIC_PRECISION ) const; + // \ru Подобные ли поверхности для объединения (слива) \en Whether the surfaces to union (joining) are similar + virtual bool IsSpecialSimilarToSurface( const MbSurface & surf, VERSION version, double precision = METRIC_PRECISION ) const; // \ru Специальный случай \en Special case + + virtual ThreeStates Salient() const; // \ru Выпуклая ли поверхность. \en Whether the surface is convex. + virtual double GetRadius() const; // \ru Дать физический радиус объекта или ноль, если это невозможно. \en Get the physical radius of the object or null if it impossible. + virtual size_t GetUMeshCount() const; // \ru Выдать количество полигонов по u. \en Get the count of polygons by u. + virtual size_t GetVMeshCount() const; // \ru Выдать количество полигонов по v. \en Get the count of polygons by v. + /** \} */ + /** \ru \name Функции поверхности сопряжения + \en \name Functions of smooth surface + \{ */ + virtual MbSmoothSurface & CurvesDuplicate() const; // \ru Копия с теми же опорными поверхностями. \en Copy with the same support surfaces. + virtual double GetSmoothRadius() const; // \ru Дать радиус. \en Get radius. + virtual void GetDistances( double u, double &d1, double &d2 ) const; // \ru Дать радиусы со знаком. \en Get radii with a sign. + virtual double GetDistance( bool s ) const; // \ru Дать радиус со знаком. \en Get radius with a sign. + // \ru Объединить поверхности путём включения поверхности init в данную поверхность. \en Unite surfaces by inclusion of 'init' surface into current surface. + virtual bool SurfacesCombine( const MbSurfaceIntersectionCurve & edge, + const MbSurface & init, bool add, MbMatrix & matr, + const MbSurfaceIntersectionCurve * seam ); + /// \ru Дать коэффициент для радиуса. \en Get coefficient for radius. + virtual double DistanceRatio( bool firstCurve, MbCartPoint3D & p, double distance ) const; + // \ru Определение разбивки параметрической области поверхности вертикалями и горизонталями. \en Determine splitting of parametric region of surface by vertical and horizontal lines. + virtual void GetTesselation( const MbStepData & stepData, + double u1, double u2, double v1, double v2, + SArray & uu, SArray & vv ) const; + + /** \} */ + /** \ru \name Функции поверхности скругления + \en \name Functions of fillet surface + \{ */ + + /** \brief \ru Веса точек средней кривой. + \en Weights of points of mid-curve. \~ + \details \ru Веса точек средней кривой. + \en Weights of points of mid-curve. \~ + \param[in] u - \ru Параметр на средней кривой (по направлению U) + \en Parameter on mid-curve (by U direction) \~ + */ + double GetWeight( double u ) const; + + /** \brief \ru Угол раствора дуги. + \en Arc opening angle. \~ + \details \ru Угол раствора дуги. + \en Arc opening angle. \~ + \param[in] u - \ru Параметр по направлению U + \en Parameter by U direction \~ + \return \ru Угол раствора + \en Angle of opening \~ + */ + double GetAngle( double u ) const; // \ru Дать угол раствора дуги v \en Get v arc opening angle + + /** \brief \ru Ось поверхности в данной точке. + \en Axis of surface at given point. \~ + \details \ru Ось поверхности в данной точке. + \en Axis of surface at given point. \~ + \param[in] u - \ru Параметр по направлению U + \en Parameter by U direction \~ + \param[out] axis - \ru Результат - ось вращения + \en Result - rotation axis \~ + */ + double GetLocalAxis ( double u, MbAxis3D & axis ) const; // \ru Дать ось поверхности в данной точке \en Get axis of surface at given point + + /** \brief \ru Дать точку на оси. + \en Get point on axis. \~ + \details \ru Дать точку на оси. + \en Get point on axis. \~ + \param[in] u - \ru Параметр по направлению U + \en Parameter by U direction \~ + \param[out] p1 - \ru Точка на первой опорной кривой по параметру U + \en Point on first support curve by U parameter \~ + \param[out] p2 - \ru Точка на второй опорной кривой по параметру U + \en Point on second support curve by U parameter \~ + \param[out] p0 - \ru Точка на кривой пересечения касательных к поверхностям по параметру u (точка на оси) + \en Point on intersection curve of tangents to surfaces by u parameter (point on axis) \~ + */ + void GetCentrePoint( double u, MbCartPoint3D &p1, MbCartPoint3D &p2, MbCartPoint3D &p0 ) const; + + /** \brief \ru Дать среднюю точку. + \en Get the mid-point. \~ + \details \ru Дать среднюю точку. + \en Get the mid-point. \~ + \param[in] u - \ru Параметр по направлению U + \en Parameter by U direction \~ + \param[out] p1 - \ru Точка на первой опорной кривой по параметру U + \en Point on first support curve by U parameter \~ + \param[out] p2 - \ru Точка на второй опорной кривой по параметру U + \en Point on second support curve by U parameter \~ + \param[out] p0 - \ru Средняя точка + \en Mid-point \~ + \param[out] w - \ru Вес полученной средней точки + \en Weight of obtained mid-point \~ + */ + bool GetMiddlePoint( double u, MbCartPoint3D &p1, MbCartPoint3D &p2, MbCartPoint3D &p0, double &w ) const; + + // \ru Если параметризация равномерная, то на продолжении по V замыкается и период зависит от U \en If parameterization is uniform, then it is closed on extension by V and period depends on U + /** \brief \ru Период по направлению V. + \en Period by direction V. \~ + \details \ru Период по направлению V.\n + Если параметризация поверхности равномерная, то период по направлению V зависит от параметра U. + \en Period by direction V.\n + If parameterization of surface is uniform, then period by direction V depends on U parameter. \~ + \param[in] u - \ru Параметр по направлению U + \en Parameter by U direction \~ + \return \ru Период для заданного параметра + \en Period for given parameter \~ + */ + double GetVPeriod( double u ) const; + + /// \ru Кривая пересечения касательных к поверхностям. \en Intersection curve of tangents to surfaces. + const MbCurve3D & GetCurve0() const { return *curve0; } + + /** \brief \ru Скругление не круговое. + \en Fillet isn't circular. \~ + \details \ru Скругление не круговое. + \en Fillet isn't circular. \~ + \return \ru true, если радиусы скруглений для поверхностей не равны + \en True if surfaces fillet radii aren't equal \~ + */ + bool IsEllipse() const { return (fabs(fabs(distance1) - fabs(distance2))>=LENGTH_EPSILON); } // \ru Не равные радиусы \en Not equal radii + + /// \ru Параметризация по дуге равномерная. \en Uniform arc length parameterization. + bool IsEven() const { return even; } + + /** \brief \ru Однородная ли поверхность скругления. + \en Whether the fillet surface is homogeneous. \~ + \details \ru Однородная ли поверхность скругления. Поверхность скругления без сохранения кромки. + \en Whether the fillet surface is homogeneous. Fillet surface without preservation of fillet. \~ + \return \ru false, если одна из кривых curve1 или curve2 является кромкой + \en False if one of curve1 or curve2 curves is fillet \~ + */ + bool IsFilletSurface() const { return equable; } + + /** \brief \ru Коническое сечение общего вида. + \en General conic section. \~ + \details \ru Коническое сечение общего вида. + \en General conic section. \~ + \return \ru false, если сечение поверхности скругления является дугой окружности + \en False if section of fillet surface is circular arc \~ + */ + bool IsConic() const { return ( ::fabs(conic - c3d::_ARC_) >= EPSILON ); } // \ru Коническое сечение общего вида \en General conic section + + /** \brief \ru Коэффициент формы. + \en Coefficient of shape. \~ + \details \ru Коэффициент формы сечения поверхности скругления.\n + Изменяется от 0.05 до 0.95, при 0 сечение является дугой окружности. + \en Coefficient of shape of section of fillet surface.\n + Is changed between 0.05 and 0.95, if 0, then section is circular arc. \~ + \return \ru Коэффициент + \en Coefficient \~ + */ + double Conic() const { return conic; } + + /** \brief \ru Поверхность скругления с сохранением кромки. + \en Fillet surface with preservation of fillet. \~ + \details \ru Поверхность скругления с сохранением кромки. + \en Fillet surface with preservation of fillet. \~ + \return \ru true, если одна из кривых curve1 или curve2 является кромкой + \en True if one of curve1 or curve2 curves is fillet \~ + */ + bool IsKerbSurface() const { return !equable; } + + /** \brief \ru Является ли первая кривая кромкой. + \en Whether the first curve is fillet. \~ + \details \ru Является ли первая кривая кромкой. + \en Whether the first curve is fillet. \~ + \return \ru true, если первая кривая является кромкой + \en True if the first curve is fillet. \~ + */ + bool ByFirstCurve() const { return byCurve1; } + + /** \brief \ru Установить поверхность скругления типа с сохранением кромки. + \en Set fillet surface with preservation of fillet. + Need to call this->Init0() after this method \~ + \details \ru Установить поверхность скругления с сохранением кромки и указать определяющую кривую на поверхности. + Далее нужно вызвать метод this->Init0(). + \en Set fillet surface with preservation of fillet. \~ + \param[in] bc1 - \ru Определяющая кривая на поверхности: curve1 (bc1 = true), curve2 (bc1 = false). + \en General curve on surface: curve1 (bc1 = true), curve2 (bc1 = false). \~ + */ + void SetKerbSurface( bool bc1 ) { if ( equable ) { equable = false; byCurve1 = bc1; } } + + // \ru Выдать функцию весов точек средней кривой curve0. \en Get weight function for points of mid-curve (curve0). + const MbFunction * GetWeights() const; + // \ru Установить функцию весов точек средней кривой curve0. \en Set weight function for points of mid-curve (curve0). + bool SetWeights( MbFunction & func ); + + MbCurve3D * GetSpine() const; + void SetSpine( MbCurve3D * ); + + /** \} */ +protected: + void WeightKoefficient( double & w ) const; // \ru Вычисление веса при заданном коэффициенте \en Calculation of weight at given coefficient + void InitFilletSurface ( const MbFilletSurface & init ); + void CalculateCurve( double wmin, double wmax, bool insertPoints ); + double CalculateVParam( const MbCartPoint3D & p, double u ) const; // \ru Нахождение параметра v проекции точки на вырожденную поверхность \en Searching of v parameter of point projection onto degenerate surface + +protected: + // \ru Вычисление точки \en Calculation of a point +// void CalculateSurface( double u ) const; + // \ru Дать коэффициент для радиуса \en Get coefficient for radius + virtual double FunctionValue( double u ) const; + void CalculateData ( double & u, double & v, + MbCartPoint3D & uPoint0, MbCartPoint3D & uPoint1, MbCartPoint3D & uPoint2, // \ru Точки на кривых curve0, curve1, curve2. \en Points on curve0, curve1, curve2. + MbVector3D * uFirst0, MbVector3D * uFirst1, MbVector3D * uFirst2, // \ru Производные кривых curve0, curve1, curve2. \en Derivatives of curve0, curve1, curve2. + MbVector3D * uSecond0, MbVector3D * uSecond1, MbVector3D * uSecond2, // \ru Производные кривых curve0, curve1, curve2. \en Derivatives of curve0, curve1, curve2. + double & uWeight, double * wFirst, double * wSecond, // \ru Вес и его производные средней точки uPoint0. \en The weight and it derivatives of the mid-point uPoint0. + double & uP0, double & uP1, double & uP2, double & uPw, // \ru Коэффициенты точек uPoint0, uPoint1, uPoint2. \en Coefficients of points uPoint0, uPoint1, uPoint2. + double & uF0, double & uF1, double & uF2, double & uFw ) const; // \ru Коэффициенты производных uFirst0, uFirst1, uFirst2. \en Coefficients of derivatives uFirst0, uFirst1, uFirst2. + void InitSpineDerives(); + void CalculatePointOn ( double & v, + const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, + const double & uWeight, + const double & uP0, const double & uP1, const double & uP2, const double & uPw, + MbCartPoint3D & ) const; // \ru Точка на поверхности. \en The point on the surface. + void CalculateDeriveU ( double & v, + const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, + const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, + const double & uWeight, const double & wFirst, + const double & uP0, const double & uP1, const double & uP2, const double & uPw, + MbVector3D & ) const; // \ru Первая производная по u. \en The first derivative with respect to u. + void CalculateDeriveV ( double & v, + const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, + const double & uWeight, + const double & uP0, const double & uP1, const double & uP2, const double & uPw, + const double & uF0, const double & uF1, const double & uF2, const double & uFw, + MbVector3D & ) const; // \ru Первая производная по v. \en The first derivative with respect to v. + void CalculateDeriveUU ( double & v, + const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, + const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, + const MbVector3D & uSecond0, const MbVector3D & uSecond1, MbVector3D &uSecond2, + const double & uWeight, const double & wFirst, const double & wSecond, + const double & uP0, const double & uP1, const double & uP2, const double & uPw, + MbVector3D & ) const; // \ru Вторая производная по u. \en The second derivative with respect to u. + void CalculateDeriveVV ( double & v, + const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, + const double & uWeight, + const double & uP0, const double & uP1, const double & uP2, const double & uPw, + const double & uF0, const double & uF1, const double & uF2, const double & uFw, + MbVector3D & ) const; // \ru Вторая производная по v. \en The second derivative with respect to v. + void CalculateDeriveUV ( double & v, + const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, + const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, + const double & uWeight, const double & wFirst, + const double & uP0, const double & uP1, const double & uP2, const double & uPw, + const double & uF0, const double & uF1, const double & uF2, const double & uFw, + MbVector3D & ) const; // \ru Вторая производная по uv. \en The second derivative with respect to uv. + void CalculateDeriveUUU( double & u, double & v, + const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, + const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, + const MbVector3D & uSecond0, const MbVector3D & uSecond1, MbVector3D &uSecond2, + const double & uWeight, const double & wFirst, const double & wSecond, + const double & uP0, const double & uP1, const double & uP2, const double & uPw, + MbVector3D & ) const; + void CalculateDeriveUUV( double & v, + const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, + const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, + const MbVector3D & uSecond0, const MbVector3D & uSecond1, MbVector3D &uSecond2, + const double & uWeight, const double & wFirst, const double & wSecond, + const double & uP0, const double & uP1, const double & uP2, const double & uPw, + const double & uF0, const double & uF1, const double & uF2, const double & uFw, + MbVector3D & ) const; + void CalculateDeriveUVV( double & v, + const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, + const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, + const double & uWeight, const double & wFirst, + const double & uP0, const double & uP1, const double & uP2, const double & uPw, + const double & uF0, const double & uF1, const double & uF2, const double & uFw, + MbVector3D & ) const; + void CalculateDeriveVVV( double & v, + const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, + const double & uWeight, + const double & uP0, const double & uP1, const double & uP2, const double & uPw, + const double & uF0, const double & uF1, const double & uF2, const double & uFw, + MbVector3D & ) const; + void CalculateNormal ( double & u, double & v, + const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, + const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, + const double & uWeight, const double & wFirst, + const double & uP0, const double & uP1, const double & uP2, const double & uPw, + const double & uF0, const double & uF1, const double & uF2, const double & uFw, + MbVector3D & ) const; // \ru Нормаль. \en Normal. + void CalculateNormalU ( double & u, double & v, + const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, + const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, + const MbVector3D & uSecond0, const MbVector3D & uSecond1, MbVector3D &uSecond2, + const double & uWeight, const double & wFirst, const double & wSecond, + const double & uP0, const double & uP1, const double & uP2, const double & uPw, + const double & uF0, const double & uF1, const double & uF2, const double & uFw, + MbVector3D & ) const; // \ru Производная нормали. \en The derivative of normal. + void CalculateNormalV ( double & u, double & v, + const MbCartPoint3D & uPoint0, const MbCartPoint3D & uPoint1, const MbCartPoint3D & uPoint2, + const MbVector3D & uFirst0, const MbVector3D & uFirst1, const MbVector3D & uFirst2, + const double & uWeight, const double & wFirst, + const double & uP0, const double & uP1, const double & uP2, const double & uPw, + const double & uF0, const double & uF1, const double & uF2, const double & uFw, + MbVector3D & ) const; // \ru Производная нормали. \en The derivative of normal. + + // \ru Проверка параметров. \en Check parameters. + void CheckUParam( double & u ) const; + void CheckVParam( double & v ) const; + + void operator = ( const MbFilletSurface & ); // \ru Не реализовано. \en Not implemented. + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbFilletSurface ) +}; // MbFilletSurface + + +IMPL_PERSISTENT_OPS( MbFilletSurface ) + + +//------------------------------------------------------------------------------ +// \ru Проверка параметра u по отношению к полюсам и замкнутости \en Check u parameter against poles and closedness +// --- +inline void MbFilletSurface::CheckUParam( double & u ) const { + if ( uclosed ) { + if ( (u < umin) || (u > umax ) ) { + double tmp = umax - umin; + u -= ::floor((u - umin) / tmp) * tmp; + } + } + else { + if ( poleMin && uumax ) + u = umax; + } +} + + +//------------------------------------------------------------------------------ +// \ru Проверка параметра v \en Check v parameter +// --- +inline void MbFilletSurface::CheckVParam( double & v ) const { + if ( v < vmin ) + v = vmin; + else + if ( v > vmax ) + v = vmax; +} + + +//------------------------------------------------------------------------------ +// \ru Вычислить вес для эллиптической точки \en Calculate weight for elliptic point +// --- +inline bool FilletWeight( double cos_a, double d1, double d2, double & uW ) +{ + bool result = false; + // \ru cos_a - косинус угла между нормалями \en Cos_a - cosine of angle between normals + if ( ::fabs( cos_a ) > PARAM_PRECISION ) { // \ru Не прямой угол \en Angle not right + if ( (d1 * d2) < 0 ) + cos_a = -cos_a; + double cos2_a = cos_a * cos_a; + double sin2_a = ::fabs( 1.0 - cos2_a ); + double sin_a = ::sqrt( sin2_a ); // \ru Синус угла между нормалями \en Sine of angle between normals + bool first = ( ::fabs(d1) > ::fabs(d2) ); + double a = first ? ::fabs( d1 ) : ::fabs( d2 ); // \ru Большая полуось эллипса \en Major semi axis of ellipse + double b = first ? ::fabs( d2 ) : ::fabs( d1 ); // \ru Малая полуось эллипса \en Minor semi axis of ellipse + // \ru Эллипс наиболее удалённой от центра точкой касался одной из поверхностей \en Ellipse concerns one of the surfaces by the point most remote from the center + if ( sin_a > PARAM_PRECISION ) { // \ru Не малый угол \en Angle not small + double aa = a * a; + double bb = b * b; + double p = ::sqrt( (aa*cos2_a) + (bb*sin2_a) ); // \ru Знаменатель (расстояние от центра эллипса до касательной) \en Denominator (distance from center of ellipse to tangent) + if ( p > NULL_EPSILON ) { // \ru Всегда должно выполняться, если a!=0 и b!=0 \en Always hold, if a! =0 and b! =0 + double d = 1.0 / p; + double cos_t = a * cos_a * d; // \ru Косинус параметрического угла эллипса \en Cosine of metric angle of ellipse + uW = ::sqrt( (1.0 + cos_t) * 0.5 ); // \ru Половина косинуса параметрического угла эллипса \en Half of cosine of metric angle of ellipse + result = true; + } + } + } + return result; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Создать поверхность сопряжения. + \en Create the fillet surface. \~ + \details \ru Создать поверхность сопряжения с сохранением кромки грани. + \en Create the fillet surface with the edge. \~ + \param[in] surface1 - \ru Сопрягаемая поверхность. + \en The conjugate surface. \~ + \param[in] points1 - \ru Точки для опорной кривой на сопрягаемой поверхности. + \en Points for curve on the conjugated surface. \~ + \param[in] surface2 - \ru Сопрягаемая поверхность. + \en The conjugate surface. \~ + \param[in] points2 - \ru Точки для опорной кривой на сопрягаемой поверхности. + \en Points for curve on the conjugated surface. \~ + \param[in] form - \ru Тип повержности сопряжения. + \en The surface type \~ + \param[in] distance1 - \ru Радиус скругления со знаком для поверхности кривой crve1 + \en Fillet radius with sign for surface of crve1 curve \~ + \param[in] distance2 - \ru Радиус скругления со знаком для поверхности кривой crve2 + \en Fillet radius with sign for surface of crve2 curve \~ + \param[in] conic - \ru Коэффициент формы, изменяется от 0.05 до 0.95 (при 0.5 - дуга окружности) + \en Coefficient of shape is changed from 0.05 to 0.95 (if 0.5 - circular arc) \~ + \param[in] curve - \ru Кривая опорной кромки. + \en The edge curve \~ + \param[in] params - \ru Параметры поверхности вдоль первого напрвыления (u). + \en The parameters of new the surface by first direction (u) \~ + \param[in] byFirstSurface - \ru Пурвая или вторая поверхность сопрягается гладко с новой поверхностью. + \en Is the first or second conjugate surface smooth with the new surface. \~ + \param[in] even - \ru Равномерная параметризация по дуге (v) или нет + \en Uniform parametrization by arc (v) or not \~ + \return \ru Возвращает созданную поверхность. + \en Return the created surface. \~ + \ingroup Surface_Modeling +*/ +MbSmoothSurface * CreateKerbSurface( const MbSurface &surface1, SArray & points1, + const MbSurface &surface2, SArray & points2, + MbeSmoothForm form, double distance1, double distance2, double conic, + const MbSurfaceIntersectionCurve & curve, SArray & params, + bool byFirstSurface, bool even, VERSION version ); + + +#endif // __SURF_FILLET_SURFACE_H diff --git a/C3d/Include/surf_grid_surface.h b/C3d/Include/surf_grid_surface.h index a64075e..c656a89 100644 --- a/C3d/Include/surf_grid_surface.h +++ b/C3d/Include/surf_grid_surface.h @@ -1,413 +1,412 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Поверхность на базе триангуляции. - \en Surface based on triangulation. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __SURF_GRID_SURFACE_H -#define __SURF_GRID_SURFACE_H - - -#include -#include -#include -#include -#include -#include - - -class MATH_CLASS MbCurve3D; -class MATH_CLASS MbMesh; -class MATH_CLASS MbGrid; - - -#define _C3D_3_ 3 -#define _C3D_4_ 4 - - -//------------------------------------------------------------------------------ -/// \ru Tреугольная пластина поверхности на сетке точек. \en Triangular plate. -// --- -class MbTrigon { -protected : - size_t index[_C3D_3_]; ///< \ru Номера вершин треугольника в массиве точек. \en The numbers of vertices in points array. - size_t neighbour[_C3D_3_]; ///< \ru Номера соседних треугольников. \en The numbers of neighbour triangles. SYS_MAX_T if it is absent. - -// points[index[2]] -// + -// / \ -// neighbour[2] / \ neighbour[1] -// / \ -// points[index[0]] +---------------+ points[index[1]] -// neighbour[0] - -public : - MbTrigon() { index[0] = SYS_MAX_T; index[1] = SYS_MAX_T; index[2] = SYS_MAX_T; - neighbour[0] = SYS_MAX_T; neighbour[1] = SYS_MAX_T; neighbour[2] = SYS_MAX_T; } - MbTrigon( size_t i0, size_t i1, size_t i2 ) { index[0] = i0; index[1] = i1; index[2] = i2; - neighbour[0] = SYS_MAX_T; neighbour[1] = SYS_MAX_T; neighbour[2] = SYS_MAX_T; } - MbTrigon( const MbTrigon & init ) { index[0] = init.index[0]; index[1] = init.index[1]; index[2] = init.index[2]; - neighbour[0] = init.neighbour[0]; neighbour[1] = init.neighbour[1]; neighbour[2] = init.neighbour[2]; } - MbTrigon( const MbTriangle & init ) { - uint i0, i1, i2; - init.GetTriangle( i0, i1, i2 ); - index[0] = i0; index[1] = i1; index[2] = i2; - neighbour[0] = SYS_MAX_T; neighbour[1] = SYS_MAX_T; neighbour[2] = SYS_MAX_T; } -public : - void Init( size_t i0, size_t i1, size_t i2, bool orientation ) { - if ( orientation ) { index[0] = i0; index[1] = i1; index[2] = i2; } - else { index[0] = i0; index[1] = i2; index[2] = i1; } - neighbour[0] = SYS_MAX_T; neighbour[1] = SYS_MAX_T; neighbour[2] = SYS_MAX_T; - } - void Init( size_t i0, size_t i1, size_t i2, size_t n0, size_t n1, size_t n2 ) { - index[0] = i0; index[1] = i1; index[2] = i2; - neighbour[0] = n0; neighbour[1] = n1; neighbour[2] = n2; - } - void GetTriangle( size_t & i0, size_t & i1, size_t & i2 ) const { i0 = index[0]; i1 = index[1]; i2 = index[2]; } - void GetTriangle( size_t & i0, size_t & i1, size_t & i2, size_t & n0, size_t & n1, size_t & n2 ) const { - i0 = index[0]; i1 = index[1]; i2 = index[2]; - n0 = neighbour[0]; n1 = neighbour[1]; n2 = neighbour[2]; - } - size_t GetNunber( size_t & i ) const { return index[i % _C3D_3_]; } - size_t GetNeihbour( size_t & i ) const { return neighbour[i % _C3D_3_]; } - // \ru Инициализация соседа. \en The neighbour initiation. - void SetNeihbour( size_t i, size_t n ) { neighbour[i % _C3D_3_] = n; } - MbTrigon & operator = ( const MbTrigon & init ) { init.GetTriangle( index[0], index[1], index[2] ); - neighbour[0] = init.neighbour[0]; neighbour[1] = init.neighbour[1]; neighbour[2] = init.neighbour[2]; - return *this; } - MbTrigon & operator = ( const MbTriangle & init ) { - uint i0, i1, i2; - init.GetTriangle( i0, i1, i2 ); - index[0] = i0; index[1] = i1; index[2] = i2; - neighbour[0] = SYS_MAX_T; neighbour[1] = SYS_MAX_T; neighbour[2] = SYS_MAX_T; - return *this; } -}; // MbTrigon - - -//------------------------------------------------------------------------------ -/** \brief \ru Поверхность на базе триангуляции. - \en Surface based on triangulation. \~ - \details \ru Поверхность на базе триангуляции образована криволинейными треугольниками, - гладко стыкующимися между собой по общим сторонам. - В общих вершинах стыкующиеся треугольники имеют общую нормаль. - Сторону треугольников изменяются по кубическому закону. \n - \en Surface based on triangulation is formed by curvilinear triangles - which are smoothly connected together through common edges. - Connected triangles have common normal at common vertices. - Edges of triangles are changed by cubic law. \n \~ - \ingroup Surfaces -*/ -// --- -class MATH_CLASS MbGridSurface : public MbSurface { - -private: - // \ru Согласованные между собой множества данных в вершинах. \en Sets of data at vertices matched each other. - std::vector params; ///< \ru Множество точек на параметрической области поверхности. \en Set of points in parametric space of surface. - std::vector points; ///< \ru Множество точек поверхности. \en Set of points of surface. - std::vector normals; ///< \ru Множество нормалей поверхности. \en Set of normals of surface. - std::vector triangles; ///< \ru Множество треугольников. \en Set of triangles. - // \ru Описание области параметров поверхности. \en Description of surface parameters region. - size_t uCount; ///< \ru Количество разбиений области по u. \en Count of splittings of region by u. - size_t vCount; ///< \ru Количество разбиений области по v. \en Count of splittings of region by v. - PArray< std::vector > cell; ///< \ru Сетка области параметров поверхности. \en Grid of surface parameters region. - std::vector boundary; ///< \ru Граничные кривые области параметров поверхности. \en Boundary curves of surface parameters region. - double umin; ///< \ru Минимальное значение параметра u. \en Minimal value of parameter u. - double vmin; ///< \ru Минимальное значение параметра v. \en Minimal value of parameter v. - double umax; ///< \ru Максимальное значение параметра u. \en Maximal value of parameter u. - double vmax; ///< \ru Максимальное значение параметра v. \en Maximal value of parameter v. - bool uclosed; ///< \ru Признак замкнутости по параметру u. \en Attribute of closedness by parameter u. - bool vclosed; ///< \ru Признак замкнутости по параметру v. \en Attribute of closedness by parameter v. - -private: - /// \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. - MbGridSurface( const MbGridSurface & init ); - MbGridSurface( const MbGridSurface & init, MbRegDuplicate * iReg ); -protected: - /// \ru Конструктор поверхности. \en Constructor of surface. - template - MbGridSurface ( const Params & _params - , const Points & _points - , const Normals & _normals - , const Triangles & _triangles - , const Bounds & _bounds) - : MbSurface () - , params () - , points () - , normals () - , triangles () - , uCount( 1 ) - , vCount( 1 ) - , cell( 0, 1, true) - , boundary () - , umin ( 0.0 ) - , vmin ( 0.0 ) - , umax ( 0.0 ) - , vmax ( 0.0 ) - , uclosed ( false ) - , vclosed ( false ) - { - size_t k, cnt; - - cnt = _params.size(); - params.reserve( cnt ); - for ( k = 0; k < cnt; k++ ) - params.push_back( _params[k] ); - cnt = _points.size(); - points.reserve( cnt ); - for ( k = 0; k < cnt; k++ ) - points.push_back( _points[k] ); - - if ( _normals.size() > 0 ) { - size_t normalsLast = _normals.size() - 1; - normals.reserve( cnt ); - for ( k = 0; k < cnt; k++ ) { - size_t ind = std_min( k, normalsLast ); - normals.push_back( _normals[ind] ); - } - } - cnt = _triangles.size(); - triangles.reserve( cnt ); - for ( k = 0; k < cnt; k++ ) - triangles.push_back( _triangles[k] ); - - cnt = _bounds.size(); - boundary.reserve(cnt); - for (k = 0; k < cnt; k++) { - _bounds[k]->AddRef(); - boundary.push_back(_bounds[k]); - } - - Init( boundary.size() == 0 ); - } - -public: - virtual ~MbGridSurface(); - -public: - VISITING_CLASS( MbGridSurface ); - - /** \name Общие функции геометрического объекта - \{ */ - // \ru Общие функции геометрического объекта \en Common functions of a geometric object - virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Cделать копию элемента \en Make a copy of element - virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; - virtual bool SetEqual( const MbSpaceItem &init ); // \ru Сделать равным \en Make equal - virtual bool IsSimilar( const MbSpaceItem &init ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar - virtual void Transform( const MbMatrix3D &matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move( const MbVector3D &to, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate( const MbAxis3D &axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis - virtual double DistanceToPoint( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. - - virtual void GetProperties( MbProperties &properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties &properties ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - virtual void Refresh(); // \ru Сбросить все временные данные. \en Reset all temporary data. - /** \} */ - - /** \name Функции описания области определения поверхности - \{ */ - // \ru Функции описания области определения поверхности. \en Functions for surface domain description. - virtual double GetUMin() const; - virtual double GetVMin() const; - virtual double GetUMax() const; - virtual double GetVMax() const; - virtual bool IsUClosed() const; // \ru Замкнута ли поверхность по параметру u. \en Whether the surface is closed by parameter u. - virtual bool IsVClosed() const; // \ru Замкнута ли поверхность по параметру v. \en Whether the surface is closed by parameter v. - /** \} */ - - /** \name Функции для работы в области определения поверхности - Функции PointOn, Derive... поверхностей корректируют параметры - при выходе их за пределы прямоугольной области определения параметров.\n - \{ */ - // \ru Функции для работы в области определения поверхности. \en Functions for working at surface domain. - virtual void PointOn ( double & u, double & v, MbCartPoint3D & p ) const; // \ru Точка на поверхности \en Point on the surface - virtual void DeriveU ( double & u, double & v, MbVector3D & der ) const; // \ru Первая производная по u \en First derivative with respect to u - virtual void DeriveV ( double & u, double & v, MbVector3D & der ) const; // \ru Первая производная по v \en First derivative with respect to v - virtual void DeriveUU ( double & u, double & v, MbVector3D & der ) const; // \ru Вторая производная по u \en Second derivative with respect to u - virtual void DeriveVV ( double & u, double & v, MbVector3D & der ) const; // \ru Вторая производная по v \en Second derivative with respect to v - virtual void DeriveUV ( double & u, double & v, MbVector3D & der ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv - virtual void DeriveUUU( double & u, double & v, MbVector3D & der ) const; // \ru Третья производная \en Third derivative - virtual void DeriveUUV( double & u, double & v, MbVector3D & der ) const; // \ru Третья производная \en Third derivative - virtual void DeriveUVV( double & u, double & v, MbVector3D & der ) const; // \ru Третья производная \en Third derivative - virtual void DeriveVVV( double & u, double & v, MbVector3D & der ) const; // \ru Третья производная \en Third derivative - /** \} */ - - /** \name Функции для работы внутри и вне области определения поверхности - функции _PointOn, _Derive... поверхностей не корректируют - параметры при выходе их за пределы прямоугольной области определения параметров. - За пределами параметрической области поверхность продолжается по касательной. - \{ */ - // \ru Функции для работы внутри и вне области определения поверхности. \en Functions for working inside and outside the surface's domain. - virtual void _PointOn ( double u, double v, MbCartPoint3D & ) const; // \ru Точка на расширенной поверхности. \en The point on the extended surface. - virtual void _DeriveU ( double u, double v, MbVector3D & ) const; // \ru Первая производная по u. \en The first derivative with respect to u. - virtual void _DeriveV ( double u, double v, MbVector3D & ) const; // \ru Первая производная по v. \en The first derivative with respect to v. - virtual void _DeriveUU ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по u. \en The second derivative with respect to u. - virtual void _DeriveVV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по v. \en The second derivative with respect to v. - virtual void _DeriveUV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная. \en The second derivative. - virtual void _DeriveUUU( double u, double v, MbVector3D & ) const; // \ru Третья производная. \en The third derivative. - virtual void _DeriveUUV( double u, double v, MbVector3D & ) const; // \ru Третья производная. \en The third derivative. - virtual void _DeriveUVV( double u, double v, MbVector3D & ) const; // \ru Третья производная. \en The third derivative. - virtual void _DeriveVVV( double u, double v, MbVector3D & ) const; // \ru Третья производная. \en The third derivative. - /** \} */ - - /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. - \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. - \{ */ - virtual void Explore( double & u, double & v, bool ext, - MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, - MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; - /** \} */ - // \ru Функции движения по поверхности \en Functions of moving along the surface - virtual double StepU( double u, double v, double sag ) const; // \ru Вычисление шага аппроксимации с учетом радиуса кривизны по U \en Calculation of the approximation step with consideration of the curvature radius by U - virtual double StepV( double u, double v, double sag ) const; // \ru Вычисление шага аппроксимации с учетом радиуса кривизны по V \en Calculation of the approximation step with consideration of the curvature radius by V - virtual double DeviationStepU( double u, double v, double ang ) const; // \ru Вычисление шага параметра u по углу отклонения нормали \en Calculation of parameter u step by the angle of deviation of normal - virtual double DeviationStepV( double u, double v, double ang ) const; // \ru Вычисление шага параметра v по углу отклонения нормали \en Calculation of parameter v step by the angle of deviation of normal - virtual size_t GetUCount() const; - virtual size_t GetVCount() const; - - // \ru Выдать граничную точку \en Get the boundary point - virtual void GetLimitPoint( ptrdiff_t num, MbCartPoint3D & ) const; // \ru Выдать граничную трехмерную точку. \en Get the three-dimensional boundary point. - virtual void GetLimitPoint( ptrdiff_t num, MbCartPoint & ) const; // \ru Выдать граничную двумерную точку (граничные параметры). \en Get the two-dimensional boundary point (boundary parameters). - - virtual MbeItemLocation PointClassification( const MbCartPoint &, bool ignoreClosed = false ) const; // \ru Находится ли точка в области, принадлежащей поверхности. \en Whether the point is in region belonging to the surface. - virtual double DistanceToBorder ( const MbCartPoint &, double & eps ) const; // \ru Параметрическое расстояние до ближайшей границы. \en Parametric distance to the nearest boundary. - // \ru Определение точек пересечения кривой с контурами поверхности. \en Determine intersection points of a curve with the contours on the surface. - virtual size_t CurveClassification( const MbCurve & curve, SArray & tcurv, SArray & dir ) const; - - // \ru Найти ближайшую проекцию точки на поверхность. \en Find the nearest projection of a point onto the surface. - virtual bool NearPointProjection( const MbCartPoint3D & p, double & u, double & v, bool ext, MbRect2D * uvRange = NULL ) const; // \ru Ближайшая проекция точки на поверхность. \en The nearest point projection onto the surface. - // \ru Найти все проекции точки на поверхность вдоль вектора в любом из двух направлений. \en Find all a point projection onto the surface along a vector in either of two directions. - virtual void DirectPointProjection( const MbCartPoint3D & p, const MbVector3D & vect, SArray & uv, bool ext, MbRect2D * uvRange = NULL ) const; - // \ru Вce точки пересечения поверхности и кривой. \en All the points of intersection of a surface and a curve. - virtual void CurveIntersection( const MbCurve3D & curv, SArray & uv, SArray & tt, - bool ext0, bool ext, bool touchInclude = false ) const; - // \ru Расчёт площади области определения параметров. \en Calculate area of parameter domain. - virtual double ParamArea() const; - virtual size_t GetUPairs( double v, SArray & u ) const; // \ru Вычислить U-пары от V. \en Calculate U-pairs by V. - virtual size_t GetVPairs( double u, SArray & v ) const; // \ru Вычислить V-пары от U. \en Calculate V-pairs by U. - virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. - virtual void CalculateSurfaceWire( const MbStepData & stepData, size_t beg, MbMesh & mesh, - size_t uMeshCount = c3d::WIRE_MAX, size_t vMeshCount = c3d::WIRE_MAX ) const; // \ru Рассчитать сетку. \en Calculate mesh. - // \ru Аппроксимация поверхности треугольными пластинами. \en Approximation of a surface by triangular plates. - virtual void CalculateSurfaceGrid( const MbStepData & stepData, bool sense, MbGrid & grid ) const; - // \ru Определение разбивки параметрической области поверхности вертикалями и горизонталями. \en Determine splitting of parametric region of surface by vertical and horizontal lines. - virtual void GetTesselation( const MbStepData & stepData, - double u1, double u2, double v1, double v2, - SArray & uu, SArray & vv ) const; - // \ru Пересчитать нормали в вершинах. \en Normals Calculation on vertex. - virtual void Normalize(); - - size_t GetBoundariesCount() const { return boundary.size(); } // \ru Выдать количество граничных двумерных кривых. \en Get the two-dimensional boundary curves count. - virtual MbContour & MakeContour( bool sense ) const; // \ru Выдать граничных двумерный контур. \en Get the two-dimensional boundary contour. - virtual MbCurve & MakeSegment( size_t i, bool sense ) const; // \ru Дать граничную двумерную кривую. \en Get the two-dimensional boundary curve. - - /// \ru Инициализация объекта по другому такому же. \en Initialization of a object by same other object. - void Init( const MbGridSurface & init ); - - /// \ru Выдать количество точек. \en Get the number of points. - size_t PointsCount() const { return points.size(); } - /// \ru Выдать количество нормалей. \en Get the number of normals. - size_t NormalsCount() const { return normals.size(); } - /// \ru Выдать количество параметров. \en Get the number of parameters. - size_t ParamsCount() const { return params.size(); } - // \ru Выдать количество треугольников. \en Get the number of triangles. - size_t TrianglesCount() const { return triangles.size(); } - // \ru Выдать количество граничных кривых. \en Get the number of boundary curves. - size_t BoundariesCount() const { return boundary.size(); } - - // \ru Добавить в контейнер параметры в опорных точках поверхности. \en Get the parameters to container. - void GetParams( std::vector & paramsVector ) const; - // \ru Добавить в контейнер опорные точки. \en Get the points to container. - void GetPoints( std::vector & pointsVector ) const; - // \ru Добавить в контейнер нормали в опорных точках. \en Add the normals to container. - void GetNormals( std::vector & normalsVector ) const; - // \ru Добавить в контейнер треугольники. \en Add the triangles to container. - void GetTriangles( std::vector & tVector ) const; - // \ru Добавить в контейнер треугольники. \en Add the triangles to container. - void GetTriangles( std::vector & tVector ) const; - // \ru Добавить в контейнер граничные кривые. \en Add the boundary curves of surface parameters region. - void GetBoundaries( std::vector & bVector ) const; - - /// \ru Создание поверхности. \en Creatying of surface. - template - - static MbGridSurface * Create( const Params & _params - , const Points & _points - , const Normals & _normals - , const Triangles & _triangles - , const Bounds & _bounds ) - { - MbGridSurface * surface = NULL; - - const size_t itemsCnt = _params.size(); - - if ( (itemsCnt > 2) && (itemsCnt == _points.size()) && (_triangles.size() > 0) ) { - if ( (itemsCnt == _normals.size()) || (_normals.size() == 1) ) - surface = new MbGridSurface( _params, _points, _normals, _triangles, _bounds ); - } - return surface; - } - -private: - /// \ru Инициализация. \en Initialization. - void Init( bool bound = true ); - // \ru Инициализация граничных кривых. \en Initialization of boundary curves. - void MakeBoundary(); - // \ru Выдать треугольник. \en Get triangle. - MbTrigon & GetTriangle( size_t i ) { return triangles[i]; } - void PointOn ( double & u, double & v, bool ext, MbCartPoint3D & p ) const; // \ru Точка на поверхности \en Point on the surface - void DeriveU ( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Первая производная по u \en First derivative with respect to u - void DeriveV ( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Первая производная по v \en First derivative with respect to v - void DeriveUU ( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Вторая производная по u \en Second derivative with respect to u - void DeriveVV ( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Вторая производная по v \en Second derivative with respect to v - void DeriveUV ( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv - void DeriveUUU( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Третья производная \en Third derivative - void DeriveUUV( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Третья производная \en Third derivative - void DeriveUVV( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Третья производная \en Third derivative - void DeriveVVV( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Третья производная \en Third derivative - // \ru Выставить взаимные связи триангуляции. \en Set mutual connections of triangulation. - bool SetTrigonNeihbours(); - // \ru Поиск ближайшего треугольника для инициализации данных ячейки. \en Search nearest triangle for initialization of data of cell. - void FindNearest( size_t i, size_t j, std::vector & indecies, - double uDelta, double vDelta, double u, double v ); - // \ru Добавить ближайший треугольник в ячейку. \en Add nearest triangle to cell. - bool AddNearest( size_t i, size_t j, size_t ind ); - // \ru Вычислить индккс ближайшего треугольника и барицентрические координаты точки для него. \en Calculate barycentric coordinates of the nearest trianle. - size_t FindIndex( const double & u, const double & v, double & a, double & b, double & c, double & d ) const; - // \ru Расстояние до треугольника. \en The distance to a triangle. - double RangeToTriangle( size_t ind, const double & u, const double & v, double eps, - double & a, double & b, double & c, double & d ) const; - // \ru Расстояние до треугольника. \en The distance to a triangle. - double DistanceToTriangle( size_t ind, const double & u, const double & v, double eps, - MbCartPoint & p ) const; - // \ru Проверка параметров. \en Check parameters. - void CheckParam( double & u, double & v ) const; - // \ru Выдать данные триангуляции. \en Get triangulation data. - void GetTriangleData( size_t tIndex, - size_t & index1, size_t & index2, size_t & index3, - size_t & neigh1, size_t & neigh2, size_t & neigh3, - MbCartPoint & param1, MbCartPoint & param2, MbCartPoint & param3, - MbCartPoint3D & point1, MbCartPoint3D & point2, MbCartPoint3D & point3, - MbVector3D & normal1, MbVector3D & normal2, MbVector3D & normal3 ) const; - // \ru Выдать данные триангуляции соседнего треугольника. \en Get neighbour triangulation data. - bool GetNeighbourData( double u, double v, - size_t neigh1, size_t neigh2, size_t neigh3, - double & aCalc, double & bCalc, double & cCalc, double & deter, double & portion, - MbCartPoint & param1, MbCartPoint & param2, MbCartPoint & param3, - MbCartPoint3D & point1, MbCartPoint3D & point2, MbCartPoint3D & point3, - MbVector3D & normal1, MbVector3D & normal2, MbVector3D & normal3 ) const; - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - void operator = ( const MbGridSurface & ); - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbGridSurface ) -}; // MbGridSurface - -IMPL_PERSISTENT_OPS( MbGridSurface ) - - -#endif // __SURF_GRID_SURFACE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Поверхность на базе триангуляции. + \en Surface based on triangulation. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __SURF_GRID_SURFACE_H +#define __SURF_GRID_SURFACE_H + + +#include +#include +#include +#include +#include +#include + + +class MATH_CLASS MbCurve3D; +class MATH_CLASS MbMesh; +class MATH_CLASS MbGrid; + + +#define _C3D_3_ 3 +#define _C3D_4_ 4 + + +//------------------------------------------------------------------------------ +/// \ru Tреугольная пластина поверхности на сетке точек. \en Triangular plate. +// --- +class MbTrigon { +protected : + size_t index[_C3D_3_]; ///< \ru Номера вершин треугольника в массиве точек. \en The numbers of vertices in points array. + size_t neighbour[_C3D_3_]; ///< \ru Номера соседних треугольников. \en The numbers of neighbour triangles. SYS_MAX_T if it is absent. + +// points[index[2]] +// + +// / \ +// neighbour[2] / \ neighbour[1] +// / \ +// points[index[0]] +---------------+ points[index[1]] +// neighbour[0] + +public : + MbTrigon() { index[0] = SYS_MAX_T; index[1] = SYS_MAX_T; index[2] = SYS_MAX_T; + neighbour[0] = SYS_MAX_T; neighbour[1] = SYS_MAX_T; neighbour[2] = SYS_MAX_T; } + MbTrigon( size_t i0, size_t i1, size_t i2 ) { index[0] = i0; index[1] = i1; index[2] = i2; + neighbour[0] = SYS_MAX_T; neighbour[1] = SYS_MAX_T; neighbour[2] = SYS_MAX_T; } + MbTrigon( const MbTrigon & init ) { index[0] = init.index[0]; index[1] = init.index[1]; index[2] = init.index[2]; + neighbour[0] = init.neighbour[0]; neighbour[1] = init.neighbour[1]; neighbour[2] = init.neighbour[2]; } + MbTrigon( const MbTriangle & init ) { + uint i0, i1, i2; + init.GetTriangle( i0, i1, i2 ); + index[0] = i0; index[1] = i1; index[2] = i2; + neighbour[0] = SYS_MAX_T; neighbour[1] = SYS_MAX_T; neighbour[2] = SYS_MAX_T; } +public : + void Init( size_t i0, size_t i1, size_t i2, bool orientation ) { + if ( orientation ) { index[0] = i0; index[1] = i1; index[2] = i2; } + else { index[0] = i0; index[1] = i2; index[2] = i1; } + neighbour[0] = SYS_MAX_T; neighbour[1] = SYS_MAX_T; neighbour[2] = SYS_MAX_T; + } + void Init( size_t i0, size_t i1, size_t i2, size_t n0, size_t n1, size_t n2 ) { + index[0] = i0; index[1] = i1; index[2] = i2; + neighbour[0] = n0; neighbour[1] = n1; neighbour[2] = n2; + } + void GetTriangle( size_t & i0, size_t & i1, size_t & i2 ) const { i0 = index[0]; i1 = index[1]; i2 = index[2]; } + void GetTriangle( size_t & i0, size_t & i1, size_t & i2, size_t & n0, size_t & n1, size_t & n2 ) const { + i0 = index[0]; i1 = index[1]; i2 = index[2]; + n0 = neighbour[0]; n1 = neighbour[1]; n2 = neighbour[2]; + } + size_t GetNunber( size_t & i ) const { return index[i % _C3D_3_]; } + size_t GetNeihbour( size_t & i ) const { return neighbour[i % _C3D_3_]; } + // \ru Инициализация соседа. \en The neighbour initiation. + void SetNeihbour( size_t i, size_t n ) { neighbour[i % _C3D_3_] = n; } + MbTrigon & operator = ( const MbTrigon & init ) { init.GetTriangle( index[0], index[1], index[2] ); + neighbour[0] = init.neighbour[0]; neighbour[1] = init.neighbour[1]; neighbour[2] = init.neighbour[2]; + return *this; } + MbTrigon & operator = ( const MbTriangle & init ) { + uint i0, i1, i2; + init.GetTriangle( i0, i1, i2 ); + index[0] = i0; index[1] = i1; index[2] = i2; + neighbour[0] = SYS_MAX_T; neighbour[1] = SYS_MAX_T; neighbour[2] = SYS_MAX_T; + return *this; } +}; // MbTrigon + + +//------------------------------------------------------------------------------ +/** \brief \ru Поверхность на базе триангуляции. + \en Surface based on triangulation. \~ + \details \ru Поверхность на базе триангуляции образована криволинейными треугольниками, + гладко стыкующимися между собой по общим сторонам. + В общих вершинах стыкующиеся треугольники имеют общую нормаль. + Сторону треугольников изменяются по кубическому закону. \n + \en Surface based on triangulation is formed by curvilinear triangles + which are smoothly connected together through common edges. + Connected triangles have common normal at common vertices. + Edges of triangles are changed by cubic law. \n \~ + \ingroup Surfaces +*/ +// --- +class MATH_CLASS MbGridSurface : public MbSurface { + +private: + // \ru Согласованные между собой множества данных в вершинах. \en Sets of data at vertices matched each other. + std::vector params; ///< \ru Множество точек на параметрической области поверхности. \en Set of points in parametric space of surface. + std::vector points; ///< \ru Множество точек поверхности. \en Set of points of surface. + std::vector normals; ///< \ru Множество нормалей поверхности. \en Set of normals of surface. + std::vector triangles; ///< \ru Множество треугольников. \en Set of triangles. + // \ru Описание области параметров поверхности. \en Description of surface parameters region. + size_t uCount; ///< \ru Количество разбиений области по u. \en Count of splittings of region by u. + size_t vCount; ///< \ru Количество разбиений области по v. \en Count of splittings of region by v. + PArray< std::vector > cell; ///< \ru Сетка области параметров поверхности. \en Grid of surface parameters region. + std::vector boundary; ///< \ru Граничные кривые области параметров поверхности. \en Boundary curves of surface parameters region. + double umin; ///< \ru Минимальное значение параметра u. \en Minimal value of parameter u. + double vmin; ///< \ru Минимальное значение параметра v. \en Minimal value of parameter v. + double umax; ///< \ru Максимальное значение параметра u. \en Maximal value of parameter u. + double vmax; ///< \ru Максимальное значение параметра v. \en Maximal value of parameter v. + bool uclosed; ///< \ru Признак замкнутости по параметру u. \en Attribute of closedness by parameter u. + bool vclosed; ///< \ru Признак замкнутости по параметру v. \en Attribute of closedness by parameter v. + +private: + /// \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en Declaration without implementation of the copy-constructor to prevent copying by default. + MbGridSurface( const MbGridSurface & init ); + MbGridSurface( const MbGridSurface & init, MbRegDuplicate * iReg ); +protected: + /// \ru Конструктор поверхности. \en Constructor of surface. + template + MbGridSurface ( const Params & _params + , const Points & _points + , const Normals & _normals + , const Triangles & _triangles + , const Bounds & _bounds) + : MbSurface () + , params () + , points () + , normals () + , triangles () + , uCount( 1 ) + , vCount( 1 ) + , cell( 0, 1, true) + , boundary () + , umin ( 0.0 ) + , vmin ( 0.0 ) + , umax ( 0.0 ) + , vmax ( 0.0 ) + , uclosed ( false ) + , vclosed ( false ) + { + size_t k, cnt; + + cnt = _params.size(); + params.reserve( cnt ); + for ( k = 0; k < cnt; k++ ) + params.push_back( _params[k] ); + cnt = _points.size(); + points.reserve( cnt ); + for ( k = 0; k < cnt; k++ ) + points.push_back( _points[k] ); + + if ( _normals.size() > 0 ) { + size_t normalsLast = _normals.size() - 1; + normals.reserve( cnt ); + for ( k = 0; k < cnt; k++ ) { + size_t ind = std_min( k, normalsLast ); + normals.push_back( _normals[ind] ); + } + } + cnt = _triangles.size(); + triangles.reserve( cnt ); + for ( k = 0; k < cnt; k++ ) + triangles.push_back( _triangles[k] ); + + cnt = _bounds.size(); + boundary.reserve(cnt); + for (k = 0; k < cnt; k++) { + _bounds[k]->AddRef(); + boundary.push_back(_bounds[k]); + } + + Init( boundary.size() == 0 ); + } + +public: + virtual ~MbGridSurface(); + +public: + VISITING_CLASS( MbGridSurface ); + + /** \name Общие функции геометрического объекта + \{ */ + // \ru Общие функции геометрического объекта \en Common functions of a geometric object + virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Cделать копию элемента \en Make a copy of element + virtual bool IsSame( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; + virtual bool SetEqual( const MbSpaceItem &init ); // \ru Сделать равным \en Make equal + virtual bool IsSimilar( const MbSpaceItem &init ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar + virtual void Transform( const MbMatrix3D &matr, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move( const MbVector3D &to, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate( const MbAxis3D &axis, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis + virtual double DistanceToPoint( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. + + virtual void GetProperties( MbProperties &properties ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties &properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + virtual void Refresh(); // \ru Сбросить все временные данные. \en Reset all temporary data. + /** \} */ + + /** \name Функции описания области определения поверхности + \{ */ + // \ru Функции описания области определения поверхности. \en Functions for surface domain description. + virtual double GetUMin() const; + virtual double GetVMin() const; + virtual double GetUMax() const; + virtual double GetVMax() const; + virtual bool IsUClosed() const; // \ru Замкнута ли поверхность по параметру u. \en Whether the surface is closed by parameter u. + virtual bool IsVClosed() const; // \ru Замкнута ли поверхность по параметру v. \en Whether the surface is closed by parameter v. + /** \} */ + + /** \name Функции для работы в области определения поверхности + Функции PointOn, Derive... поверхностей корректируют параметры + при выходе их за пределы прямоугольной области определения параметров.\n + \{ */ + // \ru Функции для работы в области определения поверхности. \en Functions for working at surface domain. + virtual void PointOn ( double & u, double & v, MbCartPoint3D & p ) const; // \ru Точка на поверхности \en Point on the surface + virtual void DeriveU ( double & u, double & v, MbVector3D & der ) const; // \ru Первая производная по u \en First derivative with respect to u + virtual void DeriveV ( double & u, double & v, MbVector3D & der ) const; // \ru Первая производная по v \en First derivative with respect to v + virtual void DeriveUU ( double & u, double & v, MbVector3D & der ) const; // \ru Вторая производная по u \en Second derivative with respect to u + virtual void DeriveVV ( double & u, double & v, MbVector3D & der ) const; // \ru Вторая производная по v \en Second derivative with respect to v + virtual void DeriveUV ( double & u, double & v, MbVector3D & der ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv + virtual void DeriveUUU( double & u, double & v, MbVector3D & der ) const; // \ru Третья производная \en Third derivative + virtual void DeriveUUV( double & u, double & v, MbVector3D & der ) const; // \ru Третья производная \en Third derivative + virtual void DeriveUVV( double & u, double & v, MbVector3D & der ) const; // \ru Третья производная \en Third derivative + virtual void DeriveVVV( double & u, double & v, MbVector3D & der ) const; // \ru Третья производная \en Third derivative + /** \} */ + + /** \name Функции для работы внутри и вне области определения поверхности + функции _PointOn, _Derive... поверхностей не корректируют + параметры при выходе их за пределы прямоугольной области определения параметров. + За пределами параметрической области поверхность продолжается по касательной. + \{ */ + // \ru Функции для работы внутри и вне области определения поверхности. \en Functions for working inside and outside the surface's domain. + virtual void _PointOn ( double u, double v, MbCartPoint3D & ) const; // \ru Точка на расширенной поверхности. \en The point on the extended surface. + virtual void _DeriveU ( double u, double v, MbVector3D & ) const; // \ru Первая производная по u. \en The first derivative with respect to u. + virtual void _DeriveV ( double u, double v, MbVector3D & ) const; // \ru Первая производная по v. \en The first derivative with respect to v. + virtual void _DeriveUU ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по u. \en The second derivative with respect to u. + virtual void _DeriveVV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по v. \en The second derivative with respect to v. + virtual void _DeriveUV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная. \en The second derivative. + virtual void _DeriveUUU( double u, double v, MbVector3D & ) const; // \ru Третья производная. \en The third derivative. + virtual void _DeriveUUV( double u, double v, MbVector3D & ) const; // \ru Третья производная. \en The third derivative. + virtual void _DeriveUVV( double u, double v, MbVector3D & ) const; // \ru Третья производная. \en The third derivative. + virtual void _DeriveVVV( double u, double v, MbVector3D & ) const; // \ru Третья производная. \en The third derivative. + /** \} */ + + /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. + \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. + \{ */ + virtual void Explore( double & u, double & v, bool ext, + MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, + MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; + /** \} */ + // \ru Функции движения по поверхности \en Functions of moving along the surface + virtual double StepU( double u, double v, double sag ) const; // \ru Вычисление шага аппроксимации с учетом радиуса кривизны по U \en Calculation of the approximation step with consideration of the curvature radius by U + virtual double StepV( double u, double v, double sag ) const; // \ru Вычисление шага аппроксимации с учетом радиуса кривизны по V \en Calculation of the approximation step with consideration of the curvature radius by V + virtual double DeviationStepU( double u, double v, double ang ) const; // \ru Вычисление шага параметра u по углу отклонения нормали \en Calculation of parameter u step by the angle of deviation of normal + virtual double DeviationStepV( double u, double v, double ang ) const; // \ru Вычисление шага параметра v по углу отклонения нормали \en Calculation of parameter v step by the angle of deviation of normal + virtual size_t GetUCount() const; + virtual size_t GetVCount() const; + + // \ru Выдать граничную точку \en Get the boundary point + virtual void GetLimitPoint( ptrdiff_t num, MbCartPoint3D & ) const; // \ru Выдать граничную трехмерную точку. \en Get the three-dimensional boundary point. + virtual void GetLimitPoint( ptrdiff_t num, MbCartPoint & ) const; // \ru Выдать граничную двумерную точку (граничные параметры). \en Get the two-dimensional boundary point (boundary parameters). + + virtual MbeItemLocation PointClassification( const MbCartPoint &, bool ignoreClosed = false ) const; // \ru Находится ли точка в области, принадлежащей поверхности. \en Whether the point is in region belonging to the surface. + virtual double DistanceToBorder ( const MbCartPoint &, double & eps ) const; // \ru Параметрическое расстояние до ближайшей границы. \en Parametric distance to the nearest boundary. + // \ru Определение точек пересечения кривой с контурами поверхности. \en Determine intersection points of a curve with the contours on the surface. + virtual size_t CurveClassification( const MbCurve & curve, SArray & tcurv, SArray & dir ) const; + + // \ru Найти ближайшую проекцию точки на поверхность. \en Find the nearest projection of a point onto the surface. + virtual bool NearPointProjection( const MbCartPoint3D & p, double & u, double & v, bool ext, MbRect2D * uvRange = NULL ) const; // \ru Ближайшая проекция точки на поверхность. \en The nearest point projection onto the surface. + // \ru Найти все проекции точки на поверхность вдоль вектора в любом из двух направлений. \en Find all a point projection onto the surface along a vector in either of two directions. + virtual void DirectPointProjection( const MbCartPoint3D & p, const MbVector3D & vect, SArray & uv, bool ext, MbRect2D * uvRange = NULL ) const; + // \ru Вce точки пересечения поверхности и кривой. \en All the points of intersection of a surface and a curve. + virtual void CurveIntersection( const MbCurve3D & curv, SArray & uv, SArray & tt, + bool ext0, bool ext, bool touchInclude = false ) const; + // \ru Расчёт площади области определения параметров. \en Calculate area of parameter domain. + virtual double ParamArea() const; + virtual size_t GetUPairs( double v, SArray & u ) const; // \ru Вычислить U-пары от V. \en Calculate U-pairs by V. + virtual size_t GetVPairs( double u, SArray & v ) const; // \ru Вычислить V-пары от U. \en Calculate V-pairs by U. + virtual void CalculateMesh( const MbStepData & stepData, const MbFormNote & note, MbMesh & mesh ) const; // \ru Построить полигональную копию mesh. \en Build polygonal copy mesh. + virtual void CalculateSurfaceWire( const MbStepData & stepData, size_t beg, MbMesh & mesh, + size_t uMeshCount = c3d::WIRE_MAX, size_t vMeshCount = c3d::WIRE_MAX ) const; // \ru Рассчитать сетку. \en Calculate mesh. + // \ru Аппроксимация поверхности треугольными пластинами. \en Approximation of a surface by triangular plates. + virtual void CalculateSurfaceGrid( const MbStepData & stepData, bool sense, MbGrid & grid ) const; + // \ru Определение разбивки параметрической области поверхности вертикалями и горизонталями. \en Determine splitting of parametric region of surface by vertical and horizontal lines. + virtual void GetTesselation( const MbStepData & stepData, + double u1, double u2, double v1, double v2, + SArray & uu, SArray & vv ) const; + // \ru Пересчитать нормали в вершинах. \en Normals Calculation on vertex. + virtual void Normalize(); + + size_t GetBoundariesCount() const { return boundary.size(); } // \ru Выдать количество граничных двумерных кривых. \en Get the two-dimensional boundary curves count. + virtual MbContour & MakeContour( bool sense ) const; // \ru Выдать граничных двумерный контур. \en Get the two-dimensional boundary contour. + virtual MbCurve & MakeSegment( size_t i, bool sense ) const; // \ru Дать граничную двумерную кривую. \en Get the two-dimensional boundary curve. + + /// \ru Инициализация объекта по другому такому же. \en Initialization of a object by same other object. + void Init( const MbGridSurface & init ); + + /// \ru Выдать количество точек. \en Get the number of points. + size_t PointsCount() const { return points.size(); } + /// \ru Выдать количество нормалей. \en Get the number of normals. + size_t NormalsCount() const { return normals.size(); } + /// \ru Выдать количество параметров. \en Get the number of parameters. + size_t ParamsCount() const { return params.size(); } + // \ru Выдать количество треугольников. \en Get the number of triangles. + size_t TrianglesCount() const { return triangles.size(); } + // \ru Выдать количество граничных кривых. \en Get the number of boundary curves. + size_t BoundariesCount() const { return boundary.size(); } + + // \ru Добавить в контейнер параметры в опорных точках поверхности. \en Get the parameters to container. + void GetParams( std::vector & paramsVector ) const; + // \ru Добавить в контейнер опорные точки. \en Get the points to container. + void GetPoints( std::vector & pointsVector ) const; + // \ru Добавить в контейнер нормали в опорных точках. \en Add the normals to container. + void GetNormals( std::vector & normalsVector ) const; + // \ru Добавить в контейнер треугольники. \en Add the triangles to container. + void GetTriangles( std::vector & tVector ) const; + // \ru Добавить в контейнер треугольники. \en Add the triangles to container. + void GetTriangles( std::vector & tVector ) const; + // \ru Добавить в контейнер граничные кривые. \en Add the boundary curves of surface parameters region. + void GetBoundaries( std::vector & bVector ) const; + + /// \ru Создание поверхности. \en Creatying of surface. + template + static MbGridSurface * Create( const Params & _params + , const Points & _points + , const Normals & _normals + , const Triangles & _triangles + , const Bounds & _bounds ) + { + MbGridSurface * surface = NULL; + + const size_t itemsCnt = _params.size(); + + if ( (itemsCnt > 2) && (itemsCnt == _points.size()) && (_triangles.size() > 0) ) { + if ( (itemsCnt == _normals.size()) || (_normals.size() == 1) ) + surface = new MbGridSurface( _params, _points, _normals, _triangles, _bounds ); + } + return surface; + } + +private: + /// \ru Инициализация. \en Initialization. + void Init( bool bound = true ); + // \ru Инициализация граничных кривых. \en Initialization of boundary curves. + void MakeBoundary(); + // \ru Выдать треугольник. \en Get triangle. + MbTrigon & GetTriangle( size_t i ) { return triangles[i]; } + void PointOn ( double & u, double & v, bool ext, MbCartPoint3D & p ) const; // \ru Точка на поверхности \en Point on the surface + void DeriveU ( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Первая производная по u \en First derivative with respect to u + void DeriveV ( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Первая производная по v \en First derivative with respect to v + void DeriveUU ( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Вторая производная по u \en Second derivative with respect to u + void DeriveVV ( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Вторая производная по v \en Second derivative with respect to v + void DeriveUV ( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv + void DeriveUUU( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Третья производная \en Third derivative + void DeriveUUV( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Третья производная \en Third derivative + void DeriveUVV( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Третья производная \en Third derivative + void DeriveVVV( double & u, double & v, bool ext, MbVector3D & der ) const; // \ru Третья производная \en Third derivative + // \ru Выставить взаимные связи триангуляции. \en Set mutual connections of triangulation. + bool SetTrigonNeihbours(); + // \ru Поиск ближайшего треугольника для инициализации данных ячейки. \en Search nearest triangle for initialization of data of cell. + void FindNearest( size_t i, size_t j, std::vector & indecies, + double uDelta, double vDelta, double u, double v ); + // \ru Добавить ближайший треугольник в ячейку. \en Add nearest triangle to cell. + bool AddNearest( size_t i, size_t j, size_t ind ); + // \ru Вычислить индккс ближайшего треугольника и барицентрические координаты точки для него. \en Calculate barycentric coordinates of the nearest trianle. + size_t FindIndex( const double & u, const double & v, double & a, double & b, double & c, double & d ) const; + // \ru Расстояние до треугольника. \en The distance to a triangle. + double RangeToTriangle( size_t ind, const double & u, const double & v, double eps, + double & a, double & b, double & c, double & d ) const; + // \ru Расстояние до треугольника. \en The distance to a triangle. + double DistanceToTriangle( size_t ind, const double & u, const double & v, double eps, + MbCartPoint & p ) const; + // \ru Проверка параметров. \en Check parameters. + void CheckParam( double & u, double & v ) const; + // \ru Выдать данные триангуляции. \en Get triangulation data. + void GetTriangleData( size_t tIndex, + size_t & index1, size_t & index2, size_t & index3, + size_t & neigh1, size_t & neigh2, size_t & neigh3, + MbCartPoint & param1, MbCartPoint & param2, MbCartPoint & param3, + MbCartPoint3D & point1, MbCartPoint3D & point2, MbCartPoint3D & point3, + MbVector3D & normal1, MbVector3D & normal2, MbVector3D & normal3 ) const; + // \ru Выдать данные триангуляции соседнего треугольника. \en Get neighbour triangulation data. + bool GetNeighbourData( double u, double v, + size_t neigh1, size_t neigh2, size_t neigh3, + double & aCalc, double & bCalc, double & cCalc, double & deter, double & portion, + MbCartPoint & param1, MbCartPoint & param2, MbCartPoint & param3, + MbCartPoint3D & point1, MbCartPoint3D & point2, MbCartPoint3D & point3, + MbVector3D & normal1, MbVector3D & normal2, MbVector3D & normal3 ) const; + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. + void operator = ( const MbGridSurface & ); + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbGridSurface ) +}; // MbGridSurface + +IMPL_PERSISTENT_OPS( MbGridSurface ) + + +#endif // __SURF_GRID_SURFACE_H diff --git a/C3d/Include/surf_join_surface.h b/C3d/Include/surf_join_surface.h index f49b465..f8cae00 100644 --- a/C3d/Include/surf_join_surface.h +++ b/C3d/Include/surf_join_surface.h @@ -1,421 +1,421 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Поверхность соединения. - \en The surface of the joint. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __SURF_JOIN_SURFACE_H -#define __SURF_JOIN_SURFACE_H - - -#include -#include -#include - - -class MATH_CLASS MbSurfaceCurve; -class MATH_CLASS MbProperties; - - -//------------------------------------------------------------------------------ -/** \brief \ru Поверхность соединения. - \en The surface of the joint. \~ - \details \ru Поверхность представляет собой сплайновую поверхность, - натянутую на набор заданных однонаправленных кривых. - Кривые не должны пересекаться или касаться друг друга. - Касание или пересечение кривых допустимо только в конечных точках. - Координата u изменяется вдоль заданных кривых в соответствии с параметризацией каждой кривой. - Значение координаты v вдоль каждой кривой постоянно. - В направлении координаты v поверхность строится аналогично NURBS кривой с заданной степенью, узловым вектором и - использующей в качестве узлов точки заданных кривых, вычисленных с одинаковыми параметрами u. - Поверхность используется для гладкого соединения краёв двух поверхностей. - \en The surface is spline surface - tensed on a set of the given unidirectional curves. - Curves shouldn't be intersected or concerned each other. - Tangency or intersection of curves is acceptable only at the end points. - Coordinate u is changed along given curves according to parameterization of each curve. - Value of coordinate v along each curve is constant. - In the direction of v coordinate the surface is constructed similar to NURBS curve with the given degree, a nodal vector and - points of the given curves calculated with identical parameters u are used as knots. - Surface is used for smooth connection of boundaries of two surfaces. \~ - \ingroup Surfaces -*/ -// --- -class MATH_CLASS MbJoinSurface : public MbSurface { -protected: - RPArray curves; ///< \ru Набор кривых для построения поверхности. \en Set of curves to construct surface. - SArray knots; ///< \ru Значения узлов для сплайна по v. \en Knot values for spline by v. - ptrdiff_t degree; ///< \ru Степень сплайна по v. \en Order of spline by v. - double umin; ///< \ru Минимальное значение параметра u. \en Minimal value of parameter u. - double umax; ///< \ru Максимальное значение параметра u. \en Maximal value of parameter u. - bool closedU; ///< \ru Замкнутость по u. \en Closedness by u. - bool closedV; ///< \ru Замкнутость по V. \en Closedness by V. - bool isPoleUmin; ///< \ru Полюс при u == umin. \en Pole at u == umin. - bool isPoleUmax; ///< \ru Полюс при u == umax. \en Pole at u == umax. - bool isPoleVmin; ///< \ru Полюс при u == vmin. \en Pole at u == vmin. - bool isPoleVmax; ///< \ru Полюс при u == vmax. \en Pole at u == vmax. - -private: - //------------------------------------------------------------------------------ - /** \brief \ru Вспомогательные данные. - \en Auxiliary data. \~ - \details \ru Вспомогательные данные служат для ускорения работы объекта. - \en Auxiliary data are used for fast calculations. \n \~ - */ - // --- - class MbJoinSurfaceAuxiliaryData : public AuxiliaryData { - public: - double calcU; ///< \ru Последнее обработанное значение u. \en Last processed value of u. - double calcV; ///< \ru Последнее обработанное значение v. \en Last processed value of v. - ptrdiff_t lastIndex; ///< \ru Левый индекс узлового вектора из последних вычислений. \en Left index of knot vector from last calculations. - double ** points; ///< \ru 2-х мерный массив для хранения данных по точкам для текущих вычислений. \en Two-dimensional array to store points data for current calculations. - double ** nMatrix; ///< \ru Матрица коэффициентов для NURBS. \en Matrix of coefficients for NURBS. - SArray tempPoints; ///< \ru Множество рабочих точек. \en Set of working points. - SArray tempVectors; ///< \ru Множество рабочих векторов. \en Set of working vectors. - SArray readyData; ///< \ru Множество для хранения вычисленных значений точки и производных. \en Set to store calculated values of point and derivatives. - - // \ru Рабочие указатели для создания базисных сплайнов \en Working pointers for creation of basis splines - double * m_left; ///< \ru Рабочие указатели для создания базисных сплайнов. \en Working pointers for creation of basis splines. - double * m_right; ///< \ru Рабочие указатели для создания базисных сплайнов. \en Working pointers for creation of basis splines. - ptrdiff_t *degree; ///< \ru Степень сплайна по v. \en Order of spline by v. - MbJoinSurfaceAuxiliaryData(); - MbJoinSurfaceAuxiliaryData( const MbJoinSurfaceAuxiliaryData & init ); - virtual ~MbJoinSurfaceAuxiliaryData(); - void CreateVars(); - void InitVars (); - void FreeVars (); - private: - void operator = ( const MbJoinSurfaceAuxiliaryData & ); - }; - mutable CacheManager cache; - -protected: - /// \ru Конструктор копирования. \en Copy-constructor. - MbJoinSurface( const MbJoinSurface &, MbRegDuplicate * ); -private: - MbJoinSurface( const MbJoinSurface & ); // \ru Не реализовано. \en Not implemented. -public: - /** \brief \ru Конструктор поверхности соединения. - \en Constructor of surface of the joint. \~ - \details \ru Конструктор поверхности соединения по набору кривых. Кривые должны быть непересекающиеся.\n - В конструкторе этот факт не проверяется.\n - \en Constructor of surface of the joint by set of curves. Curves shouldn't be intersected.\n - In constructor this fact doesn't checked.\n \~ - \param[in] initCurves - \ru Список кривых, на которых натягивается поверхность. - \en List of of curves which the surface is tensed on. \~ - \param[in] sameCurves - \ru Определяет, надо ли делать копии кривых:\n - true - использовать в объекте пришедшие в конструктор кривые не дублируя,\n - false - использовать копии кривых. - \en Determines whether to copy curves:\n - true - use curves given in the constructor in object without copying,\n - false - use copies of curves. \~ - */ - MbJoinSurface( const RPArray & initCurves, bool sameCurves ); - /** \brief \ru Конструктор поверхности соединения. - \en Constructor of surface of the joint. \~ - \details \ru Конструктор поверхности соединения по набору кривых и порядку поверхности. Кривые должны быть непересекающиеся.\n - В конструкторе этот факт не проверяется. - \en Constructor of surface of the joint by set of curves and order of surface. Curves shouldn't be intersected.\n - In constructor this fact doesn't checked. \~ - \param[in] initCurves - \ru Список кривых, на которых натягивается поверхность. - \en List of of curves which the surface is tensed on. \~ - \param[in] initDegree - \ru Порядок поверхности по v. - \en Surface order by v. \~ - \param[in] sameCurves - \ru Определяет, надо ли делать копии кривых:\n - true - использовать в объекте пришедшие в конструктор кривые не дублируя,\n - false - использовать копии кривых. - \en Determines whether to copy curves:\n - true - use curves given in the constructor in object without copying,\n - false - use copies of curves. \~ - */ - MbJoinSurface( const RPArray & initCurves, ptrdiff_t initDegree, bool sameCurves ); - /** \brief \ru Конструктор поверхности соединения. - \en Constructor of surface of the joint. \~ - \details \ru Конструктор поверхности соединения по набору кривых, порядку поверхности и узловому вектору.\n - Кривые должны быть непересекающиеся. В конструкторе этот факт не проверяется. - \en Constructor of surface of the joint by set of curves, order of surface and knot vector.\n - Curves shouldn't be intersected. In constructor this fact doesn't checked. \~ - \param[in] initCurves - \ru Список кривых, на которых натягивается поверхность. - \en List of of curves which the surface is tensed on. \~ - \param[in] initDegree - \ru Порядок поверхности по v. - \en Surface order by v. \~ - \param[in] initKnots - \ru Узловой вектор по v. - \en A knot vector by v. \~ - \param[in] sameCurves - \ru Определяет, надо ли делать копии кривых:\n - true - использовать в объекте пришедшие в конструктор кривые не дублируя,\n - false - использовать копии кривых. - \en Determines whether to copy curves:\n - true - use curves given in the constructor in object without copying,\n - false - use copies of curves. \~ - */ - MbJoinSurface( const RPArray & initCurves, ptrdiff_t initDegree, const SArray & initKnots, bool sameCurves ); - -public: - virtual ~MbJoinSurface(); - -public: - VISITING_CLASS( MbJoinSurface ); - -public: - /** \brief \ru Инициализация поверхности соединения. - \en Initialization of surface of the joint. \~ - \details \ru Инициализация поверхности соединения по набору кривых. Кривые должны быть непересекающиеся.\n - Этот факт в функции не проверяется. Порядок поверхности не изменяется. - \en Initialization of surface of the joint by set of curves. Curves shouldn't be intersected.\n - This fact isn't checked in the function. Surface order doesn't changed. \~ - \param[in] initCurves - \ru Список кривых, на которых натягивается поверхность. - \en List of of curves which the surface is tensed on. \~ - \param[in] sameCurves - \ru Определяет, надо ли делать копии кривых:\n - true - использовать в объекте пришедшие в конструктор кривые не дублируя,\n - false - использовать копии кривых. - \en Determines whether to copy curves:\n - true - use curves given in the constructor in object without copying,\n - false - use copies of curves. \~ - */ - void Init( const RPArray & initCurves, bool sameCurves ); - /** \brief \ru Инициализация поверхности соединения. - \en Initialization of surface of the joint. \~ - \details \ru Инициализация поверхности соединения по набору кривых и порядку поверхности. Кривые должны быть непересекающиеся.\n - Этот факт в функции не проверяется. Порядок поверхности не изменяется. - \en Initialization of surface of the joint by set of curves and order of surface. Curves shouldn't be intersected.\n - This fact isn't checked in the function. Surface order doesn't changed. \~ - \param[in] initCurves - \ru Список кривых, на которых натягивается поверхность. - \en List of of curves which the surface is tensed on. \~ - \param[in] initDegree - \ru Порядок поверхности по v.\n - \en Surface order by v.\n \~ - \param[in] sameCurves - \ru Определяет, надо ли делать копии кривых:\n - true - использовать в объекте пришедшие в конструктор кривые не дублируя,\n - false - использовать копии кривых. - \en Determines whether to copy curves:\n - true - use curves given in the constructor in object without copying,\n - false - use copies of curves. \~ - */ - bool Init( const RPArray & initCurves, ptrdiff_t initDegree, bool sameCurves ); - /** \brief \ru Инициализация поверхности соединения. - \en Initialization of surface of the joint. \~ - \details \ru Инициализация поверхности соединения по набору кривых, порядку поверхности и узловому вектору.\n - Кривые должны быть непересекающиеся. Этот факт в функции не проверяется. Порядок поверхности не изменяется. - \en Initialization of surface of the joint by set of curves, order of surface and knot vector.\n - Curves shouldn't be intersected. This fact isn't checked in the function. Surface order doesn't changed. \~ - \param[in] initCurves - \ru Список кривых, на которых натягивается поверхность. - \en List of of curves which the surface is tensed on. \~ - \param[in] initDegree - \ru Порядок поверхности по v. - \en Surface order by v. \~ - \param[in] initKnots - \ru Узловой вектор по v. - \en A knot vector by v. \~ - \param[in] sameCurves - \ru Определяет, надо ли делать копии кривых:\n - true - использовать в объекте пришедшие в конструктор кривые не дублируя,\n - false - использовать копии кривых. - \en Determines whether to copy curves:\n - true - use curves given in the constructor in object without copying,\n - false - use copies of curves. \~ - */ - bool Init( const RPArray & initCurves, ptrdiff_t initDegree, const SArray & initKnots, bool sameCurves ); - - /** \ru \name Общие функции геометрического объекта - \en \name Common functions of a geometric object - \{ */ - virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными \en Determine whether objects are equal - virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным \en Make equal - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis - - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the base objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - /** \} */ - - /** \ru \name Функции описания области определения поверхности - \en \name Functions for surface domain description - \{ */ - virtual double GetUMin() const; // \ru Минимальное значение параметра u \en Minimal value of parameter u - virtual double GetVMin() const; // \ru Минимальное значение параметра v \en Minimal value of parameter v - virtual double GetUMax() const; // \ru Максимальное значение параметра u \en Maximal value of parameter u - virtual double GetVMax() const; // \ru Максимальное значение параметра v \en Maximal value of parameter v - virtual bool IsUClosed() const; // \ru Замкнута ли поверхность по параметру u. \en Whether the surface is closed by parameter u. - virtual bool IsVClosed() const; // \ru Замкнута ли поверхность по параметру v. \en Whether the surface is closed by parameter v. - virtual bool GetPoleUMin() const; - virtual bool GetPoleUMax() const; - virtual bool GetPoleVMin() const; - virtual bool GetPoleVMax() const; - virtual bool IsPole( double u, double v ) const; // \ru Является ли точка особенной \en Whether the point is special - /** \} */ - - /** \ru \name Функции для работы в области определения поверхности - Функции PointOn, Derive... поверхностей корректируют параметры - при выходе их за пределы прямоугольной области определения параметров.\n - \en \name Functions for working at surface domain - Functions PointOn, Derive... of surfaces correct parameters - when they are out of bounds of rectangular domain of parameters.\n - \{ */ - virtual void PointOn ( double & u, double & v, MbCartPoint3D & ) const; // \ru Точка на поверхности \en Point on the surface - virtual void DeriveU ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по u \en First derivative with respect to u - virtual void DeriveV ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по v \en First derivative with respect to v - virtual void DeriveUU ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по u \en Second derivative with respect to u - virtual void DeriveVV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по v \en Second derivative with respect to v - virtual void DeriveUV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная \en The second derivative - virtual void DeriveUUU( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative - virtual void DeriveUUV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative - virtual void DeriveUVV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative - virtual void DeriveVVV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative - virtual void Normal ( double & u, double & v, MbVector3D & ) const; // \ru Нормаль \en Normal - /** \} */ - - /** \ru \name Функции для работы внутри и вне области определения поверхности - функции _PointOn, _Derive... поверхностей не корректируют - параметры при выходе их за пределы прямоугольной области определения параметров. - \en \name Functions for working inside and outside the surface's domain - functions _PointOn, _Derive... of surfaces don't correct - parameters when they are out of bounds of rectangular domain of parameters. - \{ */ - virtual void _PointOn ( double u, double v, MbCartPoint3D & p ) const; // \ru Точка на поверхности \en Point on the surface - virtual void _DeriveU ( double u, double v, MbVector3D & p ) const; // \ru Первая производная по u \en First derivative with respect to u - virtual void _DeriveV ( double u, double v, MbVector3D & p ) const; // \ru Первая производная по v \en First derivative with respect to v - virtual void _DeriveUU ( double u, double v, MbVector3D & p ) const; // \ru Вторая производная по u \en Second derivative with respect to u - virtual void _DeriveVV ( double u, double v, MbVector3D & p ) const; // \ru Вторая производная по v \en Second derivative with respect to v - virtual void _DeriveUV ( double u, double v, MbVector3D & p ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv - virtual void _DeriveUUU( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative - virtual void _DeriveUUV( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative - virtual void _DeriveUVV( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative - virtual void _DeriveVVV( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative - virtual void _Normal ( double u, double v, MbVector3D & ) const; // \ru Нормаль \en Normal - /** \} */ - - /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. - \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. - \{ */ - virtual void Explore( double & u, double & v, bool ext, - MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, - MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; - /** \} */ - - /** \ru \name Функции движения по поверхности - \en \name Functions of moving along the surface - \{ */ - virtual double StepU ( double u, double v, double sag ) const; // \ru Вычисление шага параметра u по по величине прогиба \en Calculation of parameter u step by the value of sag - virtual double StepV ( double u, double v, double sag ) const; // \ru Вычисление шага параметра v по по величине прогиба \en Calculation of parameter v step by the value of sag - virtual double DeviationStepU( double u, double v, double angle ) const; // \ru Вычисление шага параметра u по углу отклонения нормали \en Calculation of parameter u step by the angle of deviation of normal - virtual double DeviationStepV( double u, double v, double angle ) const; // \ru Вычисление шага параметра v по углу отклонения нормали \en Calculation of parameter v step by the angle of deviation of normal - virtual double MetricStepU ( double u, double v, double length ) const; // \ru Вычисление шага параметра u по заданной метрической длине \en Calculation of parameter u step by the given metric length - virtual size_t GetUCount() const; // \ru Количество разбиений по параметру u для проверки событий \en Count of splittings by parameter u to check for events - virtual size_t GetVCount() const; // \ru Количество разбиений по параметру v для проверки событий \en Count of splittings by parameter v to check for events - /** \} */ - - /** \ru \name Общие функции поверхности - \en \name Common functions of surface - \{ */ - virtual void Refresh (); ///< \ru Cбросить все временные данные. \en Reset all temporary data. - - virtual MbSplineSurface * NurbsSurface( double u1, double u2, double v1, double v2, bool bmatch = false ) const; - virtual MbSurface * NurbsSurface( const MbNurbsParameters & uParam, const MbNurbsParameters & vParam ) const; - virtual MbSurface * Offset( double d, bool same ) const; // \ru Построить смещенную поверхность \en Create a shifted surface - - /// \ru Изменение степени NURBS кривой по v. \en Change degree of NURBS curve by v. - void ChangeDegree ( ptrdiff_t newDegree ); - /// \ru Получить количество базовых кривых. \en Get count of base curves. - size_t GetCurvesCount () const; - /// \ru Получить кривую с индeксом num. \en Get curve with 'num' index. - const MbCurve3D * GetCurve( size_t k ) const; - const SArray & GetKnots() const { return knots; } ///< \ru Получить значения узлов для сплайна по v. \en Get knot values for spline by v. - - /** \brief \ru Получить список начальных или конечных базовых точек кривых. - \en Get list of start or end base points of curves. \~ - \details \ru Получить список начальных или конечных базовых точек кривых.\n - \en Get list of start or end base points of curves.\n \~ - \param[in] isFirstPoints - \ru Определяет конечные или начальные точки запрошены: true - начальные, false - конечные.\n - \en Determines start or end points were requested: true - start, false - end.\n \~ - \param[in] points - \ru Список, в который помещаются найденные точки. \n - Порядок точек соответствует порядку кривых в списке curves. - \en List to store found points. \n - Order of points corresponds to order of curves in 'curves' list. \~ - \return \ru false и список points остается пустым,\n - если хотя бы одна кривая не имеет базовых точек (не отрезок и не кривая, заданная точками). - \en False then 'points' list remains empty,\n - if at least one curve has no base points (not segment and not curve given by points). \~ - */ - bool GetCurvesBasePoints( bool isFirstPoints, SArray & points ) const; - /** \brief \ru Изменить крайние базовые точки кривых. - \en Change end base points of curves. \~ - \details \ru Базовые точки можно изменить в том случае, если все кривые, на которые натянута поверхность,\n - являются отрезками или кривыми, заданными точками. - \en Base points can be changed in case of all curves which the surface is tensed on\n - are segments or curves given by points. \~ - \param[in] isFirstPoints - \ru Определяет конечные или начальные точки запрошены: true - начальные, false - конечные.\n - \en Determines start or end points were requested: true - start, false - end.\n \~ - \param[in] points - \ru Список, в который помещаются новые значения базовых точек.\n - Порядок точек соответствует порядку кривых в списке curves. - \en List to store new values of base points.\n - Order of points corresponds to order of curves in 'curves' list. \~ - \return \ru false и список points остается пустым,\n - если хотя бы одна кривая не имеет базовых точек (не отрезок и не кривая, заданная точками). - \en False then 'points' list remains empty,\n - if at least one curve has no base points (not segment and not curve given by points). \~ - */ - bool SetCurvesBasePoints( bool isFirstPoints, SArray & points ); - /** \} */ - - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbJoinSurface ) - -private: - void operator = ( const MbJoinSurface & ); // \ru Не реализовано. \en Not implemented. - - void ResetTCalc(); - bool CheckData ( const SArray & newKnots, ptrdiff_t newDegree ); // \ru Проверить корректность данных для NURBS \en Check correctness of data for NURBS - void ChangeKnots ( const ptrdiff_t newDegree, const bool closed, SArray & newKnots ); // \ru Изменить массив узлов, если изменилась степень сплайна \en Change array of knots if degree of spline was changed - void CreateTempVars( MbJoinSurfaceAuxiliaryData * ucache ) const; - void InitTempVars ( MbJoinSurfaceAuxiliaryData * ucache ) const; - void FreeTempVars ( MbJoinSurfaceAuxiliaryData * ucache ) const; - void PreparePointsData( ptrdiff_t lIndex, ptrdiff_t derNum, MbJoinSurfaceAuxiliaryData * ucache ) const; - void PreparePointList ( const double u, ptrdiff_t derNumberU, MbJoinSurfaceAuxiliaryData * ucache ) const; - void CheckPointData ( const MbeSurfaceDerivativeType derUVNumber, double & u, double & v, MbVector3D & vect, MbJoinSurfaceAuxiliaryData * ucache ) const; - ptrdiff_t GetUDerNumber( const MbeSurfaceDerivativeType derUVNumber ) const; - ptrdiff_t GetVDerNumber( const MbeSurfaceDerivativeType derUVNumber ) const; - void CheckPole(); - void CheckParams ( double & u, double & v ) const; - void PoleDerive ( double u, double v, MbVector3D & vDerU, MbVector3D & vDerV ) const; - double DeviationStep( double u, double v, double angle ) const; - double StepD ( double u, double v, double sag, bool checkAngle, double angle ) const; - // \ru Вычисление точки и производных поверхности. \en Calculation of the point and derivatives of the surface. \~ - void ExploreVector( SArray & points, SArray & vectors, - ptrdiff_t lIndex, ptrdiff_t derNum, MbVector3D & vect, MbJoinSurfaceAuxiliaryData * ucache ) const; - -}; - -IMPL_PERSISTENT_OPS( MbJoinSurface ) - -//------------------------------------------------------------------------------ -// \ru Проверить параметры и в случае захода за полюс загнать в полюсную область \en Check parameters and if it is out of pole, then drive it to pole region -// --- -inline void MbJoinSurface::CheckParams( double & u, double & v ) const -{ - if ( isPoleUmin ) { - if ( u < umin ) - u = umin; - } - if ( isPoleUmax ) { - if ( u > umax ) - u = umax; - } - if ( isPoleVmin ) { - const double & vmin = knots[degree - 1]; - if ( v < vmin ) - v = vmin; - } - if ( isPoleVmax ) { - const double & vmax = knots[knots.MaxIndex() - degree + 1]; - if ( v > vmax ) - v = vmax; - } -} - - -#endif // __SURF_JOIN_SURFACE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Поверхность соединения. + \en The surface of the joint. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __SURF_JOIN_SURFACE_H +#define __SURF_JOIN_SURFACE_H + + +#include +#include +#include + + +class MATH_CLASS MbSurfaceCurve; +class MATH_CLASS MbProperties; + + +//------------------------------------------------------------------------------ +/** \brief \ru Поверхность соединения. + \en The surface of the joint. \~ + \details \ru Поверхность представляет собой сплайновую поверхность, + натянутую на набор заданных однонаправленных кривых. + Кривые не должны пересекаться или касаться друг друга. + Касание или пересечение кривых допустимо только в конечных точках. + Координата u изменяется вдоль заданных кривых в соответствии с параметризацией каждой кривой. + Значение координаты v вдоль каждой кривой постоянно. + В направлении координаты v поверхность строится аналогично NURBS кривой с заданной степенью, узловым вектором и + использующей в качестве узлов точки заданных кривых, вычисленных с одинаковыми параметрами u. + Поверхность используется для гладкого соединения краёв двух поверхностей. + \en The surface is spline surface + tensed on a set of the given unidirectional curves. + Curves shouldn't be intersected or concerned each other. + Tangency or intersection of curves is acceptable only at the end points. + Coordinate u is changed along given curves according to parameterization of each curve. + Value of coordinate v along each curve is constant. + In the direction of v coordinate the surface is constructed similar to NURBS curve with the given degree, a nodal vector and + points of the given curves calculated with identical parameters u are used as knots. + Surface is used for smooth connection of boundaries of two surfaces. \~ + \ingroup Surfaces +*/ +// --- +class MATH_CLASS MbJoinSurface : public MbSurface { +protected: + RPArray curves; ///< \ru Набор кривых для построения поверхности. \en Set of curves to construct surface. + SArray knots; ///< \ru Значения узлов для сплайна по v. \en Knot values for spline by v. + ptrdiff_t degree; ///< \ru Степень сплайна по v. \en Order of spline by v. + double umin; ///< \ru Минимальное значение параметра u. \en Minimal value of parameter u. + double umax; ///< \ru Максимальное значение параметра u. \en Maximal value of parameter u. + bool closedU; ///< \ru Замкнутость по u. \en Closedness by u. + bool closedV; ///< \ru Замкнутость по V. \en Closedness by V. + bool isPoleUmin; ///< \ru Полюс при u == umin. \en Pole at u == umin. + bool isPoleUmax; ///< \ru Полюс при u == umax. \en Pole at u == umax. + bool isPoleVmin; ///< \ru Полюс при u == vmin. \en Pole at u == vmin. + bool isPoleVmax; ///< \ru Полюс при u == vmax. \en Pole at u == vmax. + +private: + //------------------------------------------------------------------------------ + /** \brief \ru Вспомогательные данные. + \en Auxiliary data. \~ + \details \ru Вспомогательные данные служат для ускорения работы объекта. + \en Auxiliary data are used for fast calculations. \n \~ + */ + // --- + class MbJoinSurfaceAuxiliaryData : public AuxiliaryData { + public: + double calcU; ///< \ru Последнее обработанное значение u. \en Last processed value of u. + double calcV; ///< \ru Последнее обработанное значение v. \en Last processed value of v. + ptrdiff_t lastIndex; ///< \ru Левый индекс узлового вектора из последних вычислений. \en Left index of knot vector from last calculations. + double ** points; ///< \ru 2-х мерный массив для хранения данных по точкам для текущих вычислений. \en Two-dimensional array to store points data for current calculations. + double ** nMatrix; ///< \ru Матрица коэффициентов для NURBS. \en Matrix of coefficients for NURBS. + SArray tempPoints; ///< \ru Множество рабочих точек. \en Set of working points. + SArray tempVectors; ///< \ru Множество рабочих векторов. \en Set of working vectors. + SArray readyData; ///< \ru Множество для хранения вычисленных значений точки и производных. \en Set to store calculated values of point and derivatives. + + // \ru Рабочие указатели для создания базисных сплайнов \en Working pointers for creation of basis splines + double * m_left; ///< \ru Рабочие указатели для создания базисных сплайнов. \en Working pointers for creation of basis splines. + double * m_right; ///< \ru Рабочие указатели для создания базисных сплайнов. \en Working pointers for creation of basis splines. + ptrdiff_t *degree; ///< \ru Степень сплайна по v. \en Order of spline by v. + MbJoinSurfaceAuxiliaryData(); + MbJoinSurfaceAuxiliaryData( const MbJoinSurfaceAuxiliaryData & init ); + virtual ~MbJoinSurfaceAuxiliaryData(); + void CreateVars(); + void InitVars (); + void FreeVars (); + private: + void operator = ( const MbJoinSurfaceAuxiliaryData & ); + }; + mutable CacheManager cache; + +protected: + /// \ru Конструктор копирования. \en Copy-constructor. + MbJoinSurface( const MbJoinSurface &, MbRegDuplicate * ); +private: + MbJoinSurface( const MbJoinSurface & ); // \ru Не реализовано. \en Not implemented. +public: + /** \brief \ru Конструктор поверхности соединения. + \en Constructor of surface of the joint. \~ + \details \ru Конструктор поверхности соединения по набору кривых. Кривые должны быть непересекающиеся.\n + В конструкторе этот факт не проверяется.\n + \en Constructor of surface of the joint by set of curves. Curves shouldn't be intersected.\n + In constructor this fact doesn't checked.\n \~ + \param[in] initCurves - \ru Список кривых, на которых натягивается поверхность. + \en List of of curves which the surface is tensed on. \~ + \param[in] sameCurves - \ru Определяет, надо ли делать копии кривых:\n + true - использовать в объекте пришедшие в конструктор кривые не дублируя,\n + false - использовать копии кривых. + \en Determines whether to copy curves:\n + true - use curves given in the constructor in object without copying,\n + false - use copies of curves. \~ + */ + MbJoinSurface( const RPArray & initCurves, bool sameCurves ); + /** \brief \ru Конструктор поверхности соединения. + \en Constructor of surface of the joint. \~ + \details \ru Конструктор поверхности соединения по набору кривых и порядку поверхности. Кривые должны быть непересекающиеся.\n + В конструкторе этот факт не проверяется. + \en Constructor of surface of the joint by set of curves and order of surface. Curves shouldn't be intersected.\n + In constructor this fact doesn't checked. \~ + \param[in] initCurves - \ru Список кривых, на которых натягивается поверхность. + \en List of of curves which the surface is tensed on. \~ + \param[in] initDegree - \ru Порядок поверхности по v. + \en Surface order by v. \~ + \param[in] sameCurves - \ru Определяет, надо ли делать копии кривых:\n + true - использовать в объекте пришедшие в конструктор кривые не дублируя,\n + false - использовать копии кривых. + \en Determines whether to copy curves:\n + true - use curves given in the constructor in object without copying,\n + false - use copies of curves. \~ + */ + MbJoinSurface( const RPArray & initCurves, ptrdiff_t initDegree, bool sameCurves ); + /** \brief \ru Конструктор поверхности соединения. + \en Constructor of surface of the joint. \~ + \details \ru Конструктор поверхности соединения по набору кривых, порядку поверхности и узловому вектору.\n + Кривые должны быть непересекающиеся. В конструкторе этот факт не проверяется. + \en Constructor of surface of the joint by set of curves, order of surface and knot vector.\n + Curves shouldn't be intersected. In constructor this fact doesn't checked. \~ + \param[in] initCurves - \ru Список кривых, на которых натягивается поверхность. + \en List of of curves which the surface is tensed on. \~ + \param[in] initDegree - \ru Порядок поверхности по v. + \en Surface order by v. \~ + \param[in] initKnots - \ru Узловой вектор по v. + \en A knot vector by v. \~ + \param[in] sameCurves - \ru Определяет, надо ли делать копии кривых:\n + true - использовать в объекте пришедшие в конструктор кривые не дублируя,\n + false - использовать копии кривых. + \en Determines whether to copy curves:\n + true - use curves given in the constructor in object without copying,\n + false - use copies of curves. \~ + */ + MbJoinSurface( const RPArray & initCurves, ptrdiff_t initDegree, const SArray & initKnots, bool sameCurves ); + +public: + virtual ~MbJoinSurface(); + +public: + VISITING_CLASS( MbJoinSurface ); + +public: + /** \brief \ru Инициализация поверхности соединения. + \en Initialization of surface of the joint. \~ + \details \ru Инициализация поверхности соединения по набору кривых. Кривые должны быть непересекающиеся.\n + Этот факт в функции не проверяется. Порядок поверхности не изменяется. + \en Initialization of surface of the joint by set of curves. Curves shouldn't be intersected.\n + This fact isn't checked in the function. Surface order doesn't changed. \~ + \param[in] initCurves - \ru Список кривых, на которых натягивается поверхность. + \en List of of curves which the surface is tensed on. \~ + \param[in] sameCurves - \ru Определяет, надо ли делать копии кривых:\n + true - использовать в объекте пришедшие в конструктор кривые не дублируя,\n + false - использовать копии кривых. + \en Determines whether to copy curves:\n + true - use curves given in the constructor in object without copying,\n + false - use copies of curves. \~ + */ + void Init( const RPArray & initCurves, bool sameCurves ); + /** \brief \ru Инициализация поверхности соединения. + \en Initialization of surface of the joint. \~ + \details \ru Инициализация поверхности соединения по набору кривых и порядку поверхности. Кривые должны быть непересекающиеся.\n + Этот факт в функции не проверяется. Порядок поверхности не изменяется. + \en Initialization of surface of the joint by set of curves and order of surface. Curves shouldn't be intersected.\n + This fact isn't checked in the function. Surface order doesn't changed. \~ + \param[in] initCurves - \ru Список кривых, на которых натягивается поверхность. + \en List of of curves which the surface is tensed on. \~ + \param[in] initDegree - \ru Порядок поверхности по v.\n + \en Surface order by v.\n \~ + \param[in] sameCurves - \ru Определяет, надо ли делать копии кривых:\n + true - использовать в объекте пришедшие в конструктор кривые не дублируя,\n + false - использовать копии кривых. + \en Determines whether to copy curves:\n + true - use curves given in the constructor in object without copying,\n + false - use copies of curves. \~ + */ + bool Init( const RPArray & initCurves, ptrdiff_t initDegree, bool sameCurves ); + /** \brief \ru Инициализация поверхности соединения. + \en Initialization of surface of the joint. \~ + \details \ru Инициализация поверхности соединения по набору кривых, порядку поверхности и узловому вектору.\n + Кривые должны быть непересекающиеся. Этот факт в функции не проверяется. Порядок поверхности не изменяется. + \en Initialization of surface of the joint by set of curves, order of surface and knot vector.\n + Curves shouldn't be intersected. This fact isn't checked in the function. Surface order doesn't changed. \~ + \param[in] initCurves - \ru Список кривых, на которых натягивается поверхность. + \en List of of curves which the surface is tensed on. \~ + \param[in] initDegree - \ru Порядок поверхности по v. + \en Surface order by v. \~ + \param[in] initKnots - \ru Узловой вектор по v. + \en A knot vector by v. \~ + \param[in] sameCurves - \ru Определяет, надо ли делать копии кривых:\n + true - использовать в объекте пришедшие в конструктор кривые не дублируя,\n + false - использовать копии кривых. + \en Determines whether to copy curves:\n + true - use curves given in the constructor in object without copying,\n + false - use copies of curves. \~ + */ + bool Init( const RPArray & initCurves, ptrdiff_t initDegree, const SArray & initKnots, bool sameCurves ); + + /** \ru \name Общие функции геометрического объекта + \en \name Common functions of a geometric object + \{ */ + virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными \en Determine whether objects are equal + virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным \en Make equal + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis + + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты \en Get the base objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + /** \} */ + + /** \ru \name Функции описания области определения поверхности + \en \name Functions for surface domain description + \{ */ + virtual double GetUMin() const; // \ru Минимальное значение параметра u \en Minimal value of parameter u + virtual double GetVMin() const; // \ru Минимальное значение параметра v \en Minimal value of parameter v + virtual double GetUMax() const; // \ru Максимальное значение параметра u \en Maximal value of parameter u + virtual double GetVMax() const; // \ru Максимальное значение параметра v \en Maximal value of parameter v + virtual bool IsUClosed() const; // \ru Замкнута ли поверхность по параметру u. \en Whether the surface is closed by parameter u. + virtual bool IsVClosed() const; // \ru Замкнута ли поверхность по параметру v. \en Whether the surface is closed by parameter v. + virtual bool GetPoleUMin() const; + virtual bool GetPoleUMax() const; + virtual bool GetPoleVMin() const; + virtual bool GetPoleVMax() const; + virtual bool IsPole( double u, double v ) const; // \ru Является ли точка особенной \en Whether the point is special + /** \} */ + + /** \ru \name Функции для работы в области определения поверхности + Функции PointOn, Derive... поверхностей корректируют параметры + при выходе их за пределы прямоугольной области определения параметров.\n + \en \name Functions for working at surface domain + Functions PointOn, Derive... of surfaces correct parameters + when they are out of bounds of rectangular domain of parameters.\n + \{ */ + virtual void PointOn ( double & u, double & v, MbCartPoint3D & ) const; // \ru Точка на поверхности \en Point on the surface + virtual void DeriveU ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по u \en First derivative with respect to u + virtual void DeriveV ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по v \en First derivative with respect to v + virtual void DeriveUU ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по u \en Second derivative with respect to u + virtual void DeriveVV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по v \en Second derivative with respect to v + virtual void DeriveUV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная \en The second derivative + virtual void DeriveUUU( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative + virtual void DeriveUUV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative + virtual void DeriveUVV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative + virtual void DeriveVVV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная \en Third derivative + virtual void Normal ( double & u, double & v, MbVector3D & ) const; // \ru Нормаль \en Normal + /** \} */ + + /** \ru \name Функции для работы внутри и вне области определения поверхности + функции _PointOn, _Derive... поверхностей не корректируют + параметры при выходе их за пределы прямоугольной области определения параметров. + \en \name Functions for working inside and outside the surface's domain + functions _PointOn, _Derive... of surfaces don't correct + parameters when they are out of bounds of rectangular domain of parameters. + \{ */ + virtual void _PointOn ( double u, double v, MbCartPoint3D & p ) const; // \ru Точка на поверхности \en Point on the surface + virtual void _DeriveU ( double u, double v, MbVector3D & p ) const; // \ru Первая производная по u \en First derivative with respect to u + virtual void _DeriveV ( double u, double v, MbVector3D & p ) const; // \ru Первая производная по v \en First derivative with respect to v + virtual void _DeriveUU ( double u, double v, MbVector3D & p ) const; // \ru Вторая производная по u \en Second derivative with respect to u + virtual void _DeriveVV ( double u, double v, MbVector3D & p ) const; // \ru Вторая производная по v \en Second derivative with respect to v + virtual void _DeriveUV ( double u, double v, MbVector3D & p ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv + virtual void _DeriveUUU( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative + virtual void _DeriveUUV( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative + virtual void _DeriveUVV( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative + virtual void _DeriveVVV( double u, double v, MbVector3D & p ) const; // \ru Третья производная \en Third derivative + virtual void _Normal ( double u, double v, MbVector3D & ) const; // \ru Нормаль \en Normal + /** \} */ + + /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. + \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. + \{ */ + virtual void Explore( double & u, double & v, bool ext, + MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, + MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; + /** \} */ + + /** \ru \name Функции движения по поверхности + \en \name Functions of moving along the surface + \{ */ + virtual double StepU ( double u, double v, double sag ) const; // \ru Вычисление шага параметра u по по величине прогиба \en Calculation of parameter u step by the value of sag + virtual double StepV ( double u, double v, double sag ) const; // \ru Вычисление шага параметра v по по величине прогиба \en Calculation of parameter v step by the value of sag + virtual double DeviationStepU( double u, double v, double angle ) const; // \ru Вычисление шага параметра u по углу отклонения нормали \en Calculation of parameter u step by the angle of deviation of normal + virtual double DeviationStepV( double u, double v, double angle ) const; // \ru Вычисление шага параметра v по углу отклонения нормали \en Calculation of parameter v step by the angle of deviation of normal + virtual double MetricStepU ( double u, double v, double length ) const; // \ru Вычисление шага параметра u по заданной метрической длине \en Calculation of parameter u step by the given metric length + virtual size_t GetUCount() const; // \ru Количество разбиений по параметру u для проверки событий \en Count of splittings by parameter u to check for events + virtual size_t GetVCount() const; // \ru Количество разбиений по параметру v для проверки событий \en Count of splittings by parameter v to check for events + /** \} */ + + /** \ru \name Общие функции поверхности + \en \name Common functions of surface + \{ */ + virtual void Refresh (); ///< \ru Cбросить все временные данные. \en Reset all temporary data. + + virtual MbSplineSurface * NurbsSurface( double u1, double u2, double v1, double v2, bool bmatch = false ) const; + virtual MbSurface * NurbsSurface( const MbNurbsParameters & uParam, const MbNurbsParameters & vParam ) const; + virtual MbSurface * Offset( double d, bool same ) const; // \ru Построить смещенную поверхность \en Create a shifted surface + + /// \ru Изменение степени NURBS кривой по v. \en Change degree of NURBS curve by v. + void ChangeDegree ( ptrdiff_t newDegree ); + /// \ru Получить количество базовых кривых. \en Get count of base curves. + size_t GetCurvesCount () const; + /// \ru Получить кривую с индeксом num. \en Get curve with 'num' index. + const MbCurve3D * GetCurve( size_t k ) const; + const SArray & GetKnots() const { return knots; } ///< \ru Получить значения узлов для сплайна по v. \en Get knot values for spline by v. + + /** \brief \ru Получить список начальных или конечных базовых точек кривых. + \en Get list of start or end base points of curves. \~ + \details \ru Получить список начальных или конечных базовых точек кривых.\n + \en Get list of start or end base points of curves.\n \~ + \param[in] isFirstPoints - \ru Определяет конечные или начальные точки запрошены: true - начальные, false - конечные.\n + \en Determines start or end points were requested: true - start, false - end.\n \~ + \param[in] points - \ru Список, в который помещаются найденные точки. \n + Порядок точек соответствует порядку кривых в списке curves. + \en List to store found points. \n + Order of points corresponds to order of curves in 'curves' list. \~ + \return \ru false и список points остается пустым,\n + если хотя бы одна кривая не имеет базовых точек (не отрезок и не кривая, заданная точками). + \en False then 'points' list remains empty,\n + if at least one curve has no base points (not segment and not curve given by points). \~ + */ + bool GetCurvesBasePoints( bool isFirstPoints, SArray & points ) const; + /** \brief \ru Изменить крайние базовые точки кривых. + \en Change end base points of curves. \~ + \details \ru Базовые точки можно изменить в том случае, если все кривые, на которые натянута поверхность,\n + являются отрезками или кривыми, заданными точками. + \en Base points can be changed in case of all curves which the surface is tensed on\n + are segments or curves given by points. \~ + \param[in] isFirstPoints - \ru Определяет конечные или начальные точки запрошены: true - начальные, false - конечные.\n + \en Determines start or end points were requested: true - start, false - end.\n \~ + \param[in] points - \ru Список, в который помещаются новые значения базовых точек.\n + Порядок точек соответствует порядку кривых в списке curves. + \en List to store new values of base points.\n + Order of points corresponds to order of curves in 'curves' list. \~ + \return \ru false и список points остается пустым,\n + если хотя бы одна кривая не имеет базовых точек (не отрезок и не кривая, заданная точками). + \en False then 'points' list remains empty,\n + if at least one curve has no base points (not segment and not curve given by points). \~ + */ + bool SetCurvesBasePoints( bool isFirstPoints, SArray & points ); + /** \} */ + + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbJoinSurface ) + +private: + void operator = ( const MbJoinSurface & ); // \ru Не реализовано. \en Not implemented. + + void ResetTCalc(); + bool CheckData ( const SArray & newKnots, ptrdiff_t newDegree ); // \ru Проверить корректность данных для NURBS \en Check correctness of data for NURBS + void ChangeKnots ( const ptrdiff_t newDegree, const bool closed, SArray & newKnots ); // \ru Изменить массив узлов, если изменилась степень сплайна \en Change array of knots if degree of spline was changed + void CreateTempVars( MbJoinSurfaceAuxiliaryData * ucache ) const; + void InitTempVars ( MbJoinSurfaceAuxiliaryData * ucache ) const; + void FreeTempVars ( MbJoinSurfaceAuxiliaryData * ucache ) const; + void PreparePointsData( ptrdiff_t lIndex, ptrdiff_t derNum, MbJoinSurfaceAuxiliaryData * ucache ) const; + void PreparePointList ( const double u, ptrdiff_t derNumberU, MbJoinSurfaceAuxiliaryData * ucache ) const; + void CheckPointData ( const MbeSurfaceDerivativeType derUVNumber, double & u, double & v, MbVector3D & vect, MbJoinSurfaceAuxiliaryData * ucache ) const; + ptrdiff_t GetUDerNumber( const MbeSurfaceDerivativeType derUVNumber ) const; + ptrdiff_t GetVDerNumber( const MbeSurfaceDerivativeType derUVNumber ) const; + void CheckPole(); + void CheckParams ( double & u, double & v ) const; + void PoleDerive ( double u, double v, MbVector3D & vDerU, MbVector3D & vDerV ) const; + double DeviationStep( double u, double v, double angle ) const; + double StepD ( double u, double v, double sag, bool checkAngle, double angle ) const; + // \ru Вычисление точки и производных поверхности. \en Calculation of the point and derivatives of the surface. \~ + void ExploreVector( SArray & points, SArray & vectors, + ptrdiff_t lIndex, ptrdiff_t derNum, MbVector3D & vect, MbJoinSurfaceAuxiliaryData * ucache ) const; + +}; + +IMPL_PERSISTENT_OPS( MbJoinSurface ) + +//------------------------------------------------------------------------------ +// \ru Проверить параметры и в случае захода за полюс загнать в полюсную область \en Check parameters and if it is out of pole, then drive it to pole region +// --- +inline void MbJoinSurface::CheckParams( double & u, double & v ) const +{ + if ( isPoleUmin ) { + if ( u < umin ) + u = umin; + } + if ( isPoleUmax ) { + if ( u > umax ) + u = umax; + } + if ( isPoleVmin ) { + const double & vmin = knots[degree - 1]; + if ( v < vmin ) + v = vmin; + } + if ( isPoleVmax ) { + const double & vmax = knots[knots.MaxIndex() - degree + 1]; + if ( v > vmax ) + v = vmax; + } +} + + +#endif // __SURF_JOIN_SURFACE_H diff --git a/C3d/Include/surf_lofted_surface.h b/C3d/Include/surf_lofted_surface.h index 6060c09..7cebd25 100644 --- a/C3d/Include/surf_lofted_surface.h +++ b/C3d/Include/surf_lofted_surface.h @@ -1,664 +1,706 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Поверхность, проходящая через заданное семейство кривых. - \en Lofted surface. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __SURF_LOFTED_SURFACE_H -#define __SURF_LOFTED_SURFACE_H - -#include -#include -#include -#include - - -class MATH_CLASS MbSurfaceContiguousData; - -#define LOFT_NUMB 4 ///< \ru Вспомогательный параметр для поверхности MbLoftedSurface. Используется для определения количества элементов в массивах, где хранится точка и первые три производные в этой точке. \en Auxiliary parameter for MbLoftedSurface surface. Used for determination of count of elements in arrays of points and first three derivatives at this point. -const VERSION LOFTED_SURFACE_VERSION1 = 0x0F000013L; ///< \ru Корректировка коэффициентов уравнения поверхности. \en Correction of surface equation coefficients. -const VERSION LOFTED_SURFACE_VERSION2 = 0x13000015L; ///< \ru Возможность устанавливать нормали на торцевых сечениях в виде точки. \en Ability to set normals on end sections as a point. - - -//------------------------------------------------------------------------------ -/** \brief \ru Поверхность, проходящая через заданное семейство кривых. - \en Lofted surface passing through given family of curves. \~ - \details \ru Поверхность, проходящая через заданное семейство кривых, построена аналогично сплайну Эрмита MbHermit3D, - проходящего через заданное семейство точек. - Первый параметр поверхности пропорционален параметрам кривых семейства. - Вдоль второго параметра поверхность изменяется по закону сплайна Эрмита MbHermit3D, точками которого служат точки кривых семейства. - Производные по второму параметру в точках кривых вычисляются как производные параболы, - построенной по трём точкам и значениям параметров в них. - На каждом участке между двумя соседними кривыми семейства поверхность описывается кубическим полиномом - с заданными точками и производными на краях. - Поверхность проходит через кривые семейства при значениях параметра из множества vParams. - \en The surface passing through given family of curves is constructed similar to MbHermit3D Hermite spline - passing through given family of points. - First parameter of surface is proportional to parameters of curves of family. - Along the second parameter the surface changes under the law of MbHermit3D Hermite spline, which points are points of curves of family. - Derivatives by second parameter at points of curves are calculated as derivatives of parabola - constructed by three points and values of parameters at this points. - On each region between two neighboring curves of family the surface is described by the cubic polynomial - with given points and derivatives at the edges. - Surface passes through curves of family for parameter values from vParams set. \~ - \ingroup Surfaces -*/ -// --- -class MATH_CLASS MbLoftedSurface : public MbSurface { -protected: - RPArray uCurves; ///< \ru Множество кривых семейства. \en Set of curves of family. - SArray vParams; ///< \ru Множество параметров v для кривых. \en Set of parameters v for curves. - SArray vLabels; ///< \ru Множество признаков одинаковых кривых. \en Set of attributes of similar curves. - double umin; ///< \ru Минимальное значение параметра u. \en Minimal value of parameter u. - double vmin; ///< \ru Минимальное значение параметра v. \en Minimal value of parameter v. - double umax; ///< \ru Максимальное значение параметра u. \en Maximal value of parameter u. - double vmax; ///< \ru Максимальное значение параметра v. \en Maximal value of parameter v. - bool uclosed; ///< \ru Признак замкнутости по параметру u. \en Attribute of closedness by parameter u. - bool vclosed; ///< \ru Признак замкнутости по параметру v. \en Attribute of closedness by parameter v. - MbVector3D derive1; ///< \ru Направление производной в начале незамкнутой поверхности. Если не задано, то нулевой длины. \en The direction of derivative at the beginning of the open surface. If it isn't set, then its length is zero. - MbVector3D derive2; ///< \ru Направление производной в конце незамкнутой поверхности. Если не задано, то нулевой длины. \en The direction of derivative at the end of the open surface. If it isn't set, then its length is zero. - bool setNormal1; ///< \ru Установлена нормаль в начальном сечении. \en The normal is set in initial section. - bool setNormal2; ///< \ru Установлена нормаль в конечном сечении. \en The normal is set in end section. - VERSION surfaceVersion; ///< \ru Версия расчета коэффициентов уравнения поверхности. \en Version of coefficient calculation of surface equation. - -protected: - //------------------------------------------------------------------------------ - /** \brief \ru Вспомогательные данные. - \en Auxiliary data. \~ - \details \ru Вспомогательные данные служат для ускорения работы объекта. - \en Auxiliary data are used for fast calculations. \n \~ - */ - // --- - class MbLoftedSurfaceAuxiliaryData : public AuxiliaryData { - public: - DPtr data; ///< \ru Дополнительные данные о поверхности. \en Additional data about a surface. - MbLoftedSurfaceAuxiliaryData(); - MbLoftedSurfaceAuxiliaryData( const MbLoftedSurfaceAuxiliaryData & init ); - virtual ~MbLoftedSurfaceAuxiliaryData(); - }; - - mutable CacheManager cache; - -public: - /** \brief \ru Конструктор поверхности. - \en Constructor of surface. \~ - \details \ru Конструктор поверхности по массиву профильных кривых, направляющим векторам и замкнутости по v. - \en Constructor of surface by array of profile curves, guide vectors and closedness by v. \~ - \param[in] initCurves - \ru Множество задающих кривых. - \en Set of driving curves. \~ - \param[in] vc - \ru Замкнутость поверхности по v. - \en Surface closedness by v. \~ - \param[in] v1 - \ru Направляющий вектор. - \en Guide vector. \~ - \param[in] v2 - \ru Направляющий вектор. - \en Guide vector. \~ - \param[in] same - \ru Определяет, надо ли копировать профильные кривые: true - использовать полученные кривые без копирования, false - использовать копии. - \en Determines whether to copy profile curves: true - use obtained curves without copying, false - use copies. \~ - \param[in] version - \ru Версия, по умолчанию - последняя. - \en Version, last by default. \~ - */ - MbLoftedSurface( const RPArray & initCurves, - bool vc, const MbVector3D & v1, const MbVector3D & v2, bool same, - VERSION version = Math::DefaultMathVersion() ); - /** \brief \ru Конструктор поверхности. - \en Constructor of surface. \~ - \details \ru Конструктор поверхности по массиву профильных кривых, массиву параметров, направляющим векторам и замкнутости по v. - \en Constructor of surface by array of profile curves, array of parameters, guide vectors and closedness by v. \~ - \param[in] initCurves - \ru Множество задающих кривых. - \en Set of driving curves. \~ - \param[in] initParams - \ru Множество параметров, соответствующих задающим кривым. - \en Set of parameters corresponding to driving curves. \~ - \param[in] vc - \ru Замкнутость поверхности по v. - \en Surface closedness by v. \~ - \param[in] v1 - \ru Направляющий вектор. - \en Guide vector. \~ - \param[in] v2 - \ru Направляющий вектор. - \en Guide vector. \~ - \param[in] same - \ru Определяет, надо ли копировать профильные кривые: true - использовать полученные кривые без копирования, false - использовать копии. - \en Determines whether to copy profile curves: true - use obtained curves without copying, false - use copies. \~ - \param[in] version - \ru Версия, по умолчанию - последняя. - \en Version, last by default. \~ - */ - MbLoftedSurface( const RPArray & initCurves, const SArray & initParams, - bool vc, const MbVector3D & v1, const MbVector3D & v2, bool setNormal1, bool setNormal2, bool same, - VERSION version = Math::DefaultMathVersion() ); -protected: // \ru Конструкторы для наследников \en Constructors for inheritors - /** \brief \ru Конструктор поверхности. - \en Constructor of surface. \~ - \details \ru Конструктор поверхности по набору профильных кривых. - \en Constructor of surface by family of profile curves. \~ - \param[in] initCurves - \ru Множество задающих кривых. - \en Set of driving curves. \~ - \param[in] same - \ru Определяет, надо ли копировать профильные кривые: true - использовать полученные кривые без копирования, false - использовать копии. - \en Determines whether to copy profile curves: true - use obtained curves without copying, false - use copies. \~ - \param[in] version - \ru Версия, по умолчанию - последняя. - \en Version, last by default. \~ - */ - MbLoftedSurface( const RPArray & initCurves, bool same, VERSION version = Math::DefaultMathVersion() ); - /** \brief \ru Конструктор поверхности. - \en Constructor of surface. \~ - \details \ru Конструктор поверхности по массиву профильных кривых и массиву параметров. - \en Constructor of surface by array of profile curves and array of parameters. \~ - \param[in] initParams - \ru Множество параметров, соответствующих задающим кривым. - \en Set of parameters corresponding to driving curves. \~ - \param[in] initCurves - \ru Множество задающих кривых. - \en Set of driving curves. \~ - \param[in] same - \ru Определяет, надо ли копировать профильные кривые: true - использовать полученные кривые без копирования, false - использовать копии. - \en Determines whether to copy profile curves: true - use obtained curves without copying, false - use copies. \~ - \param[in] version - \ru Версия, по умолчанию - последняя. - \en Version, last by default. \~ - */ - MbLoftedSurface( const SArray & initParams, const RPArray & initCurves, bool same, VERSION version = Math::DefaultMathVersion() ); - /// \ru Конструктор-копия. \en Copy constructor. - MbLoftedSurface( const MbLoftedSurface &, MbRegDuplicate * reg ); -private: - MbLoftedSurface( const MbLoftedSurface & ); // \ru Не реализовано. \en Not implemented. -public: - virtual ~MbLoftedSurface(); - -public: - VISITING_CLASS( MbLoftedSurface ); - - /** \ru \name Общие функции геометрического объекта - \en \name Common functions of a geometric object - \{ */ - virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element - virtual MbeSpaceType Type() const; // \ru Тип элемента \en A type of element - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Равны ли объекты \en Whether the objects are equal - virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным \en Make equal - virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis - virtual void Refresh(); - - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object - virtual void GetBasisItems ( RPArray & s ); // \ru Дать базовые объекты \en Get the base objects - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - /** \} */ - - /** \ru \name Функции описания области определения поверхности - \en \name Functions for surface domain description - \{ */ - virtual double GetUMin () const; // \ru Вернуть минимальное значение параметра u \en Return the minimum value of parameter u - virtual double GetVMin () const; // \ru Вернуть минимальное значение параметра v \en Return the minimum value of parameter v - virtual double GetUMax () const; // \ru Вернуть максимальное значение параметра u \en Return the maximum value of parameter u - virtual double GetVMax () const; // \ru Вернуть максимальное значение параметра v \en Return the maximum value of parameter v - virtual bool IsUClosed() const; // \ru Замкнута ли поверхность по параметру u. \en Whether the surface is closed by parameter u. - virtual bool IsVClosed() const; // \ru Замкнута ли поверхность по параметру v. \en Whether the surface is closed by parameter v. - virtual double GetUPeriod() const; // \ru Период для замкнутой поверхности или 0. \en Period for closed surface or 0. - virtual size_t GetUCount() const; - virtual size_t GetVCount() const; - // \ru Существует ли полюс на границе параметрической области \en Whether there is pole on boundary of parametric region - virtual bool GetPoleUMin() const; - virtual bool GetPoleUMax() const; - virtual bool GetPoleVMin() const; - virtual bool GetPoleVMax() const; - virtual bool IsPole( double u, double v ) const; // \ru Является ли точка особенной \en Whether the point is special - /** \} */ - - /** \ru \name Функции для работы в области определения поверхности - Функции PointOn, Derive... поверхностей корректируют параметры - при выходе их за пределы прямоугольной области определения параметров.\n - \en \name Functions for working at surface domain - Functions PointOn, Derive... of surfaces correct parameters - when they are out of bounds of rectangular domain of parameters.\n - \{ */ - virtual void PointOn ( double & u, double & v, MbCartPoint3D & ) const; // \ru Точка на поверхности \en Point on the surface - virtual void DeriveU ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по u \en First derivative with respect to u - virtual void DeriveV ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по v \en First derivative with respect to v - virtual void DeriveUU ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по u \en Second derivative with respect to u - virtual void DeriveVV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по v \en Second derivative with respect to v - virtual void DeriveUV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv - virtual void DeriveUUU( double & u, double & v, MbVector3D & ) const; // \ru Третья производная по u \en Third derivative with respect to u - virtual void DeriveUUV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная по v \en Third derivative with respect to v - virtual void DeriveUVV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная по uv \en Third derivative with respect to uv - virtual void DeriveVVV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная по uv \en Third derivative with respect to uv - /** \} */ - - /** \ru \name Функции для работы внутри и вне области определения поверхности - функции _PointOn, _Derive... поверхностей не корректируют - параметры при выходе их за пределы прямоугольной области определения параметров. - \en \name Functions for working inside and outside the surface's domain - functions _PointOn, _Derive... of surfaces don't correct - parameters when they are out of bounds of rectangular domain of parameters. - \{ */ - virtual void _PointOn ( double u, double v, MbCartPoint3D & ) const; // \ru Точка на расширенной поверхности \en Point on the extended surface - virtual void _DeriveU ( double u, double v, MbVector3D & ) const; // \ru Первая производная по u \en First derivative with respect to u - virtual void _DeriveV ( double u, double v, MbVector3D & ) const; // \ru Первая производная по v \en First derivative with respect to v - virtual void _DeriveUU ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по u \en Second derivative with respect to u - virtual void _DeriveVV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по v \en Second derivative with respect to v - virtual void _DeriveUV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv - virtual void _DeriveUUU( double u, double v, MbVector3D & ) const; // \ru Третья производная по u \en Third derivative with respect to u - virtual void _DeriveUUV( double u, double v, MbVector3D & ) const; // \ru Третья производная по v \en Third derivative with respect to v - virtual void _DeriveUVV( double u, double v, MbVector3D & ) const; // \ru Третья производная по uv \en Third derivative with respect to uv - virtual void _DeriveVVV( double u, double v, MbVector3D & ) const; // \ru Третья производная по uv \en Third derivative with respect to uv - /** \} */ - /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. - \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. - \{ */ - virtual void Explore( double & u, double & v, bool ext, - MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, - MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; - /** \} */ - - /** \ru \name Функции движения по поверхности - \en \name Functions of moving along the surface - \{ */ - virtual double StepU ( double u, double v, double sag ) const; // \ru Вычисление шага аппроксимации с учетом радиуса кривизны \en Calculation of step of approximation with consideration of curvature radius - virtual double StepV ( double u, double v, double sag ) const; // \ru Вычисление шага аппроксимации с учетом радиуса кривизны \en Calculation of step of approximation with consideration of curvature radius - virtual double DeviationStepU( double u, double v, double sag ) const; // \ru Вычисление шага по u при пересечении поверхностей \en Calculation of step by u while intersecting surfaces - virtual double DeviationStepV( double u, double v, double sag ) const; // \ru Вычисление шага по u при пересечении поверхностей \en Calculation of step by u while intersecting surfaces - /** \} */ - - /** \ru \name Общие функции поверхности - \en \name Common functions of surface - \{ */ - virtual bool IsPlanar() const; // \ru Является ли поверхность плоской \en Whether the surface is planar - virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменение носителя \en Changing of carrier - virtual void CalculateGabarit( MbCube & ) const; // \ru Рассчитать габарит поверхности \en Calculate bounding box of surface - - // \ru Подобные ли поверхности для объединения (слива) \en Whether the surfaces to union (joining) are similar - virtual bool IsSpecialSimilarToSurface( const MbSurface & surf, VERSION version, double precision = METRIC_PRECISION ) const; // \ru Специальный случай \en Special case - - // \ru Построить касательные и нормальные плейсменты конструктивных плоскостей \en Construct tangent and normal placements of constructive planes - virtual bool CreateNormalPlacements ( const MbVector3D & axisZ, double angle, SArray & places ) const; - virtual bool CreateTangentPlacements( const MbVector3D & axisZ, SArray & places ) const; - virtual bool GetCylinderAxis( MbAxis3D & axis ) const; // \ru Дать ось вращения для поверхности \en Get a rotation axis of a surface - - virtual MbSplineSurface * NurbsSurface( double, double, double, double, bool bmatch = false ) const; // \ru NURBS копия поверхности \en NURBS copy of a surface - virtual MbSurface * Offset( double d, bool same ) const; // \ru Построить смещенную поверхность \en Create a shifted surface - - virtual MbCurve3D * CurveU( double v, MbRect1D * pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии v = const \en Spatial copy of 'v = const'-line - virtual MbCurve3D * CurveV( double u, MbRect1D * pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии u = const \en Spatial copy of 'u = const'-line - - virtual size_t GetUMeshCount() const; // \ru Выдать количество полигонов по u \en Get the count of polygons by u - virtual size_t GetVMeshCount() const; // \ru Выдать количество полигонов по v \en Get the count of polygons by v - - // \ru Определение разбивки параметрической области поверхности вертикалями и горизонталями \en Determine splitting of parametric region of surface by vertical and horizontal lines - virtual void GetTesselation( const MbStepData & stepData, - double u1, double u2, double v1, double v2, - SArray & uu, SArray & vv ) const; - - // \ru Найти ближайшую проекцию точки на поверхность или ее продолжение по заданному начальному приближению. \en Find the neares projection of a point onto the surface. - virtual bool NearPointProjection ( const MbCartPoint3D & pnt, double & u, double & v, bool ext, MbRect2D * uvRange = NULL ) const; - // \ru Найти все проекции точки на поверхность вдоль вектора в любом из двух направлений. \en Find all a point projection onto the surface along a vector in either of two directions. - virtual void DirectPointProjection( const MbCartPoint3D & pnt, const MbVector3D & vect, SArray & uv, bool ext, MbRect2D * uvRange = NULL ) const; - - virtual bool IsLineU() const; // \ru Если true все производные по U выше первой равны нулю \en If true, then all the derivatives by U higher the first one are equal to zero - virtual bool IsLineV() const; // \ru Если true все производные по V выше первой равны нулю \en If true, then all the derivatives by V higher the first one are equal to zero - - // \ru Проверить параметры и загнать в область определения, если параметр вышел за полюс. - // Аналог глобальной функции _CheckParams, оптимизированный под использование кэшей. - // \en Check parameters and move them inside domain if parameter is out of pole. - // \en Check parameters. Analogue of the global function _CheckParams, optimized for caches usage. - // \param[in] surface - \ru Поверхность. \en Surface. - // \param[in] u - \ru Первый параметр. \en First parameter. - // \param[in] v - \ru Второй параметр. \en Second parameter. - virtual void CheckSurfParams( double & u, double & v ) const; - - /// \ru Получить количество кривых, на которых построена поверхность \en Get count of curves which the surface is constructed by - ptrdiff_t CurvesCount() const { return (ptrdiff_t)uCurves.Count(); } - - /** \brief \ru Получить кривую по номеру. - \en Get curve by an index. \~ - \details \ru Получить кривую по номеру. \n - \en Get curve by an index. \n \~ - \param[in] ind - \ru Порядковый номер кривой в массиве кривых uCurves. - \en Index of curve in uCurves array of curves. \~ - \return \ru Константная кривая. - \en The constant curve. \~ - */ - const MbCurve3D * GetCurve( ptrdiff_t ind ) const { return (ind >= 0 && ind < (ptrdiff_t)uCurves.Count()) ? uCurves[ind] : NULL; } - /** \brief \ru Получить кривую для редактирования по номеру. - \en Get curve for editing by an index. \~ - \details \ru Получить кривую для редактирования по номеру. \n - \en Get curve for editing by an index. \n \~ - \param[in] ind - \ru Порядковый номер кривой в массиве кривых uCurves. - \en Index of curve in uCurves array of curves. \~ - \return \ru Кривая. - \en A curve. \~ - */ - MbCurve3D * SetCurve( ptrdiff_t ind ) { return (ind >= 0 && ind < (ptrdiff_t)uCurves.Count()) ? uCurves[ind] : NULL; } - /** \brief \ru Получить параметр по номеру. - \en Get parameter by an index. \~ - \details \ru Получить параметр по номеру.\n - \en Get parameter by an index.\n \~ - \param[in] ind - \ru Порядковый номер параметра в массиве параметров vParams. - \en Index of parameter in vParams array of parameters. \~ - \return \ru Значение параметра. - \en A parameter value. \~ - */ - double GetParam( ptrdiff_t ind ) const { return (ind >= 0 && ind < (ptrdiff_t)vParams.Count()) ? vParams[ind] : 0.0; } - /** \brief \ru Заполнить массив параметрами. - \en Fill an array by parameters. \~ - \details \ru Заполнить массив параметрами. \n - \en Fill an array by parameters. \n \~ - \param[in,out] params - \ru Множество для заполнения параметрами. - \en A set to fill by parameters. \~ - */ - void GetParams( SArray & params ) const { params = vParams; } - /** \brief \ru Заполнить массив признаков одинаковых кривых. - \en Fill array of attributes of similar curves. \~ - \details \ru Заполнить массив признаков одинаковых кривых. \n - \en Fill array of attributes of similar curves. \n \~ - \param[in,out] labels - \ru Множество для заполнения. - \en A set to fill. \~ - */ - void GetLabels( SArray & labels ) const { labels = vLabels; } - - /// \ru Направление производной в начале незамкнутой поверхности. Если не задано, то нулевой длины. \en The direction of derivative at the beginning of the open surface. If it isn't set, then its length is zero. - const MbVector3D & GetDerive1() const { return derive1; } - ///< \ru Направление производной в конце незамкнутой поверхности. Если не задано, то нулевой длины. \en The direction of derivative at the end of the open surface. If it isn't set, then its length is zero. - const MbVector3D & GetDerive2() const { return derive2; } - - bool IsEqualLabels() const; ///< \ru Определить, есть ли одинаковые кривые. \en Determine whether there are similar curves. - /** \brief \ru Определить, есть ли кривые, одинаковые с кривой под номером ind. - \en Determine whether there are curves similar to curve with 'ind' index. \~ - \details \ru Определить, есть ли кривые, одинаковые с кривой под номером ind. \n - \en Determine whether there are curves similar to curve with 'ind' index. \n \~ - \param[in] ind - \ru Номер кривой для сравнения. - \en An index of curve for comparison. \~ - \return \ru true - Если в массиве есть кривые, одинаковые с кривой под номером ind. - \en True - If there are curves similar to curve with 'ind' index in array. \~ - */ - bool IsEqualLabels( ptrdiff_t ind ) const; - - - /** \brief \ru Определить, можно ли создать эквидистантную поверхность. - \en Determine whether it is possible to create an offset surface. \~ - \details \ru Определить, можно ли создать эквидистантную поверхность.\n - \en Determine whether it is possible to create an offset surface.\n \~ - \param[in] h - \ru Величина смещения. - \en The offset distance. \~ - \param[in] uLimBeg - \ru Нижняя граница по u области, к которой надо построить эквидистантную поверхность. - \en Lower bound of region by u which offset surface is necessary to construct to. \~ - \param[in] uLimEnd - \ru Верхняя граница по u области, к которой надо построить эквидистантную поверхность. - \en Upper bound of region by u which offset surface is necessary to construct to. \~ - \param[in] vLimBeg - \ru Нижняя граница по v области, к которой надо построить эквидистантную поверхность. - \en Lower bound of region by v which offset surface is necessary to construct to. \~ - \param[in] vLimEnd - \ru Верхняя граница по v области, к которой надо построить эквидистантную поверхность. - \en Upper bound of region by v which offset surface is necessary to construct to. \~ - \return \ru true - Если в можно создать эквидистантную поверхность. - \en True - If it is possible to create an offset surface. \~ - */ - bool IsPossibleCreateThin( double h, - double uLimBeg, double uLimEnd, - double vLimBeg, double vLimEnd ) const; - /** \brief \ru Согласовать массивы признаков одинаковости кривых у смежных поверхностей. - \en Match arrays of attributes of similarity of curves between adjacent surfaces. \~ - \details \ru Согласовать массивы признаков одинаковости кривых у смежных поверхностей. \n - \en Match arrays of attributes of similarity of curves between adjacent surfaces. \n \~ - \param[in] surf - \ru Смежная поверхность. - \en Adjacent surface. \~ - \return \ru true - Если есть изменения в массиве признаков кривых хотя бы одной поверхности. - \en True - If there are changes in array of attributes of curves of at least one surface. \~ - */ - bool AgreeLabels( MbLoftedSurface & surf ); - /** \} */ - -protected: - void CheckParam( double & u, bool ext ) const; // \ru Корректировка параметров. \en Correct parameters. \~ - /** \brief \ru Определение местных координат области поверхности. - \en Determination of local coordinates of a surface region. \~ - \details \ru Определение местных координат области поверхности. \n - \en Determination of local coordinates of a surface region. \n \~ - \param[in] v - \ru Координата v на поверхности. - \en V coordinate on the surface. \~ - \param[in,out] j1 - \ru Номер ближайшей кривой с параметром, меньшим v. - \en Index of nearest curve with parameter less than v. \~ - \param[in,out] j2 - \ru Номер ближайшей кривой с параметром, большим v. - \en Index of nearest curve with parameter greater than v. \~ - \param[in,out] y1 - \ru Параметрическое расстояние от точки с координатой v до кривой j1, при условии, что расстояние между кривыми j1 и j2 равно 1. - \en Parametric distance from point with v coordinate to j1 curve provided that distance between j1 and j2 curves is equal to 1. \~ - \param[in,out] y2 - \ru Параметрическое расстояние от точки с координатой v до кривой j2, при условии, что расстояние между кривыми j1 и j2 равно 1. - \en Parametric distance from point with v coordinate to j2 curve provided that distance between j1 and j2 curves is equal to 1. \~ - \param[in,out] t1 - \ru Значение параметра для кривой j1. - \en Value of parameter for j1 curve. \~ - \param[in,out] t2 - \ru Значение параметра для кривой j2. - \en Value of parameter for j2 curve. \~ - */ - void LocalCoordinate( double & v, ptrdiff_t & j1, ptrdiff_t & j2, double & y1, double & y2, double & t1, double & t2 ) const; - /** \brief \ru Определение массива векторов кривой. - \en Determination of the array of curve vectors. \~ - \details \ru Определение массива векторов кривой. \n - \en Determination of the array of curve vectors. \n \~ - \param[in] i - \ru Номер кривой. - \en Index of curve. \~ - \param[in] u - \ru Координата u на поверхности. - \en U coordinate on the surface. \~ - \param[in] der - \ru Ссылка на массив векторов для хранения вычисленной точки и производных. - \en Reference to array of vectors to store calculated point and derivatives. \~ - \param[in] ext - \ru Можно ли продолжить кривую за границы области определения ее параметра. - \en Whether it is possible to extend curve out of its parametric domain bounds. \~ - */ - void CalculateCurve( ptrdiff_t i, double u, MbVector3D & point, bool ext, size_t numb ) const; - void CalculateCurve( ptrdiff_t i, double u, MbVector3D & pnt, MbVector3D & fir, MbVector3D * sec, bool ext ) const; - /** \brief \ru Определение массива векторов параметрa u для точки на поверхности с координатами (u, v). - \en Determination of array of vectors of u parameter for point on surface with coordinates (u, v). \~ - \details \ru Определение массива векторов параметрa u для точки на поверхности с координатами (u, v). \n - \en Determination of array of vectors of u parameter for point on surface with coordinates (u, v). \n \~ - \param[in] u - \ru Координата u на поверхности. - \en U coordinate on the surface. \~ - \param[in] j1 - \ru Номер ближайшей кривой с параметром, меньшим v. - \en Index of nearest curve with parameter less than v. \~ - \param[in] j2 - \ru Номер ближайшей кривой с параметром, большим v. - \en Index of nearest curve with parameter greater than v. \~ - \param[in] t1 - \ru Значение параметра для кривой j1. - \en Value of parameter for j1 curve. \~ - \param[in] t2 - \ru Значение параметра для кривой j2. - \en Value of parameter for j2 curve. \~ - \param[in] ext - \ru Можно ли продолжить поверхность за границы области определения ее параметров. - \en Whether it is possible to extend surface out of its parametric domain bounds. \~ - */ - void CalculateSurface( double & u, ptrdiff_t j1, ptrdiff_t j2, - double t1, double t2, bool ext, size_t numb, - MbVector3D & point1, MbVector3D & point2, - MbVector3D & vector1, MbVector3D & vector2, bool correctVectors = true ) const; - void CalculateExplore( double & u, ptrdiff_t j1, ptrdiff_t j2, - double t1, double t2, bool ext, bool boolsecond, - MbVector3D * point1, MbVector3D * point2, - MbVector3D * vector1, MbVector3D * vector2, - double * tLoft ) const; - - void ParamPoint ( double y1, double y2, double t1, double t2, double * tLoft ) const; - void ParamFirst ( double y1, double y2, double t1, double t2, double * tLoft ) const; - void ParamSecond( double y1, double y2, double t1, double t2, double * tLoft ) const; - void ParamThird ( double t1, double t2, double * tLoft ) const; - - /** \brief \ru Проверка полюсов на кривых. - \en Check poles on curves. \~ - \details \ru Определяет, есть ли полюс на границе области определения по длине кривой, определяющей границу.\n - Результат вычислений можно получить с помощью функций GetPoleUMin, GetPoleUMax, GetPoleVMin, GetPoleVMax. - \en Determines whether the pole at domain boundary by curve length determining boundary.\n - Result of calculations can be obtained with help of GetPoleUMin, GetPoleUMax, GetPoleVMin, GetPoleVMax functions. \~ - */ - bool CheckPoles( MbLoftedSurfaceAuxiliaryData * ) const; // \ru Проверка полюсов на кривых \en Check poles on curves - -private: - void Init( bool close ); - bool IsSimilarCurves( ptrdiff_t i1, ptrdiff_t i2 ) const; // \ru Определение одинаковых кривых \en Determination of similar curves - bool IsSimilarLabels( ptrdiff_t i1, ptrdiff_t i2 ) const; // \ru Определение одинаковых кривых по меткам. \en Determination of similar curves by labels. - void InitLabels(); // \ru Инициализация признаков одинаковых кривых. \en Initialization of attributes of similar curves. - - MbVector3D DirByGivenNormal( bool isStart, const MbVector3D & point1, const MbVector3D & point2 ) const; // \ru Определить вектор направления с заданной нормалью. \en Determine the direction vector with a given normal. - - void operator = ( const MbLoftedSurface & ); // \ru Не реализовано. \en Not implemented. - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbLoftedSurface ) -}; - -IMPL_PERSISTENT_OPS( MbLoftedSurface ) - -//------------------------------------------------------------------------------ -// \ru Корректировка параметров. \en Correct parameters. \~ -// --- -inline void MbLoftedSurface::CheckParam( double & u, bool ext ) const -{ - if ( !ext ) { - if ( uclosed ) { // переписана на ::floor(), т.к. подвисала на while - if ( (u < umin) || (u > umax) ) { - double pRgn = ( umax - umin ); - u -= ( ::floor((u - umin) / pRgn) * pRgn ); - } - } - else if ( u < umin ) { - u = umin; - } - else if ( u > umax ) { - u = umax; - } - } - else { - if ( u < umin && GetPoleUMin() ) { - u = umin; - } - else if ( u > umax && GetPoleUMax() ) { - u = umax; - } - } -} - - -//------------------------------------------------------------------------------ -// \ru Определение местных координат области поверхности \en Determination of local coordinates in a surface region -// --- -inline void MbLoftedSurface::LocalCoordinate( double & v, ptrdiff_t & j1, ptrdiff_t & j2, - double & y1, double & y2, - double & t1, double & t2 ) const -{ - if ( v < vmin || v > vmax ) { // \ru Параметр вне границ \en Parameter is out of bounds - if ( vclosed ) { - double tmp = vmax - vmin; - v -= ::floor((v - vmin) / tmp) * tmp; - } - else { - if ( v < vmin ) - v = vmin; - else - v = vmax; - } - } - - j1 = 0; - j2 = vParams.MaxIndex(); - - ptrdiff_t ind, delta = j2; // \ru Диапазон \en A range - - // \ru Поиск половинным делением \en Search by bisection - while ( delta > 1 ) { - ind = j1 + ( delta / (ptrdiff_t)2 ); // \ru Индекс в середине \en The index in the middle - if ( v < vParams[ind] ) // \ru Если v меньше серединного параметра \en If v is less than the middle parameter - j2 = ind; // \ru Изменить правую границу \en Change the right bound - else - j1 = ind; // \ru Изменить левую границу \en Change the left bound - delta = j2 - j1; // \ru Диапазон \en A range - } - - t1 = vParams[j1]; - t2 = vParams[j2]; - double dt = t2 - t1; - double antiDt = 1.0; - if ( dt > NULL_EPSILON ) - antiDt /= dt; - y1 = (t2 - v) * antiDt; - y2 = (v - t1) * antiDt; -} - - -//------------------------------------------------------------------------------ -// \ru Определение массива степеней параметрa точки \en Determination of array of degrees of point parameter -// --- -inline void MbLoftedSurface::ParamPoint( double y1, double y2, double t1, double t2, double * tLoft ) const -{ - double y1pow2 = y1 * y1; - double y2pow2 = y2 * y2; - double y1pow3 = y1pow2 * y1; - double y2pow3 = y2pow2 * y2; - tLoft[0] = 3.0 * y1pow2 - 2.0 * y1pow3; - tLoft[1] = 3.0 * y2pow2 - 2.0 * y2pow3; - tLoft[2] = (y1pow3 - y1pow2) * (t1-t2); - tLoft[3] = (y2pow3 - y2pow2) * (t2-t1); -} - - -//------------------------------------------------------------------------------ -// \ru Определение массива степеней параметрa производной \en Determination of array of degrees of derivative parameter -// --- -inline void MbLoftedSurface::ParamFirst( double y1, double y2, double t1, double t2, double * tLoft ) const -{ - double y1pow2 = y1 * y1; - double y2pow2 = y2 * y2; - double kdt1 = 1.0 / (t1 - t2); - double kdt2 = -kdt1; - tLoft[0] = 6.0 * (y1 - y1pow2) * kdt1; - tLoft[1] = 6.0 * (y2 - y2pow2) * kdt2; - tLoft[2] = (3.0 * y1pow2 - 2.0 * y1); - tLoft[3] = (3.0 * y2pow2 - 2.0 * y2); -} - - -//------------------------------------------------------------------------------ -// \ru Определение массива степеней параметрa второй производной \en Determination of array of degrees of second derivative parameter -// --- -inline void MbLoftedSurface::ParamSecond( double y1, double y2, double t1, double t2, double * tLoft ) const { - double d1 = 1 / (t1-t2); - double d2 = -d1; - tLoft[0] = (6 - 12 * y1) * d1 * d1; - tLoft[1] = (6 - 12 * y2) * d2 * d2; - tLoft[2] = (6 * y1 - 2) * d1; - tLoft[3] = (6 * y2 - 2) * d2; -} - - -//------------------------------------------------------------------------------ -// \ru Определение массива степеней параметрa третьей производной \en Determination of array of degrees of third derivative parameter -// --- -inline void MbLoftedSurface::ParamThird( double t1, double t2, double * tLoft ) const { - double d1 = 1 / (t1-t2); - double d2 = -d1; - double d1pow2 = d1 * d1; - double d2pow2 = d2 * d2; - tLoft[0] = -12 * d1pow2 * d1; - tLoft[1] = -12 * d2pow2 * d2; - tLoft[2] = 6 * d1pow2; - tLoft[3] = 6 * d2pow2; -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Наполнить массив v-параметров и весовых центров заданных кривых. - \en Fill array of v-parameters and weight centers of given curves. \~ - \details \ru Если все профильные кривые плоские, параметры вычисляются функцией CreateElevationParam. - Иначе параметр для каждой кривой вычисляется как координата вдоль направляющей проекции центра масс кривой на направляющую. - \en If all the profile curves are planar, then parameters are calculated by CreateElevationParam function. - Otherwise parameter for each curve is calculated as coordinate along guide projection of center of mass of curve to guide. \~ - \param[in] uCurves - \ru Множество профильных кривых. - \en Set of profile curves. \~ - \param[in] vcls - \ru Замкнута ли поверхность по параметру v. - \en Whether the surface is closed by parameter v. \~ - \param[in,out] vParams - \ru Множество параметров. - \en Set of parameters. \~ - \param[in,out] tiePnts - \ru Множество центров масс профильных кривых. Не заполняется, если в функцию передать NULL. - \en Set of centers of mass of profile curves. If giving NULL to function, then it isn't filled. \~ - \param[in] version - \ru Версия. - \en Version. \~ - \return \ru true - если массив параметров успешно создан. - \en True - if the array of parameters successfully created. \~ - \ingroup Algorithms_3D -*/ -// --- -bool CreateLoftedParams( const RPArray & uCurves, - bool vcls, - SArray & vParams, - SArray * tiePnts, - VERSION version ); - - -#endif // __SURF_LOFTED_SURFACE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Поверхность, проходящая через заданное семейство кривых. + \en Lofted surface. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __SURF_LOFTED_SURFACE_H +#define __SURF_LOFTED_SURFACE_H + +#include +#include +#include +#include + + +class MATH_CLASS MbSurfaceContiguousData; + +const_expr size_t LOFT_NUMB = 4; ///< \ru Вспомогательный параметр для поверхности MbLoftedSurface. Используется для определения количества элементов в массивах, где хранится точка и первые три производные в этой точке. \en Auxiliary parameter for MbLoftedSurface surface. Used for determination of count of elements in arrays of points and first three derivatives at this point. +const_expr VERSION LOFTED_SURFACE_VERSION1 = 0x0F000013L; ///< \ru Корректировка коэффициентов уравнения поверхности. \en Correction of surface equation coefficients. +const_expr VERSION LOFTED_SURFACE_VERSION2 = 0x13000015L; ///< \ru Возможность устанавливать нормали на торцевых сечениях в виде точки. \en Ability to set normals on end sections as a point. +const_expr VERSION LOFTED_SURFACE_VERSION3 = 0x13000019L; ///< \ru Введены множители производной в случае установки нормали на торцевых сечениях. \en Factors of the derivative are added in the case of setting the normal on the end sections. +const_expr VERSION LOFTED_SURFACE_VERSION4 = 0x1300001BL; ///< \ru Добавлен вектор направлени движение повехности в случае установки нормали. Необходимо для тела с операцией Купол. \en The direction vector of the surface progress is added in the case of setting the normal. Necessary for the body with the operation Dome. + + +//------------------------------------------------------------------------------ +/** \brief \ru Поверхность, проходящая через заданное семейство кривых. + \en Lofted surface passing through given family of curves. \~ + \details \ru Поверхность, проходящая через заданное семейство кривых, построена аналогично сплайну Эрмита MbHermit3D, + проходящего через заданное семейство точек. + Первый параметр поверхности пропорционален параметрам кривых семейства. + Вдоль второго параметра поверхность изменяется по закону сплайна Эрмита MbHermit3D, точками которого служат точки кривых семейства. + Производные по второму параметру в точках кривых вычисляются как производные параболы, + построенной по трём точкам и значениям параметров в них. + На каждом участке между двумя соседними кривыми семейства поверхность описывается кубическим полиномом + с заданными точками и производными на краях. + Если крайние кривые семейства представлены точками, то возможно установить производную на краях с ортогональным направлением + относительно нормали плоскости XoY лежащей в локальной системе координат начальной или конечной точечной кривой. Величина производной + зависит от расстояния между точечной кривой и ближайшей к ней. Для возможности изменения величины производной введены неотрицательные + множители, по умолчанию равные 1.0. + Поверхность проходит через кривые семейства при значениях параметра из множества vParams. + \en The surface passing through given family of curves is constructed similar to MbHermit3D Hermite spline + passing through given family of points. + First parameter of surface is proportional to parameters of curves of family. + Along the second parameter the surface changes under the law of MbHermit3D Hermite spline, which points are points of curves of family. + Derivatives by second parameter at points of curves are calculated as derivatives of parabola + constructed by three points and values of parameters at this points. + On each region between two neighboring curves of family the surface is described by the cubic polynomial + with given points and derivatives at the edges. + If the first or last curves of the family are represented by points, then it is possible to define the derivative at the edges + with the orthogonal direction relative to the normal of the XoY plane in the local coordinate system of the initial or final edges. + The value of the derivative depends on the distance between the point curve and the one closest to it. In order to change + the value of the derivative, non-negative factor are introduced by default equal to 1.0. + Surface passes through curves of family for parameter values from vParams set. \~ + \ingroup Surfaces +*/ +// --- +class MATH_CLASS MbLoftedSurface : public MbSurface { +protected: + struct MbLoftedBorder { + MbVector3D derive; ///< \ru Направление производной незамкнутой поверхности. Если не задано, то нулевой длины. \en The direction of derivative of the open surface. If it isn't set, then its length is zero. + bool setNormal; ///< \ru Установлена нормаль. Направление производной выбирается ортогонально нормали крайнего сечения. \en The normal is set. The direction of the derivative is chosen orthogonally to the normal of the ends section. + double derFactor; ///< \ru Множитель величины производной при установке нормали. По умолчанию 1.0. \en Factor of the derivative when setting the normal. The default is 1.0. + MbVector3D directSurf; ///< \ru Ось направления движения поверхности у концевого участка при установке нормали. По умолчанию соединяет центры концевого и ближайшего сечений. \en Direction axis of the surface progress near the end curve when setting the normal. By default, it connects the centers of the end curve and the nearest curve. + + MbLoftedBorder(); /// \ru Конструктор по умолчанию. \en Default constructor. + MbLoftedBorder( const MbLoftedBorder & obj ); /// \ru Конструктор копирования. \en Copy-constructor. + MbLoftedBorder & operator = ( const MbLoftedBorder & obj ); /// \ru Оператор присваивания. \en Assignment operator. + }; + + RPArray uCurves; ///< \ru Множество кривых семейства. \en Set of curves of family. + SArray vParams; ///< \ru Множество параметров v для кривых. \en Set of parameters v for curves. + SArray vLabels; ///< \ru Множество признаков одинаковых кривых. \en Set of attributes of similar curves. + double umin; ///< \ru Минимальное значение параметра u. \en Minimal value of parameter u. + double vmin; ///< \ru Минимальное значение параметра v. \en Minimal value of parameter v. + double umax; ///< \ru Максимальное значение параметра u. \en Maximal value of parameter u. + double vmax; ///< \ru Максимальное значение параметра v. \en Maximal value of parameter v. + bool uclosed; ///< \ru Признак замкнутости по параметру u. \en Attribute of closedness by parameter u. + bool vclosed; ///< \ru Признак замкнутости по параметру v. \en Attribute of closedness by parameter v. + MbLoftedBorder border1; ///< \ru Условия примыкания поверхности к начальной кривой семейства. \en The adjacency conditions of the surface to the starting curve of family. + MbLoftedBorder border2; ///< \ru Условия примыкания поверхности к конечной кривой семейства. \en The adjacency conditions of the surface to the ending curve of family. + VERSION surfaceVersion; ///< \ru Версия расчета коэффициентов уравнения поверхности. \en Version of coefficient calculation of surface equation. + +protected: + //------------------------------------------------------------------------------ + /** \brief \ru Вспомогательные данные. + \en Auxiliary data. \~ + \details \ru Вспомогательные данные служат для ускорения работы объекта. + \en Auxiliary data are used for fast calculations. \n \~ + */ + // --- + class MbLoftedSurfaceAuxiliaryData : public AuxiliaryData { + public: + DPtr data; ///< \ru Дополнительные данные о поверхности. \en Additional data about a surface. + MbLoftedSurfaceAuxiliaryData(); + MbLoftedSurfaceAuxiliaryData( const MbLoftedSurfaceAuxiliaryData & init ); + virtual ~MbLoftedSurfaceAuxiliaryData(); + }; + + mutable CacheManager cache; + +public: + /** \brief \ru Конструктор поверхности. + \en Constructor of surface. \~ + \details \ru Конструктор поверхности по массиву профильных кривых, направляющим векторам и замкнутости по v. + \en Constructor of surface by array of profile curves, guide vectors and closedness by v. \~ + \param[in] initCurves - \ru Множество задающих кривых. + \en Set of driving curves. \~ + \param[in] vc - \ru Замкнутость поверхности по v. + \en Surface closedness by v. \~ + \param[in] v1 - \ru Направляющий вектор. + \en Guide vector. \~ + \param[in] v2 - \ru Направляющий вектор. + \en Guide vector. \~ + \param[in] same - \ru Определяет, надо ли копировать профильные кривые: true - использовать полученные кривые без копирования, false - использовать копии. + \en Determines whether to copy profile curves: true - use obtained curves without copying, false - use copies. \~ + \param[in] version - \ru Версия, по умолчанию - последняя. + \en Version, last by default. \~ + */ + MbLoftedSurface( const RPArray & initCurves, + bool vc, const MbVector3D & v1, const MbVector3D & v2, bool same, + VERSION version = Math::DefaultMathVersion() ); + /** \brief \ru Конструктор поверхности. + \en Constructor of surface. \~ + \details \ru Конструктор поверхности по массиву профильных кривых, массиву параметров, направляющим векторам и замкнутости по v. + \en Constructor of surface by array of profile curves, array of parameters, guide vectors and closedness by v. \~ + \param[in] initCurves - \ru Множество задающих кривых. + \en Set of driving curves. \~ + \param[in] initParams - \ru Множество параметров, соответствующих задающим кривым. + \en Set of parameters corresponding to driving curves. \~ + \param[in] vc - \ru Замкнутость поверхности по v. + \en Surface closedness by v. \~ + \param[in] v1 - \ru Направляющий вектор. + \en Guide vector. \~ + \param[in] v2 - \ru Направляющий вектор. + \en Guide vector. \~ + \param[in] setNormal1 - \ru Установить нормаль поверхности в начале ортогонально плоскости XoY локальной системы координат первой кривой семейства, если кривая точечная. + \en Set the surface normal at the beginning orthogonal to the XoY plane in the local coordinate system of the first family curve, if it is point curve. \~ + \param[in] setNormal2 - \ru Установить нормаль поверхности в конце ортогонально плоскости XoY локальной системы координат последней кривой семейства, если кривая точечная. + \en Set the surface normal at the end orthogonal to the XoY plane in the local coordinate system of the last family curve, if it is point curve. \~ + \param[in] derFactor1 - \ru Множитель длины производной в начале, если первая кривая семейства точечная. По умолчанию 1.0. + \en The derivative length factor at the beginning, if the first family curve is point curve. The default is 1.0. \~ + \param[in] derFactor2 - \ru Множитель длины производной в конце, если последняя кривая семейства точечная. По умолчанию 1.0. + \en The derivative length factor at the end, if the last family curve is point curve. The default is 1.0. \~ + \param[in] directSurf1 - \ru Ось направления движения поверхности на начальном участке при установке нормали. По умолчанию соединяет центры концевого и ближайшего сечений. + \en Direction axis of the surface progress in the initial section when setting the normal. By default, it connects the centers of the end curve and the nearest curve. \~ + \param[in] directSurf2 - \ru Ось направления движения поверхности на конечном участке при установке нормали. По умолчанию соединяет центры концевого и ближайшего сечений. + \en Direction axis of the surface progress in the final section when setting the normal. By default, it connects the centers of the end curve and the nearest curve. \~ + \param[in] same - \ru Определяет, надо ли копировать профильные кривые: true - использовать полученные кривые без копирования, false - использовать копии. + \en Determines whether to copy profile curves: true - use obtained curves without copying, false - use copies. \~ + \param[in] version - \ru Версия, по умолчанию - последняя. + \en Version, last by default. \~ + */ + MbLoftedSurface( const RPArray & initCurves, const SArray & initParams, bool vc, + const MbVector3D & v1, const MbVector3D & v2, bool setNormal1, bool setNormal2, double derFactor1, double derFactor2, + const MbVector3D & directSurf1, const MbVector3D & directSurf2, bool same, VERSION version = Math::DefaultMathVersion() ); +protected: // \ru Конструкторы для наследников \en Constructors for inheritors + /** \brief \ru Конструктор поверхности. + \en Constructor of surface. \~ + \details \ru Конструктор поверхности по набору профильных кривых. + \en Constructor of surface by family of profile curves. \~ + \param[in] initCurves - \ru Множество задающих кривых. + \en Set of driving curves. \~ + \param[in] same - \ru Определяет, надо ли копировать профильные кривые: true - использовать полученные кривые без копирования, false - использовать копии. + \en Determines whether to copy profile curves: true - use obtained curves without copying, false - use copies. \~ + \param[in] version - \ru Версия, по умолчанию - последняя. + \en Version, last by default. \~ + */ + MbLoftedSurface( const RPArray & initCurves, bool same, VERSION version = Math::DefaultMathVersion() ); + /** \brief \ru Конструктор поверхности. + \en Constructor of surface. \~ + \details \ru Конструктор поверхности по массиву профильных кривых и массиву параметров. + \en Constructor of surface by array of profile curves and array of parameters. \~ + \param[in] initParams - \ru Множество параметров, соответствующих задающим кривым. + \en Set of parameters corresponding to driving curves. \~ + \param[in] initCurves - \ru Множество задающих кривых. + \en Set of driving curves. \~ + \param[in] same - \ru Определяет, надо ли копировать профильные кривые: true - использовать полученные кривые без копирования, false - использовать копии. + \en Determines whether to copy profile curves: true - use obtained curves without copying, false - use copies. \~ + \param[in] version - \ru Версия, по умолчанию - последняя. + \en Version, last by default. \~ + */ + MbLoftedSurface( const SArray & initParams, const RPArray & initCurves, bool same, VERSION version = Math::DefaultMathVersion() ); + /// \ru Конструктор-копия. \en Copy constructor. + MbLoftedSurface( const MbLoftedSurface &, MbRegDuplicate * reg ); +private: + MbLoftedSurface( const MbLoftedSurface & ); // \ru Не реализовано. \en Not implemented. +public: + virtual ~MbLoftedSurface(); + +public: + VISITING_CLASS( MbLoftedSurface ); + + /** \ru \name Общие функции геометрического объекта + \en \name Common functions of a geometric object + \{ */ + virtual MbeSpaceType IsA() const; // \ru Тип элемента \en A type of element + virtual MbeSpaceType Type() const; // \ru Тип элемента \en A type of element + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента \en Create a copy of the element + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Равны ли объекты \en Whether the objects are equal + virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным \en Make equal + virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными \en Whether the objects are similar + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице \en Transform element according to the matrix + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг \en Translation + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси \en Rotate around an axis + virtual void Refresh(); + + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта \en Get properties of the object + virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта \en Set properties of the object + virtual void GetBasisItems ( RPArray & s ); // \ru Дать базовые объекты \en Get the base objects + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + /** \} */ + + /** \ru \name Функции описания области определения поверхности + \en \name Functions for surface domain description + \{ */ + virtual double GetUMin () const; // \ru Вернуть минимальное значение параметра u \en Return the minimum value of parameter u + virtual double GetVMin () const; // \ru Вернуть минимальное значение параметра v \en Return the minimum value of parameter v + virtual double GetUMax () const; // \ru Вернуть максимальное значение параметра u \en Return the maximum value of parameter u + virtual double GetVMax () const; // \ru Вернуть максимальное значение параметра v \en Return the maximum value of parameter v + virtual bool IsUClosed() const; // \ru Замкнута ли поверхность по параметру u. \en Whether the surface is closed by parameter u. + virtual bool IsVClosed() const; // \ru Замкнута ли поверхность по параметру v. \en Whether the surface is closed by parameter v. + virtual double GetUPeriod() const; // \ru Период для замкнутой поверхности или 0. \en Period for closed surface or 0. + virtual size_t GetUCount() const; + virtual size_t GetVCount() const; + // \ru Существует ли полюс на границе параметрической области \en Whether there is pole on boundary of parametric region + virtual bool GetPoleUMin() const; + virtual bool GetPoleUMax() const; + virtual bool GetPoleVMin() const; + virtual bool GetPoleVMax() const; + virtual bool IsPole( double u, double v ) const; // \ru Является ли точка особенной \en Whether the point is special + /** \} */ + + /** \ru \name Функции для работы в области определения поверхности + Функции PointOn, Derive... поверхностей корректируют параметры + при выходе их за пределы прямоугольной области определения параметров.\n + \en \name Functions for working at surface domain + Functions PointOn, Derive... of surfaces correct parameters + when they are out of bounds of rectangular domain of parameters.\n + \{ */ + virtual void PointOn ( double & u, double & v, MbCartPoint3D & ) const; // \ru Точка на поверхности \en Point on the surface + virtual void DeriveU ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по u \en First derivative with respect to u + virtual void DeriveV ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по v \en First derivative with respect to v + virtual void DeriveUU ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по u \en Second derivative with respect to u + virtual void DeriveVV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по v \en Second derivative with respect to v + virtual void DeriveUV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv + virtual void DeriveUUU( double & u, double & v, MbVector3D & ) const; // \ru Третья производная по u \en Third derivative with respect to u + virtual void DeriveUUV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная по v \en Third derivative with respect to v + virtual void DeriveUVV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная по uv \en Third derivative with respect to uv + virtual void DeriveVVV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная по uv \en Third derivative with respect to uv + /** \} */ + + /** \ru \name Функции для работы внутри и вне области определения поверхности + функции _PointOn, _Derive... поверхностей не корректируют + параметры при выходе их за пределы прямоугольной области определения параметров. + \en \name Functions for working inside and outside the surface's domain + functions _PointOn, _Derive... of surfaces don't correct + parameters when they are out of bounds of rectangular domain of parameters. + \{ */ + virtual void _PointOn ( double u, double v, MbCartPoint3D & ) const; // \ru Точка на расширенной поверхности \en Point on the extended surface + virtual void _DeriveU ( double u, double v, MbVector3D & ) const; // \ru Первая производная по u \en First derivative with respect to u + virtual void _DeriveV ( double u, double v, MbVector3D & ) const; // \ru Первая производная по v \en First derivative with respect to v + virtual void _DeriveUU ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по u \en Second derivative with respect to u + virtual void _DeriveVV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по v \en Second derivative with respect to v + virtual void _DeriveUV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv + virtual void _DeriveUUU( double u, double v, MbVector3D & ) const; // \ru Третья производная по u \en Third derivative with respect to u + virtual void _DeriveUUV( double u, double v, MbVector3D & ) const; // \ru Третья производная по v \en Third derivative with respect to v + virtual void _DeriveUVV( double u, double v, MbVector3D & ) const; // \ru Третья производная по uv \en Third derivative with respect to uv + virtual void _DeriveVVV( double u, double v, MbVector3D & ) const; // \ru Третья производная по uv \en Third derivative with respect to uv + /** \} */ + /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. + \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. + \{ */ + virtual void Explore( double & u, double & v, bool ext, + MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, + MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; + /** \} */ + + /** \ru \name Функции движения по поверхности + \en \name Functions of moving along the surface + \{ */ + virtual double StepU ( double u, double v, double sag ) const; // \ru Вычисление шага аппроксимации с учетом радиуса кривизны \en Calculation of step of approximation with consideration of curvature radius + virtual double StepV ( double u, double v, double sag ) const; // \ru Вычисление шага аппроксимации с учетом радиуса кривизны \en Calculation of step of approximation with consideration of curvature radius + virtual double DeviationStepU( double u, double v, double sag ) const; // \ru Вычисление шага по u при пересечении поверхностей \en Calculation of step by u while intersecting surfaces + virtual double DeviationStepV( double u, double v, double sag ) const; // \ru Вычисление шага по u при пересечении поверхностей \en Calculation of step by u while intersecting surfaces + /** \} */ + + /** \ru \name Общие функции поверхности + \en \name Common functions of surface + \{ */ + virtual bool IsPlanar() const; // \ru Является ли поверхность плоской \en Whether the surface is planar + virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменение носителя \en Changing of carrier + virtual void CalculateGabarit( MbCube & ) const; // \ru Рассчитать габарит поверхности \en Calculate bounding box of surface + + // \ru Подобные ли поверхности для объединения (слива) \en Whether the surfaces to union (joining) are similar + virtual bool IsSpecialSimilarToSurface( const MbSurface & surf, VERSION version, double precision = METRIC_PRECISION ) const; // \ru Специальный случай \en Special case + + // \ru Построить касательные и нормальные плейсменты конструктивных плоскостей \en Construct tangent and normal placements of constructive planes + virtual bool CreateNormalPlacements ( const MbVector3D & axisZ, double angle, SArray & places ) const; + virtual bool CreateTangentPlacements( const MbVector3D & axisZ, SArray & places ) const; + virtual bool GetCylinderAxis( MbAxis3D & axis ) const; // \ru Дать ось вращения для поверхности \en Get a rotation axis of a surface + + virtual MbSplineSurface * NurbsSurface( double, double, double, double, bool bmatch = false ) const; // \ru NURBS копия поверхности \en NURBS copy of a surface + virtual MbSurface * Offset( double d, bool same ) const; // \ru Построить смещенную поверхность \en Create a shifted surface + + virtual MbCurve3D * CurveU( double v, MbRect1D * pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии v = const \en Spatial copy of 'v = const'-line + virtual MbCurve3D * CurveV( double u, MbRect1D * pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии u = const \en Spatial copy of 'u = const'-line + + virtual size_t GetUMeshCount() const; // \ru Выдать количество полигонов по u \en Get the count of polygons by u + virtual size_t GetVMeshCount() const; // \ru Выдать количество полигонов по v \en Get the count of polygons by v + + // \ru Определение разбивки параметрической области поверхности вертикалями и горизонталями \en Determine splitting of parametric region of surface by vertical and horizontal lines + virtual void GetTesselation( const MbStepData & stepData, + double u1, double u2, double v1, double v2, + SArray & uu, SArray & vv ) const; + + // \ru Найти ближайшую проекцию точки на поверхность или ее продолжение по заданному начальному приближению. \en Find the neares projection of a point onto the surface. + virtual bool NearPointProjection ( const MbCartPoint3D & pnt, double & u, double & v, bool ext, MbRect2D * uvRange = NULL ) const; + // \ru Найти все проекции точки на поверхность вдоль вектора в любом из двух направлений. \en Find all a point projection onto the surface along a vector in either of two directions. + virtual void DirectPointProjection( const MbCartPoint3D & pnt, const MbVector3D & vect, SArray & uv, bool ext, MbRect2D * uvRange = NULL ) const; + + virtual bool IsLineU() const; // \ru Если true все производные по U выше первой равны нулю \en If true, then all the derivatives by U higher the first one are equal to zero + virtual bool IsLineV() const; // \ru Если true все производные по V выше первой равны нулю \en If true, then all the derivatives by V higher the first one are equal to zero + + // \ru Проверить параметры и загнать в область определения, если параметр вышел за полюс. + // Аналог глобальной функции _CheckParams, оптимизированный под использование кэшей. + // \en Check parameters and move them inside domain if parameter is out of pole. + // \en Check parameters. Analogue of the global function _CheckParams, optimized for caches usage. + // \param[in] surface - \ru Поверхность. \en Surface. + // \param[in] u - \ru Первый параметр. \en First parameter. + // \param[in] v - \ru Второй параметр. \en Second parameter. + virtual void CheckSurfParams( double & u, double & v ) const; + + /// \ru Получить количество кривых, на которых построена поверхность \en Get count of curves which the surface is constructed by + ptrdiff_t CurvesCount() const { return (ptrdiff_t)uCurves.Count(); } + + /** \brief \ru Получить кривую по номеру. + \en Get curve by an index. \~ + \details \ru Получить кривую по номеру. \n + \en Get curve by an index. \n \~ + \param[in] ind - \ru Порядковый номер кривой в массиве кривых uCurves. + \en Index of curve in uCurves array of curves. \~ + \return \ru Константная кривая. + \en The constant curve. \~ + */ + const MbCurve3D * GetCurve( ptrdiff_t ind ) const { return (ind >= 0 && ind < (ptrdiff_t)uCurves.Count()) ? uCurves[ind] : NULL; } + /** \brief \ru Получить кривую для редактирования по номеру. + \en Get curve for editing by an index. \~ + \details \ru Получить кривую для редактирования по номеру. \n + \en Get curve for editing by an index. \n \~ + \param[in] ind - \ru Порядковый номер кривой в массиве кривых uCurves. + \en Index of curve in uCurves array of curves. \~ + \return \ru Кривая. + \en A curve. \~ + */ + MbCurve3D * SetCurve( ptrdiff_t ind ) { return (ind >= 0 && ind < (ptrdiff_t)uCurves.Count()) ? uCurves[ind] : NULL; } + /** \brief \ru Получить параметр по номеру. + \en Get parameter by an index. \~ + \details \ru Получить параметр по номеру.\n + \en Get parameter by an index.\n \~ + \param[in] ind - \ru Порядковый номер параметра в массиве параметров vParams. + \en Index of parameter in vParams array of parameters. \~ + \return \ru Значение параметра. + \en A parameter value. \~ + */ + double GetParam( ptrdiff_t ind ) const { return (ind >= 0 && ind < (ptrdiff_t)vParams.Count()) ? vParams[ind] : 0.0; } + /** \brief \ru Заполнить массив параметрами. + \en Fill an array by parameters. \~ + \details \ru Заполнить массив параметрами. \n + \en Fill an array by parameters. \n \~ + \param[in,out] params - \ru Множество для заполнения параметрами. + \en A set to fill by parameters. \~ + */ + void GetParams( SArray & params ) const { params = vParams; } + /** \brief \ru Заполнить массив признаков одинаковых кривых. + \en Fill array of attributes of similar curves. \~ + \details \ru Заполнить массив признаков одинаковых кривых. \n + \en Fill array of attributes of similar curves. \n \~ + \param[in,out] labels - \ru Множество для заполнения. + \en A set to fill. \~ + */ + void GetLabels( SArray & labels ) const { labels = vLabels; } + + /// \ru Направление производной в начале незамкнутой поверхности. Если не задано, то нулевой длины. \en The direction of derivative at the beginning of the open surface. If it isn't set, then its length is zero. + const MbVector3D & GetDerive1() const { return border1.derive; } + ///< \ru Направление производной в конце незамкнутой поверхности. Если не задано, то нулевой длины. \en The direction of derivative at the end of the open surface. If it isn't set, then its length is zero. + const MbVector3D & GetDerive2() const { return border2.derive; } + + bool IsEqualLabels() const; ///< \ru Определить, есть ли одинаковые кривые. \en Determine whether there are similar curves. + /** \brief \ru Определить, есть ли кривые, одинаковые с кривой под номером ind. + \en Determine whether there are curves similar to curve with 'ind' index. \~ + \details \ru Определить, есть ли кривые, одинаковые с кривой под номером ind. \n + \en Determine whether there are curves similar to curve with 'ind' index. \n \~ + \param[in] ind - \ru Номер кривой для сравнения. + \en An index of curve for comparison. \~ + \return \ru true - Если в массиве есть кривые, одинаковые с кривой под номером ind. + \en True - If there are curves similar to curve with 'ind' index in array. \~ + */ + bool IsEqualLabels( ptrdiff_t ind ) const; + + + /** \brief \ru Определить, можно ли создать эквидистантную поверхность. + \en Determine whether it is possible to create an offset surface. \~ + \details \ru Определить, можно ли создать эквидистантную поверхность.\n + \en Determine whether it is possible to create an offset surface.\n \~ + \param[in] h - \ru Величина смещения. + \en The offset distance. \~ + \param[in] uLimBeg - \ru Нижняя граница по u области, к которой надо построить эквидистантную поверхность. + \en Lower bound of region by u which offset surface is necessary to construct to. \~ + \param[in] uLimEnd - \ru Верхняя граница по u области, к которой надо построить эквидистантную поверхность. + \en Upper bound of region by u which offset surface is necessary to construct to. \~ + \param[in] vLimBeg - \ru Нижняя граница по v области, к которой надо построить эквидистантную поверхность. + \en Lower bound of region by v which offset surface is necessary to construct to. \~ + \param[in] vLimEnd - \ru Верхняя граница по v области, к которой надо построить эквидистантную поверхность. + \en Upper bound of region by v which offset surface is necessary to construct to. \~ + \return \ru true - Если в можно создать эквидистантную поверхность. + \en True - If it is possible to create an offset surface. \~ + */ + bool IsPossibleCreateThin( double h, + double uLimBeg, double uLimEnd, + double vLimBeg, double vLimEnd ) const; + /** \brief \ru Согласовать массивы признаков одинаковости кривых у смежных поверхностей. + \en Match arrays of attributes of similarity of curves between adjacent surfaces. \~ + \details \ru Согласовать массивы признаков одинаковости кривых у смежных поверхностей. \n + \en Match arrays of attributes of similarity of curves between adjacent surfaces. \n \~ + \param[in] surf - \ru Смежная поверхность. + \en Adjacent surface. \~ + \return \ru true - Если есть изменения в массиве признаков кривых хотя бы одной поверхности. + \en True - If there are changes in array of attributes of curves of at least one surface. \~ + */ + bool AgreeLabels( MbLoftedSurface & surf ); + /** \brief \ru Установлена ли нормаль на конце. + \en Is the normal set at the end. \~ + \details \ru Установлена ли нормаль на конце. \n + \en Is the normal set at the end. \n \~ + \param[in] atStart - \ru Проверить в начале иначе в конце. + \en Check at the beginning otherwise at the end. \~ + \return \ru true - Нормаль установлена. + \en True - The Normal installed. \~ + */ + bool IsSetNormal( bool atStart ) { return atStart ? border1.setNormal : border2.setNormal; } + /** \} */ + +protected: + void CheckParam( double & u, bool ext ) const; // \ru Корректировка параметров. \en Correct parameters. \~ + /** \brief \ru Определение местных координат области поверхности. + \en Determination of local coordinates of a surface region. \~ + \details \ru Определение местных координат области поверхности. \n + \en Determination of local coordinates of a surface region. \n \~ + \param[in] v - \ru Координата v на поверхности. + \en V coordinate on the surface. \~ + \param[in,out] j1 - \ru Номер ближайшей кривой с параметром, меньшим v. + \en Index of nearest curve with parameter less than v. \~ + \param[in,out] j2 - \ru Номер ближайшей кривой с параметром, большим v. + \en Index of nearest curve with parameter greater than v. \~ + \param[in,out] y1 - \ru Параметрическое расстояние от точки с координатой v до кривой j1, при условии, что расстояние между кривыми j1 и j2 равно 1. + \en Parametric distance from point with v coordinate to j1 curve provided that distance between j1 and j2 curves is equal to 1. \~ + \param[in,out] y2 - \ru Параметрическое расстояние от точки с координатой v до кривой j2, при условии, что расстояние между кривыми j1 и j2 равно 1. + \en Parametric distance from point with v coordinate to j2 curve provided that distance between j1 and j2 curves is equal to 1. \~ + \param[in,out] t1 - \ru Значение параметра для кривой j1. + \en Value of parameter for j1 curve. \~ + \param[in,out] t2 - \ru Значение параметра для кривой j2. + \en Value of parameter for j2 curve. \~ + */ + void LocalCoordinate( double & v, ptrdiff_t & j1, ptrdiff_t & j2, double & y1, double & y2, double & t1, double & t2 ) const; + /** \brief \ru Определение массива векторов кривой. + \en Determination of the array of curve vectors. \~ + \details \ru Определение массива векторов кривой. \n + \en Determination of the array of curve vectors. \n \~ + \param[in] i - \ru Номер кривой. + \en Index of curve. \~ + \param[in] u - \ru Координата u на поверхности. + \en U coordinate on the surface. \~ + \param[in] der - \ru Ссылка на массив векторов для хранения вычисленной точки и производных. + \en Reference to array of vectors to store calculated point and derivatives. \~ + \param[in] ext - \ru Можно ли продолжить кривую за границы области определения ее параметра. + \en Whether it is possible to extend curve out of its parametric domain bounds. \~ + */ + void CalculateCurve( ptrdiff_t i, double u, MbVector3D & point, bool ext, size_t numb ) const; + void CalculateCurve( ptrdiff_t i, double u, MbVector3D & pnt, MbVector3D & fir, MbVector3D * sec, bool ext ) const; + /** \brief \ru Определение массива векторов параметрa u для точки на поверхности с координатами (u, v). + \en Determination of array of vectors of u parameter for point on surface with coordinates (u, v). \~ + \details \ru Определение массива векторов параметрa u для точки на поверхности с координатами (u, v). \n + \en Determination of array of vectors of u parameter for point on surface with coordinates (u, v). \n \~ + \param[in] u - \ru Координата u на поверхности. + \en U coordinate on the surface. \~ + \param[in] j1 - \ru Номер ближайшей кривой с параметром, меньшим v. + \en Index of nearest curve with parameter less than v. \~ + \param[in] j2 - \ru Номер ближайшей кривой с параметром, большим v. + \en Index of nearest curve with parameter greater than v. \~ + \param[in] t1 - \ru Значение параметра для кривой j1. + \en Value of parameter for j1 curve. \~ + \param[in] t2 - \ru Значение параметра для кривой j2. + \en Value of parameter for j2 curve. \~ + \param[in] ext - \ru Можно ли продолжить поверхность за границы области определения ее параметров. + \en Whether it is possible to extend surface out of its parametric domain bounds. \~ + */ + void CalculateSurface( double & u, ptrdiff_t j1, ptrdiff_t j2, + double t1, double t2, bool ext, size_t numb, + MbVector3D & point1, MbVector3D & point2, + MbVector3D & vector1, MbVector3D & vector2, bool correctVectors = true ) const; + void CalculateExplore( double & u, ptrdiff_t j1, ptrdiff_t j2, + double t1, double t2, bool ext, bool boolsecond, + MbVector3D * point1, MbVector3D * point2, + MbVector3D * vector1, MbVector3D * vector2, + double * tLoft ) const; + + void ParamPoint ( double y1, double y2, double t1, double t2, double * tLoft ) const; + void ParamFirst ( double y1, double y2, double t1, double t2, double * tLoft ) const; + void ParamSecond( double y1, double y2, double t1, double t2, double * tLoft ) const; + void ParamThird ( double t1, double t2, double * tLoft ) const; + + /** \brief \ru Проверка полюсов на кривых. + \en Check poles on curves. \~ + \details \ru Определяет, есть ли полюс на границе области определения по длине кривой, определяющей границу.\n + Результат вычислений можно получить с помощью функций GetPoleUMin, GetPoleUMax, GetPoleVMin, GetPoleVMax. + \en Determines whether the pole at domain boundary by curve length determining boundary.\n + Result of calculations can be obtained with help of GetPoleUMin, GetPoleUMax, GetPoleVMin, GetPoleVMax functions. \~ + */ + bool CheckPoles( MbLoftedSurfaceAuxiliaryData * ) const; // \ru Проверка полюсов на кривых \en Check poles on curves + +private: + void Init( bool close ); + bool IsSimilarCurves( ptrdiff_t i1, ptrdiff_t i2 ) const; // \ru Определение одинаковых кривых \en Determination of similar curves + bool IsSimilarLabels( ptrdiff_t i1, ptrdiff_t i2 ) const; // \ru Определение одинаковых кривых по меткам. \en Determination of similar curves by labels. + void InitLabels(); // \ru Инициализация признаков одинаковых кривых. \en Initialization of attributes of similar curves. + + void InitNormalCondition( double derFactor, const MbVector3D & directSurf, bool isStart); // \ru Инициализация структуры граничных условий в случае установки нормали. \en Initialization structure of boundary conditions in the case of the normal setup. + MbVector3D DirByGivenNormal ( bool isStart, const MbVector3D & point1, const MbVector3D & point2 ) const; // \ru Определить вектор направления с заданной нормалью. \en Determine the direction vector with a given normal. + + void operator = ( const MbLoftedSurface & ); // \ru Не реализовано. \en Not implemented. + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbLoftedSurface ) +}; + +IMPL_PERSISTENT_OPS( MbLoftedSurface ) + +//------------------------------------------------------------------------------ +// \ru Корректировка параметров. \en Correct parameters. \~ +// --- +inline void MbLoftedSurface::CheckParam( double & u, bool ext ) const +{ + if ( !ext ) { + if ( uclosed ) { // переписана на ::floor(), т.к. подвисала на while + if ( (u < umin) || (u > umax) ) { + double pRgn = ( umax - umin ); + u -= ( ::floor((u - umin) / pRgn) * pRgn ); + } + } + else if ( u < umin ) { + u = umin; + } + else if ( u > umax ) { + u = umax; + } + } + else { + if ( u < umin && GetPoleUMin() ) { + u = umin; + } + else if ( u > umax && GetPoleUMax() ) { + u = umax; + } + } +} + + +//------------------------------------------------------------------------------ +// \ru Определение местных координат области поверхности \en Determination of local coordinates in a surface region +// --- +inline void MbLoftedSurface::LocalCoordinate( double & v, ptrdiff_t & j1, ptrdiff_t & j2, + double & y1, double & y2, + double & t1, double & t2 ) const +{ + if ( v < vmin || v > vmax ) { // \ru Параметр вне границ \en Parameter is out of bounds + if ( vclosed ) { + double tmp = vmax - vmin; + v -= ::floor((v - vmin) / tmp) * tmp; + } + else { + if ( v < vmin ) + v = vmin; + else + v = vmax; + } + } + + j1 = 0; + j2 = vParams.MaxIndex(); + + ptrdiff_t ind, delta = j2; // \ru Диапазон \en A range + + // \ru Поиск половинным делением \en Search by bisection + while ( delta > 1 ) { + ind = j1 + ( delta / (ptrdiff_t)2 ); // \ru Индекс в середине \en The index in the middle + if ( v < vParams[ind] ) // \ru Если v меньше серединного параметра \en If v is less than the middle parameter + j2 = ind; // \ru Изменить правую границу \en Change the right bound + else + j1 = ind; // \ru Изменить левую границу \en Change the left bound + delta = j2 - j1; // \ru Диапазон \en A range + } + + t1 = vParams[j1]; + t2 = vParams[j2]; + double dt = t2 - t1; + double antiDt = 1.0; + if ( dt > NULL_EPSILON ) + antiDt /= dt; + y1 = (t2 - v) * antiDt; + y2 = (v - t1) * antiDt; +} + + +//------------------------------------------------------------------------------ +// \ru Определение массива степеней параметрa точки \en Determination of array of degrees of point parameter +// --- +inline void MbLoftedSurface::ParamPoint( double y1, double y2, double t1, double t2, double * tLoft ) const +{ + double y1pow2 = y1 * y1; + double y2pow2 = y2 * y2; + double y1pow3 = y1pow2 * y1; + double y2pow3 = y2pow2 * y2; + tLoft[0] = 3.0 * y1pow2 - 2.0 * y1pow3; + tLoft[1] = 3.0 * y2pow2 - 2.0 * y2pow3; + tLoft[2] = (y1pow3 - y1pow2) * (t1-t2); + tLoft[3] = (y2pow3 - y2pow2) * (t2-t1); +} + + +//------------------------------------------------------------------------------ +// \ru Определение массива степеней параметрa производной \en Determination of array of degrees of derivative parameter +// --- +inline void MbLoftedSurface::ParamFirst( double y1, double y2, double t1, double t2, double * tLoft ) const +{ + double y1pow2 = y1 * y1; + double y2pow2 = y2 * y2; + double kdt1 = 1.0 / (t1 - t2); + double kdt2 = -kdt1; + tLoft[0] = 6.0 * (y1 - y1pow2) * kdt1; + tLoft[1] = 6.0 * (y2 - y2pow2) * kdt2; + tLoft[2] = (3.0 * y1pow2 - 2.0 * y1); + tLoft[3] = (3.0 * y2pow2 - 2.0 * y2); +} + + +//------------------------------------------------------------------------------ +// \ru Определение массива степеней параметрa второй производной \en Determination of array of degrees of second derivative parameter +// --- +inline void MbLoftedSurface::ParamSecond( double y1, double y2, double t1, double t2, double * tLoft ) const { + double d1 = 1 / (t1-t2); + double d2 = -d1; + tLoft[0] = (6 - 12 * y1) * d1 * d1; + tLoft[1] = (6 - 12 * y2) * d2 * d2; + tLoft[2] = (6 * y1 - 2) * d1; + tLoft[3] = (6 * y2 - 2) * d2; +} + + +//------------------------------------------------------------------------------ +// \ru Определение массива степеней параметрa третьей производной \en Determination of array of degrees of third derivative parameter +// --- +inline void MbLoftedSurface::ParamThird( double t1, double t2, double * tLoft ) const { + double d1 = 1 / (t1-t2); + double d2 = -d1; + double d1pow2 = d1 * d1; + double d2pow2 = d2 * d2; + tLoft[0] = -12 * d1pow2 * d1; + tLoft[1] = -12 * d2pow2 * d2; + tLoft[2] = 6 * d1pow2; + tLoft[3] = 6 * d2pow2; +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Наполнить массив v-параметров и весовых центров заданных кривых. + \en Fill array of v-parameters and weight centers of given curves. \~ + \details \ru Если все профильные кривые плоские, параметры вычисляются функцией CreateElevationParam. + Иначе параметр для каждой кривой вычисляется как координата вдоль направляющей проекции центра масс кривой на направляющую. + \en If all the profile curves are planar, then parameters are calculated by CreateElevationParam function. + Otherwise parameter for each curve is calculated as coordinate along guide projection of center of mass of curve to guide. \~ + \param[in] uCurves - \ru Множество профильных кривых. + \en Set of profile curves. \~ + \param[in] vcls - \ru Замкнута ли поверхность по параметру v. + \en Whether the surface is closed by parameter v. \~ + \param[in,out] vParams - \ru Множество параметров. + \en Set of parameters. \~ + \param[in,out] tiePnts - \ru Множество центров масс профильных кривых. Не заполняется, если в функцию передать NULL. + \en Set of centers of mass of profile curves. If giving NULL to function, then it isn't filled. \~ + \param[in] version - \ru Версия. + \en Version. \~ + \return \ru true - если массив параметров успешно создан. + \en True - if the array of parameters successfully created. \~ + \ingroup Algorithms_3D +*/ +// --- +bool CreateLoftedParams( const RPArray & uCurves, + bool vcls, + SArray & vParams, + SArray * tiePnts, + VERSION version ); + + +#endif // __SURF_LOFTED_SURFACE_H diff --git a/C3d/Include/surf_mesh_surface.h b/C3d/Include/surf_mesh_surface.h index ff22843..66e3075 100644 --- a/C3d/Include/surf_mesh_surface.h +++ b/C3d/Include/surf_mesh_surface.h @@ -23,6 +23,8 @@ class MATH_CLASS MbFunction; class MATH_CLASS MbSurfaceContiguousData; class MbPatchWorkingData; +typedef std::map MapCurveParam; +typedef std::map MapCrosses; //------------------------------------------------------------------------------ /** \brief \ru Версия реализации поверхности на сетке кривых. @@ -99,7 +101,7 @@ private: RPArray tvParams; ///< \ru Множество функций перехода к параметрам второго семейства. \en Set of transformations to parameters of second set. SArray uParams; ///< \ru Множество параметров u для задающих кривых. \en Set of parameters u for driving curves. SArray vParams; ///< \ru Множество параметров v для задающих кривых. \en Set of parameters v for driving curves. - + SArray tuCurve; ///< \ru Множество параметров uCurves[i] точек пересечения кривых. \en Set of parameters of uCurves[i] intersection points of curves. SArray tvCurve; ///< \ru Множество параметров vCurves[j] точек пересечения кривых. \en Set of parameters of vCurves[j] intersection points of curves. @@ -137,7 +139,12 @@ private: uint type3; ///< \ru Вид сопряжения, заданный на curvesV[nv-1]. \en Type of conjugation given on curvesV[nv-1]. MbeMeshSurfaceVersion version; ///< \ru Версия реализации определяет форму поверхности. \en Version of implementation determines a shape of surface. - + + MbCurve3D * extCurves[4]; ///< \ru Дополнительные кривые с соседних патчей. \en Additional curves from adjacent patches. + double extParams[4]; ///< \ru Параметры поверхности для дополнительных кривых с соседних патчей. \en Surface parameters for additional curves from adjacent patches. + bool extFlags [4]; ///< \ru Есть ли соседний патч через границу. \en Is there a adjacent patch across the border. + MbFunction * extFuncs [4]; ///< \ru Функция перехода к параметрам кривых соседних патчей. \en Mapping functions for curves from adjacent patches. + private: //------------------------------------------------------------------------------ /** \brief \ru Вспомогательные данные. @@ -185,7 +192,7 @@ public: MbMeshSurface( RPArray & initU, RPArray & initV, bool uClosed, bool vClosed, bool same, const SArray * types = NULL, - MbeMeshSurfaceVersion vers = msv_Ver1 ); + MbeMeshSurfaceVersion vers = msv_Ver3 ); /** \brief \ru Конструктор поверхности. \en Constructor of surface. \~ \details \ru Конструктор поверхности по двум семействам кривых. Каждая кривая семейства U должна пересекаться или @@ -219,7 +226,44 @@ public: SArray & parsU, SArray & parsV, bool uClosed, bool vClosed, bool same, const SArray * types = NULL, - MbeMeshSurfaceVersion vers = msv_Ver1 ); + MbeMeshSurfaceVersion vers = msv_Ver3 ); + +private: + friend class CompositeMeshShellCreator; + friend class CompositeMeshShellCreatorV19; + /** \brief \ru Конструктор поверхности с дополнительными задающими кривыми. + \en Surface constructor with additional defining curves. \~ + \details \ru Конструктор вызывается из генератора CompositeMeshShellCreator для создания + фрагментов составной поверхности по сети кривых с возможностью тангенциального + сопряжения их между собой. + \en The constructor is called from the CompositeMeshShellCreator generator to create + fragments of the composite surface by mesh of curves with the possibility of + tangential mating between them. \~ + \param[in] initU, initV - \ru Множество кривых в направлении параметра u и v. + \en Set of curves at direction of parameter u and v. \~ + \param[in] parsU, parsV - \ru Множество параметров u и v для задающих кривых. + \en Set of parameters u and v for driving curves. \~ + \param[in] uClosed, vclosed - \ru Замкнута ли поверхность по параметру u и v. + \en Whether the surface is closed by parameter u and v. \~ + \param[in] adjPatch - \ru Признаки сопряжения с соседними фрагментами через соответствующие границы. + \en Flags of mating with neighboring fragments through the corresponding borders. + \param[in] same - \ru Определяет, надо ли делать копии кривых:\n + true - использовать в объекте пришедшие в конструктор кривые не дублируя,\n + false - использовать копии кривых. + \en Determines whether to copy curves:\n + true - use curves given in the constructor in object without copying,\n + false - use copies of curves. \~ + \param[in] types - \ru Ссылка на массив с типами сопряжений на границах. + \en Reference to array with types of conjugations at boundaries. \~ + \param[in] vers - \ru Версия реализации поверхности. + \en Version of surface implementation. \~ + */ + MbMeshSurface( c3d::SpaceCurvesSPtrVector & initU, c3d::SpaceCurvesSPtrVector & initV, + c3d::DoubleVector & parsU, c3d::DoubleVector & parsV, + bool uClosed, bool vClosed, + const bool (&adjPatch)[4], + const MbeMatingType( &types )[4], + MbeMeshSurfaceVersion vers ); protected: /// \ru Конструктор-копия. \en Copy constructor. MbMeshSurface( const MbMeshSurface &, MbRegDuplicate * ); @@ -426,6 +470,19 @@ public: */ void GetVParams( SArray & params ) const { params = vParams; } + /** \brief \ru Проверить выставленный тип сопряжения. + \en Check mating type. \~ + \details \ru Проверить выставленный тип сопряжения. \n + \en Check mating type.\n \~ + \param[in] t - \ru Проверяемый тип сопряжения. + \en Mating type to check. \~ + \param[in] n - \ru Номер сопряжения (от 0 до 3). + \en Mating type number (0 to 3). \~ + \return \ru Возвращает true, если такой тип сопряжения установлен. + \en Returns true if this mating type is set. \~ + */ + bool IsMatingType( MbeMatingType t, size_t n ) const; + /** \brief \ru Получить версию алгоритма расчета поверхности. \en Get version of the algorithm for calculating the surface. \~ \details \ru Получить версию алгоритма расчета поверхности.\n @@ -438,11 +495,33 @@ public: private: void AddCurvesRef(); void ReleaseCurves(); - void Init(); + void Init( bool calcParams, bool callFromMultiPatchGenerator = false ); bool CheckPoles( MbMeshSurfaceAuxiliaryData * ) const; // \ru Инициализировать полюсы на границе параметрической области. \en Initialize poles on the border of parameters area. + + // \ru Аналоги публичных функций для внутреннего использования (используют присланный кэш). \en Analongs of public functions for internal use (use the sent cache). + bool GetPoleUMin( MbMeshSurfaceAuxiliaryData * ) const; // \ru Существует ли полюс на границе параметрической области \en Whether there is pole on boundary of parametric region + bool GetPoleUMax( MbMeshSurfaceAuxiliaryData * ) const; // \ru Существует ли полюс на границе параметрической области \en Whether there is pole on boundary of parametric region + bool GetPoleVMin( MbMeshSurfaceAuxiliaryData * ) const; // \ru Существует ли полюс на границе параметрической области \en Whether there is pole on boundary of parametric region + bool GetPoleVMax( MbMeshSurfaceAuxiliaryData * ) const; // \ru Существует ли полюс на границе параметрической области \en Whether there is pole on boundary of parametric region + void _DeriveU( double u, double v, MbVector3D & p, MbMeshSurfaceAuxiliaryData * ) const; // \ru Первая производная по u \en First derivative with respect to u + void _DeriveV( double u, double v, MbVector3D & p, MbMeshSurfaceAuxiliaryData * ) const; // \ru Первая производная по v \en First derivative with respect to v + void DeriveU( double & u, double & v, MbVector3D & p, MbMeshSurfaceAuxiliaryData * ) const; // \ru Первая производная по u \en First derivative with respect to u + void DeriveV( double & u, double & v, MbVector3D & p, MbMeshSurfaceAuxiliaryData * ) const; // \ru Первая производная по v \en First derivative with respect to v + void DeriveUU( double & u, double & v, MbVector3D & p, MbMeshSurfaceAuxiliaryData * ) const; // \ru Вторая производная по u \en Second derivative with respect to u + void DeriveVV( double & u, double & v, MbVector3D & p, MbMeshSurfaceAuxiliaryData * ) const; // \ru Вторая производная по v \en Second derivative with respect to v + void DeriveUV( double & u, double & v, MbVector3D & p, MbMeshSurfaceAuxiliaryData * ) const; // \ru Вторая производная по uv \en Second derivative with respect to uv + void DeriveUUU( double & u, double & v, MbVector3D & p, MbMeshSurfaceAuxiliaryData * ) const; // \ru Третья производная \en Third derivative + void DeriveUUV( double & u, double & v, MbVector3D & p, MbMeshSurfaceAuxiliaryData * ) const; // \ru Третья производная \en Third derivative + void DeriveUVV( double & u, double & v, MbVector3D & p, MbMeshSurfaceAuxiliaryData * ) const; // \ru Третья производная \en Third derivative + void DeriveVVV( double & u, double & v, MbVector3D & p, MbMeshSurfaceAuxiliaryData * ) const; // \ru Третья производная \en Third derivative + bool IsLineU( MbMeshSurfaceAuxiliaryData * ) const; // \ru Если true все производные по U выше первой равны нулю \en If true, then all the derivatives by U higher the first one are equal to zero + bool IsLineV( MbMeshSurfaceAuxiliaryData * ) const; // \ru Если true все производные по V выше первой равны нулю \en If true, then all the derivatives by V higher the first one are equal to zero + void _PointOn( double u, double v, MbCartPoint3D & p, MbMeshSurfaceAuxiliaryData * ) const; // \ru Точка на поверхности \en Point on the surface + void PointOn( double & u, double & v, MbCartPoint3D & p, MbMeshSurfaceAuxiliaryData * ) const; // \ru Точка на поверхности \en Point on the surface + // \ru Определить местные координаты области поверхности. \en Determine local coordinates of surface region. void LocalCoordinate( double u, double v, double & ul, double & vl, size_t & i0,size_t & j0,size_t & i1, size_t & j1, MbMeshSurfaceAuxiliaryData * ucache = NULL ) const; - void LocalCoordinate_v2( double u, double v, double & ul, double & vl, size_t & i0, size_t & j0, size_t ord, MbMeshSurfaceAuxiliaryData * ucache ) const; + void LocalCoordinate_v2( double u, double v, double & ul, double & vl, size_t & i0, size_t & j0, size_t & i1, size_t & j1, size_t ord, MbMeshSurfaceAuxiliaryData * ucache ) const; // \ru Вычислить вспомогательные вектора производных вдоль U кривых патча. \en Calculate auxiliary vectors of derivatives along U curves of patch. void CalculateAlongU( const double & ul, const size_t & j0, const size_t & j1, MbMeshSurfaceAuxiliaryData * ucache ) const; void CalculateAlongU_v2( const double & u, const size_t & j0, const size_t & j1, size_t indP, MbMeshSurfaceAuxiliaryData * ucache ) const; @@ -456,11 +535,17 @@ private: void AdditionalCalculateVertex( size_t i0, size_t j0, size_t i1, size_t j1, MbMeshSurfaceAuxiliaryData * ucache ) const; // \ru Создать массив смешанных производных. \en Create an array of mixed derivatives. void CreateTwists (); - void CreateTwists_v1(); + void CreateTwists_v1( const MapCrosses & crosses, const MapCrosses & outCrosses ); // \ru Аппроксимировать смешанную производную. \en Approximate mixed derivative. void ApproxTwistBilinear ( size_t iL, size_t iR, size_t jD, size_t jU, size_t iCent, size_t jCent, MbVector3D & resTwist ); void ApproxTwistBilinear_v1( size_t iCent, size_t jCent, MbVector3D & resTwist ); void ApproximateOneCornerTwist_v1( size_t iL, size_t iR, size_t jD, size_t jU, size_t corner, MbVector3D & resTwist ) const; + void ApproxTwistBilinear_v3( ptrdiff_t i, ptrdiff_t j, MbVector3D & resTwist, const MapCrosses & crosses, const MapCrosses & outCrosses ); + // \ru Рассчитать частную производную в вершине ячейки. \en Calculate the partial derivative at the top of the cell. + void CalculateCellDerivative_v3( MbVector3D & res, bool dirU, ptrdiff_t uind, ptrdiff_t vind, bool isOut, + const MapCrosses & crosses, const MapCrosses & outCrosses ) const; + // \ru Рассчитать координаты вершины ячейки. \en Calculate cell vertex coordinates. + void CalculateCellPoint_v3( MbCartPoint3D & res, ptrdiff_t uind, ptrdiff_t vind, const MapCrosses & crosses ) const; // \ru Вычислить вспомогательные массивы трансверсальных производных. \en Calculate auxiliary arrays of transversal derivatives. void CalculateTransDiffs ( double ul, double vl, size_t i0, size_t j0, size_t i1, size_t j1, MbMeshSurfaceAuxiliaryData * ucache ) const; // \ru Вычислить выводящую производную с линии V = const и ее первые производные по U. \en Calculate leading out derivative from 'V = const'-line and its first derivatives by U. @@ -568,26 +653,28 @@ private: double & t1min, double & t1max, double & t3min, double & t3max, bool dir = true ) const; - void ExactNormal( double u, double v, const MbVector3D & uDer, const MbVector3D & vDer, MbVector3D & nor ) const; + void ExactNormal( double u, double v, const MbVector3D & uDer, const MbVector3D & vDer, MbVector3D & nor, MbMeshSurfaceAuxiliaryData * ) const; // \ru Проверить параметры и в случае выхода за пределы загнать в область определения. // \en Check parameters and if it is out of limits, then drive it to domain void CheckParams( double & u, double & v ) const; // \ru Проверить параметры и в случае захода за полюс или выходе за период загнать в область определения. // \en Check parameters and if it is out of pole or it is out of period, then drive it to the domain region. - void CheckParamsEx( double & u, double & v ) const; - - // \ru Нормализовать семейство кривых по параметрической длине \en Normalize family of curves by parametric length - void CreateReparamCurves( const MbCurve3D & curveBeg, - const MbCurve3D & curveEnd, - double tBeg, - double tEnd, - bool closed, - RPArray & curves ) ; + void CheckParamsEx( double & u, double & v, MbMeshSurfaceAuxiliaryData * ucache ) const; // \ru Попытаться вычислить шаг по U, исходя из шагов по соответствующим операторам Loft-ов \en Try to calculate step by U through steps of corresponding Loft operators bool SurfDeviationStepU( double & u, double & v, double ang, double & resStep, MbMeshSurfaceAuxiliaryData * ucache ) const; // \ru Попытаться вычислить шаг по V, исходя из шага по соответствующим операторам Loft-ов \en Try to calculate step by V through steps of corresponding Loft operators bool SurfDeviationStepV( double & u, double & v, double ang, double & resStepv, MbMeshSurfaceAuxiliaryData * ucache ) const; + // \ru Создать таблицу пересечений для дополнительных кривых. \en Create an intersection table for additional curves. + void CreateCurvesCrossTable( MapCrosses & crosses, MapCrosses & outCrosses ); + // \ru Создать функции перехода к параметрам кривых. \en Create functions for mapping to curve parameters. + void CreateParamFunctions( bool dirU, const MapCrosses & crosses ); + // \ru Расчет производных в точке для образующей кривой. \en Calculation of derivatives at a point for a generating curve. + void GeneratrixCurveExplore_v3( bool dirU, size_t ind, double t, + MbCartPoint3D & p, MbVector3D & fir, MbVector3D & sec, MbVector3D & thir ) const; + // \ru Инициализировать массивы расширения. \en Init expansion arrays. + void InitExtArrays( const bool (*adjPatch)[4] = NULL ); + /** \} */ DECLARE_PERSISTENT_CLASS_NEW_DEL( MbMeshSurface ) @@ -627,24 +714,24 @@ inline void MbMeshSurface::CheckParams( double & u, double & v ) const //------------------------------------------------------------------------------ // \ru Проверить параметры и в случае захода за полюс или выходе за период загнать в область определения. \en Check parameters and if it is out of pole or it is out of period, then drive it to the domain region. // --- -inline void MbMeshSurface::CheckParamsEx( double & u, double & v ) const +inline void MbMeshSurface::CheckParamsEx( double & u, double & v, MbMeshSurfaceAuxiliaryData * ucache ) const { - if ( GetPoleUMin() ) { + if ( GetPoleUMin( ucache ) ) { const double & umin_ = uParams[0]; if ( u < umin_ ) u = umin_; } - if ( GetPoleUMax() ) { + if ( GetPoleUMax( ucache ) ) { const double & umax_ = uParams[uParams.MaxIndex()]; if ( u > umax_ ) u = umax_; } - if ( GetPoleVMin() ) { + if ( GetPoleVMin( ucache ) ) { const double & vmin_ = vParams[0]; if ( v < vmin_ ) v = vmin_; } - if ( GetPoleVMax() ) { + if ( GetPoleVMax( ucache ) ) { const double & vmax_ = vParams[vParams.MaxIndex()]; if ( v > vmax_ ) v = vmax_; @@ -665,21 +752,67 @@ inline void MbMeshSurface::CheckParamsEx( double & u, double & v ) const //------------------------------------------------------------------------------ -// \ru Пересечение MbMeshSurface с MbMeshSurface. \en Intersection of MbMeshSurface with MbMeshSurface. +// \ru Получить граничные кривые MbMeshSurface. \en Get boundary curves of MbMeshSurface. // --- -inline void GetBoundCurves( const MbMeshSurface & mesh, RPArray & meshCurves ) //-V801 +template +void GetBoundCurves( const MbMeshSurface & mesh, ConstCurvesVector & meshCurves ) //-V801 { // \ru Не менять порядок выдачи кривых \en Not to change an order of output of curves + meshCurves.reserve( meshCurves.size() + 4 ); + c3d::ConstSpaceCurveSPtr meshCurve; + size_t cnt = mesh.GetUCurvesCount(); if ( cnt > 0 ) { - meshCurves.Add( mesh.GetUCurve( 0 ) ); - if ( cnt > 1 ) - meshCurves.Add( mesh.GetUCurve( --cnt ) ); + meshCurve = mesh.GetUCurve( 0 ); + meshCurves.push_back( meshCurve ); + if ( cnt > 1 ) { + meshCurve = mesh.GetUCurve( --cnt ); + meshCurves.push_back( meshCurve ); + } } cnt = mesh.GetVCurvesCount(); if ( cnt > 0 ) { - meshCurves.Add( mesh.GetVCurve( 0 ) ); - if ( cnt > 1 ) - meshCurves.Add( mesh.GetVCurve( --cnt ) ); + meshCurve = mesh.GetVCurve( 0 ); + meshCurves.push_back( meshCurve ); + if ( cnt > 1 ) { + meshCurve = mesh.GetVCurve( --cnt ); + meshCurves.push_back( meshCurve ); + } + } +} + + +//------------------------------------------------------------------------------ +// \ru Получить граничные кривые MbMeshSurface. \en Get boundary curves of MbMeshSurface. +// --- +template +void GetBoundCurves( const MbMeshSurface & mesh, ConstCurvesVector & meshCurves, c3d::BoolVector & tangentMatingFlags ) //-V801 +{ // \ru Не менять порядок выдачи кривых \en Not to change an order of output of curves + meshCurves.reserve( meshCurves.size() + 4 ); + tangentMatingFlags.reserve( tangentMatingFlags.size() + 4 ); + c3d::ConstSpaceCurveSPtr meshCurve; + + size_t cnt = mesh.GetUCurvesCount(); + if ( cnt > 0 ) { + meshCurve = mesh.GetUCurve( 0 ); + meshCurves.push_back( meshCurve ); + tangentMatingFlags.push_back( mesh.IsMatingType( trt_Tangent, 0 ) ); + + if ( cnt > 1 ) { + meshCurve = mesh.GetUCurve( --cnt ); + meshCurves.push_back( meshCurve ); + tangentMatingFlags.push_back( mesh.IsMatingType( trt_Tangent, 2 ) ); + } + } + cnt = mesh.GetVCurvesCount(); + if ( cnt > 0 ) { + meshCurve = mesh.GetVCurve( 0 ); + meshCurves.push_back( meshCurve ); + tangentMatingFlags.push_back( mesh.IsMatingType( trt_Tangent, 1 ) ); + if ( cnt > 1 ) { + meshCurve = mesh.GetVCurve( --cnt ); + meshCurves.push_back( meshCurve ); + tangentMatingFlags.push_back( mesh.IsMatingType( trt_Tangent, 3 ) ); + } } } @@ -702,4 +835,33 @@ inline void GetBoundCurves( const MbMeshSurface & mesh, RPArray bool MakeMonotoneParams( SArray & params, double period ); +/** \brief \ru Расчет параметризации поверхности. \en Calculate mesh surface parameters. + \details \ru Расчет параметров поверхности для выбранного направления. + \en Calculation of surface parameters for the selected direction.\~ + \param[in] dirU - \ru Выбранное направление поверхности. + \en Selected surface direction. \~ + \param[in] cls - \ru Замкнутость поверхности в направлении dirU. + \en Is the surface closed in the direction dirU. \~ + \param[in] curves - \ru Семейство кривых dirU. + \en Curves family dirU. \~ + \param[in] tcurves- \ru Семейство кривых !dirU. + \en Curves family !dirU. \~ + \param[in] tCurve - \ru Таблица пересечений кривых u и v. + \en The table of intersection of the curves u and v. \~ + \param[out] sParams - \ru Расчитанный набор параметров. + \en The calculated set of parameters. \~ +*/ +void SetParams_v3( bool dirU, bool cls, const RPArray & curves, const RPArray & tcurves, + const SArray & tCurve, SArray & sParams ); + + +/** \brief \ru Какую версию поверхности создавать в зависимости от версии математики. + \en Which version of the surface to create depending on the version of mathematics. \~ + \param[in] mathVers - \ru Версия математики. + \en Version of mathematics. \~ + \return \ru Версия поверхности по сети кривых. + \en Mesh surface version. \~ +*/ +MbeMeshSurfaceVersion GetMeshSurfaceVersion( const VERSION & mathVers ); + #endif // __SURF_MESH_SURFACE_H diff --git a/C3d/Include/surf_offset_surface.h b/C3d/Include/surf_offset_surface.h index b9ab675..b784f34 100644 --- a/C3d/Include/surf_offset_surface.h +++ b/C3d/Include/surf_offset_surface.h @@ -27,7 +27,7 @@ class MATH_CLASS MbSurfaceContiguousData; Параметры offsetUminVmin, offsetUmaxVmin, offsetUminVmax, offsetUmaxVmax могут быть как больше нуля, так и меньше нуля. Область определения параметров эквидистантной поверхности может отличаться от область определения параметров базовой поверхности basisSurface. Это отличие задано параметрами deltaUmin, deltaUmax, deltaVmin, deltaVmax. - Радиус-вектор эквидистантной поверхности описывается векторной функцией \n + Радиус-вектор эквидистантной поверхности описывается векторной функцией \n r(u,v) = basisSurface(u,v) + (Offset0(u,v) * basisSurface->Normal(u,v)). \n Базовой поверхностью для эквидистантной поверхности не может служить другая эквидистантная поверхность. В подобной ситуации выполняется переход к первичной базовой поверхности. @@ -35,7 +35,7 @@ class MATH_CLASS MbSurfaceContiguousData; 'offsetUminVmin, offsetUmaxVmin, offsetUminVmax, offsetUmaxVmax' parameters can be both greater than zero and less than zero. Domain of parameters of the offset surface can differs from domain of parameters of basisSurface base surface. This difference is given by the parameters deltaUmin, deltaUmax, deltaVmin, deltaVmax. - Radius-vector of offset surface is described by the vector function \n + Radius-vector of offset surface is described by the vector function \n r(u,v) = basisSurface(u,v) + (Offset0(u,v) * basisSurface->Normal(u,v)). \n Base surface for offset surface can't be other offset surface. In this situation it changes to the initial base surface. \~ @@ -363,7 +363,8 @@ public: // \ru Постоянное ли смещение точек? \en Is const the offset type? bool IsConstOffset() const { return ( (type == off_Empty) || (type == off_Const) ); } // \ru Величина смещения. \en The offset distance. - double GetDistance( size_t i = 0 ) const { + double GetDistance( size_t i ) const { + i = i % 4; if ( i == 1 ) return offsetUmaxVmin; else if ( i == 2 ) return offsetUminVmax; @@ -371,14 +372,18 @@ public: if ( i == 3 ) return offsetUmaxVmax; return offsetUminVmin; } + // \ru Средняя величина смещения. \en The average offset distance. + double GetDistance() const { return ( offsetUminVmin + offsetUmaxVmin + offsetUminVmax + offsetUmaxVmax ) / 4; } /** \brief \ru Установить величины смещения. \en Set offset distances. \~ \param[in] d - \ru Новая величина смещения \en New offset distance \~ */ - void SetDistance( double d, size_t i = 0 ); - + void SetDistance( double d, size_t i ); + // \ru Установить постоянную величину смещения. Set new constant offset distance. + void SetDistance( double d ); + /** \brief \ru Проверить корректность точки поверхности. \en Check the correctness of the point of a surface. \~ \details \ru Проверить корректность точки поверхности по кривизне подложки.\n diff --git a/C3d/Include/surf_plane.h b/C3d/Include/surf_plane.h index 7565580..e8642bb 100644 --- a/C3d/Include/surf_plane.h +++ b/C3d/Include/surf_plane.h @@ -27,7 +27,7 @@ class MATH_CLASS MbLine3D; Плоскость ведёт себя как бесконечная поверхность, хотя в своих данных имеет граничные значения параметров umin, umax и vmin, vmax. В отличие от других поверхностей Функции PointOn и Derive... плоскости не корректируют параметры при выходе их за пределы прямоугольной области определения параметров. \n - Радиус-вектор поверхности описывается векторной функцией \n + Радиус-вектор поверхности описывается векторной функцией \n r(u,v) = position.origin + (u position.axisX) + (v position.axisY). \n \en A plane is located on the XY coordinate plane of the local coordinate system 'position'. \n Parameters of plane are calculated from the coordinates origin 'position.origin'. @@ -35,7 +35,7 @@ class MATH_CLASS MbLine3D; A plane behaves like an infinite surface, although it has boundary parameter values umin, umax and vmin, vmax. In contrast to other surfaces functions PointOn and Derive.. of plane don't correct parameters when getting out of rectangular domain bounds. \n \n - Radius-vector of the surface is described by the vector function \n + Radius-vector of the surface is described by the vector function \n r(u,v) = position.origin + (u position.axisX) + (v position.axisY). \n \~ \ingroup Surfaces */ // --- @@ -326,7 +326,7 @@ public: // \ru Ближайшая проекция точки на поверхность в направлении вектора. \en The nearest projection of a point to the surface in direction of the vector. virtual bool NearDirectPointProjection( const MbCartPoint3D & p, const MbVector3D & vector, double & u, double & v, bool ext, MbRect2D * uvRange = NULL, bool onlyPositiveDirection = false ) const; - // \ru Пересечения с линией. \en Intersection with a line. + // \ru Пересечения с кривой. \en Intersection with a curve. virtual MbeNewtonResult CurveIntersectNewton( const MbCurve3D &, double funcEpsilon, size_t limit, double & u, double & v, double & t, bool ext0, bool ext ) const; // \ru Нахождениe точки пересечения c кривой. \en Search of a point of intersection with curve. virtual void CurveIntersection ( const MbCurve3D &, SArray & uv, SArray & tt, diff --git a/C3d/Include/surf_polysurface.h b/C3d/Include/surf_polysurface.h index 4ba7549..01da7de 100644 --- a/C3d/Include/surf_polysurface.h +++ b/C3d/Include/surf_polysurface.h @@ -200,9 +200,9 @@ public: \{ */ /** \brief \ru Получить узловой вектор по выбранному параметру. - \en Get a knot vector by the chosen parameter. \~ + \en Get a knots vector by the chosen parameter. \~ \details \ru Получить узловой вектор по выбранному параметру.\n - \en Get a knot vector by the chosen parameter.\n \~ + \en Get a knots vector by the chosen parameter.\n \~ \param[in] isU - \ru Определяет, по какой координате запрашивается узловой вектор: true - по u, false - по v. \en Determines the requested coordinate of a knot vector: true - u, false - v. \~ \param[in,out] knots - \ru Матрица для хранения узлового вектора. diff --git a/C3d/Include/surf_revolution_surface.h b/C3d/Include/surf_revolution_surface.h index 99521bf..e43466d 100644 --- a/C3d/Include/surf_revolution_surface.h +++ b/C3d/Include/surf_revolution_surface.h @@ -26,14 +26,14 @@ class MATH_CLASS MbOffsetSurface; \en Revolution surface. \~ \details \ru Поверхность вращения является кинематической поверхностью с образующей в форме дуги окружности. Поверхность вращения получена путем движения образующей кривой curve по окружности или её дуге. - Радиус-вектор поверхности описывается векторной функцией \n + Радиус-вектор поверхности описывается векторной функцией \n r(u,v) = position.origin + M(v)(curve(u) - position.origin), \n где M(v) - матрица поворота точки вокруг оси position.axisZ. \n Первый параметр поверхности совпадает с параметром образующей кривой. Второй параметр поверхности совпадает с углом поворота образующей кривой. \en A revolution surface is a swept surface with generatrix in a circle arc form. A revolution surface is obtained by moving of the generatrix 'curve' along the circle or its arc. - Radius-vector of line surface is described by the vector function \n + Radius-vector of line surface is described by the vector function \n r(u,v) = position.origin + M(v)(curve(u) - position.origin), \n where M(v) - rotate matrix by axis position.axisZ. \n The first surface parameter coincides with the parameter of generatrix. diff --git a/C3d/Include/surf_ruled_surface.h b/C3d/Include/surf_ruled_surface.h index 90117d0..1548bda 100644 --- a/C3d/Include/surf_ruled_surface.h +++ b/C3d/Include/surf_ruled_surface.h @@ -24,13 +24,13 @@ class MATH_CLASS MbExtrusionSurface; /** \brief \ru Линейчатая поверхность. \en Ruled surface. \~ \details \ru Линейчатая поверхность построена по двум кривым путём соединения их соответствующих точек отрезками прямой. - Радиус-вектор поверхности описывается векторной функцией \n + Радиус-вектор поверхности описывается векторной функцией \n r(u,v) = (1 - v) curve(u) + v sline(w(u)). \n Первый параметр поверхности совпадает с параметром кривой curve. Параметр w кривой sline пропорционален первому параметру поверхности. Вдоль второго параметра поверхность прямолинейна. \en A ruled surface is constructed on two curves by connection of its corresponding points by linear segments. - Radius-vector of surface is described by the vector function \n + Radius-vector of surface is described by the vector function \n r(u,v) = (1 - v) curve(u) + v sline(w(u)). \n First parameter of surface coincides with parameter of 'curve' curve. Parameter w of 'sline' curve is proportional to first parameter of surface. diff --git a/C3d/Include/surf_section_surface.h b/C3d/Include/surf_section_surface.h new file mode 100644 index 0000000..2aa4992 --- /dev/null +++ b/C3d/Include/surf_section_surface.h @@ -0,0 +1,473 @@ +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Поверхность заметания переменного сечения. + \en The swept mutable section surface. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __SURF_SECTION_SURFACE_H +#define __SURF_SECTION_SURFACE_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define _RO_MIN_ 0.05 // \ru Минимальный параметр переменного сечения. \en The minimum parameter of the swept section. +#define _CIRCLE_ 0.4142135623730950488016887242097 // \ru Параметр дуги окружности в сечении. \en The parameter is corresponding to the circle. +#define _PARABOLA_ 0.5 // \ru Параметр параболы в сечении. \en The parameter is corresponding to the parabola. +#define _RO_MAX_ 0.95 // \ru Максимальный параметр переменного сечения. \en The maximum parameter of the swept section. + + +class MATH_CLASS MbSurfaceWorkingData; + + +//------------------------------------------------------------------------------ +/** \brief \ru Поверхность заметания переменного сечения. + \en The swept mutable section surface. \~ + \details \ru Поверхность переменного (конического) сечения образуется путем движения плоской кривой, являющейся коническим сечением, вдоль опорной кривой. + В процессе движения форма плоской кривой меняется в соответствии с дискриминантом конического сечения. + Начало плоской кривой располагается на начальной направляющей кривой, а конец - на конечной направляющей кривой. + Плоскость переменного сечения сохраняет ортогональность опорной кривой в процессе движения. + Первый параметр поверхности совпадает с параметром опорной кривой. + Второй параметр поверхности совпадает с параметром плоской кривой. + \en The swept (conic) section surface is formed by moving the flat conic section curve along the reference curve. + In the process of movement, the shape of the flat curve changes in accordance with the discriminant of the conic section. + The beginning of the flat curve is located on the first guide curve and the end is located on the second guide curve. + The plane of the conic section preserves the orthogonality to the reference curve during movement. + First parameter of surface coincides with the parameter of reference curve. + Second parameter of surface coincides with the parameter of conic section curve. \~ + \ingroup Surfaces +*/ +// --- +class MATH_CLASS MbSectionSurface : public MbSurface { + +public: + +protected: + MbSpine * spine; ///< \ru Опорная кривая. \en The reference curve. + MbCurve3D * guide1; ///< \ru Первая направляющая кривая на первой поверхности (может быть NULL). \en The first guide curve on the first surface (may be NULL). + MbCurve3D * guide2; ///< \ru Вторая направляющая кривая на второй поверхности (может быть NULL). \en The second guide curve on the second surface (may be NULL). + std::vector curves; ///< \ru Дополнительные направляющие кривые (могут отсутствовать). \en The additional guide curves (may be empty). + MbFunction * function; ///< \ru Функция управления сечением (радиус или дискриминант, может быть NULL)). \en Section control function (radius or discriminant, may be NULL). + MbPolyCurve * pattern; ///< \ru Образующая кривая при form==cs_Shape (для других форм NULL). \en Forming curve for form==cs_Shape (NULL on other case). + std::vector shape; ///< \ru Описание сечения при form==cs_Shape (пуст в других случаях). \en Description of shape cross-section for form==cs_Shape (is empty on other case). + std::vector knots; ///< \ru Узловой вектор сплайна. \en Knot vector of the spline. + size_t order; ///< \ru Порядок сплайна (степень + 1). \en Order of spline (degree + 1). + MbeSectionShape form; ///< \ru Форма сечения поверхности при фиксированном втором параметре. \en The surface cross-section shape with the second parameter fixed. + double umin; ///< \ru Минимальное значение первого параметра u. \en Minimal value of parameter u. + double umax; ///< \ru Максимальное значение первого параметра u. \en Maximal value of parameter u. + double vmin; ///< \ru Минимальное значение второго параметра v. \en Minimal value of parameter v. + double vmax; ///< \ru Максимальное значение второго параметра v. \en Maximal value of parameter v. + bool uclosed; ///< \ru Признак замкнутости поверхности по параметру u. \en An attribute of closedness in u-parameter direction. + bool vclosed; ///< \ru Признак замкнутости поверхности по параметру v. \en An attribute of closedness in v-parameter direction. + bool poleVMin; ///< \ru Наличие полюса поверхности при vmin. \en Existence of a pole at vmin. + bool poleVMax; ///< \ru Наличие полюса поверхности при vmax. \en Existence of a pole at vmax. + + //------------------------------------------------------------------------------ + /// \ru Вспомогательные данные служат для ускорения работы объекта. \en Auxiliary data are used for fast calculations. \n \~ + // --- + class MbSectionSurfaceAuxiliaryData : public AuxiliaryData { + public: + DPtr wData; ///< \ru Рабочие данные для расчета поверхности. \en Working data for the calculation of a surface. + MbPlacement3D wPlace; ///< \ru Плоскость XY локальной системы, перпендикулярная опорной кривой в текущей точке. \en The XY plane of local coordinate system perpendicular to the reference curve at its current points. + MbCartPoint wPnt0; ///< \ru Точка на плоскости от её пересечения с первой направляющей кривой. \en The point on plane of its intersection with first guide curve on surface. + MbCartPoint wPnt1; ///< \ru Точка на плоскости от её пересечения с первой направляющей кривой. \en The point on plane of its intersection with first guide curve on surface. + MbCartPoint wPnt2; ///< \ru Точка на плоскости от её пересечения со второй направляющей кривой. \en The point on plane of its intersection with second guide curve on surface. + MbVector wVec1; ///< \ru Вектор на плоскости от её пересечения с поверхностью первой направляющей. \en The vector on plane of its intersection with surface of first guide curve. + MbVector wVec2; ///< \ru Вектор на плоскости от её пересечения с поверхностью второй направляющей. \en The vector on plane of its intersection with surface of second guide curve. + double wPar0; ///< \ru Параметр первой направляющей кривой её пересечения с плоскости. \en The parameter of the first guide curve. + double wPar1; ///< \ru Параметр первой направляющей кривой её пересечения с плоскости. \en The parameter of the first guide curve. + double wPar2; ///< \ru Параметр второй направляющей кривой её пересечения с плоскости. \en The parameter of the second guide curve. + double wPar; ///< \ru Параметр v, для которого вычислена плоскость wPlace. \en The parameter v for which was calculated the plane wPlace. + MbVector3D wOrig1; ///< \ru Первая производная по v начала координат плоскости. \en The first derivative of the v origin of the wPlace. + MbVector3D wOrig2; ///< \ru Вторая производная по v начала координат плоскости. \en The second derivative of the v origin of the wPlace. + MbVector3D wDerv1; ///< \ru Первая производная по v оси axisX плоскости. \en The first derivative of the v axisX of wPlace. + MbVector3D wDerv2; ///< \ru Первая производная по v оси axisY плоскости. \en The first derivative of the v axisY of wPlace. + MbVector3D wSecn1; ///< \ru Вторая производная по v оси axisX плоскости. \en The second derivative of the v axisX of wPlace. + MbVector3D wSecn2; ///< \ru Вторая производная по v оси axisY плоскости. \en The second derivative of the v axisY of wPlace. + std::vector wSplns; ///< \ru Рабочее множество В-сплайнов. \en The work set of B-splines. + std::vector wxy; ///< \ru Рабочее множество точек. \en The work set of the points. + std::vector wxyV; ///< \ru Рабочее множество первых производных по V. \en The work set of the firsf derivatives. + std::vector wxyVV; ///< \ru Рабочее множество вторых производных по V. \en The work set of the second derivatives. + std::vector wxyVVV; ///< \ru Рабочее множество третьих производных по V. \en The work set of the third derivatives. + double wVal; ///< \ru Параметр v, для которого вычислены производные начала и орт плоскости wPlace. \en The parameter v for which was calculated the plane origin derivative and axises derivatives. + + MbSectionSurfaceAuxiliaryData(); + MbSectionSurfaceAuxiliaryData( const MbSectionSurfaceAuxiliaryData & ); + virtual ~MbSectionSurfaceAuxiliaryData(); + + void Init(); // \ru Инициализация данных. \en Init data. + void Init( const MbSectionSurfaceAuxiliaryData & ); + void Move( const MbVector3D & ); // \ru Сдвиг. \en Move. + }; + + mutable CacheManager cache; + +protected: + /** \brief \ru Конструктор поверхности переменного сечения. + \en Swept mutable section surface constructor. \~ + \details \ru Конструктор поверхности переменного сечения по опорной кривой и двум направляющим. + \en Swept section surface constructor by reference curve and guide curves. \~ + \param[in] sp - \ru Опорная кривая. + \en The reference curve (spine). \~ + \param[in] c1 - \ru Первая направляющая кривая. + \en The first guide curve. \~ + \param[in] c2 - \ru Вторая направляющая кривая. + \en The second guide curve. \~ + \param[in] cs - \ru Дополнительные направляющие кривые (могут отсутствовать). + \en The additional guide curves (may be empty). \~ + \param[in] f - \ru Форма сечения поверхности. + \en The form of the surface section. \~ + \param[in] uBeg - \ru Минимальное значение первого параметра поверхности. + \en The minimum value of the first surface parameter. \~ + \param[in] uEnd - \ru Максимальное значение первого параметра поверхности. + \en The maximum value of the first surface parameter. \~ + \param[in] func - \ru Функция управления сечением поверхности. + \en Section control function. \~ + \param[in] patt - \ru Образующая кривая при form==cs_Shape. + \en Forming curve for form==cs_Shape. \~ + \param[in] sh - \ru Описание сечения при form==cs_Shape (пуст в других случаях). + \en Description of shape cross-section for form==cs_Shape. \~ + */ + MbSectionSurface( MbSpine & sp, MbCurve3D & c1, MbCurve3D & c2, + std::vector cs, MbeSectionShape f, + double uBeg, double uEnd, MbFunction * func, + MbPolyCurve * patt, std::vector & sh ); + + /** \brief \ru Конструктор поверхности переменного сечения. + \en Swept mutable section surface constructor. \~ + \details \ru Конструктор поверхности переменного сечения по осевой кривой и закону изменения радиуса вращения. + \en Swept mutable section surface constructor with axis curve and radius law. \~ + \param[in] sp - \ru Опорная кривая. + \en The reference curve (spine). \~ + \param[in] c0 - \ru Осевая кривая. + \en The axis curve. \~ + \param[in] f - \ru Форма сечения поверхности (cs_Round). + \en The form of the surface section (cs_Round). \~ + \param[in] uBeg - \ru Минимальное значение первого параметра поверхности (uBeg & ); // \ru Дать базовые объекты. \en Get the base objects. + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + /** \} */ + /** \ru \name Функции описания области определения поверхности + \en \name Functions for surface domain description + \{ */ + + virtual double GetUMin() const; // \ru Вернуть минимальное значение параметра u. \en Get the minimum value of u. + virtual double GetVMin() const; // \ru Вернуть минимальное значение параметра v. \en Get the minimum value of v. + virtual double GetUMax() const; // \ru Вернуть максимальное значение параметра u. \en Get the maximum value of u. + virtual double GetVMax() const; // \ru Вернуть максимальное значение параметра v. \en Get the maximum value of v. + virtual bool IsUClosed() const; // \ru Проверка замкнутости по параметру u. \en Check of surface closedness in u direction. + virtual bool IsVClosed() const; // \ru Проверка замкнутости по параметру v. \en Check of surface closedness in v direction. + + /** \} */ + /** \ru \name Функции для работы в области определения поверхности + Функции PointOn, Derive... поверхностей корректируют параметры + при выходе их за пределы прямоугольной области определения параметров.\n + \en \name Functions for working at surface domain + Functions PointOn, Derive... of surfaces correct parameters + when they are out of bounds of rectangular domain of parameters.\n + \{ */ + + virtual void PointOn ( double & u, double & v, MbCartPoint3D & ) const; // \ru Точка на поверхности. \en The point on the surface. + virtual void DeriveU ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по u. \en The first derivative with respect to u. + virtual void DeriveV ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по v. \en The first derivative with respect to v. + virtual void DeriveUU ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по u. \en The second derivative with respect to u. + virtual void DeriveVV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по v. \en The second derivative with respect to v. + virtual void DeriveUV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по uv. \en The second derivative with respect to uv. + virtual void DeriveUUU( double & u, double & v, MbVector3D & ) const; + virtual void DeriveUUV( double & u, double & v, MbVector3D & ) const; + virtual void DeriveUVV( double & u, double & v, MbVector3D & ) const; + virtual void DeriveVVV( double & u, double & v, MbVector3D & ) const; + /** \} */ + /** \ru \name Функции для работы внутри и вне области определения поверхности + функции _PointOn, _Derive... поверхностей не корректируют + параметры при выходе их за пределы прямоугольной области определения параметров. + \en \name Functions for working inside and outside the surface's domain + functions _PointOn, _Derive... of surfaces don't correct + parameters when they are out of bounds of rectangular domain of parameters. + \{ */ + virtual void _PointOn ( double u, double v, MbCartPoint3D & ) const; // \ru Точка на расширенной поверхности. \en The point on the extended surface. + virtual void _DeriveU ( double u, double v, MbVector3D & ) const; // \ru Первая производная по u. \en The first derivative with respect to u. + virtual void _DeriveV ( double u, double v, MbVector3D & ) const; // \ru Первая производная по v. \en The first derivative with respect to v. + virtual void _DeriveUU ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по u. \en The second derivative with respect to u. + virtual void _DeriveVV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по v. \en The second derivative with respect to v. + virtual void _DeriveUV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по uv. \en The second derivative with respect to uv. + virtual void _DeriveUUU( double u, double v, MbVector3D & ) const; + virtual void _DeriveUUV( double u, double v, MbVector3D & ) const; + virtual void _DeriveUVV( double u, double v, MbVector3D & ) const; + virtual void _DeriveVVV( double u, double v, MbVector3D & ) const; + + // \ru Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. + // \en Functions for get of the group of data inside and outside the surface's domain of parameters. + virtual void Explore( double & u, double & v, bool ext, + MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, + MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; + + /** \} */ + /** \ru \name Функции движения по поверхности + \en \name Functions of moving along the surface + \{ */ + + virtual double StepU ( double u, double v, double sag ) const; // \ru Вычисление шага по u по заданной стрелке прогиба. \en Calculation of the parameter step in u direction by the sag. + virtual double StepV ( double u, double v, double sag ) const; // \ru Вычисление шага по v по заданной стрелке прогиба. \en Calculation of the parameter step in v direction by the sag. + virtual double DeviationStepU( double u, double v, double angle ) const; // \ru Вычисление шага по u по заданному углу отклонения. \en Calculation of the parameter step in u direction by the deviation angle. + virtual double DeviationStepV( double u, double v, double angle ) const; // \ru Вычисление шага по v по заданному углу отклонения. \en Calculation of the parameter step in v direction by the deviation angle. + virtual double MetricStepU ( double u, double v, double length ) const; // \ru Вычисление шага по u по заданной длине. \en Calculation of the parameter step in u direction by the given length. + virtual double MetricStepV ( double u, double v, double length ) const; // \ru Вычисление шага по v по заданной длине. \en Calculation of the parameter step in v direction by the given length. + virtual size_t GetUCount() const; + virtual size_t GetVCount() const; + virtual bool GetPoleVMin() const; // \ru Существует ли полюс на границе параметрической области. \en Whether a pole exists on parametric region boundary. + virtual bool GetPoleVMax() const; // \ru Существует ли полюс на границе параметрической области. \en Whether a pole exists on parametric region boundary. + virtual bool IsPole( double u, double v ) const; // \ru Является ли точка полюсом. \en Whether the point is a pole. + + /** \} */ + /** \ru \name Общие функции поверхности + \en \name Common functions of surface + \{ */ + + virtual double CurvatureU ( double u, double v ) const; // \ru Kривизна линии по u. \en Curvature of line by u. + virtual bool IsPlanar() const; // \ru Является ли поверхность плоской. \en Whether the surface is planar. + + virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменение носителя. \en Changing of carrier. + + virtual void CalculateGabarit( MbCube & ) const; // \ru Выдать габарит. \en Get the bounding box. + virtual void CalculateLocalGabarit( const MbMatrix3D &, MbCube & ) const; // \ru Рассчитать габарит относительно л.с.к. \en Calculate bounding box relative to the local coordinate system. + + virtual MbSplineSurface * NurbsSurface( double, double, double, double, bool bmatch = false ) const; // \ru NURBS копия поверхности. \en NURBS copy of a surface. + virtual MbSurface * NurbsSurface( const MbNurbsParameters & uParam, const MbNurbsParameters & vParam ) const; // \ru NURBS копия поверхности. \en NURBS copy of a surface. + virtual MbSurface * Offset( double d, bool same ) const; // \ru Создание эквидистантной поверхности. \en Create an offset surface. + + // \ru Подобные ли поверхности для объединения (слива) \en Whether the surfaces to union (joining) are similar + //virtual bool IsSpecialSimilarToSurface( const MbSurface & surf, VERSION version, double precision = METRIC_PRECISION ) const; // \ru Специальный случай \en Special case + + virtual MbCurve3D * CurveU( double v, MbRect1D *pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии v = const. \en Spatial copy of 'v = const'-line. + virtual MbCurve3D * CurveV( double u, MbRect1D *pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии u = const. \en Spatial copy of 'u = const'-line. + + // \ru Построить касательные и нормальные плейсменты конструктивных плоскостей. \en Construct tangent and normal placements of constructive planes. + virtual bool CreateNormalPlacements ( const MbVector3D & axisZ, double angle, SArray & places ) const; + virtual bool CreateTangentPlacements( const MbVector3D & axisZ, SArray & places ) const; + // \ru Подобные ли поверхности для объединения (слива). \en Whether the surfaces to union (joining) are similar. + virtual bool IsSimilarToSurface( const MbSurface & surf, VERSION version, double precision = METRIC_PRECISION ) const; + // \ru Дать двумерную матрицу преобразования из своей параметрической области в параметрическую область surf. \en Get two-dimensional transformation matrix from own parametric region to parametric region of 'surf'. + virtual bool GetMatrixToSurface( const MbSurface & surf, MbMatrix & matr, VERSION version, double precision = METRIC_PRECISION ) const; + + /// \ru Определить, выпуклая ли поверхность. \en Determine whether the surface is convex. + virtual ThreeStates Salient() const; + + // \ru Определение разбивки параметрической области поверхности вертикалями и горизонталями. \en Determine splitting of parametric region of surface by vertical and horizontal lines. + virtual void GetTesselation( const MbStepData & stepData, + double u1, double u2, double v1, double v2, + SArray & uu, SArray & vv ) const; + //virtual bool IsSpinePeriodic() const; // \ru Периодичность направляющей. \en Periodicity of a reference curve. + + virtual size_t GetUMeshCount() const; // \ru Выдать количество полигонов по u. \en Get the count of polygons by u. + virtual size_t GetVMeshCount() const; // \ru Выдать количество полигонов по v. \en Get the count of polygons by v. + + /** \} */ + /** \ru \name Функции поверхности заметания переменного сечения. + \en \name Functions of the swept mutable section surface. + \{ */ + + /// \ru Направляющая. \en Guide curve. + const MbSpine & GetSpine() const { return *spine; } + /// \ru Направляющая кривая. \en The spine (reference) curve. + const MbCurve3D & GetSpineCurve() const { return spine->GetCurve(); } + /// \ru Первая направляющая кривая на первой поверхности (может быть NULL). \en The first guide curve on the first surface (may be NULL). + const MbCurve3D * GetGuide1() const { return guide1; } + /// \ru Вторая направляющая кривая на второй поверхности (может быть NULL). \en The second guide curve on the second surface (may be NULL). + const MbCurve3D * GetGuide2() const { return guide2; } + /// \ru Дополнительные направляющие кривые (могут отсутствовать). \en The additional guide curves (may be empty). + const MbCurve3D * GetCurve( size_t i ) const { return ( i < curves.size() ) ? curves[i] : NULL; } + /// \ru Функция управления сечением (радиус или дискриминант, может быть NULL)). \en Section control function (radius or discriminant, may be NULL). + const MbFunction * GetFunction() const { return function; } + /// \ru Образующая кривая при form==cs_Shape (для других форм NULL). \en Forming curve for form==cs_Shape (NULL on other case). + const MbPolyCurve * GetPattern() const { return pattern; } + + /// \ru Вычисление параметров направляющих кривых по второму параметру поверхности. \en Calculating the parameters of guide curves by the second surface parameter. + bool GuideParams( double v, double & t1, double & t2 ) const; + /// \ru Вычисление параметра вершинной кривой по второму параметру поверхности. \en Calculating the parameter of apex curve by the second surface parameter. + bool ApexParam( double v, double & t0 ) const; + /// \ru Вычисление точки поверхности по параметру направляющей кривой. \en Calculating the surface point by the parameter of first the guide curves. + bool ParamByGuide1( double t1, MbCartPoint & p ) const; + /// \ru Вычисление точки поверхности по параметру направляющей кривой. \en Calculating the surface point by the parameter of the second guide curves. + bool ParamByGuide2( double t2, MbCartPoint & p ) const; + /// \ru Вычисление второго параметра поверхности по параметру вершинной кривой. \en Calculating the second surface parameter by the parameter of the apex curve. + bool ParamByApex( double t0, double & v ) const; + /// \ru Вычисление точек поверхности по параметрам первой направляющей кривой. \en Calculating surface points by the first guide curve. + bool PointsByGuide1( std::vector & points ) const; + /// \ru Вычисление точек поверхности по параметрам второй направляющей кривой. \en Calculating surface points by the second guide curve. + bool PointsByGuide2( std::vector & points ) const; + + /** \} */ + +protected : + void Init(); // \ru Инициализация данных. \en Data init. + void CacheInit(); // \ru Инициализация вспомогательных данных. \en Auxiliary data init. + // \ru Проверить и изменить при необходимости параметры поверхности. \en Check and correct parameters of the surface. + void CheckParams( double & u, double & v, bool ext ) const; + // \ru Вычисление пересечений направляющих с плоскостью сечения поверхности. \ en Calculating the intersections of guides with the surface cross -section plane. + bool SectionData( const MbCurve3D * curve0, const double & v, MbPlacement3D & place, + MbCartPoint & xy0, double & t0, + MbCartPoint & xy1, MbVector & vec1, double & t1, + MbCartPoint & xy2, MbVector & vec2, double & t2 ) const; + // \ru Расчет двумерных точек и их производных на плоском сечении поверхности. \en Calculation of two-dimension points and their derivatives on the surface plane section. + void Round( const double & u, const double & v, + const MbCartPoint & xy1, const MbVector & xy1V, const MbVector & xy1VV, const MbVector & xy1VVV, + const MbCartPoint & xy0, const MbVector & xy0V, const MbVector & xy0VV, const MbVector & xy0VVV, + MbCartPoint & p, MbVector & pU, MbVector & pV, MbVector * pUU, MbVector * pUV, MbVector * pVV, + MbVector * pUUU, MbVector * pUUV, MbVector * pUVV, MbVector * pVVV ) const; + void Linea( const double & u, + const MbCartPoint & xy1, const MbVector & xy1V, const MbVector & xy1VV, const MbVector & xy1VVV, + const MbCartPoint & xy2, const MbVector & xy2V, const MbVector & xy2VV, const MbVector & xy2VVV, + MbCartPoint & p, MbVector & pU, MbVector & pV, MbVector * pUU, MbVector * pUV, MbVector * pVV, + MbVector * pUUU, MbVector * pUUV, MbVector * pUVV, MbVector * pVVV ) const; + void Conic( const double & u, const double & v, + const MbCartPoint & xy1, const MbVector & xy1V, const MbVector & xy1VV, const MbVector & xy1VVV, + const MbCartPoint & xy2, const MbVector & xy2V, const MbVector & xy2VV, const MbVector & xy2VVV, + const MbCartPoint & xy0, const MbVector & xy0V, const MbVector & xy0VV, const MbVector & xy0VVV, + MbCartPoint & p, MbVector & pU, MbVector & pV, MbVector * pUU, MbVector * pUV, MbVector * pVV, + MbVector * pUUU, MbVector * pUUV, MbVector * pUVV, MbVector * pVVV ) const; + void Cubic( const double & u, const double & v, std::vector & bSplines, + const std::vector & xy, const std::vector & xyV, + const std::vector & xyVV, const std::vector & xyVVV, + MbCartPoint & p, MbVector & pU, MbVector & pV, MbVector * pUU, MbVector * pUV, MbVector * pVV, + MbVector * pUUU, MbVector * pUUV, MbVector * pUVV, MbVector * pVVV ) const; + void Shape( const double & u, const double & v, + const MbCartPoint & xy1, const MbVector & xy1V, const MbVector & xy1VV, const MbVector & xy1VVV, + const MbCartPoint & xy2, const MbVector & xy2V, const MbVector & xy2VV, const MbVector & xy2VVV, + const MbCartPoint & xy0, const MbVector & xy0V, const MbVector & xy0VV, const MbVector & xy0VVV, + MbCartPoint & p, MbVector & pU, MbVector & pV, MbVector * pUU, MbVector * pUV, MbVector * pVV, + MbVector * pUUU, MbVector * pUUV, MbVector * pUVV, MbVector * pVVV ) const; + ptrdiff_t B_Splanes( double & u, std::vector & bSplanes ) const; // \ru Вычисление B-сплайнов.\en B_splines calculation. + // \ru Вычисление радиуса-вектора точки его производных поверхности. \en The surface radius-vector and it derivatives calculation. + void Section ( const double & u, const double & v, MbCartPoint3D & pnt, + MbVector3D * uDer, MbVector3D * vDer, + MbVector3D * uuDer, MbVector3D * uvDer, MbVector3D * vvDer, + MbVector3D * uuuDer, MbVector3D * uuvDer, MbVector3D * uvvDer, MbVector3D * vvvDer ) const; + void PointOn ( double & v, double & u, bool ext, MbCartPoint3D & p ) const; // \ru Вычисления точки поверхности. \en Calculate surface point. + void DeriveU ( double & u, double & v, bool ext, MbVector3D & ) const; // \ru Первая производная по u. \en The first derivative with respect to u. + void DeriveV ( double & u, double & v, bool ext, MbVector3D & ) const; // \ru Первая производная по v. \en The first derivative with respect to v. + void DeriveUU ( double & u, double & v, bool ext, MbVector3D & ) const; // \ru Вторая производная по u. \en The second derivative with respect to u. + void DeriveUV ( double & u, double & v, bool ext, MbVector3D & ) const; // \ru Вторая производная по uv. \en The second derivative with respect to uv. + void DeriveVV ( double & u, double & v, bool ext, MbVector3D & ) const; // \ru Вторая производная по v. \en The second derivative with respect to v. + void DeriveUUU( double & u, double & v, bool ext, MbVector3D & ) const; // \ru Третья производная по uuu. \en The third derivative with respect to uuu. + void DeriveUUV( double & u, double & v, bool ext, MbVector3D & ) const; // \ru Третья производная по uuv. \en The third derivative with respect to uuv. + void DeriveUVV( double & u, double & v, bool ext, MbVector3D & ) const; // \ru Третья производная по uvv. \en The third derivative with respect to uvv. + void DeriveVVV( double & u, double & v, bool ext, MbVector3D & ) const; // \ru Третья производная по vvv. \en The third derivative with respect to vvv. + // \ru Вычисление точек для создания NURBS копии кривых поверхности. \en Points calculation for NURBS copy surface. + bool CollectNurbsPoints( double vin, double vax, size_t pCount, double angle, + SArray & params, + SArray & points1, + SArray & points2, + SArray & points_0, + SArray & points_1, + SArray & points_2, + SArray & points_3, + SArray & points_4 ) const; + +private: + void operator = ( const MbSectionSurface & ); // \ru Не реализовано. \en Not implemented. + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSectionSurface ) +}; + + +IMPL_PERSISTENT_OPS( MbSectionSurface ) + + +#endif // __SURF_SECTION_SURFACE_H diff --git a/C3d/Include/surf_sector_surface.h b/C3d/Include/surf_sector_surface.h index 27a4291..a560b7a 100644 --- a/C3d/Include/surf_sector_surface.h +++ b/C3d/Include/surf_sector_surface.h @@ -22,13 +22,13 @@ \en Sectorial surface. \~ \details \ru Секториальная поверхность построена по кривой и точке. Секториальная поверхность является частным случаем линейчатой поверхности с вырожденной в точку второй кривой. - Радиус-вектор поверхности описывается векторной функцией \n + Радиус-вектор поверхности описывается векторной функцией \n r(u,v) = (1 - v) curve(u) + v origin. \n Первый параметр поверхности совпадает с параметром кривой curve. Вдоль второго параметра поверхность прямолинейна. \en Sectorial surface is created by curve and point. Sectorial surface is special case of ruled surface with a second curve degenerated to a point. - Radius-vector of line surface is described by the vector function \n + Radius-vector of line surface is described by the vector function \n r(u,v) = (1 - v) curve(u) + v origin. \n The first surface parameter coincides with the parameter of curve 'curve'. A surface is rectilinear along the second parameter. \~ diff --git a/C3d/Include/surf_smooth_surface.h b/C3d/Include/surf_smooth_surface.h index 301b469..ea81c37 100644 --- a/C3d/Include/surf_smooth_surface.h +++ b/C3d/Include/surf_smooth_surface.h @@ -1,410 +1,410 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Поверхность сопряжения. - \en Smooth surface. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __SURF_SMOOTH_SURFACE_H -#define __SURF_SMOOTH_SURFACE_H - - -#include - - -#define _EVEN_ false // \ru Неравномерная параметризация по дуге при u = const \en Uneven parameterization along an arc where u = const - - -class MATH_CLASS MbSurfaceCurve; -class MATH_CLASS MbSurfaceIntersectionCurve; - - -//------------------------------------------------------------------------------ -/** \brief \ru Поверхность сопряжения. - \en Smooth surface. \~ - \details \ru Поверхность сопряжения соединяет две кривые curve1 и curve2 на сопрягаемых поверхностях. - Поверхность сопряжения является родительским классом поверхности скругления MbFilletSurface и поверхности фаски MbCamferSurface. - В отличие от других поверхностей Функции PointOn и Derive... поверхностей сопряжения не корректируют - первый параметр (u) при его выходе за пределы определения параметров (umin umax). - \en A smooth surface connects two curves ('curve1' and 'curve2') on interfacing surfaces. - Smooth surface is the parent class of fillet surface (MbFilletSurface) and chamfer surface (MbChamferSurface). - In contrast to other surfaces functions PointOn and Derive.. of smooth surface don't correct - the first parameter (u) when getting out of domain bounds (umin and umax). \~ - \ingroup Surfaces -*/ -// --- -class MATH_CLASS MbSmoothSurface : public MbSurface { -protected: - MbSurfaceCurve * curve1; ///< \ru Опорная кривая на первой поверхности (всегда не NULL). \en Support curve on the first surface (it never equals NULL). - MbSurfaceCurve * curve2; ///< \ru Опорная кривая на второй поверхности (всегда не NULL). \en Support curve on the second surface (it never equals NULL). - MbeSmoothForm form; ///< \ru Тип сопряжения. \en Conjugation type. - double distance1; ///< \ru Радиус скругления или "катет" фаски со знаком для поверхности кривой curve1. \en Fillet radius or chamfer "cathetus" with sign for surface of curve1 curve. - double distance2; ///< \ru Радиус скругления или "катет" фаски со знаком для поверхности кривой curve2. \en Fillet radius or chamfer "cathetus" with sign for surface of curve2 curve. - double umin; ///< \ru Минимальное значение параметра u. \en Minimal value of parameter u. - double umax; ///< \ru Максимальное значение параметра u. \en Maximal value of parameter u. - double vmin; ///< \ru Минимальное значение параметра v. \en Minimal value of parameter v. - double vmax; ///< \ru Максимальное значение параметра v. \en Maximal value of parameter v. - bool uclosed; ///< \ru Признак замкнутости по параметру u. \en An attribute of closedness in u-parameter direction. - bool poleMin; ///< \ru Наличие полюса при umin. \en Existence of a pole at umin. - bool poleMax; ///< \ru Наличие полюса при umax. \en Existence of a pole at umax. - -protected: - - /** \brief \ru Конструктор по двум кривым и типу сопряжения. - \en Constructor by two curves and conjugation type. \~ - \details \ru Конструктор по двум кривым и типу сопряжения. - \en Constructor by two curves and conjugation type. \~ - \param[in] crv1 - \ru Опорная кривая на первой поверхности - \en Support curve on the first surface. \~ - \param[in] crv2 - \ru Опорная кривая на второй поверхности - \en Support curve on the second surface. \~ - \param[in] fm - \ru Тип сопряжения (0 - скругление, 1 - фаска) - \en Conjugation type (0 - fillet, 1 - chamfer) \~ - */ - MbSmoothSurface( MbSurfaceCurve & crv1, MbSurfaceCurve & crv2, MbeSmoothForm fm, double d1, double d2 ); - - /** \brief \ru Конструктор по двум кривым и типу сопряжения. - \en Constructor by two curves and conjugation type. \~ - \details \ru Конструктор по двум кривым и типу сопряжения. - \en Constructor by two curves and conjugation type. \~ - \param[in] surf1 - \ru Первая поверхность - \en First surface. \~ - \param[in] curv1 - \ru Кривая в области определения первой поверхности - \en A curve in domain of the first surface \~ - \param[in] surf2 - \ru Вторая поверхность - \en Second surface. \~ - \param[in] curv2 - \ru Кривая в области определения второй поверхности - \en A curve in domain of the second surface \~ - \param[in] fm - \ru Тип сопряжения (0 - скругление, 1 - фаска) - \en Conjugation type (0 - fillet, 1 - chamfer) \~ - */ - MbSmoothSurface( MbSurface &surf1, MbCurve &curv1, MbSurface &surf2, MbCurve &curv2, MbeSmoothForm fm, double d1, double d2 ); - /// \ru Конструктор копирования. \en Copy-constructor. - MbSmoothSurface( const MbSmoothSurface &, MbRegDuplicate * ); - /// \ru Конструктор копирования с теми же опорными поверхностями. \en Copy constructor with the same support surfaces. - MbSmoothSurface( const MbSmoothSurface * ); // \ru Для CurvesDuplicate() \en For CurvesDuplicate() -private: - MbSmoothSurface( const MbSmoothSurface & ); // \ru Не реализовано. \en Not implemented. -public: - virtual ~MbSmoothSurface(); - -public: - VISITING_CLASS( MbSmoothSurface ); - - /** \ru \name Функции инициализации - \en \name Initialization functions - \{ */ - - /** \brief \ru Коррекция средней линии поверхности скругления. - \en Correction of the middle line of fillet surface. \~ - \details \ru Коррекция средней линии поверхности скругления. - \en Correction of the middle line of fillet surface. \~ - \param[in] tmin - \ru Новый минимальный параметр средней линии - \en New minimal parameter of the middle line. \~ - \param[in] tmax - \ru Новый максимальный параметр средней линии - \en New maximal parameter of the middle line. \~ - \param[in] insertPoints - \ru Увеличить число контрольных точек средней кривоф. - \en Insert control points for middle cerve0. \~ - */ - virtual void Init0( double tmin, double tmax, bool insertPoints = true ) = 0; - - /** \} */ - /** \ru \name Общие функции геометрического объекта - \en \name Common functions of a geometric object - \{ */ - virtual MbeSpaceType IsA() const = 0; // \ru Тип элемента. \en A type of element. - virtual MbeSpaceType Type() const; // \ru Тип элемента. \en A type of element. - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const = 0; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const = 0; - virtual bool SetEqual ( const MbSpaceItem & ) = 0; // \ru Сделать равным. \en Make equal. - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ) = 0; // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ) = 0; // \ru Сдвиг. \en Translation. - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ) = 0; // \ru Повернуть вокруг оси. \en Rotate around an axis. - - virtual void GetProperties( MbProperties &properties ) = 0; // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties &properties ) = 0; // \ru Записать свойства объекта. \en Set properties of the object. - /** \} */ - /** \ru \name Функции описания области определения поверхности - \en \name Functions for surface domain description - \{ */ - virtual double GetUMin() const; // \ru Вернуть минимальное значение параметра u. \en Get the minimum value of u. - virtual double GetUMax() const; // \ru Вернуть максимальное значение параметра u. \en Get the maximum value of u. - virtual double GetVMin() const; // \ru Вернуть минимальное значение параметра v. \en Get the minimum value of v. - virtual double GetVMax() const; // \ru Вернуть максимальное значение параметра v. \en Get the maximum value of v. - virtual bool IsUClosed() const; // \ru Проверка замкнутости по параметру u. \en Check of surface closedness in u direction. - virtual bool IsVClosed() const; // \ru Проверка замкнутости по параметру v. \en Check of surface closedness in v direction. - virtual double GetUPeriod() const; // \ru Вернуть период. \en Return period. - - // \ru Существует ли полюс на границе параметрической области сплайновой кривой. \en Whether a pole exists on parametric region boundary of spline curve. - virtual bool GetPoleUMin() const; - virtual bool GetPoleUMax() const; - virtual bool GetPoleVMin() const; - virtual bool GetPoleVMax() const; - virtual bool IsPole( double u, double v ) const; // \ru Является ли точка особенной. \en Whether the point is singular. - /** \} */ - /** \ru \name Функции для работы в области определения поверхности - Функции PointOn и Derive... поверхностей сопряжения не корректируют - первый параметр при его выходе за пределы определения параметров. - \en \name Functions for working at surface domain - Functions PointOn and Derive... of smooth surfaces don't correct - the first parameter when getting out of domain bounds. - \{ */ - virtual void PointOn ( double &u, double &v, MbCartPoint3D & ) const = 0; // \ru Точка на поверхности. \en A point on surface. - virtual void DeriveU ( double &u, double &v, MbVector3D & ) const = 0; // \ru Первая производная по u. \en First derivative with respect to u. - virtual void DeriveV ( double &u, double &v, MbVector3D & ) const = 0; // \ru Первая производная по v. \en First derivative with respect to v. - virtual void DeriveUU ( double &u, double &v, MbVector3D & ) const = 0; // \ru Вторая производная по u. \en Second derivative with respect to u. - virtual void DeriveVV ( double &u, double &v, MbVector3D & ) const = 0; // \ru Вторая производная по v. \en Second derivative with respect to v. - virtual void DeriveUV ( double &u, double &v, MbVector3D & ) const = 0; // \ru Вторая производная по uv. \en Second derivative with respect to u and v. - virtual void DeriveUUU( double &u, double &v, MbVector3D & ) const = 0; - virtual void DeriveUUV( double &u, double &v, MbVector3D & ) const = 0; - virtual void DeriveUVV( double &u, double &v, MbVector3D & ) const = 0; - virtual void DeriveVVV( double &u, double &v, MbVector3D & ) const = 0; - /** \} */ - /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. - \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. - \{ */ - virtual void Explore( double & u, double & v, bool ext, - MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, - MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const = 0; - /** \} */ - /** \ru \name Функции движения по поверхности - \en \name Functions of moving over the surface - \{ */ - virtual size_t GetUCount() const; - virtual size_t GetVCount() const; - /** \} */ - /** \ru \name Общие функции поверхности - \en \name Common functions of surface. - \{ */ - // \ru Определениe точки пересечения поверхности и кривой. \en Determination of a point of intersection between a surface and a curve. - virtual MbeNewtonResult CurveIntersectNewton( const MbCurve3D &, double funcEpsilon, size_t iterLimit, - double &u0, double &v0, double &t1, bool ext0, bool ext1 ) const; - // \ru Дать максимальное приращение параметра. \en Get the maximum increment of parameter. - virtual double GetParamDelta() const; - // \ru Дать мимнимально различимую величину параметра. \en Get the minimum distinguishable value of parameter. - virtual double GetParamPrice() const; - // \ru Построить NURBS-копию поверхности. \en Construct a NURBS copy of a surface. - virtual MbSurface * NurbsSurface( const MbNurbsParameters & uParam, const MbNurbsParameters & vParam ) const; - - /** \} */ - /** \ru \name Функции поверхности сопряжения - \en \name Functions of smooth surface - \{ */ - /// \ru Копия с теми же опорными поверхностям. \en A copy with the same support surfaces. - virtual MbSmoothSurface & CurvesDuplicate() const = 0; - /// \ru Сделать полное копирование поверхности. \en Perform a full copying of a surface. - MbSurface & TotalDuplicate() const; - /// \ru Дать радиус. \en Get radius. - virtual double GetSmoothRadius() const = 0; - /// \ru Дать радиусы со знаком. \en Get radii with a sign. - virtual void GetDistances( double u, double &d1, double &d2 ) const = 0; - /// \ru Дать радиус со знаком. \en Get radius with a sign. - virtual double GetDistance( bool s ) const = 0; - - /** \brief \ru Объединить поверхности путём включения поверхности init в данную поверхность. - \en Combine surfaces by inclusion of the surface 'init' into the given surface. \~ - \details \ru Объединить поверхности путём включения поверхности init в данную поверхность. - \en Combine surfaces by inclusion of the surface 'init' into the given surface. \~ - \param[in] edge - \ru Кривая разделяющего ребра - \en A curve of the splitting edge. \~ - \param[in] init - \ru Поверхность, которую нужно добавить в данную - \en A surface which should be added into the given one \~ - \param[in] add - \ru Добавить в конец (true), добавить в начало (false) - \en Add to end (true) or add to start (false) \~ - \param[in] matr - \ru Матрица преобразования объектов с init в данную поверхность, - \en A matrix of transformation of objects from 'init' to the given surface, \~ - \param[in] seam - \ru Кривая другого разделяющего ребра (может быть NULL) - \en A curve of another splitting edge (possibly it is NULL) \~ - */ - virtual bool SurfacesCombine( const MbSurfaceIntersectionCurve & edge, - const MbSurface & init, bool add, MbMatrix & matr, - const MbSurfaceIntersectionCurve * seam ); - - /// \ru Дать коэффициент для радиуса. \en Get coefficient for radius. - virtual double DistanceRatio( bool firstCurve, MbCartPoint3D & p, double distance ) const; - - /// \ru Опорная кривая на первой поверхности. \en Support curve on the first surface. - const MbSurfaceCurve & GetCurve1() const { return *curve1; } - /// \ru Опорная кривая на второй поверхности. \en Support curve on the second surface. - const MbSurfaceCurve & GetCurve2() const { return *curve2; } - /// \ru Дать опорную кривую на первой поверхности для изменения. \en Get the support curve on the first surface for changing. - MbSurfaceCurve & SetCurve1() const { return *curve1; } - /// \ru Дать опорную кривую на второй поверхности для изменения. \en Get the support curve on the second surface for changing. - MbSurfaceCurve & SetCurve2() const { return *curve2; } - - /** \brief \ru Построить граничную кривую вдоль поверхности (V = const). - \en Construct boundary curve along a surface (V = const). \~ - \details \ru Построить граничную кривую вдоль поверхности (V = const). - \en Construct boundary curve along a surface (V = const). \~ - \param[in] s - \ru Если true, то вдоль минимального значения V,\n - если false, то вдоль максимального значения V - \en If it equals true then construct along the minimal value of V,\n - otherwise construct along the maximal value of V \~ - */ - MbCurve * CreateBound( bool s ) const; - - /** \brief \ru Вид опорных кривых. - \en Type of support curves. \~ - \details \ru Вид опорных кривых. - \en Type of support curves. \~ - \return \ru cbt_Specific если кривые построены по отдельным точкам\n - cbt_Ordinary если кривые аналитические - \en Cbt_Specific if the curves have been constructed by the separate points\n - cbt_Ordinary if the curves are analytical \~ - */ - MbeCurveBuildType GetBuildType() const; - - /** \brief \ru Форма поверхности. - \en Form of a surface. \~ - \details \ru Форма поверхности. - \en Form of a surface. \~ - \return \ru 0 в случае поверхности скругления\n - 1 в случае поверхности фаски - \en 0 in a case of fillet\n - 1 in a case of chamfer \~ - */ - MbeSmoothForm Form() const { return form; } - - /** \brief \ru Добавить точку в опорные кривые границы. - \en Add a point to the support curves of the boundary. \~ - \details \ru Добавить точку в опорные кривые границы.\n - Точка будет добавлена в кривую, если она имеет тип pt_LineSegment, pt_CubicSpline или pt_Hermit. - \en Add a point to the support curves of the boundary.\n - A point will be added into a curve if it has a type pt_LineSegment, pt_CubicSpline or pt_Hermit. \~ - \param[out] t1 - \ru Параметр точки на первой кривой (если add1 = true) - \en Parameter of a point on the first curve (if add1 equals true) \~ - \param[in] p1 - \ru Точка на первой кривой - \en Point on the first curve \~ - \param[in] add1 - \ru Нужно ли добавлять точку в первую кривую - \en Whether to add a point to the first curve \~ - \param[out] t2 - \ru Параметр точки на второй кривой (если add2 = true) - \en Parameter of a point on the second curve (if add2 equals true) \~ - \param[in] p2 - \ru Точка на второй кривой - \en Point on the second curve \~ - \param[in] add2 - \ru Нужно ли добавлять точку во вторую кривую - \en Whether to add a point to the second curve \~ - */ - virtual bool InsertPoints( double & t1, const MbCartPoint & p1, bool add1, - double & t2, const MbCartPoint & p2, bool add2 ); - - /** \brief \ru Продлить поверхность. - \en Prolong surface. \~ - \details \ru Построить и добавить точки в опорные кривые до или после границы, удлиннив поверхность.\n - Точки будут построены и добавлены в кривые, если они имеют тип pt_Hermit. - \en Build and add points to the support curves of the boundary.\n - A points will be builded and added into curves if they have a type pt_Hermit. \~ - \param[in] t - \ru Первый параметр поверхности - \en First parameter of surface \~ - \param[in] p1 - \ru Точка на первой кривой - \en Point on the first curve \~ - \param[in] p2 - \ru Точка на второй кривой - \en Point on the second curve \~ - \param[in] anyCase - \ru Штатная работа со значением false (true исключение). - \en Regular work with the value false (true exception). \~ - */ - bool ProlongSurface( double u, const MbCartPoint & p1, const MbCartPoint & p2, bool anyCase ); - - /** \brief \ru Скорректировать кривые. - \en Correct curves. \~ - \details \ru Скорректировать опорные кривые после вставки точек.\n - Кривая будет скорректирована, если поверхность имеет полюс на краю, опорная кривая имеет тип pt_Hermit и содержит опорную точку с заданным параметром. - Корректируется опорная точка кривой, ближайшая к точке с заданным параметром со стороны полюса поверхности. - \en Correct the support curves after inserting of the points.\n - A curve will be corrected if the surface has a pole on the boundary, a support curve has the type pt_Hermit and contains the support point with the given parameter. - The curve support point which is the nearest to the point with the given parameter from the side of the surface pole is corrected. \~ - \param[in] t1 - \ru Параметр точки на первой опорной кривой. - \en Parameter of a point on the first support curve. \~ - \param[in] t2 - \ru Параметр точки на второй опорной кривой. - \en Parameter of a point on the second support curve. \~ - */ - bool CurveStraighten( double t1, double t2 ); - /// \ru Дать свойства объекта. \en Get the object properties. - void AddProperties( MbProperties &properties ); - /// \ru Проверить полюса. \en Check poles. - void SetPole(); - /** \} */ -protected: - /// \ru Корректировка параметров. \en Correction of parameters. - inline void CheckParam ( double &u, double &v ) const; - - void InitSmoothSurface ( const MbSmoothSurface & ); - void Init ( const MbSmoothSurface & ); - -private: - // \ru Определениe точки пересечения края поверхности и кривой на смежной поверхности. \en Determination of intersection point between the surface boundary and the adjacent surface. - MbeNewtonResult TangentIntersection( const MbSurfaceIntersectionCurve &, - size_t iterLimit, double &u, double &v, double &t, bool ext0, bool ext1 ) const; - MbeNewtonResult CurveTangentIntersection( const MbCurve3D &, double funcEpsilon, size_t iterLimit, - double &u, double &v, double &t, bool ext0, bool ext1 ) const; - bool IsSameSurface( const MbCurve3D &, double &u, double &v, double &t ) const; // \ru Идентификация кривой. \en Identification of a curve. - - void operator = ( const MbSmoothSurface & ); // \ru Не реализовано. \en Not implemented. - - DECLARE_PERSISTENT_CLASS( MbSmoothSurface ) -}; - -IMPL_PERSISTENT_OPS( MbSmoothSurface ) - -//------------------------------------------------------------------------------ -// \ru Корректировка параметров \en Correction of parameters -// --- -inline void MbSmoothSurface::CheckParam( double &u, double &v ) const { - if ( v < vmin ) - v = vmin; - else - if ( v > vmax ) - v = vmax; - - if ( uclosed ) { - if ( (u < umin) || (u > umax ) ) { - double tmp = umax - umin; - u -= ::floor((u - umin) / tmp) * tmp; - } - } - else { - if ( poleMin && uumax ) - u = umax; - } -} - - -//------------------------------------------------------------------------------ -// Скорректировать крайние точки контейнеров, если они лежат в полюсах поверхности. -// --- -void CorrectPolePoins(const MbSurface & surface, SArray & points ); - - -//------------------------------------------------------------------------------ -// \ru Наполнить массив параметров для кривых на поверхностях \en Fill an array of parameters for the curves on surfaces -// --- -void CreateParams( const MbSurface & surface1, SArray & points1, - const MbSurface & surface2, SArray & points2, - SArray * values, SArray * valuesDerive, - bool °enerate1, bool °enerate2, ptrdiff_t & begN, ptrdiff_t & endN, - SArray & params ); - - -//------------------------------------------------------------------------------ -// \ru Создать кривые на поверхности \en Create curves on a surface. -// --- -void CreateSurfaceCurves( const MbSurface & surface1, SArray & points1, - const MbSurface & surface2, SArray & points2, - MbeSmoothForm form, bool firstFree, double distance1, double distance2, - bool insert, ptrdiff_t begN, ptrdiff_t endN, VERSION version, - MbSurfaceCurve *& curve1, MbSurfaceCurve *& curve2 ); - - -//------------------------------------------------------------------------------ -// \ru Создать поверхность \en Create a surface -// --- -MbSmoothSurface * CreateSmoothSurface( const MbSurface & surface1, SArray & points1, - const MbSurface & surface2, SArray & points2, - MbeSmoothForm form, bool firstFree, double distance1, double distance2, - double conic, bool even, ptrdiff_t begN, ptrdiff_t endN, VERSION version ); - - -#endif // __SURF_SMOOTH_SURFACE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Поверхность сопряжения. + \en Smooth surface. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __SURF_SMOOTH_SURFACE_H +#define __SURF_SMOOTH_SURFACE_H + + +#include + + +const_expr bool _EVEN_ = false; // \ru Неравномерная параметризация по дуге при u = const \en Uneven parameterization along an arc where u = const + + +class MATH_CLASS MbSurfaceCurve; +class MATH_CLASS MbSurfaceIntersectionCurve; + + +//------------------------------------------------------------------------------ +/** \brief \ru Поверхность сопряжения. + \en Smooth surface. \~ + \details \ru Поверхность сопряжения соединяет две кривые curve1 и curve2 на сопрягаемых поверхностях. + Поверхность сопряжения является родительским классом поверхности скругления MbFilletSurface и поверхности фаски MbCamferSurface. + В отличие от других поверхностей Функции PointOn и Derive... поверхностей сопряжения не корректируют + первый параметр (u) при его выходе за пределы определения параметров (umin umax). + \en A smooth surface connects two curves ('curve1' and 'curve2') on interfacing surfaces. + Smooth surface is the parent class of fillet surface (MbFilletSurface) and chamfer surface (MbChamferSurface). + In contrast to other surfaces functions PointOn and Derive.. of smooth surface don't correct + the first parameter (u) when getting out of domain bounds (umin and umax). \~ + \ingroup Surfaces +*/ +// --- +class MATH_CLASS MbSmoothSurface : public MbSurface { +protected: + MbSurfaceCurve * curve1; ///< \ru Опорная кривая на первой поверхности (всегда не NULL). \en Support curve on the first surface (it never equals NULL). + MbSurfaceCurve * curve2; ///< \ru Опорная кривая на второй поверхности (всегда не NULL). \en Support curve on the second surface (it never equals NULL). + MbeSmoothForm form; ///< \ru Тип сопряжения. \en Conjugation type. + double distance1; ///< \ru Радиус скругления или "катет" фаски со знаком для поверхности кривой curve1. \en Fillet radius or chamfer "cathetus" with sign for surface of curve1 curve. + double distance2; ///< \ru Радиус скругления или "катет" фаски со знаком для поверхности кривой curve2. \en Fillet radius or chamfer "cathetus" with sign for surface of curve2 curve. + double umin; ///< \ru Минимальное значение параметра u. \en Minimal value of parameter u. + double umax; ///< \ru Максимальное значение параметра u. \en Maximal value of parameter u. + double vmin; ///< \ru Минимальное значение параметра v. \en Minimal value of parameter v. + double vmax; ///< \ru Максимальное значение параметра v. \en Maximal value of parameter v. + bool uclosed; ///< \ru Признак замкнутости по параметру u. \en An attribute of closedness in u-parameter direction. + bool poleMin; ///< \ru Наличие полюса при umin. \en Existence of a pole at umin. + bool poleMax; ///< \ru Наличие полюса при umax. \en Existence of a pole at umax. + +protected: + + /** \brief \ru Конструктор по двум кривым и типу сопряжения. + \en Constructor by two curves and conjugation type. \~ + \details \ru Конструктор по двум кривым и типу сопряжения. + \en Constructor by two curves and conjugation type. \~ + \param[in] crv1 - \ru Опорная кривая на первой поверхности + \en Support curve on the first surface. \~ + \param[in] crv2 - \ru Опорная кривая на второй поверхности + \en Support curve on the second surface. \~ + \param[in] fm - \ru Тип сопряжения (0 - скругление, 1 - фаска) + \en Conjugation type (0 - fillet, 1 - chamfer) \~ + */ + MbSmoothSurface( MbSurfaceCurve & crv1, MbSurfaceCurve & crv2, MbeSmoothForm fm, double d1, double d2 ); + + /** \brief \ru Конструктор по двум кривым и типу сопряжения. + \en Constructor by two curves and conjugation type. \~ + \details \ru Конструктор по двум кривым и типу сопряжения. + \en Constructor by two curves and conjugation type. \~ + \param[in] surf1 - \ru Первая поверхность + \en First surface. \~ + \param[in] curv1 - \ru Кривая в области определения первой поверхности + \en A curve in domain of the first surface \~ + \param[in] surf2 - \ru Вторая поверхность + \en Second surface. \~ + \param[in] curv2 - \ru Кривая в области определения второй поверхности + \en A curve in domain of the second surface \~ + \param[in] fm - \ru Тип сопряжения (0 - скругление, 1 - фаска) + \en Conjugation type (0 - fillet, 1 - chamfer) \~ + */ + MbSmoothSurface( MbSurface &surf1, MbCurve &curv1, MbSurface &surf2, MbCurve &curv2, MbeSmoothForm fm, double d1, double d2 ); + /// \ru Конструктор копирования. \en Copy-constructor. + MbSmoothSurface( const MbSmoothSurface &, MbRegDuplicate * ); + /// \ru Конструктор копирования с теми же опорными поверхностями. \en Copy constructor with the same support surfaces. + MbSmoothSurface( const MbSmoothSurface * ); // \ru Для CurvesDuplicate() \en For CurvesDuplicate() +private: + MbSmoothSurface( const MbSmoothSurface & ); // \ru Не реализовано. \en Not implemented. +public: + virtual ~MbSmoothSurface(); + +public: + VISITING_CLASS( MbSmoothSurface ); + + /** \ru \name Функции инициализации + \en \name Initialization functions + \{ */ + + /** \brief \ru Коррекция средней линии поверхности скругления. + \en Correction of the middle line of fillet surface. \~ + \details \ru Коррекция средней линии поверхности скругления. + \en Correction of the middle line of fillet surface. \~ + \param[in] tmin - \ru Новый минимальный параметр средней линии + \en New minimal parameter of the middle line. \~ + \param[in] tmax - \ru Новый максимальный параметр средней линии + \en New maximal parameter of the middle line. \~ + \param[in] insertPoints - \ru Увеличить число контрольных точек средней кривоф. + \en Insert control points for middle cerve0. \~ + */ + virtual void Init0( double tmin, double tmax, bool insertPoints = true ) = 0; + + /** \} */ + /** \ru \name Общие функции геометрического объекта + \en \name Common functions of a geometric object + \{ */ + virtual MbeSpaceType IsA() const = 0; // \ru Тип элемента. \en A type of element. + virtual MbeSpaceType Type() const; // \ru Тип элемента. \en A type of element. + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const = 0; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const = 0; + virtual bool SetEqual ( const MbSpaceItem & ) = 0; // \ru Сделать равным. \en Make equal. + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ) = 0; // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ) = 0; // \ru Сдвиг. \en Translation. + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ) = 0; // \ru Повернуть вокруг оси. \en Rotate around an axis. + + virtual void GetProperties( MbProperties &properties ) = 0; // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties &properties ) = 0; // \ru Записать свойства объекта. \en Set properties of the object. + /** \} */ + /** \ru \name Функции описания области определения поверхности + \en \name Functions for surface domain description + \{ */ + virtual double GetUMin() const; // \ru Вернуть минимальное значение параметра u. \en Get the minimum value of u. + virtual double GetUMax() const; // \ru Вернуть максимальное значение параметра u. \en Get the maximum value of u. + virtual double GetVMin() const; // \ru Вернуть минимальное значение параметра v. \en Get the minimum value of v. + virtual double GetVMax() const; // \ru Вернуть максимальное значение параметра v. \en Get the maximum value of v. + virtual bool IsUClosed() const; // \ru Проверка замкнутости по параметру u. \en Check of surface closedness in u direction. + virtual bool IsVClosed() const; // \ru Проверка замкнутости по параметру v. \en Check of surface closedness in v direction. + virtual double GetUPeriod() const; // \ru Вернуть период. \en Return period. + + // \ru Существует ли полюс на границе параметрической области сплайновой кривой. \en Whether a pole exists on parametric region boundary of spline curve. + virtual bool GetPoleUMin() const; + virtual bool GetPoleUMax() const; + virtual bool GetPoleVMin() const; + virtual bool GetPoleVMax() const; + virtual bool IsPole( double u, double v ) const; // \ru Является ли точка особенной. \en Whether the point is singular. + /** \} */ + /** \ru \name Функции для работы в области определения поверхности + Функции PointOn и Derive... поверхностей сопряжения не корректируют + первый параметр при его выходе за пределы определения параметров. + \en \name Functions for working at surface domain + Functions PointOn and Derive... of smooth surfaces don't correct + the first parameter when getting out of domain bounds. + \{ */ + virtual void PointOn ( double &u, double &v, MbCartPoint3D & ) const = 0; // \ru Точка на поверхности. \en A point on surface. + virtual void DeriveU ( double &u, double &v, MbVector3D & ) const = 0; // \ru Первая производная по u. \en First derivative with respect to u. + virtual void DeriveV ( double &u, double &v, MbVector3D & ) const = 0; // \ru Первая производная по v. \en First derivative with respect to v. + virtual void DeriveUU ( double &u, double &v, MbVector3D & ) const = 0; // \ru Вторая производная по u. \en Second derivative with respect to u. + virtual void DeriveVV ( double &u, double &v, MbVector3D & ) const = 0; // \ru Вторая производная по v. \en Second derivative with respect to v. + virtual void DeriveUV ( double &u, double &v, MbVector3D & ) const = 0; // \ru Вторая производная по uv. \en Second derivative with respect to u and v. + virtual void DeriveUUU( double &u, double &v, MbVector3D & ) const = 0; + virtual void DeriveUUV( double &u, double &v, MbVector3D & ) const = 0; + virtual void DeriveUVV( double &u, double &v, MbVector3D & ) const = 0; + virtual void DeriveVVV( double &u, double &v, MbVector3D & ) const = 0; + /** \} */ + /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. + \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. + \{ */ + virtual void Explore( double & u, double & v, bool ext, + MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, + MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const = 0; + /** \} */ + /** \ru \name Функции движения по поверхности + \en \name Functions of moving over the surface + \{ */ + virtual size_t GetUCount() const; + virtual size_t GetVCount() const; + /** \} */ + /** \ru \name Общие функции поверхности + \en \name Common functions of surface. + \{ */ + // \ru Определениe точки пересечения поверхности и кривой. \en Determination of a point of intersection between a surface and a curve. + virtual MbeNewtonResult CurveIntersectNewton( const MbCurve3D &, double funcEpsilon, size_t iterLimit, + double &u0, double &v0, double &t1, bool ext0, bool ext1 ) const; + // \ru Дать максимальное приращение параметра. \en Get the maximum increment of parameter. + virtual double GetParamDelta() const; + // \ru Дать мимнимально различимую величину параметра. \en Get the minimum distinguishable value of parameter. + virtual double GetParamPrice() const; + // \ru Построить NURBS-копию поверхности. \en Construct a NURBS copy of a surface. + virtual MbSurface * NurbsSurface( const MbNurbsParameters & uParam, const MbNurbsParameters & vParam ) const; + + /** \} */ + /** \ru \name Функции поверхности сопряжения + \en \name Functions of smooth surface + \{ */ + /// \ru Копия с теми же опорными поверхностям. \en A copy with the same support surfaces. + virtual MbSmoothSurface & CurvesDuplicate() const = 0; + /// \ru Сделать полное копирование поверхности. \en Perform a full copying of a surface. + MbSurface & TotalDuplicate() const; + /// \ru Дать радиус. \en Get radius. + virtual double GetSmoothRadius() const = 0; + /// \ru Дать радиусы со знаком. \en Get radii with a sign. + virtual void GetDistances( double u, double &d1, double &d2 ) const = 0; + /// \ru Дать радиус со знаком. \en Get radius with a sign. + virtual double GetDistance( bool s ) const = 0; + + /** \brief \ru Объединить поверхности путём включения поверхности init в данную поверхность. + \en Combine surfaces by inclusion of the surface 'init' into the given surface. \~ + \details \ru Объединить поверхности путём включения поверхности init в данную поверхность. + \en Combine surfaces by inclusion of the surface 'init' into the given surface. \~ + \param[in] edge - \ru Кривая разделяющего ребра + \en A curve of the splitting edge. \~ + \param[in] init - \ru Поверхность, которую нужно добавить в данную + \en A surface which should be added into the given one \~ + \param[in] add - \ru Добавить в конец (true), добавить в начало (false) + \en Add to end (true) or add to start (false) \~ + \param[in] matr - \ru Матрица преобразования объектов с init в данную поверхность, + \en A matrix of transformation of objects from 'init' to the given surface, \~ + \param[in] seam - \ru Кривая другого разделяющего ребра (может быть NULL) + \en A curve of another splitting edge (possibly it is NULL) \~ + */ + virtual bool SurfacesCombine( const MbSurfaceIntersectionCurve & edge, + const MbSurface & init, bool add, MbMatrix & matr, + const MbSurfaceIntersectionCurve * seam ); + + /// \ru Дать коэффициент для радиуса. \en Get coefficient for radius. + virtual double DistanceRatio( bool firstCurve, MbCartPoint3D & p, double distance ) const; + + /// \ru Опорная кривая на первой поверхности. \en Support curve on the first surface. + const MbSurfaceCurve & GetCurve1() const { return *curve1; } + /// \ru Опорная кривая на второй поверхности. \en Support curve on the second surface. + const MbSurfaceCurve & GetCurve2() const { return *curve2; } + /// \ru Дать опорную кривую на первой поверхности для изменения. \en Get the support curve on the first surface for changing. + MbSurfaceCurve & SetCurve1() const { return *curve1; } + /// \ru Дать опорную кривую на второй поверхности для изменения. \en Get the support curve on the second surface for changing. + MbSurfaceCurve & SetCurve2() const { return *curve2; } + + /** \brief \ru Построить граничную кривую вдоль поверхности (V = const). + \en Construct boundary curve along a surface (V = const). \~ + \details \ru Построить граничную кривую вдоль поверхности (V = const). + \en Construct boundary curve along a surface (V = const). \~ + \param[in] s - \ru Если true, то вдоль минимального значения V,\n + если false, то вдоль максимального значения V + \en If it equals true then construct along the minimal value of V,\n + otherwise construct along the maximal value of V \~ + */ + MbCurve * CreateBound( bool s ) const; + + /** \brief \ru Вид опорных кривых. + \en Type of support curves. \~ + \details \ru Вид опорных кривых. + \en Type of support curves. \~ + \return \ru cbt_Specific если кривые построены по отдельным точкам\n + cbt_Ordinary если кривые аналитические + \en Cbt_Specific if the curves have been constructed by the separate points\n + cbt_Ordinary if the curves are analytical \~ + */ + MbeCurveBuildType GetBuildType() const; + + /** \brief \ru Форма поверхности. + \en Form of a surface. \~ + \details \ru Форма поверхности. + \en Form of a surface. \~ + \return \ru 0 в случае поверхности скругления\n + 1 в случае поверхности фаски + \en 0 in a case of fillet\n + 1 in a case of chamfer \~ + */ + MbeSmoothForm Form() const { return form; } + + /** \brief \ru Добавить точку в опорные кривые границы. + \en Add a point to the support curves of the boundary. \~ + \details \ru Добавить точку в опорные кривые границы.\n + Точка будет добавлена в кривую, если она имеет тип pt_LineSegment, pt_CubicSpline или pt_Hermit. + \en Add a point to the support curves of the boundary.\n + A point will be added into a curve if it has a type pt_LineSegment, pt_CubicSpline or pt_Hermit. \~ + \param[out] t1 - \ru Параметр точки на первой кривой (если add1 = true) + \en Parameter of a point on the first curve (if add1 equals true) \~ + \param[in] p1 - \ru Точка на первой кривой + \en Point on the first curve \~ + \param[in] add1 - \ru Нужно ли добавлять точку в первую кривую + \en Whether to add a point to the first curve \~ + \param[out] t2 - \ru Параметр точки на второй кривой (если add2 = true) + \en Parameter of a point on the second curve (if add2 equals true) \~ + \param[in] p2 - \ru Точка на второй кривой + \en Point on the second curve \~ + \param[in] add2 - \ru Нужно ли добавлять точку во вторую кривую + \en Whether to add a point to the second curve \~ + */ + virtual bool InsertPoints( double & t1, const MbCartPoint & p1, bool add1, + double & t2, const MbCartPoint & p2, bool add2 ); + + /** \brief \ru Продлить поверхность. + \en Prolong surface. \~ + \details \ru Построить и добавить точки в опорные кривые до или после границы, удлиннив поверхность.\n + Точки будут построены и добавлены в кривые, если они имеют тип pt_Hermit. + \en Build and add points to the support curves of the boundary.\n + A points will be builded and added into curves if they have a type pt_Hermit. \~ + \param[in] t - \ru Первый параметр поверхности + \en First parameter of surface \~ + \param[in] p1 - \ru Точка на первой кривой + \en Point on the first curve \~ + \param[in] p2 - \ru Точка на второй кривой + \en Point on the second curve \~ + \param[in] anyCase - \ru Штатная работа со значением false (true исключение). + \en Regular work with the value false (true exception). \~ + */ + bool ProlongSurface( double u, const MbCartPoint & p1, const MbCartPoint & p2, bool anyCase ); + + /** \brief \ru Скорректировать кривые. + \en Correct curves. \~ + \details \ru Скорректировать опорные кривые после вставки точек.\n + Кривая будет скорректирована, если поверхность имеет полюс на краю, опорная кривая имеет тип pt_Hermit и содержит опорную точку с заданным параметром. + Корректируется опорная точка кривой, ближайшая к точке с заданным параметром со стороны полюса поверхности. + \en Correct the support curves after inserting of the points.\n + A curve will be corrected if the surface has a pole on the boundary, a support curve has the type pt_Hermit and contains the support point with the given parameter. + The curve support point which is the nearest to the point with the given parameter from the side of the surface pole is corrected. \~ + \param[in] t1 - \ru Параметр точки на первой опорной кривой. + \en Parameter of a point on the first support curve. \~ + \param[in] t2 - \ru Параметр точки на второй опорной кривой. + \en Parameter of a point on the second support curve. \~ + */ + bool CurveStraighten( double t1, double t2 ); + /// \ru Дать свойства объекта. \en Get the object properties. + void AddProperties( MbProperties &properties ); + /// \ru Проверить полюса. \en Check poles. + void SetPole(); + /** \} */ +protected: + /// \ru Корректировка параметров. \en Correction of parameters. + inline void CheckParam ( double &u, double &v ) const; + + void InitSmoothSurface ( const MbSmoothSurface & ); + void Init ( const MbSmoothSurface & ); + +private: + // \ru Определениe точки пересечения края поверхности и кривой на смежной поверхности. \en Determination of intersection point between the surface boundary and the adjacent surface. + MbeNewtonResult TangentIntersection( const MbSurfaceIntersectionCurve &, + size_t iterLimit, double &u, double &v, double &t, bool ext0, bool ext1 ) const; + MbeNewtonResult CurveTangentIntersection( const MbCurve3D &, double funcEpsilon, size_t iterLimit, + double &u, double &v, double &t, bool ext0, bool ext1 ) const; + bool IsSameSurface( const MbCurve3D &, double &u, double &v, double &t ) const; // \ru Идентификация кривой. \en Identification of a curve. + + void operator = ( const MbSmoothSurface & ); // \ru Не реализовано. \en Not implemented. + + DECLARE_PERSISTENT_CLASS( MbSmoothSurface ) +}; + +IMPL_PERSISTENT_OPS( MbSmoothSurface ) + +//------------------------------------------------------------------------------ +// \ru Корректировка параметров \en Correction of parameters +// --- +inline void MbSmoothSurface::CheckParam( double &u, double &v ) const { + if ( v < vmin ) + v = vmin; + else + if ( v > vmax ) + v = vmax; + + if ( uclosed ) { + if ( (u < umin) || (u > umax ) ) { + double tmp = umax - umin; + u -= ::floor((u - umin) / tmp) * tmp; + } + } + else { + if ( poleMin && uumax ) + u = umax; + } +} + + +//------------------------------------------------------------------------------ +// Скорректировать крайние точки контейнеров, если они лежат в полюсах поверхности. +// --- +void CorrectPolePoins(const MbSurface & surface, SArray & points ); + + +//------------------------------------------------------------------------------ +// \ru Наполнить массив параметров для кривых на поверхностях \en Fill an array of parameters for the curves on surfaces +// --- +void CreateParams( const MbSurface & surface1, SArray & points1, + const MbSurface & surface2, SArray & points2, + SArray * values, SArray * valuesDerive, + bool °enerate1, bool °enerate2, ptrdiff_t & begN, ptrdiff_t & endN, + SArray & params ); + + +//------------------------------------------------------------------------------ +// \ru Создать кривые на поверхности \en Create curves on a surface. +// --- +void CreateSurfaceCurves( const MbSurface & surface1, SArray & points1, + const MbSurface & surface2, SArray & points2, + MbeSmoothForm form, bool firstFree, double distance1, double distance2, + bool insert, ptrdiff_t begN, ptrdiff_t endN, VERSION version, + MbSurfaceCurve *& curve1, MbSurfaceCurve *& curve2 ); + + +//------------------------------------------------------------------------------ +// \ru Создать поверхность \en Create a surface +// --- +MbSmoothSurface * CreateSmoothSurface( const MbSurface & surface1, SArray & points1, + const MbSurface & surface2, SArray & points2, + MbeSmoothForm form, bool firstFree, double distance1, double distance2, + double conic, bool even, ptrdiff_t begN, ptrdiff_t endN, VERSION version ); + + +#endif // __SURF_SMOOTH_SURFACE_H diff --git a/C3d/Include/surf_spine.h b/C3d/Include/surf_spine.h index db0cdb6..75dadc3 100644 --- a/C3d/Include/surf_spine.h +++ b/C3d/Include/surf_spine.h @@ -21,10 +21,19 @@ const VERSION SPINE_ALG_VERSION1 = 0x0A000000L; // \ru Добавлена option const VERSION SPINE_ALG_VERSION2 = 0x0D000023L; // \ru Добавлено создание optionalCurve в общем случае \en Added creation of "optionalCurve" in general case +class MATH_CLASS MbSpine; + +namespace c3d // namespace C3D +{ +typedef SPtr SpineSPtr; +typedef SPtr ConstSpineSPtr; +} + + //------------------------------------------------------------------------------ /** \brief \ru Криволинейная направляющая для кинематической поверхности. \en Curvilinear spine for sweep surface. \~ - \details \ru Криволинейная направляющая для кинематической поверхности (поверхности заметания) служит для расчёта в каждой точке направляющей кривой локальной система координат. \n + \details \ru Криволинейная направляющая для кинематической поверхности (поверхности заметания) служит для расчёта в каждой точке направляющей кривой локальной системы координат. \n Локальная ось 0 ориентирована по касательной кривой "curve". \n Локальная ось 1 ориентирована в сторону вектора "direction" или в сторону кривой "optionalCurve". \n Локальная ось 2 дополняет локальную систему до правой системы координат. @@ -37,27 +46,27 @@ const VERSION SPINE_ALG_VERSION2 = 0x0D000023L; // \ru Добавлено соз class MATH_CLASS MbSpine : public MbRefItem, public TapeBase { public: /// \ru Способы движения локальной системы координат вдоль направляющей кривой "curve". \en Methods of movement of the local coordinate system along the guide curve "curve". - enum LocalAxises { + enum LocalAxes { la_planeParallel = 0, ///< \ru Плоскопараллельный, сохраняющий исходную ориентацию осей. \en Plane-parallel, preserving the original orientation of the axes. - la_culcDirection = 1, ///< \ru Вектор "direction" рассчитан объектом. Ось 0 ориентирована по касательной кривой "curve", ось 1 - в сторону вектора "direction". \en Vector "direction" was calculated by object. Axis 0 is oriented along the tangent of the curve "curve", the axis 1 in the direction of the vector "direction". + la_calcDirection = 1, ///< \ru Вектор "direction" рассчитан объектом. Ось 0 ориентирована по касательной кривой "curve", ось 1 - в сторону вектора "direction". \en Vector "direction" was calculated by object. Axis 0 is oriented along the tangent of the curve "curve", the axis 1 in the direction of the vector "direction". la_userDirection = 2, ///< \ru Вектор "direction" задан конструктору. Ось 0 ориентирована по касательной кривой "curve", ось 1 - в сторону вектора "direction". \en Vector "direction" was sent to conctructor. Axis 0 is oriented along the tangent of the curve "curve", the axis 1 in the direction of the vector "direction". la_surfaceNormal = 3, ///< \ru Ось 0 ориентирована по касательной кривой на поверхности "curve", ось 1 - по нормали поверхности кривой "curve". \en The 0 axis is oriented along the tangent curve on the curve surface, the 1 axis is oriented along the normal of the curve surface. la_optionalCurve = 4, ///< \ru Ось 0 ориентирована по касательной кривой "curve", ось 1 - в сторону кривой "optionalCurve". \en Axis 0 is oriented along the tangent of the curve "curve", the axis 1 in the direction of the curve "optionalCurve". }; private: - MbCurve3D * curve; ///< \ru Направляющая кривая - всегда не NULL. \en Spine curve - it is always not NULL. - MbVector3D direction; ///< \ru Вектор ориентации матрицы преобразования. \en Vector of transformation matrix orientation. - MbCurve3D * optionalCurve; ///< \ru Кривая векторa ориентации матрицы преобразования (может быть NULL для простой траектории). \en A curve of the transformation matrix orientation (it may be NULL for a simple trajectory). - MbSurface * spineSurface; ///< \ru Поверхность направляющей кривой, если "curve" - кривая на поверхности, или NULL. \en The surface of the "curve", if it is curve on surface, or NULL. - MbCurve * featureCurve; ///< \ru Двумерная кривая, если "curve" - кривая на поверхности, или NULL. \en Two-dimensional curve of the "curve", if it is curve on surface, or NULL. - LocalAxises localAxises; ///< \ru Способы ориентации локальной системы координат вдоль направляющей кривой "curve". \en Methods of orientation of the local coordinate system along the guide curve "curve". - double crossSize; ///< \ru Поперечный масштаб при построении optionalCurve. \en Transverse scale in construction of "optionalCurve". - double ortParam; ///< \ru Параметр кривой, для которой расчитаны ort0, ort1, ort2. \en Parameter of a curve with evaluated ort0, ort1 and ort2. - MbVector3D ort0; ///< \ru Вектор базиса в точке ortParam направляющей. \en A basis vector in the point ortParam of the spine. - MbVector3D ort1; ///< \ru Вектор базиса в точке ortParam направляющей. \en A basis vector in the point ortParam of the spine. - MbVector3D ort2; ///< \ru Вектор базиса в точке ortParam направляющей. \en A basis vector in the point ortParam of the spine. - VERSION version; ///< \ru Версия расчета вектора direction. \en Version of vector "direction" calculation. + SPtr curve; ///< \ru Направляющая кривая - всегда не NULL. \en Spine curve - it is always not NULL. + MbVector3D direction; ///< \ru Вектор ориентации матрицы преобразования. \en Vector of transformation matrix orientation. + SPtr optionalCurve; ///< \ru Кривая вектора ориентации матрицы преобразования (может быть NULL для простой траектории). \en A curve of the transformation matrix orientation (it may be NULL for a simple trajectory). + SPtr spineSurface; ///< \ru Поверхность направляющей кривой, если "curve" - кривая на поверхности, или NULL. \en The surface of the "curve", if it is curve on surface, or NULL. + SPtr featureCurve; ///< \ru Двумерная кривая, если "curve" - кривая на поверхности, или NULL. \en Two-dimensional curve of the "curve", if it is curve on surface, or NULL. + LocalAxes localAxes; ///< \ru Способы ориентации локальной системы координат вдоль направляющей кривой "curve". \en Methods of orientation of the local coordinate system along the guide curve "curve". + double crossSize; ///< \ru Поперечный масштаб при построении optionalCurve. \en Transverse scale in construction of "optionalCurve". + double ortParam; ///< \ru Параметр кривой, для которой рассчитаны ort0, ort1, ort2. \en Parameter of a curve with evaluated ort0, ort1 and ort2. + MbVector3D ort0; ///< \ru Вектор базиса в точке ortParam направляющей. \en A basis vector in the point ortParam of the spine. + MbVector3D ort1; ///< \ru Вектор базиса в точке ortParam направляющей. \en A basis vector in the point ortParam of the spine. + MbVector3D ort2; ///< \ru Вектор базиса в точке ortParam направляющей. \en A basis vector in the point ortParam of the spine. + VERSION version; ///< \ru Версия расчета вектора direction. \en Version of vector "direction" calculation. //------------------------------------------------------------------------------ /** \brief \ru Вспомогательные данные. @@ -133,17 +142,17 @@ protected: */ MbSpine( const MbCurve3D & cur, bool same, const MbVector3D & dir, bool par, VERSION vers = Math::DefaultMathVersion() ); - /** \brief \ru Конструктор по направляющей кривой и кривой векторa ориентации матрицы преобразования. + /** \brief \ru Конструктор по направляющей кривой и кривой вектора ориентации матрицы преобразования. \en Constructor by spine curve and a curve of the vector of transformation matrix orientation. \~ - \details \ru Конструктор по направляющей кривой и кривой векторa ориентации матрицы преобразования.\n - \en Constructor by spine curve and a curve of the vector of transformation matrix orientation.\n \~ + \details \ru Конструктор по направляющей кривой и кривой вектора ориентации матрицы преобразования.\n + \en Constructor by spine curve and a curve of the vector of transformation matrix orientation.\n \~ \param[in] cur - \ru Направляющая кривая \en A spine curve \~ \param[in] same - \ru Признак использования оригинала направляющей кривой, а не ее копии \en Attribute of using the original of spine curve instead of its copy \~ - \param[in] opt - \ru Кривая векторa ориентации матрицы преобразования + \param[in] opt - \ru Кривая вектора ориентации матрицы преобразования \en A curve of the vector of transformation matrix orientation \~ - \param[in] sameO - \ru Признак использования оригинала кривой векторa ориентации матрицы преобразования, а не ее копии + \param[in] sameO - \ru Признак использования оригинала кривой вектора ориентации матрицы преобразования, а не ее копии \en Attribute of using the original of a curve of the vector of transformation matrix orientation instead of its copy \~ \param[in] par - \ru Признак параллельного переноса. \en Attribute of parallel translation. \~ @@ -151,6 +160,8 @@ protected: \en Version of calculation the local coordinate system. \~ */ MbSpine( const MbCurve3D & cur, bool same, const MbCurve3D & opt, bool sameO, bool par, VERSION vers = Math::DefaultMathVersion() ); + // \ru Конструктор по направляющей кривой и поверхности, на которой лежит направляющая кривая. \en Constructor by spine curve and the surface on which the spine curve lies. \~ + MbSpine( const MbCurve3D & spineCurve, bool sameSpine, const MbSurface & surface, bool sameSurface, const MbCurve & pCurve, bool sameCurve, VERSION ver ); protected: MbSpine( const MbSpine &, MbRegDuplicate * ); @@ -160,6 +171,81 @@ public: public: VISITING_CLASS( MbSpine ); + /** \brief \ru Конструктор по направляющей кривой. + \en Constructor by spine curve. \~ + \details \ru Конструктор по направляющей кривой.\n + \en Constructor by spine curve.\n \~ + \param[in] c - \ru Направляющая кривая + \en A spine curve \~ + \param[in] parallel - \ru Признак параллельного переноса + \en Attribute of parallel translation. \~ + \param[in] same - \ru Признак использования оригинала направляющей кривой, а не ее копии + \en Attribute of using the original of spine curve instead of its copy \~ + \param[in] vers - \ru Версия вычисления осей локальной системы координат. + \en Version of calculation the local coordinate system. \~ + */ + static MbSpine & Create( const MbCurve3D & c, bool parallel, bool same, + VERSION vers = Math::DefaultMathVersion() ); + /** \brief \ru Конструктор по направляющей кривой и вектору ориентации матрицы преобразования. + \en Constructor by spine curve and a vector of transformation matrix orientation. \~ + \details \ru Конструктор по направляющей кривой и вектору ориентации матрицы преобразования.\n + \en Constructor by spine curve and a vector of transformation matrix orientation.\n \~ + \param[in] c - \ru Направляющая кривая + \en A spine curve \~ + \param[in] same - \ru Признак использования оригинала направляющей кривой, а не ее копии + \en Attribute of using the original of spine curve instead of its copy \~ + \param[in] direction - \ru Вектор ориентации матрицы преобразования. + \en Vector of transformation matrix orientation. \~ + \param[in] parallel - \ru Признак параллельного переноса + \en Attribute of parallel translation. \~ + \param[in] vers - \ru Версия вычисления осей локальной системы координат. + \en Version of calculation the local coordinate system. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ + */ + static MbSpine * Create( const MbCurve3D & c, bool same, const MbVector3D & dir, bool parallel, + VERSION vers = Math::DefaultMathVersion() ); + /** \brief \ru Конструктор по направляющей кривой и кривой векторa ориентации матрицы преобразования. + \en Constructor by spine curve and a curve of the vector of transformation matrix orientation. \~ + \details \ru Конструктор по направляющей кривой и кривой векторa ориентации матрицы преобразования.\n + \en Constructor by spine curve and a curve of the vector of transformation matrix orientation.\n \~ + \param[in] sp - \ru Направляющая кривая + \en A spine curve \~ + \param[in] sameS - \ru Признак использования оригинала направляющей кривой, а не ее копии + \en Attribute of using the original of spine curve instead of its copy \~ + \param[in] dc - \ru Кривая векторa ориентации матрицы преобразования + \en A curve of the vector of transformation matrix orientation \~ + \param[in] sameD - \ru Признак использования оригинала кривой векторa ориентации матрицы преобразования, а не ее копии + \en Attribute of using the original of a curve of the vector of transformation matrix orientation instead of its copy \~ + \param[in] parallel - \ru Признак параллельного переноса + \en Attribute of parallel translation. \~ + \param[in] vers - \ru Версия вычисления осей локальной системы координат. + \en Version of calculation the local coordinate system. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ + */ + static MbSpine * Create( const MbCurve3D & sp, bool sameS, const MbCurve3D & dc, bool sameD, bool parallel, + VERSION vers = Math::DefaultMathVersion() ); + /** \brief \ru Конструктор по направляющей кривой на кривой, связанной с поверхностью. + \en Constructor by spine curve on a curve associated with a surface. \~ + \details \ru Конструктор по направляющей кривой и поверхности, на которой лежит направляющая кривая.\n + \en Constructor by spine curve and the surface on which the spine curve lies.\n \~ + \param[in] sp - \ru Направляющая кривая + \en A spine curve \~ + \param[in] sameS - \ru Признак использования оригинала направляющей кривой, а не ее копии + \en Attribute of using the original of spine curve instead of its copy \~ + \param[in] surf - \ru Поверхность направляющей кривой. + \en The surface of the sp, it should be curve on surface. \~ + \param[in] pCrv - \ru Двумерная кривая на поверхности направляющей кривой. + \en The two-dimensional curve on the surface of the guide curve. \~ + \param[in] vers - \ru Версия вычисления осей локальной системы координат. + \en Version of calculation the local coordinate system. \~ + \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи. + \en Returns pointer to the created object or null pointer in case of failure. \~ + */ + static MbSpine * Create( const MbCurve3D & sp, bool sameS, const MbSurface & surf, const MbCurve * pCrv, + VERSION vers ); + /** \} */ /** \ru \name Базовые функции \en \name Base functions @@ -192,7 +278,7 @@ public: double GetTMax() const { return curve->GetTMax(); } /// \ru Вернуть минимальное значение параметра. \en Get the minimum value of parameter. double GetTMin() const { return curve->GetTMin(); } - /// \ru Проверка замкнутости кривой. \en Check for curve closedness + /// \ru Проверка замкнутости кривой. \en Check for curve closeness bool IsClosed() const { return curve->IsClosed(); } /** \} */ /** \ru \name Функции для работы в области определения направляющей кривой @@ -272,7 +358,7 @@ public: \return \ru Вектор \en A vector \~ */ - const MbVector3D & GetOrt0() const { return ort0; } + const MbVector3D & GetOrt0() const { return ort0; } /** \brief \ru Вычисление матриц преобразования. \en Calculation of transformation matrices. \~ @@ -293,14 +379,6 @@ public: // \ru Вычисление матрицы преобразования второй производной радиуса-вектора. \en Calculation of transformation matrix for second derivative of radius vector. \~ void CalculateMatrix2( double & v, bool ext, MbMatrix3D & matrix2 ) const; - /** \brief \ru Построить плейсмент в заданной точке. - \en Construct placement in the given point. \~ - \details \ru Построить плейсмент в заданной точке.\n - \en Construct placement in the given point.\n \~ - \param[in] v - \ru Параметр на направляющей кривой - \en Parameter on spine curve \~ - */ - MbPlacement3D GetPlacement( double v ) const; /// \ru Направляющая кривая. \en The spine curve. const MbCurve3D & GetCurve() const { return *curve; } /// \ru Дать направляющую кривую для изменения. \en Get spine curve for editing. @@ -313,28 +391,53 @@ public: VERSION GetVersion() const { return version; } /// \ru Изменить направляющую кривую. \en Change the spine curve. - void ChangeCurve( const MbCurve3D & c ); + void ChangeCurve( const MbCurve3D & ); /// \ru Изменить ориентирующую кривую. \en Change the direction curve. - void ChangeOptionalCurve( const MbCurve3D * d ); + void ChangeOptionalCurve( const MbCurve3D & ); /// \ru Вектор ориентации матрицы преобразования. \en Vector of transformation matrix orientation. const MbVector3D & GetDirection() const { return direction; } /// \ru Изменить вектор ориентации матрицы преобразования. \en Change the vector of transformation matrix orientation. bool SetDirection( const MbVector3D & d, bool checkBySpineCurve = false ); - /// \ru Лать способ ориентации локальной системы координат. \en Пуе еhe method of orientation of the local coordinate system. \~ - LocalAxises GetLocalAxisMethod() const { return localAxises; } - /// \ru Лать способ ориентации локальной системы координат. \en Пуе еhe method of orientation of the local coordinate system. \~ - void SetLocalAxisMethod( LocalAxises la ) { localAxises = la; } + /// \ru Дать способ ориентации локальной системы координат. \en Get the method of orientation of the local coordinate system. \~ + LocalAxes GetLocalAxisMethod() const { return localAxes; } + /// \ru Дать способ ориентации локальной системы координат. \en Get the method of orientation of the local coordinate system. \~ + void SetLocalAxisMethod( LocalAxes la ) { localAxes = la; } /// \ru Признак параллельного переноса. \en Attribute of plane-parallel translation. - bool IsParallel() const { return localAxises == MbSpine::la_planeParallel; } + bool IsParallel() const { return localAxes == la_planeParallel; } /// \ru Признак пользовательского направления. \en Attribute of user direction. - bool IsUserDirection() const { return ( localAxises >= MbSpine::la_userDirection ); } - - /// \ru Параметр кривой, для которой расчитан базис направляющей. \en Parameter of a curve with calculated basis of spine. + bool IsUserDirection() const { return ( localAxes == la_userDirection ); } + /// \ru Признак направления по нормали к поверхности. \en Attribute of direction as surface normal. + bool BySurfaceNormal() const { return ( localAxes == la_surfaceNormal ); } + /// \ru Признак направления по вспомогательной кривой. \en Attribute of direction by optional curve. + bool ByOptionalCurve() const { return ( localAxes == la_optionalCurve ); } + /// \ru Параметр кривой, для которой рассчитан базис направляющей. \en Parameter of a curve with calculated basis of spine. double GetOrtParam() const { return ortParam; } - /// \ru Установить параметр кривой, для которой расчитан базис направляющей. \en Set the parameter of a curve with calculated basis of spine. - void SetOrtParam( double t ); + /// \ru Установить параметр кривой, для которой рассчитан базис направляющей. \en Set the parameter of a curve with calculated basis of spine. + void SetOrtParam( double t ); + /// \ru Вычисление ортов для матриц преобразования. \ en The basis vectors calculation for matrix. + void BaseVectors0( double & v, bool ext, MbCartPoint3D & origin, + MbVector3D & vector0, MbVector3D & vector1, MbVector3D & vector2 ) const; + /// \ru Вычисление ортов и их первых производных. \ en Calculation of the basis vectors and their first derivatives. + void BaseVectors1( double & v, bool ext, MbVector3D & origin1, + MbVector3D & vector0, MbVector3D & vector1, MbVector3D & vector2, + MbVector3D & derive0, MbVector3D & derive1, MbVector3D & derive2 ) const; + /// \ru Вычисление ортов и их первых и вторых производных. \ en Calculation of the basis vectors and their first and second derivatives. + void BaseVectors2( double & v, bool ext, MbVector3D & origin1, MbVector3D & origin2, + MbVector3D & vector0, MbVector3D & vector1, MbVector3D & vector2, + MbVector3D & derive0, MbVector3D & derive1, MbVector3D & derive2, + MbVector3D & second0, MbVector3D & second1, MbVector3D & second2 ) const; + /** \brief \ru Построить плейсмент в заданной точке. + \en Construct placement in the given point. \~ + \details \ru Построить плейсмент в заданной точке.\n + \en Construct placement in the given point.\n \~ + \param[in] v - \ru Параметр на направляющей кривой + \en Parameter on spine curve \~ + */ + MbPlacement3D GetPlacement( double v ) const; + /// \ru Инициализировать плейсмент для заданной точке. \en Initialize placement for the given point. \~ + void GetPlacement( double v, MbPlacement3D & pl ) const; /// \ru Количество сегментов направляющей кривой. \en The number of segments of the spine curve. size_t GetSegmentsCount() const; @@ -344,78 +447,25 @@ public: \details \ru Функция регистрации по количеству ссылок для предотвращения многократной записи.\n \en Function of registration by the number of references to avoid multiple writing.\n \~ */ - void PrepareWrite() { SetRegistrable( (GetUseCount() > 1) ? registrable : noRegistrable ); } + void PrepareWrite() const { SetRegistrable( (GetUseCount() > 1) ? registrable : noRegistrable ); } + + const MbSurface * GetSpineSurface() const { return spineSurface; } ///< \ru Поверхность направляющей кривой, если "curve" - кривая на поверхности. \en The surface of the "curve", if it is curve on surface. + const MbCurve * GetFeatureCurve() const { return featureCurve; } ///< \ru Двумерная кривая, если "curve" - кривая на поверхности. \en Two-dimensional curve of the "curve", if it is curve on surface. + /** \} */ - /** \brief \ru Конструктор по направляющей кривой. - \en Constructor by spine curve. \~ - \details \ru Конструктор по направляющей кривой.\n - \en Constructor by spine curve.\n \~ - \param[in] c - \ru Направляющая кривая - \en A spine curve \~ - \param[in] parallel - \ru Признак параллельного переноса - \en Attribute of parallel translation. \~ - \param[in] same - \ru Признак использования оригинала направляющей кривой, а не ее копии - \en Attribute of using the original of spine curve instead of its copy \~ - \param[in] vers - \ru Версия вычисления осей локальной системы координат. - \en Version of calculation the local coordinate system. \~ - */ - static MbSpine & Create( const MbCurve3D & c, bool parallel, bool same, VERSION vers = Math::DefaultMathVersion() ); - /** \brief \ru Конструктор по направляющей кривой и вектору ориентации матрицы преобразования. - \en Constructor by spine curve and a vector of transformation matrix orientation. \~ - \details \ru Конструктор по направляющей кривой и вектору ориентации матрицы преобразования.\n - \en Constructor by spine curve and a vector of transformation matrix orientation.\n \~ - \param[in] c - \ru Направляющая кривая - \en A spine curve \~ - \param[in] same - \ru Признак использования оригинала направляющей кривой, а не ее копии - \en Attribute of using the original of spine curve instead of its copy \~ - \param[in] direction - \ru Вектор ориентации матрицы преобразования. - \en Vector of transformation matrix orientation. \~ - \param[in] parallel - \ru Признак параллельного переноса - \en Attribute of parallel translation. \~ - \param[in] vers - \ru Версия вычисления осей локальной системы координат. - \en Version of calculation the local coordinate system. \~ - */ - static MbSpine * Create( const MbCurve3D & c, bool same, const MbVector3D & dir, bool parallel, VERSION vers = Math::DefaultMathVersion() ); - /** \brief \ru Конструктор по направляющей кривой и кривой векторa ориентации матрицы преобразования. - \en Constructor by spine curve and a curve of the vector of transformation matrix orientation. \~ - \details \ru Конструктор по направляющей кривой и кривой векторa ориентации матрицы преобразования.\n - \en Constructor by spine curve and a curve of the vector of transformation matrix orientation.\n \~ - \param[in] sp - \ru Направляющая кривая - \en A spine curve \~ - \param[in] sameS - \ru Признак использования оригинала направляющей кривой, а не ее копии - \en Attribute of using the original of spine curve instead of its copy \~ - \param[in] dc - \ru Кривая векторa ориентации матрицы преобразования - \en A curve of the vector of transformation matrix orientation \~ - \param[in] sameD - \ru Признак использования оригинала кривой векторa ориентации матрицы преобразования, а не ее копии - \en Attribute of using the original of a curve of the vector of transformation matrix orientation instead of its copy \~ - \param[in] parallel - \ru Признак параллельного переноса - \en Attribute of parallel translation. \~ - \param[in] vers - \ru Версия вычисления осей локальной системы координат. - \en Version of calculation the local coordinate system. \~ - */ - static MbSpine * Create( const MbCurve3D & sp, bool sameS, const MbCurve3D & dc, bool sameD, bool parallel, VERSION vers = Math::DefaultMathVersion() ); - private: - void InitDirection(); // \ru Вычисление direction; разбор частных случаев типа плоских кривых, дуг окружности и т.п. \en Calculation of "direction"; analysis of such special cases as planar curves, circle arcs etc. - bool CalculateSurface(); // \ru Для кривой на поверхности вять поверхность и двумерную кривую. \en Get "spineSurface" and "featureCurve" if "curve" is a curve on surface. void CheckParam( double & v ) const ; // \ru Функция переносит параметр v в область определения направляющей кривой. \en The function moves the parameter v inside the spine curve domain. - void CalculateVector1( double & v, bool ext, - MbVector3D & vector0, MbVector3D & vector1, MbVector3D & vector2, - MbVector3D & derive0, MbVector3D & derive1, MbVector3D & derive2 ) const; // \ru Вычисление векторов для матриц преобразования. \ en Vectors сalculation for matrix. - void CalculateVector2( double & v, bool ext, - MbVector3D & vector0, MbVector3D & vector1, MbVector3D & vector2, - MbVector3D & derive0, MbVector3D & derive1, MbVector3D & derive2, - MbVector3D & second0, MbVector3D & second1, MbVector3D & second2 ) const; // \ru Вычисление векторов для матриц преобразования. \ en Vectors сalculation for matrix. + void InitDirection(); // \ru Вычисление direction; разбор частных случаев типа плоских кривых, дуг окружности и т.п. \en Calculation of "direction"; analysis of such special cases as planar curves, circle arcs etc. + bool CalculateSpineSurface(); // \ru Для кривой на поверхности выделить поверхность и двумерную кривую. \en Get "spineSurface" and "featureCurve" if "curve" is a curve on surface. void CalculateDirection( double v, MbVector3D & direct ) const; // \ru Вектор ориентации матрицы преобразования. \en Vector of transformation matrix orientation. - bool InitOptionalCurve( bool exactOnly ); // \ru Вычисление optionalCurve; использовать, если нетривиальный случай. \en Calculation of "optionalCurve"; use if the case is nontrivial. void Multiplication( const MbVector3D & vector0, const MbVector3D & vector1, const MbVector3D & vector2, MbMatrix3D & matrix ) const; // \ru Определение матрицы преобразования. \en Determination of transformation matrix. DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSpine ) OBVIOUS_PRIVATE_COPY( MbSpine ) -}; +}; // MbSpine IMPL_PERSISTENT_OPS( MbSpine ) @@ -478,7 +528,17 @@ MATH_FUNC (void) MakeSpines( const MbSpine & sp, SArray & items ); \param[in,out] items - \ru Массив направляющих. \en An array of spines. \~ */ // --- -MATH_FUNC (void) DeleteNonUsedSpines( SArray & items ); +template +void DeleteNonUsedSpines( SpineDataVector & spineDataItems ) +{ + if ( !spineDataItems.empty() ) { + for ( size_t i = spineDataItems.size(); i--; ) { + MbSpine * spine = &spineDataItems[i].spine; + ::DeleteItem( spine ); + } + spineDataItems.clear(); + } +} //------------------------------------------------------------------------------ @@ -511,10 +571,12 @@ MATH_FUNC (ThreeStates) InitSpineDirection( const MbCurve3D & curve, MbVector3D \en A curve. \~ \param[in] direction - \ru Вектор, который не должен совпадать с касательной к кривой. \en A vector which must not be coincident with the curve tangent. \~ + \param[in] version - \ru Версия. + \en Version. \~ \return \ru true, если вектор не совпадает с касательной к кривой. \en It equals true if the vector is not coincident with the curve tangent. \~ */ // --- -MATH_FUNC (bool) CheckSpineDirection( const MbCurve3D & curve, const MbVector3D & direction ); +MATH_FUNC (bool) CheckSpineDirection( const MbCurve3D & curve, const MbVector3D & direction, VERSION version = Math::DefaultMathVersion() ); #endif //__SURF_SPINE_H diff --git a/C3d/Include/surf_spiral_surface.h b/C3d/Include/surf_spiral_surface.h index e1f9bf2..762bc71 100644 --- a/C3d/Include/surf_spiral_surface.h +++ b/C3d/Include/surf_spiral_surface.h @@ -1,400 +1,401 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Спиральная поверхность. - \en Spiral surface. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __SURF_SPIRAL_SURFACE_H -#define __SURF_SPIRAL_SURFACE_H - - -#include -#include -#include - - -class MATH_CLASS MbConeSpiral; - - -//------------------------------------------------------------------------------ -/** \brief \ru Спиральная поверхность. - \en Spiral surface. \~ - \details \ru Спиральная поверхность получена путем движения образующей кривой curve по цилиндрической спирали. - Спиральная поверхность является частным случаем кинематической поверхности. - Ось спирали направлена вдоль оси Z локальной системы координат. - Второй параметр поверхности отсчитывается от оси position.axisX локальной системы координат. - Радиус-вектор поверхности описывается векторной функцией \n - r(u,v) = position.origin + (M(v) (curve(u) - origin)), \n - где M(v) - матрица вращения. \n - Первый параметр поверхности совпадает с параметром образующей кривой. - Второй параметр поверхности совпадает с углом поворота точки спирали вокруг её оси. - \en A spiral surface is obtained by moving of a generating curve along a cylindrical spiral. - Spiral surface is a special case of sweep surface. - A spiral axis is directed along the Z-axis of the local coordinate system. - The second parameter of a surface is measured from the axis "position.axisX" of the local coordinate system. - Radius-vector of the surface is described by the vector function \n - r(u,v) = position.origin + (M(v) (curve(u) - origin)), \n - where M(v) is rotation matrix. \n - The first surface parameter coincides with the parameter of generatrix. - The second surface parameter coincides with rotation angle of a spiral point around its axis. \~ - \ingroup Surfaces -*/ -// --- -class MATH_CLASS MbSpiralSurface : public MbSweptSurface { -private: - MbPlacement3D position; ///< \ru Местная система координат (position.axisZ - ось спирали). \en Local coordinate system ('position.axisZ' is axis of spiral). - double radius; ///< \ru Радиус спирали. \en A spiral radius. - double step; ///< \ru Шаг спирали. \en A pitch of spiral. - MbCartPoint3D origin; ///< \ru Центр тяжести образующей. \en Center of gravity of generatrix. - MbMatrix3D into; ///< \ru Матрица преобразования в систему position. \en Matrix of transformation to the system 'position'. - MbMatrix3D from; ///< \ru Матрица преобразования из системы position. \en Matrix of transformation from the system 'position'. - double stepd2pi; ///< \ru Шаг приходящийся на период. \en A step corresponding to period. - -public: - - /** \brief \ru Конструктор по образующей, локальной системе координат, радиусу спирали, шагу спирали. - \en Constructor by generatrix, local coordinate system, spiral radius and spiral pitch. \~ - \details \ru Конструктор по образующей, локальной системе координат, радиусу спирали, шагу спирали. - \en Constructor by generatrix, local coordinate system, spiral radius and spiral pitch. \~ - \param[in] c - \ru Образующая - \en Generatrix \~ - \param[in] pos - \ru Локальная система координат - \en Local coordinate system \~ - \param[in] r - \ru Радиус спирали - \en Spiral radius \~ - \param[in] s - \ru Шаг спирали - \en Spiral pitch \~ - \param[in] t1 - \ru Начальный параметр спирали - \en Start parameter of spiral \~ - \param[in] t2 - \ru Конечный параметр спирали - \en End parameter of spiral \~ - \param[in] sameCurve - \ru Признак использования оригинала образующей, а не ее копии - \en Attribute of using the original of generatrix instead of its copy. \~ - */ - MbSpiralSurface( const MbCurve3D & c, const MbPlacement3D & pos, double r, double s, double t1, double t2, - bool sameCurve ); - - /** \brief \ru Конструктор по образующей и спирали. - \en Constructor by generatrix and spiral. \~ - \details \ru Конструктор по образующей и спирали. - \en Constructor by generatrix and spiral. \~ - \param[in] c - \ru Образующая - \en Generatrix \~ - \param[in] s - \ru Спираль - \en Spiral \~ - \param[in] sameCurve - \ru Признак использования оригинала образующей, а не ее копии - \en Attribute of using the original of generatrix instead of its copy. \~ - */ - MbSpiralSurface( const MbCurve3D & c, const MbConeSpiral & s, bool sameCurve ); - -protected: - MbSpiralSurface( const MbSpiralSurface &, MbRegDuplicate * ); -private: - MbSpiralSurface( const MbSpiralSurface & ); // \ru Не реализовано. \en Not implemented. -public: - virtual ~MbSpiralSurface(); - -public: - VISITING_CLASS( MbSpiralSurface ); - - /** \ru \name Общие функции геометрического объекта - \en \name Common functions of a geometric object - \{ */ - virtual MbeSpaceType IsA() const; // \ru Тип элемента. \en A type of element. - virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. - virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Равны ли объекты. \en Whether the objects are equal. - virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным. \en Make equal. - virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными. \en Determine whether the objects are similar. - virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. - virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг. \en Translation. - virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate around an axis. - - virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. - virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта. \en Set properties of the object. - virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты. \en Get the base objects. - virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. - virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. - - /** \} */ - /** \ru \name Функции для работы в области определения поверхности - Функции PointOn, Derive... поверхностей корректируют параметры - при выходе их за пределы прямоугольной области определения параметров. - \en \name Functions for working at surface domain - Functions PointOn, Derive... correct parameters - when getting out of rectangular domain bounds. - \{ */ - virtual void PointOn ( double & u, double & v, MbCartPoint3D & ) const; // \ru Точка на поверхности. \en A point on surface. - virtual void DeriveU ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по u. \en First derivative with respect to u. - virtual void DeriveV ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по v. \en First derivative with respect to v. - virtual void DeriveUU ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по u. \en Second derivative with respect to u. - virtual void DeriveVV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по v. \en Second derivative with respect to v. - virtual void DeriveUV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по uv. \en Second derivative with respect to u and v. - virtual void DeriveUUU( double & u, double & v, MbVector3D & ) const; - virtual void DeriveUUV( double & u, double & v, MbVector3D & ) const; - virtual void DeriveUVV( double & u, double & v, MbVector3D & ) const; - virtual void DeriveVVV( double & u, double & v, MbVector3D & ) const; - /** \} */ - /** \ru \name Функции для работы внутри и вне области определения поверхности - функции _PointOn, _Derive... поверхностей не корректируют - параметры при выходе их за пределы прямоугольной области определения параметров. - \en \name Functions for working inside and outside the surface domain. - functions _PointOn, _Derive... of surfaces don't correct - parameters when getting out of rectangular domain bounds. - \{ */ - virtual void _PointOn ( double u, double v, MbCartPoint3D & ) const; // \ru Точка на расширенной поверхности. \en A point on extended surface. - virtual void _DeriveU ( double u, double v, MbVector3D & ) const; // \ru Первая производная по u. \en First derivative with respect to u. - virtual void _DeriveV ( double u, double v, MbVector3D & ) const; // \ru Первая производная по v. \en First derivative with respect to v. - virtual void _DeriveUU ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по u. \en Second derivative with respect to u. - virtual void _DeriveVV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по v. \en Second derivative with respect to v. - virtual void _DeriveUV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по uv. \en Second derivative with respect to u and v. - virtual void _DeriveUUU( double u, double v, MbVector3D & ) const; - virtual void _DeriveUUV( double u, double v, MbVector3D & ) const; - virtual void _DeriveUVV( double u, double v, MbVector3D & ) const; - virtual void _DeriveVVV( double u, double v, MbVector3D & ) const; - /** \} */ - /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. - \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. - \{ */ - virtual void Explore( double & u, double & v, bool ext, - MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, - MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; - /** \} */ - /** \ru \name Функции движения по поверхности - \en \name Functions of moving on surface - \{ */ - virtual double StepU ( double u, double v, double sag ) const; // \ru Вычисление шага по u по заданной стрелке прогиба. \en Calculation of the parameter step in u direction by the sag. - virtual double StepV ( double u, double v, double sag ) const; // \ru Вычисление шага по v по заданной стрелке прогиба. \en Calculation of the parameter step in v direction by the sag. - virtual double DeviationStepU( double u, double v, double angle ) const; // \ru Вычисление шага по u по заданному углу отклонения. \en Calculation of the parameter step in u direction by the deviation angle. - virtual double DeviationStepV( double u, double v, double angle ) const; // \ru Вычисление шага по v по заданному углу отклонения. \en Calculation of the parameter step in v direction by the deviation angle. - virtual double MetricStepU ( double u, double v, double length ) const; // \ru Вычисление шага по u по заданной длине. \en Calculation of the parameter step in u direction by the given length. - virtual double MetricStepV ( double u, double v, double length ) const; // \ru Вычисление шага по v по заданной длине. \en Calculation of the parameter step in v direction by the given length. - virtual size_t GetUCount() const; - virtual size_t GetVCount() const; - /** \} */ - /** \ru \name Общие функции поверхности - \en \name Common functions of surface. - \{ */ - virtual double CurvatureU ( double u, double v ) const; // \ru Kривизна линии по u. \en Curvature of line in u direction. - virtual bool IsPlanar() const; // \ru Является ли поверхность плоской. \en Whether a surface is planar. - virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменение носителя \en Changing of carrier - - virtual MbSplineSurface * NurbsSurface( double, double, double, double, bool bmatch = false ) const; // \ru NURBS копия поверхности. \en NURBS copy of surface. - virtual MbSurface * Offset( double d, bool same ) const; // \ru Создание эквидистантной поверхности \en Creation of an offset surface - - virtual MbCurve3D * CurveU( double v, MbRect1D * pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии v = const. \en A spatial copy of the line v = const. - virtual MbCurve3D * CurveV( double u, MbRect1D * pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии u = const. \en A spatial copy of the line u = const. - - // \ru Найти проекцию точки на поверхность. \en Find the projection of a point onto the surface. - virtual bool NearPointProjection ( const MbCartPoint3D & pnt, double & u, double & v, bool ext, MbRect2D * uvRange = NULL ) const; - - // \ru Построить касательные и нормальные плейсменты конструктивных плоскостей. \en Construct tangent and normal placements of constructive planes. - virtual bool CreateNormalPlacements ( const MbVector3D & axisZ, double angle, SArray & places ) const; - virtual bool CreateTangentPlacements( const MbVector3D & axisZ, SArray & places ) const; - - // \ru Подобные ли поверхности для объединения (слива). \en Whether the surfaces are similar to merge. - virtual bool IsSimilarToSurface( const MbSurface & surf, VERSION version, double precision = METRIC_PRECISION ) const; - // \ru Дать двумерную матрицу преобразования из своей параметрической области в параметрическую область surf. \en Get two-dimensional matrix of transformation from its parametric region to the parametric region of 'surf'. - virtual bool GetMatrixToSurface( const MbSurface & surf, MbMatrix & matr, VERSION version, double precision = METRIC_PRECISION ) const; - - // \ru Определение разбивки параметрической области поверхности вертикалями и горизонталями. \en Determine a splitting of parametric region of a surface by verticals and horizontals. - virtual void GetTesselation( const MbStepData & stepData, - double u1, double u2, double v1, double v2, - SArray & uu, SArray & vv ) const; - - virtual size_t GetUMeshCount() const; // \ru Выдать количество полигонов по u. \en Get the number of polygons in u-direction. - virtual size_t GetVMeshCount() const; // \ru Выдать количество полигонов по v. \en Get the number of polygons in v-direction. - - virtual bool IsLineU() const; // \ru Если true все производные по U выше первой равны нулю. \en If it equals true then all derivatives with respect to u which have more than first order are equal to null. - /** \} */ - /** \ru \name Функции спиральной поверхности - \en \name Functions of spiral surface - \{ */ - - /** \brief \ru Определение матрицы переноса для образующей. - \en Determination of a transfer matrix for generatrix. \~ - \details \ru Определение матрицы при переносе образующей - из параметра vmin в параметр v. - \en Determination of matrix when transferring the generatrix - from the parameter vmin to the parameter v. \~ - \param[in] v - \ru Новый параметр на спирали - \en A new parameter on the spiral \~ - \param[out] matr - \ru Матрица-резцультат - \en A matrix - the result \~ - */ - void TransformMatrix( double v, MbMatrix3D & matr ) const; - - /// \ru Внутренний радиус витков. \en Internal radius of coils. - double GetSpiralR() const { return radius; } - /// \ru Внутренний шаг витков. \en Internal pitch of coils. - double GetStep() const { return step; } - - /// \ru Физический радиус витков. \en Physical radius of coils. - double GetSpiralRadius() const; - /// \ru Физический шаг витков. \en Physical pitch of coils. - double GetSpiralStep() const; - - /// \ru Местная система координат (ось position.axisZ - ось спирали). \en Local coordinate system ('position.axisZ' is axis of spiral). - const MbPlacement3D & GetPlacement() const { return position; } - /// \ru Центр тяжести образующей. \en Center of gravity of generatrix. - const MbCartPoint3D & GetOrigin() const { return origin; } - - /// \ru Построить спираль. \en Construct a spiral. - MbConeSpiral & CreateSpiral() const; - - /// \ru Является ли локальная система координат поверхности ортонормированной. \en Whether the local coordinate system of a surface is orthonormalized. - bool IsPositionNormal() const { return ( position.IsNormal() ); } - /// \ru Является ли локальная система координат поверхности ортогональной с равными по длине осями X,Y. \en Whether the local coordinate system of a surface is orthogonal with X and Y axes equal by length. - bool IsPositionCircular() const { return ( position.IsCircular() ); } - /// \ru Является ли локальная система координат поверхности ортогональной и изотропной по осям. \en Whether the local coordinate system of a surface is orthogonal and isotropic by the axes. - bool IsPositionIsotropic() const { return ( position.IsIsotropic() ); } - /// \ru Является ли образующая кривая окружностью. \en Whether a generatrix is a circle. - bool IsCircleType() const; - - /// \ru Оценить рабочий диапазон для проецирования. \en Estimate the projection range along V. - bool GetProjectionRange( const MbCartPoint3D & pnt, bool ext, const MbRect2D * userRange, MbRect2D & resRange ) const; - /// \ru Скорректировать разбивку для проецирования точки. \en Correct the number of splittings by v-parameter for point projection. - bool CorrectVCount( double vbeg, double vend, size_t & cntv ) const; - /** \} */ -private: - - void Init(); // \ru Инициализация. \en Initialization. - inline void CheckParam ( double & v ) const; - inline void RotateVector ( const double & sin_V, const double & cos_V, MbVector3D & ) const; - inline void RotateDeriveV ( const double & sin_V, const double & cos_V, MbVector3D & ) const; - inline void RotateDeriveVV ( const double & sin_V, const double & cos_V, MbVector3D & ) const; - inline void RotateDeriveVVV( const double & sin_V, const double & cos_V, MbVector3D & ) const; - inline void DirectrixPointOn ( const double & v, const double & sinV, const double & cosV, MbCartPoint3D & ) const; - inline void DirectrixDeriveV ( const double & sinV, const double & cosV, MbVector3D & ) const; - inline void DirectrixDeriveVV ( const double & sinV, const double & cosV, MbVector3D & ) const; - inline void DirectrixDeriveVVV( const double & sinV, const double & cosV, MbVector3D & ) const; - - void operator = ( const MbSpiralSurface & ); // \ru Не реализовано. \en Not implemented. - - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSpiralSurface ) -}; - -IMPL_PERSISTENT_OPS( MbSpiralSurface ) - -//------------------------------------------------------------------------------ -// \ru Проверить параметр \en Check parameter -// --- -inline void MbSpiralSurface::CheckParam( double & v ) const -{ - if ( v < vmin ) - v = vmin; - else - if ( v > vmax ) - v = vmax; -} - - -//------------------------------------------------------------------------------ -// \ru Выдать физический радиус спирали \en Get physical radius of spiral -// --- -inline double MbSpiralSurface::GetSpiralStep() const -{ - if ( position.IsNormal() ) - return step; - else if ( position.IsOrthogonal() ) { - return (step * position.GetAxisZ().Length()); - } - return 0.0; -} - - -//------------------------------------------------------------------------------ -// \ru Поворот вектора вокруг оси спирали \en Rotation of vector around spiral axis -// --- -inline void MbSpiralSurface::RotateVector( const double & sin_V, const double & cos_V, MbVector3D & _vector ) const -{ - _vector.Transform( into ); - double x = (_vector.x * cos_V) - (_vector.y * sin_V); - double y = (_vector.x * sin_V) + (_vector.y * cos_V); - _vector.x = x; - _vector.y = y; - _vector.Transform( from ); -} - - -//------------------------------------------------------------------------------- -// \ru Первая производная поворота вектора вокруг оси спирали \en First derivative of vector rotation around the spiral axis -// --- -inline void MbSpiralSurface::RotateDeriveV( const double & sin_V, const double & cos_V, MbVector3D & _vector ) const -{ - _vector.Transform( into ); - double x = - (_vector.x * sin_V) - (_vector.y * cos_V); - double y = (_vector.x * cos_V) - (_vector.y * sin_V); - _vector.x = x; - _vector.y = y; - _vector.z = 0.0; - _vector.Transform( from ); -} - - -//------------------------------------------------------------------------------- -// \ru Вторая производная поворота вектора вокруг оси спирали \en Second derivative of vector rotation around spiral axis -// --- -inline void MbSpiralSurface::RotateDeriveVV( const double & sin_V, const double & cos_V, MbVector3D & _vector ) const -{ - _vector.Transform( into ); - double x = - (_vector.x * cos_V) + (_vector.y * sin_V); - double y = - (_vector.x * sin_V) - (_vector.y * cos_V); - _vector.x = x; - _vector.y = y; - _vector.z = 0.0; - _vector.Transform( from ); -} - - -//------------------------------------------------------------------------------- -// \ru Третья производная поворота вектора вокруг оси спирали \en Third derivative of vector rotation around spiral axis -// --- -inline void MbSpiralSurface::RotateDeriveVVV( const double & sin_V, const double & cos_V, MbVector3D & _vector ) const -{ - _vector.Transform( into ); - double x = (_vector.x * sin_V) + (_vector.y * cos_V); - double y = - (_vector.x * cos_V) + (_vector.y * sin_V); - _vector.x = x; - _vector.y = y; - _vector.z = 0.0; - _vector.Transform( from ); -} - - -//------------------------------------------------------------------------------ -// \ru Точка спирали \en A point of spiral -// --- -inline void MbSpiralSurface::DirectrixPointOn( const double & v, const double & sinV, const double & cosV, MbCartPoint3D & p ) const { - p = position.GetOrigin(); - p.Add( position.GetAxisZ(), (stepd2pi * v), position.GetAxisX(), (radius * cosV), position.GetAxisY(), (radius * sinV) ); -} - - -//------------------------------------------------------------------------------- -// \ru Первая производная спирали \en First derivative of spiral -// --- -inline void MbSpiralSurface::DirectrixDeriveV( const double & sinV, const double & cosV, MbVector3D & d ) const { - d.Set( position.GetAxisZ(), stepd2pi, position.GetAxisX(), -(radius * sinV), position.GetAxisY(), (radius * cosV) ); -} - - -//------------------------------------------------------------------------------- -// \ru Вторая производная спирали \en Second derivative of spiral -// --- -inline void MbSpiralSurface::DirectrixDeriveVV( const double & sinV, const double & cosV, MbVector3D & d ) const { - d.Set( position.GetAxisX(), -(radius * cosV), position.GetAxisY(), -(radius * sinV) ); -} - - -//------------------------------------------------------------------------------- -// \ru Третья производная спирали \en Third derivative of spiral -// --- -inline void MbSpiralSurface::DirectrixDeriveVVV( const double & sinV, const double & cosV, MbVector3D & d ) const { - d.Set( position.GetAxisX(), (radius * sinV), position.GetAxisY(), -(radius * cosV) ); -} - - -#endif // __SURF_SPIRAL_SURFACE_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Спиральная поверхность. + \en Spiral surface. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __SURF_SPIRAL_SURFACE_H +#define __SURF_SPIRAL_SURFACE_H + + +#include +#include +#include +#include + + +class MATH_CLASS MbConeSpiral; + + +//------------------------------------------------------------------------------ +/** \brief \ru Спиральная поверхность. + \en Spiral surface. \~ + \details \ru Спиральная поверхность получена путем движения образующей кривой curve по цилиндрической спирали. + Спиральная поверхность является частным случаем кинематической поверхности. + Ось спирали направлена вдоль оси Z локальной системы координат. + Второй параметр поверхности отсчитывается от оси position.axisX локальной системы координат. + Радиус-вектор поверхности описывается векторной функцией \n + r(u,v) = position.origin + (M(v) (curve(u) - origin)), \n + где M(v) - матрица вращения. \n + Первый параметр поверхности совпадает с параметром образующей кривой. + Второй параметр поверхности совпадает с углом поворота точки спирали вокруг её оси. + \en A spiral surface is obtained by moving of a generating curve along a cylindrical spiral. + Spiral surface is a special case of sweep surface. + A spiral axis is directed along the Z-axis of the local coordinate system. + The second parameter of a surface is measured from the axis "position.axisX" of the local coordinate system. + Radius-vector of the surface is described by the vector function \n + r(u,v) = position.origin + (M(v) (curve(u) - origin)), \n + where M(v) is rotation matrix. \n + The first surface parameter coincides with the parameter of generatrix. + The second surface parameter coincides with rotation angle of a spiral point around its axis. \~ + \ingroup Surfaces +*/ +// --- +class MATH_CLASS MbSpiralSurface : public MbSweptSurface { +private: + MbPlacement3D position; ///< \ru Местная система координат (position.axisZ - ось спирали). \en Local coordinate system ('position.axisZ' is axis of spiral). + double radius; ///< \ru Радиус спирали. \en A spiral radius. + double step; ///< \ru Шаг спирали. \en A pitch of spiral. + MbCartPoint3D origin; ///< \ru Центр тяжести образующей. \en Center of gravity of generatrix. + MbMatrix3D into; ///< \ru Матрица преобразования в систему position. \en Matrix of transformation to the system 'position'. + MbMatrix3D from; ///< \ru Матрица преобразования из системы position. \en Matrix of transformation from the system 'position'. + double stepd2pi; ///< \ru Шаг приходящийся на период. \en A step corresponding to period. + +public: + + /** \brief \ru Конструктор по образующей, локальной системе координат, радиусу спирали, шагу спирали. + \en Constructor by generatrix, local coordinate system, spiral radius and spiral pitch. \~ + \details \ru Конструктор по образующей, локальной системе координат, радиусу спирали, шагу спирали. + \en Constructor by generatrix, local coordinate system, spiral radius and spiral pitch. \~ + \param[in] c - \ru Образующая + \en Generatrix \~ + \param[in] pos - \ru Локальная система координат + \en Local coordinate system \~ + \param[in] r - \ru Радиус спирали + \en Spiral radius \~ + \param[in] s - \ru Шаг спирали + \en Spiral pitch \~ + \param[in] t1 - \ru Начальный параметр спирали + \en Start parameter of spiral \~ + \param[in] t2 - \ru Конечный параметр спирали + \en End parameter of spiral \~ + \param[in] sameCurve - \ru Признак использования оригинала образующей, а не ее копии + \en Attribute of using the original of generatrix instead of its copy. \~ + */ + MbSpiralSurface( const MbCurve3D & c, const MbPlacement3D & pos, double r, double s, double t1, double t2, + bool sameCurve ); + + /** \brief \ru Конструктор по образующей и спирали. + \en Constructor by generatrix and spiral. \~ + \details \ru Конструктор по образующей и спирали. + \en Constructor by generatrix and spiral. \~ + \param[in] c - \ru Образующая + \en Generatrix \~ + \param[in] s - \ru Спираль + \en Spiral \~ + \param[in] sameCurve - \ru Признак использования оригинала образующей, а не ее копии + \en Attribute of using the original of generatrix instead of its copy. \~ + */ + MbSpiralSurface( const MbCurve3D & c, const MbConeSpiral & s, bool sameCurve ); + +protected: + MbSpiralSurface( const MbSpiralSurface &, MbRegDuplicate * ); +private: + MbSpiralSurface( const MbSpiralSurface & ); // \ru Не реализовано. \en Not implemented. +public: + virtual ~MbSpiralSurface(); + +public: + VISITING_CLASS( MbSpiralSurface ); + + /** \ru \name Общие функции геометрического объекта + \en \name Common functions of a geometric object + \{ */ + virtual MbeSpaceType IsA() const; // \ru Тип элемента. \en A type of element. + virtual MbSpaceItem & Duplicate( MbRegDuplicate * = NULL ) const; // \ru Сделать копию элемента. \en Create a copy of the element. + virtual bool IsSame ( const MbSpaceItem & other, double accuracy = LENGTH_EPSILON ) const; // \ru Равны ли объекты. \en Whether the objects are equal. + virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать равным. \en Make equal. + virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными. \en Determine whether the objects are similar. + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвиг. \en Translation. + virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate around an axis. + + virtual void GetProperties( MbProperties & properties ); // \ru Выдать свойства объекта. \en Get properties of the object. + virtual void SetProperties( const MbProperties & properties ); // \ru Записать свойства объекта. \en Set properties of the object. + virtual void GetBasisItems ( RPArray & ); // \ru Дать базовые объекты. \en Get the base objects. + virtual void GetBasisPoints( MbControlData3D & ) const; // \ru Выдать контрольные точки объекта. \en Get control points of object. + virtual void SetBasisPoints( const MbControlData3D & ); // \ru Изменить объект по контрольным точкам. \en Change the object by control points. + + /** \} */ + /** \ru \name Функции для работы в области определения поверхности + Функции PointOn, Derive... поверхностей корректируют параметры + при выходе их за пределы прямоугольной области определения параметров. + \en \name Functions for working at surface domain + Functions PointOn, Derive... correct parameters + when getting out of rectangular domain bounds. + \{ */ + virtual void PointOn ( double & u, double & v, MbCartPoint3D & ) const; // \ru Точка на поверхности. \en A point on surface. + virtual void DeriveU ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по u. \en First derivative with respect to u. + virtual void DeriveV ( double & u, double & v, MbVector3D & ) const; // \ru Первая производная по v. \en First derivative with respect to v. + virtual void DeriveUU ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по u. \en Second derivative with respect to u. + virtual void DeriveVV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по v. \en Second derivative with respect to v. + virtual void DeriveUV ( double & u, double & v, MbVector3D & ) const; // \ru Вторая производная по uv. \en Second derivative with respect to u and v. + virtual void DeriveUUU( double & u, double & v, MbVector3D & ) const; + virtual void DeriveUUV( double & u, double & v, MbVector3D & ) const; + virtual void DeriveUVV( double & u, double & v, MbVector3D & ) const; + virtual void DeriveVVV( double & u, double & v, MbVector3D & ) const; + /** \} */ + /** \ru \name Функции для работы внутри и вне области определения поверхности + функции _PointOn, _Derive... поверхностей не корректируют + параметры при выходе их за пределы прямоугольной области определения параметров. + \en \name Functions for working inside and outside the surface domain. + functions _PointOn, _Derive... of surfaces don't correct + parameters when getting out of rectangular domain bounds. + \{ */ + virtual void _PointOn ( double u, double v, MbCartPoint3D & ) const; // \ru Точка на расширенной поверхности. \en A point on extended surface. + virtual void _DeriveU ( double u, double v, MbVector3D & ) const; // \ru Первая производная по u. \en First derivative with respect to u. + virtual void _DeriveV ( double u, double v, MbVector3D & ) const; // \ru Первая производная по v. \en First derivative with respect to v. + virtual void _DeriveUU ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по u. \en Second derivative with respect to u. + virtual void _DeriveVV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по v. \en Second derivative with respect to v. + virtual void _DeriveUV ( double u, double v, MbVector3D & ) const; // \ru Вторая производная по uv. \en Second derivative with respect to u and v. + virtual void _DeriveUUU( double u, double v, MbVector3D & ) const; + virtual void _DeriveUUV( double u, double v, MbVector3D & ) const; + virtual void _DeriveUVV( double u, double v, MbVector3D & ) const; + virtual void _DeriveVVV( double u, double v, MbVector3D & ) const; + /** \} */ + /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметров поверхности. + \en \name Functions for get of the group of data inside and outside the surface's domain of parameters. + \{ */ + virtual void Explore( double & u, double & v, bool ext, + MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer, + MbVector3D * uuDer, MbVector3D * vvDer, MbVector3D * uvDer, MbVector3D * nor ) const; + /** \} */ + /** \ru \name Функции движения по поверхности + \en \name Functions of moving on surface + \{ */ + virtual double StepU ( double u, double v, double sag ) const; // \ru Вычисление шага по u по заданной стрелке прогиба. \en Calculation of the parameter step in u direction by the sag. + virtual double StepV ( double u, double v, double sag ) const; // \ru Вычисление шага по v по заданной стрелке прогиба. \en Calculation of the parameter step in v direction by the sag. + virtual double DeviationStepU( double u, double v, double angle ) const; // \ru Вычисление шага по u по заданному углу отклонения. \en Calculation of the parameter step in u direction by the deviation angle. + virtual double DeviationStepV( double u, double v, double angle ) const; // \ru Вычисление шага по v по заданному углу отклонения. \en Calculation of the parameter step in v direction by the deviation angle. + virtual double MetricStepU ( double u, double v, double length ) const; // \ru Вычисление шага по u по заданной длине. \en Calculation of the parameter step in u direction by the given length. + virtual double MetricStepV ( double u, double v, double length ) const; // \ru Вычисление шага по v по заданной длине. \en Calculation of the parameter step in v direction by the given length. + virtual size_t GetUCount() const; + virtual size_t GetVCount() const; + /** \} */ + /** \ru \name Общие функции поверхности + \en \name Common functions of surface. + \{ */ + virtual double CurvatureU ( double u, double v ) const; // \ru Kривизна линии по u. \en Curvature of line in u direction. + virtual bool IsPlanar() const; // \ru Является ли поверхность плоской. \en Whether a surface is planar. + virtual void ChangeCarrier( const MbSpaceItem & item, MbSpaceItem & init ); // \ru Изменение носителя \en Changing of carrier + + virtual MbSplineSurface * NurbsSurface( double, double, double, double, bool bmatch = false ) const; // \ru NURBS копия поверхности. \en NURBS copy of surface. + virtual MbSurface * Offset( double d, bool same ) const; // \ru Создание эквидистантной поверхности \en Creation of an offset surface + + virtual MbCurve3D * CurveU( double v, MbRect1D * pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии v = const. \en A spatial copy of the line v = const. + virtual MbCurve3D * CurveV( double u, MbRect1D * pRgn, bool bApprox = true ) const; // \ru Пространственная копия линии u = const. \en A spatial copy of the line u = const. + + // \ru Найти проекцию точки на поверхность. \en Find the projection of a point onto the surface. + virtual bool NearPointProjection ( const MbCartPoint3D & pnt, double & u, double & v, bool ext, MbRect2D * uvRange = NULL ) const; + + // \ru Построить касательные и нормальные плейсменты конструктивных плоскостей. \en Construct tangent and normal placements of constructive planes. + virtual bool CreateNormalPlacements ( const MbVector3D & axisZ, double angle, SArray & places ) const; + virtual bool CreateTangentPlacements( const MbVector3D & axisZ, SArray & places ) const; + + // \ru Подобные ли поверхности для объединения (слива). \en Whether the surfaces are similar to merge. + virtual bool IsSimilarToSurface( const MbSurface & surf, VERSION version, double precision = METRIC_PRECISION ) const; + // \ru Дать двумерную матрицу преобразования из своей параметрической области в параметрическую область surf. \en Get two-dimensional matrix of transformation from its parametric region to the parametric region of 'surf'. + virtual bool GetMatrixToSurface( const MbSurface & surf, MbMatrix & matr, VERSION version, double precision = METRIC_PRECISION ) const; + + // \ru Определение разбивки параметрической области поверхности вертикалями и горизонталями. \en Determine a splitting of parametric region of a surface by verticals and horizontals. + virtual void GetTesselation( const MbStepData & stepData, + double u1, double u2, double v1, double v2, + SArray & uu, SArray & vv ) const; + + virtual size_t GetUMeshCount() const; // \ru Выдать количество полигонов по u. \en Get the number of polygons in u-direction. + virtual size_t GetVMeshCount() const; // \ru Выдать количество полигонов по v. \en Get the number of polygons in v-direction. + + virtual bool IsLineU() const; // \ru Если true все производные по U выше первой равны нулю. \en If it equals true then all derivatives with respect to u which have more than first order are equal to null. + /** \} */ + /** \ru \name Функции спиральной поверхности + \en \name Functions of spiral surface + \{ */ + + /** \brief \ru Определение матрицы переноса для образующей. + \en Determination of a transfer matrix for generatrix. \~ + \details \ru Определение матрицы при переносе образующей + из параметра vmin в параметр v. + \en Determination of matrix when transferring the generatrix + from the parameter vmin to the parameter v. \~ + \param[in] v - \ru Новый параметр на спирали + \en A new parameter on the spiral \~ + \param[out] matr - \ru Матрица-резцультат + \en A matrix - the result \~ + */ + void TransformMatrix( double v, MbMatrix3D & matr ) const; + + /// \ru Внутренний радиус витков. \en Internal radius of coils. + double GetSpiralR() const { return radius; } + /// \ru Внутренний шаг витков. \en Internal pitch of coils. + double GetStep() const { return step; } + + /// \ru Физический радиус витков. \en Physical radius of coils. + double GetSpiralRadius() const; + /// \ru Физический шаг витков. \en Physical pitch of coils. + double GetSpiralStep() const; + + /// \ru Местная система координат (ось position.axisZ - ось спирали). \en Local coordinate system ('position.axisZ' is axis of spiral). + const MbPlacement3D & GetPlacement() const { return position; } + /// \ru Центр тяжести образующей. \en Center of gravity of generatrix. + const MbCartPoint3D & GetOrigin() const { return origin; } + + /// \ru Построить спираль. \en Construct a spiral. + MbConeSpiral * CreateSpiral() const { return MbConeSpiral::Create( radius, step, position, vmin, vmax ); } + + /// \ru Является ли локальная система координат поверхности ортонормированной. \en Whether the local coordinate system of a surface is orthonormalized. + bool IsPositionNormal() const { return ( position.IsNormal() ); } + /// \ru Является ли локальная система координат поверхности ортогональной с равными по длине осями X,Y. \en Whether the local coordinate system of a surface is orthogonal with X and Y axes equal by length. + bool IsPositionCircular() const { return ( position.IsCircular() ); } + /// \ru Является ли локальная система координат поверхности ортогональной и изотропной по осям. \en Whether the local coordinate system of a surface is orthogonal and isotropic by the axes. + bool IsPositionIsotropic() const { return ( position.IsIsotropic() ); } + /// \ru Является ли образующая кривая окружностью. \en Whether a generatrix is a circle. + bool IsCircleType() const; + + /// \ru Оценить рабочий диапазон для проецирования. \en Estimate the projection range along V. + bool GetProjectionRange( const MbCartPoint3D & pnt, bool ext, const MbRect2D * userRange, MbRect2D & resRange ) const; + /// \ru Скорректировать разбивку для проецирования точки. \en Correct the number of splittings by v-parameter for point projection. + bool CorrectVCount( double vbeg, double vend, size_t & cntv ) const; + /** \} */ +private: + + void Init(); // \ru Инициализация. \en Initialization. + inline void CheckParam ( double & v ) const; + inline void RotateVector ( const double & sin_V, const double & cos_V, MbVector3D & ) const; + inline void RotateDeriveV ( const double & sin_V, const double & cos_V, MbVector3D & ) const; + inline void RotateDeriveVV ( const double & sin_V, const double & cos_V, MbVector3D & ) const; + inline void RotateDeriveVVV( const double & sin_V, const double & cos_V, MbVector3D & ) const; + inline void DirectrixPointOn ( const double & v, const double & sinV, const double & cosV, MbCartPoint3D & ) const; + inline void DirectrixDeriveV ( const double & sinV, const double & cosV, MbVector3D & ) const; + inline void DirectrixDeriveVV ( const double & sinV, const double & cosV, MbVector3D & ) const; + inline void DirectrixDeriveVVV( const double & sinV, const double & cosV, MbVector3D & ) const; + + void operator = ( const MbSpiralSurface & ); // \ru Не реализовано. \en Not implemented. + + DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSpiralSurface ) +}; + +IMPL_PERSISTENT_OPS( MbSpiralSurface ) + +//------------------------------------------------------------------------------ +// \ru Проверить параметр \en Check parameter +// --- +inline void MbSpiralSurface::CheckParam( double & v ) const +{ + if ( v < vmin ) + v = vmin; + else + if ( v > vmax ) + v = vmax; +} + + +//------------------------------------------------------------------------------ +// \ru Выдать физический радиус спирали \en Get physical radius of spiral +// --- +inline double MbSpiralSurface::GetSpiralStep() const +{ + if ( position.IsNormal() ) + return step; + else if ( position.IsOrthogonal() ) { + return (step * position.GetAxisZ().Length()); + } + return 0.0; +} + + +//------------------------------------------------------------------------------ +// \ru Поворот вектора вокруг оси спирали \en Rotation of vector around spiral axis +// --- +inline void MbSpiralSurface::RotateVector( const double & sin_V, const double & cos_V, MbVector3D & _vector ) const +{ + _vector.Transform( into ); + double x = (_vector.x * cos_V) - (_vector.y * sin_V); + double y = (_vector.x * sin_V) + (_vector.y * cos_V); + _vector.x = x; + _vector.y = y; + _vector.Transform( from ); +} + + +//------------------------------------------------------------------------------- +// \ru Первая производная поворота вектора вокруг оси спирали \en First derivative of vector rotation around the spiral axis +// --- +inline void MbSpiralSurface::RotateDeriveV( const double & sin_V, const double & cos_V, MbVector3D & _vector ) const +{ + _vector.Transform( into ); + double x = - (_vector.x * sin_V) - (_vector.y * cos_V); + double y = (_vector.x * cos_V) - (_vector.y * sin_V); + _vector.x = x; + _vector.y = y; + _vector.z = 0.0; + _vector.Transform( from ); +} + + +//------------------------------------------------------------------------------- +// \ru Вторая производная поворота вектора вокруг оси спирали \en Second derivative of vector rotation around spiral axis +// --- +inline void MbSpiralSurface::RotateDeriveVV( const double & sin_V, const double & cos_V, MbVector3D & _vector ) const +{ + _vector.Transform( into ); + double x = - (_vector.x * cos_V) + (_vector.y * sin_V); + double y = - (_vector.x * sin_V) - (_vector.y * cos_V); + _vector.x = x; + _vector.y = y; + _vector.z = 0.0; + _vector.Transform( from ); +} + + +//------------------------------------------------------------------------------- +// \ru Третья производная поворота вектора вокруг оси спирали \en Third derivative of vector rotation around spiral axis +// --- +inline void MbSpiralSurface::RotateDeriveVVV( const double & sin_V, const double & cos_V, MbVector3D & _vector ) const +{ + _vector.Transform( into ); + double x = (_vector.x * sin_V) + (_vector.y * cos_V); + double y = - (_vector.x * cos_V) + (_vector.y * sin_V); + _vector.x = x; + _vector.y = y; + _vector.z = 0.0; + _vector.Transform( from ); +} + + +//------------------------------------------------------------------------------ +// \ru Точка спирали \en A point of spiral +// --- +inline void MbSpiralSurface::DirectrixPointOn( const double & v, const double & sinV, const double & cosV, MbCartPoint3D & p ) const { + p = position.GetOrigin(); + p.Add( position.GetAxisZ(), (stepd2pi * v), position.GetAxisX(), (radius * cosV), position.GetAxisY(), (radius * sinV) ); +} + + +//------------------------------------------------------------------------------- +// \ru Первая производная спирали \en First derivative of spiral +// --- +inline void MbSpiralSurface::DirectrixDeriveV( const double & sinV, const double & cosV, MbVector3D & d ) const { + d.Set( position.GetAxisZ(), stepd2pi, position.GetAxisX(), -(radius * sinV), position.GetAxisY(), (radius * cosV) ); +} + + +//------------------------------------------------------------------------------- +// \ru Вторая производная спирали \en Second derivative of spiral +// --- +inline void MbSpiralSurface::DirectrixDeriveVV( const double & sinV, const double & cosV, MbVector3D & d ) const { + d.Set( position.GetAxisX(), -(radius * cosV), position.GetAxisY(), -(radius * sinV) ); +} + + +//------------------------------------------------------------------------------- +// \ru Третья производная спирали \en Third derivative of spiral +// --- +inline void MbSpiralSurface::DirectrixDeriveVVV( const double & sinV, const double & cosV, MbVector3D & d ) const { + d.Set( position.GetAxisX(), (radius * sinV), position.GetAxisY(), -(radius * cosV) ); +} + + +#endif // __SURF_SPIRAL_SURFACE_H diff --git a/C3d/Include/surf_spline_surface.h b/C3d/Include/surf_spline_surface.h index 0fe4664..3c5190b 100644 --- a/C3d/Include/surf_spline_surface.h +++ b/C3d/Include/surf_spline_surface.h @@ -31,7 +31,7 @@ class MbSplineWorkingData; \en Weights of a NURBS surface. \~ \ingroup Data_Structures */ // --- -class MbWeightMatrix { +class MATH_CLASS MbWeightMatrix { private: size_t linesCnt; ///< \ru Количество строк матрицы. \en A number of matrix lines. size_t columnsCnt; ///< \ru Количество столбцов матрицы. \en A number of matrix columns. @@ -44,52 +44,52 @@ public: MbWeightMatrix( const MbWeightMatrix & wm ); ~MbWeightMatrix() {} public: - // Количество строк. + /// \ru Количество строк. \en Number of lines. size_t Lines() const { return linesCnt; } - // Количество столбцов. + /// \ru Количество столбцов. \en Number of columns. size_t Columns() const { return columnsCnt; } - // Общий вес? + /// \ru Общий вес? \en Is it used a common weight? bool IsCommonWeight() const; - // Инициализация по общему весу. + /// \ru Инициализация по общему весу. \en Initialize by common weight. bool InitWeights( double wt, size_t linesCnt, size_t columnsCnt ); - // Инициализация по матрице весов. + /// \ru Инициализация по матрице весов. \en Initialize by weights array. bool InitWeights( const Array2 & wts ); - // Инициализация по матрице весов. + /// \ru Инициализация по матрице весов. \en Initialize by weights array. void InitWeights( const MbWeightMatrix & wm ); - // Получить веса. + /// \ru Получить веса. \en . void GetWeights( Array2 & wts ) const; - // Установить веса. + /// \ru Установить веса. \en Get weights array. bool SetWeights( const Array2 & wts ) { return InitWeights( wts ); } - // Получить вес. + /// \ru Получить вес. \en Get a weight by indices. double GetWeight( size_t lineIndex, size_t columnIndex ) const; - // Установить вес. + /// \ru Установить вес. \en Set weight. bool SetWeight( size_t lineIndex, size_t columnIndex, double wt ); - // Вставить строку. + /// \ru Вставить строку. \en Insert a line. void InsertLine ( size_t k, double wt = 1.0 ); - // Вставить столбец. + /// \ru Вставить столбец \en Insert a column. void InsertColumn( size_t k, double wt = 1.0 ); - // Добавить строку. + /// \ru Добавить строку. \en Add a line. void AddLine( double wt = 1.0 ) { InsertLine( linesCnt, wt ); } - // Добавить столбец. + /// \ru Добавить столбец. \en Add a column. void AddColumn( double wt = 1.0 ) { InsertColumn( columnsCnt, wt ); }; - // Удалить строку. + /// \ru Удалить строку. \en Delete line by line index. void RemoveLine( size_t k ); - // Удалить столбец. + /// \ru Удалить столбец. \en Delete column by column index. void RemoveColumn( size_t k ); - // Получить указатель на строку, если не используется общий вес. Иначе возвращает NULL. + /// \ru Получить указатель на строку, если не используется общий вес. Иначе возвращает нулевой указатель. \en Get a pointer to a string if the common weight is not used. Otherwise returns a null pointer. const double * _GetLine( size_t k ) const; - // Можно ли оптимизировать расход памяти. + /// \ru Можно ли оптимизировать расход памяти? \en Is it possible to adjust memory of weights array? bool CanAdjust( const Array2 & wts ) const; - // Оптимизировать расход памяти. + /// \ru Оптимизировать расход памяти. \en Adjust memory of weights array. bool Adjust(); - // Оператор присваивания. + /// \ru Оператор присваивания. \en Assignment operator. MbWeightMatrix & operator = ( const MbWeightMatrix & wm ) { InitWeights( wm ); return *this; } - // Оператор присваивания. + /// \ru Оператор присваивания. \en Assignment operator. MbWeightMatrix & operator = ( const Array2 & wm ) { InitWeights( wm ); return *this; } protected: - // Сформировать полную матрицу весов, если используется общий вес. + /// \ru Сформировать полную матрицу весов, если используется общий вес. \en Fill full weights array if common weight is used. bool FillArray2(); private: MbWeightMatrix( const Array2 & ); // не реализовано, запрещено @@ -181,8 +181,13 @@ public: \param[in] initVKnots - \ru Узловой вектор по v. \en A knot vector by V. \~ */ - MbSplineSurface( size_t uDeg, size_t vDeg, bool uCls, bool vCls, const Array2 & initPoints, - const SArray & initUKnots, const SArray & initVKnots ); + MbSplineSurface( size_t uDeg, + size_t vDeg, + bool uCls, + bool vCls, + const Array2 & initPoints, + const SArray & initUKnots, + const SArray & initVKnots ); /** \brief \ru Конструктор NURBS поверхности. \en Constructor of NURBS surface. \~ @@ -205,8 +210,14 @@ public: \param[in] initVKnots - \ru Узловой вектор по v. \en A knot vector by V. \~ */ - MbSplineSurface( size_t uDeg, size_t vDeg, bool uCls, bool vCls, const Array2 & initPoints, const Array2 & initWeights, - const SArray & initUKnots, const SArray & initVKnots ); + MbSplineSurface( size_t uDeg, + size_t vDeg, + bool uCls, + bool vCls, + const Array2 & initPoints, + const Array2 & initWeights, + const SArray & initUKnots, + const SArray & initVKnots ); /** \brief \ru Конструктор NURBS поверхности по четырем углам при обходе против часовой стрелки. \en Constructor of NURBS surface by four angles of counterclockwise traverse. \~ @@ -256,7 +267,7 @@ public: \en Matrix of point weights. \~ */ bool Init( const Array2 & cPoints, - const Array2 & pWeights ); + const Array2 & pWeights ); /** \brief \ru Инициализация заполненной поверхности. \en Initialization of filled surface. \~ \details \ru Инициализация заполненной поверхности.\n @@ -316,24 +327,24 @@ public: \param[in] scl - \ru Коэффициент масштабирования. \en Scale factor. \~ */ - bool InitParasolid( bool uCls, - bool vCls, - bool brational, - size_t uDgr, - size_t vDgr, - ptrdiff_t uCnt, - ptrdiff_t vCnt, - const CcArray & vcs, - ptrdiff_t vcsCnt, - const CcArray & uKMul, - ptrdiff_t uKMulCnt, - const CcArray & vKMul, - ptrdiff_t vKMulCnt, - const CcArray & uKnt, - ptrdiff_t uKntCnt, - const CcArray & vKnt, - ptrdiff_t vKntCnt, - double scl ); + bool InitParasolid( bool uCls, + bool vCls, + bool brational, + size_t uDgr, + size_t vDgr, + ptrdiff_t uCnt, + ptrdiff_t vCnt, + const CcArray & vcs, + ptrdiff_t vcsCnt, + const CcArray & uKMul, + ptrdiff_t uKMulCnt, + const CcArray & vKMul, + ptrdiff_t vKMulCnt, + const CcArray & uKnt, + ptrdiff_t uKntCnt, + const CcArray & vKnt, + ptrdiff_t vKntCnt, + double scl ); /** \ru \name Общие функции геометрического объекта. \en \name Common functions of geometric object. @@ -393,6 +404,9 @@ public: virtual void DeriveUVV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная по uvv. \en Third derivative with respect to uvv. virtual void DeriveVVV( double & u, double & v, MbVector3D & ) const; // \ru Третья производная по v. \en Third derivative with respect to v. virtual void Normal ( double & u, double & v, MbVector3D & ) const; // \ru Нормаль. \en Normal. + // \ru Точка и производные по u и по v в пределах области определения. \en Point and derivatives of object for parameters inside surface parameters domain. + virtual void Explore ( double & u, double & v, + MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer ) const; /** \} */ /** \ru \name Функции для работы внутри и вне области определения поверхности @@ -464,7 +478,7 @@ public: \param[in] u - \ru Первый параметр. \en First parameter. \~ \param[in] v - \ru Второй параметр. \en Second parameter. \~ */ - virtual void CheckSurfParams( double & u, double & v ) const; + virtual void CheckSurfParams( double & u, double & v ) const; /** \brief \ru Построить усеченную поверхность. \en Construct a trimmed surface. \~ @@ -556,7 +570,7 @@ public: \return \ru Значение веса. \en A value of weight. \~ */ - double GetWeight( ptrdiff_t i, ptrdiff_t j ) const { return weights.GetWeight( i, j ); } + double GetWeight( ptrdiff_t i, ptrdiff_t j ) const { return weights.GetWeight( i, j ); } // \ru Получить матрицу весов вершин. \en Get the matrix of vertices weights. virtual void GetWeights( Array2 & wts ) const { weights.GetWeights( wts ); } @@ -572,7 +586,7 @@ public: */ size_t GetKnotsCount( bool isU ) const { return (isU ? uknots.Count() : vknots.Count()); } - // \ru Получить узловой вектор по выбранному параметру. \en Get a knot vector by the chosen parameter. + // \ru Получить узловой вектор по выбранному параметру. \en Get a knots vector by the chosen parameter. virtual void GetKnots( bool isU, SArray & knots ) const { knots = (isU ? uknots : vknots); } /** \brief \ru Получить значение одного узла. \en Get the value of one knot. \~ @@ -771,23 +785,18 @@ public: */ void DirectParallelRects( const MbVector3D & direction, std::vector & parallelRects ) const; -private: - bool CheckPoles( MbSplineSurfaceAuxiliaryData * ) const; // \ru Проверить наличие полюсов. \en Check poles existence. + /// \ru Может ли быть поверхность замкнутой по первому параметру? \en Can the surface be closed by the first parameter? + bool CheckUTouch( double precision ) const; + /// \ru Может ли быть поверхность замкнутой по второму параметру? \en Can the surface be closed by the second parameter? + bool CheckVTouch( double precision ) const; +private: void SetClosed( bool isU, bool cls ); // \ru Установить признак замкнутости. \en Set attribute of closedness. void SetKnots ( size_t degree, ptrdiff_t count, bool close, SArray & knots ); // \ru Установка значений узлового вектора \en Setting of knot vector values. void OpenKnotsVector ( size_t degree, ptrdiff_t count, SArray & knots ); // \ru Переопределение базисного узлового вектора из Close в Open. \en Redetermination of the basis knot vector from Close to Open. void CloseKnotsVector ( size_t degree, ptrdiff_t count, SArray & knots, double ); // \ru Переопределение базисного узлового вектора из Open в Close. \en Redetermination of the basis knot vector from Open to Close. - bool InitPatch ( MbSplineSurfaceAuxiliaryData * ) const; - void CalculatePatch ( double & u, double & v, MbSplineSurfaceAuxiliaryData * ) const; - void CalculateSpline ( int, MbSplineSurfaceAuxiliaryData * ) const; // \ru Расчет вектора точки и его первых, вторых и третьих производных. \en Calculation of the point vector and its first, second and third derivatives - void CalculateSplineWeight( int, MbSplineSurfaceAuxiliaryData * ) const; - - bool CatchMemory( MbSplineSurfaceAuxiliaryData * ) const; - void FreeMemory ( MbSplineSurfaceAuxiliaryData * ) const; - void ResetCache(); void operator = ( const MbSplineSurface & ); // \ru Не реализовано. \en Not implemented. @@ -795,7 +804,18 @@ private: double GetMeanParam( bool isU, double, double ) const; // \ru Получить среднее расстояние между band-ами. \en Get the middle distance between 'band'-s. double GetKoef( bool isU ) const; // \ru Получить коэффициент пересчета из длины в параметры. \en Get a coefficient of recalculation of the length to the parameters. - // \ru Служебные аналоги публичных функций, которые используют заданный кэш. \en Service analogs of public functions that use a given cache. + //--- + // \ru Служебные функции, которые используют заданный кэш (должен быть != NULL). \en Service functions that use a given cache (must != NULL). + bool CheckPoles( MbSplineSurfaceAuxiliaryData * ) const; // \ru Проверить наличие полюсов. \en Check poles existence. + + bool CatchMemory( MbSplineSurfaceAuxiliaryData * ) const; + void FreeMemory ( MbSplineSurfaceAuxiliaryData * ) const; + + bool InitPatch ( MbSplineSurfaceAuxiliaryData * ) const; + void CalculatePatch ( double & u, double & v, MbSplineSurfaceAuxiliaryData * ) const; + void CalculateSpline ( int, MbSplineSurfaceAuxiliaryData * ) const; // \ru Расчет вектора точки и его первых, вторых и третьих производных. \en Calculation of the point vector and its first, second and third derivatives + void CalculateSplineWeight( int, MbSplineSurfaceAuxiliaryData * ) const; + void DeriveU ( double & u, double & v, MbVector3D &, MbSplineSurfaceAuxiliaryData * ) const; // \ru Первая производная по u. \en First derivative with respect to u. void DeriveV ( double & u, double & v, MbVector3D &, MbSplineSurfaceAuxiliaryData * ) const; // \ru Первая производная по v. \en First derivative with respect to v. void DeriveUU ( double & u, double & v, MbVector3D &, MbSplineSurfaceAuxiliaryData * ) const; // \ru Вторая производная по u. \en Second derivative with respect to u. @@ -827,6 +847,15 @@ private: double DeviationStep ( bool isU, double u, double v, double angle, MbSplineSurfaceAuxiliaryData * ) const; double DeviationStepPlus ( bool isU, double u, double v, double angle, MbSplineSurfaceAuxiliaryData * ) const; void TypedStepPlus ( double u, double v, bool alongU, const MbStepData & stepData, double & step, MbSplineSurfaceAuxiliaryData * ) const; + + void PoleDerive( double u, double v, MbVector3D & vDerU, MbVector3D & vDerV, MbSplineSurfaceAuxiliaryData * ) const; + + void CalculateDerivativesAlong( double & u, double & v, bool isU, MbVector3D & tDer, MbVector3D & ttDer, + MbSplineSurfaceAuxiliaryData * ) const; + void CalculateDerivativesAlong( double & u, double & v, bool isU, MbVector3D & tDer, MbVector3D & ttDer, MbVector3D & tttDer, + MbSplineSurfaceAuxiliaryData * ) const; + //--- + double GetMinStep ( bool isU, const double * pRng = NULL ) const; bool ApproxAsPlane() const; // \ru Можно ли аппроксимировать поверхность как плоскость. \en Whether a surface can be approximated by a plane. @@ -834,8 +863,6 @@ private: MbSplineSurface * CalcApproxAsPlane( size_t nUDegree, size_t nVDegree, ptrdiff_t nUCount, ptrdiff_t nVCount ) const; bool UseMultiplKnots() const; - void PoleDerive( double u, double v, MbVector3D & vDerU, MbVector3D & vDerV, MbSplineSurfaceAuxiliaryData * ) const; - inline void CheckParam( const SArray & knots, const ptrdiff_t & degree, const bool & closed, double & t ) const; // \ru Создать двумерную кривую, если пространственная кривая является границей поверхности. \en Create a two-dimensional curve if a space curve is surface boundary. @@ -843,12 +870,7 @@ private: MbCurve * IsPartSplineBorder( const MbCurve3D & curve ) const; void GetVertecisRects( const MbVector3D & direction, bool u, - std::vector > & vertecesRects ) const; - private: - void CalculateDerivativesAlong( double & u, double & v, bool isU, MbVector3D & tDer, MbVector3D & ttDer, - MbSplineSurfaceAuxiliaryData * ) const; - void CalculateDerivativesAlong( double & u, double & v, bool isU, MbVector3D & tDer, MbVector3D & ttDer, MbVector3D & tttDer, - MbSplineSurfaceAuxiliaryData * ) const; + std::vector & vertecesRects ) const; DECLARE_PERSISTENT_CLASS_NEW_DEL( MbSplineSurface ) }; diff --git a/C3d/Include/surface.h b/C3d/Include/surface.h index a7d178a..b510365 100644 --- a/C3d/Include/surface.h +++ b/C3d/Include/surface.h @@ -91,7 +91,7 @@ typedef std::pair ConstSurfacesSetRet; \ingroup Surfaces */ // --- -class MATH_CLASS MbSurface : public MbSpaceItem { +class MATH_CLASS MbSurface : public MbSpaceItem, public MbNestSyncItem { protected : /** \brief \ru Габаритный куб поверхности. \en Bounding box of surface. \~ @@ -308,6 +308,25 @@ public: virtual void NormalU ( double & u, double & v, MbVector3D & ) const; /// \ru Вычислить производную нормали по V. \en Calculate derivative of normal with respect to V. virtual void NormalV ( double & u, double & v, MbVector3D & ) const; + /** \brief \ru Вычислить значения точки и первых производных по u и по v. + \en Calculate point and first derivatives with respect to u and with respect to v. \~ + \details \ru Скорректировать параметры при выходе их за пределы прямоугольной области определения и вычислить значения точки и первых производных по u и по v. + \en Correct parameters when getting out of rectangular domain bounds and calculate point and first derivatives with respect to u and with respect to v. \~ + \param[in] u - \ru Параметр. + \en Parameter. \~ + \param[in] v - \ru Параметр. + \en Parameter. \~ + \param[out] pnt - \ru Точка. + \en Point. \~ + \param[out] uDer - \ru Производная по u. + \en Derivative with respect to u. \~ + \param[out] vDer - \ru Производная по v. + \en Derivative with respect to v. \~ + \ingroup Surfaces + */ + virtual void Explore( double & u, double & v, + MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer ) const; + /** \} */ /** \ru \name Функции для работы внутри и вне области определения поверхности @@ -833,6 +852,24 @@ public: */ virtual MbSplineSurface * NurbsSurface( double u1, double u2, double v1, double v2, bool bmatch = false ) const; + // \ru Построить NURBS-копию поверхности. \en Construct a NURBS copy of a surface. + /** \brief \ru Построить NURBS копию поверхности. + \en Construct a NURBS copy of a surface. \~ + \details \ru Строит NURBS поверхность, аппроксимирующую исходную с заданными параметрами по каждому направлению. + В параметрах можно задать степень и количество узлов сплайна, диапазон изменения параметра кривой. + Если в параметрах не задан флаг точной аппроксимации, то строит NURBS без кратных узлов. + \en Constructs a NURBS surface which approximates a given surface with the given parameters in each direction. + In parameters the degree and the number of knots of a spline and the range of curve's parameters changing may be set. + If the flag of accurate approximation is not set in parameters then NURBS without multiple knots is constructed. \~ + \param[in] uParam - \ru Параметры построения по направлению u. + \en Parameters of construction in u direction. \~ + \param[in] vParam - \ru Параметры построения по направлению v. + \en Parameters of construction in v direction. \~ + \result \ru Построенная NURBS поверхность или NULL при неуспешном построении. + \en The constructed NURBS surface or NULL in a case of failure. \~ + */ + virtual MbSurface * NurbsSurface( const MbNurbsParameters & uParam, const MbNurbsParameters & vParam ) const; + /** \brief \ru Подготовить параметры для преобразования в NURBS поверхность. \en Prepare parameters for the transformation to NURBS surface. \~ \details \ru Подготовить параметры для преобразования в NURBS поверхность. @@ -930,23 +967,7 @@ public: */ void CheckApproxPointParamsClosed( bool isU, double par, size_t degree, size_t pCount, SArray & tList, SArray & aKnots ) const; - // \ru Построить NURBS-копию поверхности. \en Construct a NURBS copy of a surface. - /** \brief \ru Построить NURBS копию поверхности. - \en Construct a NURBS copy of a surface. \~ - \details \ru Строит NURBS поверхность, аппроксимирующую исходную с заданными параметрами по каждому направлению. - В параметрах можно задать степень и количество узлов сплайна, диапазон изменения параметра кривой. - Если в параметрах не задан флаг точной аппроксимации, то строит NURBS без кратных узлов. - \en Constructs a NURBS surface which approximates a given surface with the given parameters in each direction. - In parameters the degree and the number of knots of a spline and the range of curve's parameters changing may be set. - If the flag of accurate approximation is not set in parameters then NURBS without multiple knots is constructed. \~ - \param[in] uParam - \ru Параметры построения по направлению u. - \en Parameters of construction in u direction. \~ - \param[in] vParam - \ru Параметры построения по направлению v. - \en Parameters of construction in v direction. \~ - \result \ru Построенная NURBS поверхность или NULL при неуспешном построении. - \en The constructed NURBS surface or NULL in a case of failure. \~ - */ - virtual MbSurface * NurbsSurface( const MbNurbsParameters & uParam, const MbNurbsParameters & vParam ) const; + // \ru Построить эквидистантую поверхность. \en Create an offset surface. /** \brief \ru Построить эквидистантую поверхность. \en Create an offset surface. \~ @@ -1044,7 +1065,7 @@ public: /** \brief \ru Определить точки пересечения кривoй с контурами поверхности. \en Determine points of intersections between the curve and the surface contours. \~ \details \ru Определить точки пересечения кривoй с границами поверхности - для нахождение частей кривой в пределах поверхности + для нахождения частей кривой в пределах поверхности и векторов для их сдвига в пределы поверхности. Вектор сдвига определен для каждой точки пересечения. Если вектор сдвига найти нельзя, то в массив сохраняется вектор нулевой длины. @@ -1079,7 +1100,7 @@ public: /** \brief \ru Определить точки пересечения кривoй с контурами поверхности. \en Determine points of intersections between the curve and the surface contours. \~ \details \ru Определить точки пересечения кривoй с границами поверхности - для нахождение частей кривой в пределах поверхности + для нахождения частей кривой в пределах поверхности и векторов для их сдвига в пределы поверхности. Вектор сдвига определен для каждой точки пересечения. Если вектор сдвига найти нельзя, то в массив сохраняется вектор нулевой длины. @@ -1153,12 +1174,12 @@ public: \en A given point. \~ \param[in] vect - \ru Вектор направления. \en A direction vector. \~ - \param[in,out] uv - \ru Множество точек проекции. - \en A set of projection points. \~ - \param[in] ext - \ru Флаг, определяющий, искать ли проекцию на продолжении кривой (если true, то искать). - \en A flag defining whether to seek projection on the extension of the curve. \~ - \param[in] uvRange - \ru Диапазон изменения параметров, в котором надо найти решение. - \en A range of parameters changing in which the solution should be found. \~ + \param[out] uv - \ru Множество точек проекции в области определения поверхности. + \en A set of projection points in the area of surface definition. \~ + \param[in] ext - \ru Флаг, определяющий, искать ли проекцию на продолжении поверхности (если true, то искать). + \en A flag defining whether to seek projection on the extension of the surface. \~ + \param[in] uvRange - \ru Диапазон изменения параметров поверхности, в котором надо найти решение. + \en A range of surface parameters changing in which the solution should be found. \~ */ virtual void DirectPointProjection( const MbCartPoint3D & pnt, const MbVector3D & vect, SArray & uv, bool ext, MbRect2D * uvRange = NULL ) const; @@ -1407,7 +1428,7 @@ public: \param[out] uv - \ru Множество параметров точек с искомой касательной. \en A set of parameters of points for the required tangent. \~ */ - virtual void GetIsoclinal( const MbVector3D & nor, SArray & uv ) const; + virtual void GetIsoclinal( const MbVector3D & nor, SArray & uv ) const; /// \ru Рассчитать габарит поверхности. Рекомендуется использовать GetGabarit. \en Calculate bounding box of surface. It is recommended to use GetGabarit. virtual void CalculateGabarit( MbCube & cube ) const; @@ -1415,7 +1436,7 @@ public: virtual void CalculateLocalGabarit( const MbMatrix3D & into, MbCube & cube ) const; /// \ru Рассчитать габаритный куб поверхности. \en Calculate bounding box of surface. - const MbCube & GetGabarit() const { if ( cube.IsEmpty() ) CalculateGabarit( cube ); return cube; } + const MbCube & GetGabarit() const { if ( cube.IsEmpty() ) { MbCube tmp; CalculateGabarit( tmp ); } return cube; } /// \ru Вернуть сохраненный габаритный куб. Он должен быть пустой. Рекомендуется использовать GetGabarit. \en Return saved bounding box. It should be empty. It is recommended to use GetGabarit. const MbCube & Cube() const { return cube; } /// \ru Сделать габарит пустым. Для внутреннего использования. \en Make the bounding box empty. For internal use only. @@ -1432,7 +1453,7 @@ public: */ void CopyGabarit( const MbSurface & s, const MbVector3D * to = NULL ) { cube = s.cube; if ( (to != NULL) && !cube.IsEmpty() ) { cube.Move( *to ); } } /// \ru Вычислить диагональ габаритного куба. \en Calculate the diagonal of the bounding box. - double GetGabDiagonal() const { if ( cube.IsEmpty() ) CalculateGabarit( cube ); return cube.GetDiagonal(); } + double GetGabDiagonal() const { if ( cube.IsEmpty() ) { MbCube tmp; CalculateGabarit( tmp ); } return cube.GetDiagonal(); } /** \brief \ru Вычислить прямоугольный габарит поверхности в заданной плоскости. \en Calculate the rectangular bounding box of a surface in the given plane. \~ @@ -1443,7 +1464,7 @@ public: \param[out] rect - \ru Вычисленный прямоугольник. \en Calculated rectangle. \~ */ - void CalculateRect( const MbPlacement3D & place, MbRect & rect ) const; + void CalculateRect( const MbPlacement3D & place, MbRect & rect ) const; /** \brief \ru Вернуть граничный двумерный контур. \en Return the bounding two-dimensional contour. \~ @@ -1525,6 +1546,10 @@ public: virtual double GetUParamToUnit( double u, double v ) const; /// \ru Дать приращение параметра v, соответствующее единичной длине в пространстве. \en Get increment of v-parameter, corresponding to the unit length in space. virtual double GetVParamToUnit( double u, double v ) const; + /// \ru Дать приращение параметра u, соответствующее единичной длине в пространстве. \en Get increment of u-parameter, corresponding to the unit length in space. + double GetUParamToUnit( const MbCartPoint & uv ) const { return GetUParamToUnit( uv.x, uv.y ); } + /// \ru Дать приращение параметра v, соответствующее единичной длине в пространстве. \en Get increment of v-parameter, corresponding to the unit length in space. + double GetVParamToUnit( const MbCartPoint & uv ) const { return GetVParamToUnit( uv.x, uv.y ); } /// \ru Дать приращение параметра u и параметра v, соответствующее единичной длине в пространстве. \en Get increment of parameters, corresponding to the unit length in space. virtual void GetParamsToUnit( double u, double v, double & uParam, double & vParam ) const; @@ -1696,7 +1721,7 @@ public: \param[in] u - \ru Первый параметр. \en First parameter. \~ \param[in] v - \ru Второй параметр. \en Second parameter. \~ */ - virtual void CheckSurfParams( double & u, double & v ) const; + virtual void CheckSurfParams( double & u, double & v ) const; /// \ru Дать плоскость (или только возможность ее выдачи). \en Get a plane (or only a possibility of getting a plane) bool GetPlacement ( MbPlacement3D * place, bool exact = false ) const; @@ -1752,7 +1777,7 @@ public: /** \} */ // \ru Функции унификации объекта и вектора объектов в шаблонных функциях. \en Functions for compatibility of a object and a vector of objects in template functions. - size_t size() const { return 1; } ///< \ru Количество объектов при трактовке объекта как вектора объектов. \en Number of objects if object is interpreted as vector of objects. + size_t size() const { return 1; } ///< \ru Количество объектов при трактовке объекта как вектора объектов. \en Number of objects if object is interpreted as vector of objects. const MbSurface * operator [] ( size_t ) const { return this; } ///< \ru Оператор доступа. \en An access operator. protected: @@ -1784,6 +1809,7 @@ private: IMPL_PERSISTENT_OPS( MbSurface ) + //------------------------------------------------------------------------------ // \ru Вычислить точку на поверхности. \en Calculate a point on a surface. // --- @@ -1792,6 +1818,18 @@ inline void MbSurface::PointOn( MbCartPoint & uv, MbCartPoint3D & p ) const { } +//------------------------------------------------------------------------------ +// \ru Вычислить значения точки и производных для параметров в пределах области определения. +// \en Calculate point and derivatives of object for parameters inside the surface's domain of parameters. \~ +// --- +inline void MbSurface::Explore( double & u, double & v, + MbCartPoint3D & pnt, MbVector3D & uDer, MbVector3D & vDer ) const { + PointOn( u, v, pnt ); + DeriveU( u, v, uDer ); + DeriveV( u, v, vDer ); +} + + //------------------------------------------------------------------------------ // \ru Вычислить точку на продолженной поверхности. \en Calculate a point on a surface extension. // --- diff --git a/C3d/Include/system_atomic.h b/C3d/Include/system_atomic.h index 90eaea1..8eef130 100644 --- a/C3d/Include/system_atomic.h +++ b/C3d/Include/system_atomic.h @@ -1,205 +1,279 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Системозависимые атомарные операции. - Если требуются атомарные операции, должен использоваться этот файл ( не использовать!!!). - \en System-dependent atomic operations. - If atomic operations are required, this file should used ( must not be used!!!).\~ -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __SYSTEM_ATOMIC_H -#define __SYSTEM_ATOMIC_H - -#include -#include -#include - -//------------------------------------------------------------------------------ -// \ru Использование атомарных операций согласно стандарту C++11. -// \en Using atomic operations according to C++11 standard. -// -//--- - -#ifdef STANDARD_C11 -#ifndef _NOT_USE_ATOMIC -#define STANDARD_C11_ATOMIC -#endif -#endif - -#ifdef STANDARD_C11_ATOMIC - -#include - -typedef std::atomic_ptrdiff_t use_count_type; // \ru Потокобезопасный тип счётчика ссылок. \en Thread-safe references count type. -typedef std::atomic_size_t serial_type; // \ru Потокобезопасный тип счётчика ссылок. \en Thread-safe references count type. -typedef std::atomic atomic_bool; // \ru Потокобезопасный логический тип. \en Thread-safe boolean type. - -//------------------------------------------------------------------------------ -/** \ru Получить значение. \en Get value. -*/ -//--- -template -Type LoadTypeValue( const AtomicType & v ) { - return v.load(); -} - -//------------------------------------------------------------------------------ -/** \ru Установить значение. \en Get value. -*/ -//--- -template -void StoreTypeValue( const AtomicType & src, AtomicType & dst ) { - dst.store( src.load() ); -} - -//------------------------------------------------------------------------------ -/** \ru Установить значение. \en Get value. -*/ -//--- -template -void StoreTypeValue( const Type src, AtomicType & dst ) { - dst.store( src ); -} - -//------------------------------------------------------------------------------ -/** \ru Получить значение. \en Get value. -*/ -//--- -inline serial_type SerialTypeValue( const serial_type & v ) { - return v.load(); -} -inline use_count_type UseCountValue( const use_count_type & v ) { - return v.load(); -} - -#else - -//------------------------------------------------------------------------------ -// \ru Шаблонный класс, работающий с целочисленными типами данных. Базовый класс для потокобезопасных счётчиков. -// \en Template class working with integer data types. Base class for thread-safe counters. -//-- -template< class Type > -class atomic_itype -{ -protected: - CommonMutex m_lock; - Type m_value; -public: - atomic_itype() {} - atomic_itype( const atomic_itype & t ) : m_value( t.m_value ) {} - atomic_itype( const Type t ) : m_value( t ) {} - - // \ru Операторы присваивания. \en Assignment operators. - atomic_itype & operator = ( const atomic_itype & t ) { ScopedLock l( &m_lock ); m_value = t.m_value; return *this; } - atomic_itype & operator = ( Type t ) { ScopedLock l( &m_lock ); m_value = t; return *this; } - - // \ru Операторы инкремента и декремента. \en Increment and decrement operators. - atomic_itype & operator ++ () { ScopedLock l( &m_lock ); ++m_value; return *this; } // prefix ++ - atomic_itype & operator -- () { ScopedLock l( &m_lock ); --m_value; return *this; } // prefix -- - Type operator ++ ( int ) { // postfix ++ - Type t = m_value; - { - ScopedLock l( &m_lock ); - ++m_value; - } - return t; - } - Type operator -- ( int ) { // postfix -- - Type t = m_value; - { - ScopedLock l( &m_lock ); - --m_value; - } - return t; - } - - // \ru Операторы сравнения. \en Comparison operators. - bool operator == ( const atomic_itype & t ) const { return m_value == t.m_value; } - bool operator != ( const atomic_itype & t ) const { return m_value != t.m_value; } - bool operator < ( const atomic_itype & t ) const { return m_value < t.m_value; } - bool operator > ( const atomic_itype & t ) const { return m_value > t.m_value; } - bool operator <= ( const atomic_itype & t ) const { return m_value <= t.m_value; } - bool operator >= ( const atomic_itype & t ) const { return m_value >= t.m_value; } - - // \ru Доступ к данным. \en Data access. - Type operator()() const { return m_value; } - -}; - -//------------------------------------------------------------------------------ -// \ru Потокобезопасный логический тип. \en Thread-safe boolean type. -//-- -class atomic_bool : public atomic_itype -{ -public: - atomic_bool() {} - - atomic_bool( const atomic_bool & t ) : atomic_itype( t.m_value ) {} - atomic_bool( const bool t ) : atomic_itype( t ? 1 : 0 ) {} - atomic_bool( const int t ) : atomic_itype( t != 0 ? 1 : 0 ) {} - - // \ru Операторы присваивания. \en Assignment operators. - atomic_bool & operator = ( const atomic_bool & t ) { ScopedLock l( &m_lock ); m_value = t.m_value; return *this; } - atomic_bool & operator = ( bool t ) { ScopedLock l( &m_lock ); m_value = t ? 1 : 0; return *this; } - atomic_bool & operator = ( int t ) { ScopedLock l( &m_lock ); m_value = t != 0 ? 1 : 0; return *this; } - - bool operator && ( bool t ) { return t == !!m_value; } - bool operator || ( bool t ) { return t || !!m_value; } - - // \ru Доступ к данным. \en Data access. - bool operator()() const { return !!m_value; } - -private: - // \ru Операторы инкремента и декремента не разрешены. \en Increment and decrement operators not allowed. - atomic_bool & operator ++ (); - atomic_bool & operator -- (); - bool operator ++ ( int ); - bool operator -- ( int ); -}; - -typedef atomic_itype serial_type; // \ru Потокобезопасный тип счётчика ссылок. \en Thread-safe references count type. -typedef atomic_itype use_count_type; // \ru Потокобезопасный тип счётчика ссылок. \en Thread-safe references count type. - -//------------------------------------------------------------------------------ -/** \ru Получить значение. \en Get value. -*/ -//--- -template -Type LoadTypeValue( const AtomicType & v ) { - return v(); -} - -//------------------------------------------------------------------------------ -/** \ru Установить значение. \en Get value. -*/ -//--- -template -void StoreTypeValue( const AtomicType & src, AtomicType & dst ) { - dst = src; -} - -//------------------------------------------------------------------------------ -/** \ru Установить значение. \en Get value. -*/ -//--- -template -void StoreTypeValue( const Type src, AtomicType & dst ) { - dst = src; -} - -//------------------------------------------------------------------------------ -/** \ru Получить значение. \en Get value. -*/ -//--- -inline size_t SerialTypeValue( const serial_type & v ) { - return v(); -} -inline ptrdiff_t UseCountValue( const use_count_type & v ) { - return v(); -} - -#endif // STANDARD_C11_ATOMIC - - -#endif // __SYSTEM_ATOMIC_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Системозависимые атомарные операции. + Если требуются атомарные операции, должен использоваться этот файл ( не использовать!!!). + \en System-dependent atomic operations. + If atomic operations are required, this file should used ( must not be used!!!).\~ +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __SYSTEM_ATOMIC_H +#define __SYSTEM_ATOMIC_H + +#include +#include +#include + +//------------------------------------------------------------------------------ +// \ru Использование атомарных операций согласно стандарту C++11. +// \en Using atomic operations according to C++11 standard. +// +//--- + +#ifdef C3D_STANDARD_CXX_11_PARTIAL + #ifndef _NOT_USE_ATOMIC + #define C3D_USE_CXX11_ATOMIC + #endif +#endif + +#ifdef C3D_USE_CXX11_ATOMIC + +#include + +typedef std::atomic_ptrdiff_t use_count_type; ///< \ru Потокобезопасный тип счётчика ссылок. \en Thread-safe references count type. +typedef std::atomic_size_t serial_type; ///< \ru Потокобезопасный тип счётчика ссылок. \en Thread-safe references count type. +typedef std::atomic_schar flag_type; ///< \ru Потокобезопасный тип флага. \en Thread-safe flag type. +typedef std::atomic atomic_bool; ///< \ru Потокобезопасный логический тип. \en Thread-safe boolean type. + +//------------------------------------------------------------------------------ +/** \ru Получить значение. \en Get value. +*/ +//--- +template +Type LoadTypeValue( const AtomicType & v ) { + return v.load(); +} + +//------------------------------------------------------------------------------ +/** \ru Установить значение. \en Get value. +*/ +//--- +template +void StoreTypeValue( const AtomicType & src, AtomicType & dst ) { + dst.store( src.load() ); +} + +//------------------------------------------------------------------------------ +/** \ru Установить значение. \en Get value. +*/ +//--- +template +void StoreTypeValue( const Type src, AtomicType & dst ) { + dst.store( src ); +} + +//------------------------------------------------------------------------------ +/** \ru Получить значение. \en Get value. +*/ +//--- +inline size_t SerialTypeValue( const serial_type & v ) { + return v.load(); +} +inline ptrdiff_t UseCountValue( const use_count_type & v ) { + return v.load(); +} + +//------------------------------------------------------------------------------ +// \ru Шаблонный класс для перечислений (Type - тип перечисления). +// \en Template class for enums (Type - a type of enum). +//-- +template< class Type > +class atomic_enum_type +{ + use_count_type m_value; + +public: + atomic_enum_type() :m_value (0) {} + atomic_enum_type( const Type & val ) : m_value (val) {} + atomic_enum_type( const atomic_enum_type & val ) : m_value( val.m_value.load() ) {} + + // \ru Операторы присваивания. \en Assignment operators. + atomic_enum_type & operator = ( const atomic_enum_type & t ) { m_value.store( t.m_value.load() ); return *this; } + atomic_enum_type & operator = ( const Type & t ) { m_value.store( t ); return *this; } + + // \ru Операторы сравнения. \en Comparison operators. + bool operator == ( const atomic_enum_type & t ) const { return m_value.load() == t.m_value.load(); } + bool operator == ( const Type & t ) const { return m_value.load() == t; } + bool operator != ( const atomic_enum_type & t ) const { return m_value.load() != t.m_value.load(); } + bool operator != ( const Type & t ) const { return m_value.load() != t; } + bool operator < ( const atomic_enum_type & t ) const { return m_value.load() < t.m_value.load(); } + bool operator < ( Type t ) const { return m_value.load() < t; } + bool operator > ( const atomic_enum_type & t ) const { return m_value.load() > t.m_value; } + bool operator > ( const Type & t ) const { return m_value.load() > t; } + bool operator <= ( const atomic_enum_type & t ) const { return m_value.load() <= t.m_value.load(); } + bool operator <= ( const Type & t ) const { return m_value.load() <= t; } + bool operator >= ( const atomic_enum_type & t ) const { return m_value.load() >= t.m_value.load(); } + bool operator >= ( const Type & t ) const { return m_value.load() >= t; } + + // \ru Доступ к данным. \en Data access. + Type operator()() const { return static_cast( m_value.load() ); } +}; + +#else + +//------------------------------------------------------------------------------ +// \ru Шаблонный класс, работающий с целочисленными типами данных. Базовый класс для потокобезопасных счётчиков. +// \en Template class working with integer data types. Base class for thread-safe counters. +//-- +template< class Type > +class atomic_itype +{ +protected: + CommonMutex m_lock; + Type m_value; +public: + atomic_itype() {} + atomic_itype( const atomic_itype & t ) : m_value( t.m_value ) {} + atomic_itype( const Type & t ) : m_value( t ) {} + + // \ru Операторы присваивания. \en Assignment operators. + atomic_itype & operator = ( const atomic_itype & t ) { ScopedLock l( &m_lock ); m_value = t.m_value; return *this; } + atomic_itype & operator = ( Type t ) { ScopedLock l( &m_lock ); m_value = t; return *this; } + + // \ru Операторы инкремента и декремента. \en Increment and decrement operators. + atomic_itype & operator ++ () { ScopedLock l( &m_lock ); ++m_value; return *this; } // prefix ++ + atomic_itype & operator -- () { ScopedLock l( &m_lock ); --m_value; return *this; } // prefix -- + Type operator ++ ( int ) { // postfix ++ + Type t = m_value; + { + ScopedLock l( &m_lock ); + ++m_value; + } + return t; + } + Type operator -- ( int ) { // postfix -- + Type t = m_value; + { + ScopedLock l( &m_lock ); + --m_value; + } + return t; + } + + // \ru Операторы сравнения. \en Comparison operators. + bool operator == ( const atomic_itype & t ) const { return m_value == t.m_value; } + bool operator != ( const atomic_itype & t ) const { return m_value != t.m_value; } + bool operator < ( const atomic_itype & t ) const { return m_value < t.m_value; } + bool operator > ( const atomic_itype & t ) const { return m_value > t.m_value; } + bool operator <= ( const atomic_itype & t ) const { return m_value <= t.m_value; } + bool operator >= ( const atomic_itype & t ) const { return m_value >= t.m_value; } + + // \ru Доступ к данным. \en Data access. + Type operator()() const { return m_value; } + +}; + +//------------------------------------------------------------------------------ +// \ru Потокобезопасный логический тип. \en Thread-safe boolean type. +//-- +class atomic_bool : public atomic_itype +{ +public: + atomic_bool() {} + + atomic_bool( const atomic_bool & t ) : atomic_itype( t.m_value ) {} + atomic_bool( const bool t ) : atomic_itype( t ? 1 : 0 ) {} + atomic_bool( const int t ) : atomic_itype( t != 0 ? 1 : 0 ) {} + + // \ru Операторы присваивания. \en Assignment operators. + atomic_bool & operator = ( const atomic_bool & t ) { ScopedLock l( &m_lock ); m_value = t.m_value; return *this; } + atomic_bool & operator = ( bool t ) { ScopedLock l( &m_lock ); m_value = t ? 1 : 0; return *this; } + atomic_bool & operator = ( int t ) { ScopedLock l( &m_lock ); m_value = t != 0 ? 1 : 0; return *this; } + + bool operator && ( bool t ) { return t == !!m_value; } + bool operator || ( bool t ) { return t || !!m_value; } + + // \ru Доступ к данным. \en Data access. + bool operator()() const { return !!m_value; } + +private: + // \ru Операторы инкремента и декремента не разрешены. \en Increment and decrement operators not allowed. + atomic_bool & operator ++ (); + atomic_bool & operator -- (); + bool operator ++ ( int ); + bool operator -- ( int ); +}; + +typedef atomic_itype serial_type; // \ru Потокобезопасный тип счётчика ссылок. \en Thread-safe references count type. +typedef atomic_itype use_count_type; // \ru Потокобезопасный тип счётчика ссылок. \en Thread-safe references count type. +typedef atomic_itype flag_type; ///< \ru Потокобезопасный тип флага. \en Thread-safe flag type. + +//------------------------------------------------------------------------------ +/** \ru Получить значение. \en Get value. +*/ +//--- +template +Type LoadTypeValue( const AtomicType & v ) { + return v(); +} + +//------------------------------------------------------------------------------ +/** \ru Установить значение. \en Get value. +*/ +//--- +template +void StoreTypeValue( const AtomicType & src, AtomicType & dst ) { + dst = src; +} + +//------------------------------------------------------------------------------ +/** \ru Установить значение. \en Get value. +*/ +//--- +template +void StoreTypeValue( const Type src, AtomicType & dst ) { + dst = src; +} + +//------------------------------------------------------------------------------ +/** \ru Получить значение. \en Get value. +*/ +//--- +inline size_t SerialTypeValue( const serial_type & v ) { + return v(); +} +inline ptrdiff_t UseCountValue( const use_count_type & v ) { + return v(); +} + +//------------------------------------------------------------------------------ +// \ru Шаблонный класс для перечислений (Type - тип перечисления). +// \en Template class for enums (Type - a type of enum). +//-- +template< class Type > +class atomic_enum_type : public atomic_itype +{ +public: + atomic_enum_type() : atomic_itype() {} + atomic_enum_type( Type & val ) : atomic_itype( val ) {} + atomic_enum_type( const Type & val ) : atomic_itype( val ) {} + atomic_enum_type( const atomic_enum_type & val ) : atomic_itype( val.m_value ) {} + atomic_enum_type( atomic_enum_type & val ) : atomic_itype( val.m_value ) {} + + // \ru Операторы присваивания. \en Assignment operators. + atomic_enum_type & operator = ( const atomic_enum_type & t ) { ScopedLock l( &m_lock ); m_value = t.m_value; return *this; } + atomic_enum_type & operator = ( const Type & t ) { ScopedLock l( &m_lock ); m_value = t; return *this; } + + // \ru Операторы сравнения. \en Comparison operators. + bool operator == ( const atomic_enum_type & t ) const { return m_value == t.m_value; } + bool operator == ( const Type & t ) const { return m_value == t; } + bool operator != ( const atomic_enum_type & t ) const { return m_value != t.m_value; } + bool operator != ( const Type & t ) const { return m_value != t; } + bool operator < ( const atomic_enum_type & t ) const { return m_value < t.m_value; } + bool operator < ( Type t ) const { return m_value < t; } + bool operator > ( const atomic_enum_type & t ) const { return m_value > t.m_value; } + bool operator > ( const Type & t ) const { return m_value > t; } + bool operator <= ( const atomic_enum_type & t ) const { return m_value <= t.m_value; } + bool operator <= ( const Type & t ) const { return m_value <= t; } + bool operator >= ( const atomic_enum_type & t ) const { return m_value >= t.m_value; } + bool operator >= ( const Type & t ) const { return m_value >= t; } + + // \ru Доступ к данным. \en Data access. + Type operator()() const { return static_cast( m_value ); } +}; + +#endif // C3D_USE_CXX11_ATOMIC + + +#endif // __SYSTEM_ATOMIC_H diff --git a/C3d/Include/system_cpp_standard.h b/C3d/Include/system_cpp_standard.h index d04677a..f6481b4 100644 --- a/C3d/Include/system_cpp_standard.h +++ b/C3d/Include/system_cpp_standard.h @@ -1,71 +1,182 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Макросы стандартов C и C++. - \en C\C++ standards. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __SYSTEM_CPP_STANDARD_H -#define __SYSTEM_CPP_STANDARD_H - -#include -#include - -// С11 -#if (defined(_MSC_VER) && (_MSC_VER > 1600)) || (defined(__INTEL_C) ) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || (defined(__BORLANDC__)) // BORLAND version? -#define STANDARD_C11 -#endif -// C11 with thread_local support -#if (defined(_MSC_VER) && (_MSC_VER >= 1900)) || (defined(__INTEL_C) ) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || (defined(__BORLANDC__)) // BORLAND version? -#define STANDARD_C11_THREAD -#endif - -// С++11 -#if defined(__cplusplus) && (__cplusplus >= 201103L) // С++11 (fully supported) -#define STANDARD_CPP11 -#define STANDARD_C11_THREAD -#endif - -// GCC version (https://sourceforge.net/p/predef/wiki/Compilers/) -#if !defined(C3D_WINDOWS) // _MSC_VER - #if defined(__GNUC__) - #if defined(__GNUC_MINOR__) - #if defined(__GNUC_PATCHLEVEL__) - #define C3D_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) - #else - #define C3D_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) - #endif - #else - #define #define C3D_GCC_VERSION (__GNUC__ * 10000) - #endif - #else - #define C3D_GCC_VERSION 0 - #endif -#else // C3D_WINDOWS - #define C3D_GCC_VERSION 0 -#endif // C3D_WINDOWS - -#if defined(__clang__) // Clang compiler - #define C3D_CLANG_COMPILER -#endif // Clang compiler - -// GNU glibc version. -#if defined(C3D_LINUX) // Linux - #include - #if defined(__GLIBC__) && defined(__GLIBC_MINOR__) - #define C3D_GLIBC_VERSION (__GLIBC__ * 1000 + __GLIBC_MINOR__) - #else - #define C3D_GLIBC_VERSION 0 - #endif -#else - #define C3D_GLIBC_VERSION 0 -#endif - -#if ( defined(STANDARD_C11) || defined(STANDARD_CPP11) ) -#define STANDARD_CPP11_RVALUE_REFERENCES -#endif - - -#endif // __SYSTEM_CPP_STANDARD_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Макросы стандартов C и C++. + \en C\C++ standards. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __SYSTEM_CPP_STANDARD_H +#define __SYSTEM_CPP_STANDARD_H + +#include +#include + +// Clang version (https://sourceforge.net/p/predef/wiki/Compilers/) +// LLVM versions of Apple Clang: https://gist.github.com/yamaya/2924292 + https://en.wikipedia.org/wiki/Xcode#Toolchain_versions +#if defined(__clang__) // Clang compiler + #define C3D_CLANG_COMPILER + #if defined(__clang_major__) + #if defined(__clang_minor__) + #define C3D_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) + #else + #define C3D_CLANG_VERSION (__clang_major__ * 100) + #endif + #else + #define C3D_CLANG_VERSION 0 + #endif +#else + #define C3D_CLANG_VERSION 0 +#endif // Clang compiler + +// Intel compiler version (https://sourceforge.net/p/predef/wiki/Compilers/) +#if defined(__INTEL_COMPILER) + #define C3D_ICC_VERSION __INTEL_COMPILER +#else + #define C3D_ICC_VERSION 0 +#endif + +// GCC version (https://sourceforge.net/p/predef/wiki/Compilers/) +#if !defined(C3D_WINDOWS) && C3D_CLANG_VERSION == 0 && C3D_ICC_VERSION == 0 // GCC compiler + #if defined(__GNUC__) + #if defined(__GNUC_MINOR__) + #if defined(__GNUC_PATCHLEVEL__) + #define C3D_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + #else + #define C3D_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) + #endif + #else + #define C3D_GCC_VERSION (__GNUC__ * 10000) + #endif + #else + #define C3D_GCC_VERSION 0 + #endif +#else + #define C3D_GCC_VERSION 0 +#endif // GCC compiler + +// GNU glibc version. +#if defined(C3D_LINUX) // glibc version. + #include + #if defined(__GLIBC__) && defined(__GLIBC_MINOR__) + #define C3D_GLIBC_VERSION (__GLIBC__ * 1000 + __GLIBC_MINOR__) + #else + #define C3D_GLIBC_VERSION 0 + #endif +#else + #define C3D_GLIBC_VERSION 0 +#endif // glibc version. + +// Available C++ standard to use. +// https://en.cppreference.com/w/cpp/compiler_support +// https://clang.llvm.org/cxx_status.html +// https://gcc.gnu.org/projects/cxx-status.html#cxx11 +// https://software.intel.com/en-us/articles/c0x-features-supported-by-intel-c-compiler +// https://software.intel.com/en-us/articles/c14-features-supported-by-intel-c-compiler +// https://software.intel.com/en-us/articles/c17-features-supported-by-intel-c-compiler +#if (defined(_MSC_VER) && (_MSC_VER >= 1914)) || (C3D_GCC_VERSION >= 70000) || (C3D_ICC_VERSION >= 1910) || \ + (!defined(__apple_build_version__) && C3D_CLANG_VERSION >= 500) || \ + (defined(__apple_build_version__) && __apple_build_version__ >= 901) + #define C3D_STANDARD_CXX 2017 +#elif (defined(_MSC_VER) && (_MSC_VER >= 1910)) || (C3D_GCC_VERSION >= 50000) || (C3D_ICC_VERSION >= 1700) || \ + (!defined(__apple_build_version__) && C3D_CLANG_VERSION >= 304) || \ + (defined(__apple_build_version__) && __apple_build_version__ >= 501) + #define C3D_STANDARD_CXX 2014 +#elif (defined(_MSC_VER) && (_MSC_VER >= 1900)) || (C3D_GCC_VERSION >= 40801) || (C3D_ICC_VERSION >= 1500) || \ + (!defined(__apple_build_version__) && C3D_CLANG_VERSION >= 303) || \ + (defined(__apple_build_version__) && __apple_build_version__ >= 500) + #define C3D_STANDARD_CXX 2011 +#else + #define C3D_STANDARD_CXX 2003 +#endif // C++ version + +#if !defined(C3D_STANDARD_CXX_LIMIT) + #define C3D_STANDARD_CXX_LIMIT 99999 +#endif + +#if C3D_STANDARD_CXX_LIMIT >= 2011 && C3D_STANDARD_CXX >= 2011 + #define C3D_STANDARD_CXX_11 +#endif + +#if defined(C3D_STANDARD_CXX_11) || ((defined(_MSC_VER) && (_MSC_VER > 1600)) && C3D_STANDARD_CXX_LIMIT >= 2011) + #define C3D_STANDARD_CXX_11_PARTIAL +#endif + +#if C3D_STANDARD_CXX_LIMIT >= 2014 && C3D_STANDARD_CXX >= 2014 + #define C3D_STANDARD_CXX_14 +#endif + +#if C3D_STANDARD_CXX_LIMIT >= 2017 && C3D_STANDARD_CXX >= 2017 + #define C3D_STANDARD_CXX_17 +#endif + +#if C3D_STANDARD_CXX_LIMIT >= 2020 && C3D_STANDARD_CXX >= 2020 + #define C3D_STANDARD_CXX_20 +#endif + +// Available C standard to use. +// https://en.wikipedia.org/wiki/C99 + https://gcc.gnu.org/c99status.html +// https://gcc.gnu.org/wiki/C11Status + https://en.wikipedia.org/wiki/C11_(C_standard_revision) + https://releases.llvm.org/3.6.0/tools/clang/docs/UsersManual.html#c-language-features +// https://en.wikipedia.org/wiki/C18_(C_standard_revision) +// https://software.intel.com/en-us/articles/c99-support-in-intel-c-compiler + https://software.intel.com/en-us/articles/c11-support-in-intel-c-compiler +#if (C3D_GCC_VERSION >= 80100) || (!defined(__apple_build_version__) && C3D_CLANG_VERSION >= 700) || \ + (defined(__apple_build_version__) && __apple_build_version__ >= 1000) || (C3D_ICC_VERSION >= 1910) + #define C3D_STANDARD_C 2018 +#elif (C3D_GCC_VERSION >= 40900) || (!defined(__apple_build_version__) && C3D_CLANG_VERSION >= 306) || \ + (defined(__apple_build_version__) && __apple_build_version__ >= 601) || (C3D_ICC_VERSION >= 1800) + #define C3D_STANDARD_C 2011 +#elif (defined(_MSC_VER) && (_MSC_VER >= 1900)) || (C3D_GCC_VERSION >= 40500) || \ + (!defined(__apple_build_version__) && C3D_CLANG_VERSION >= 301) || \ + (defined(__apple_build_version__) && __apple_build_version__ >= 301) || (C3D_ICC_VERSION >= 1200) + #define C3D_STANDARD_C 1999 +#else + #define C3D_STANDARD_C 1989 +#endif + +#if C3D_STANDARD_C >= 1999 + #define C3D_STANDARD_C99 +#endif + +#if C3D_STANDARD_C >= 2011 + #define C3D_STANDARD_C11 +#endif + +#if C3D_STANDARD_C >= 2018 + #define C3D_STANDARD_C18 +#endif + + +////////////////////////////////////////////////////////////////////////////////////////// +// C++ standard dependent syntax. +////////////////////////////////////////////////////////////////////////////////////////// + +//------------------------------------------------------------------------------ +/// \ru Обертка для override. \en Wrapper for override. +//--- +#if defined(C3D_STANDARD_CXX_11_PARTIAL) + #define c3d_override override +#else + #define c3d_override +#endif + +//------------------------------------------------------------------------------ +/// \ru Обертка для constexpr. \en Wrapper for constexpr. +//--- +#ifdef C3D_STANDARD_CXX_11 + #define const_expr constexpr +#else + #define const_expr const +#endif // C3D_STANDARD_CXX_11 + +//------------------------------------------------------------------------------ +// \ru Нулевой указатель. \en Null pointer. +//--- +#ifdef C3D_STANDARD_CXX_11_PARTIAL + #define C3D_NULL_PTR nullptr +#else + #define C3D_NULL_PTR NULL +#endif // C3D_STANDARD_CXX_11_PARTIAL + + +#endif // __SYSTEM_CPP_STANDARD_H diff --git a/C3d/Include/system_dependency.h b/C3d/Include/system_dependency.h index 8cee41c..c5c227e 100644 --- a/C3d/Include/system_dependency.h +++ b/C3d/Include/system_dependency.h @@ -1,259 +1,249 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Системозависимые функции. - \en System-dependent functions. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __SYSTEM_DEPENDENCY_H -#define __SYSTEM_DEPENDENCY_H - -#include -#include -#include - -#ifdef C3D_WINDOWS //_MSC_VER -#define LOG_PATH _T("C:\\Logs\\") -#else // C3D_WINDOWS -#define LOG_PATH _T("") -#endif // C3D_WINDOWS - -#ifndef C3D_WINDOWS //_MSC_VER - -#include -#include -#include -#include -#include -#include -#include - - -#if (C3D_GLIBC_VERSION >= 2023) // C3D_GLIBC_VERSION - #include - #define c3d_isnan(d) std::isnan(d) -#else // C3D_GLIBC_VERSION - #include - #define c3d_isnan(d) isnan(d) -#endif // C3D_GLIBC_VERSION - -#define _finite std::isfinite - - -//------------------------------------------------------------------------------ -// \ru RGB-цвет. \en RGB color. -// --- -typedef uint32 COLORREF; - - -//------------------------------------------------------------------------------ -// RGBs of int colors from WinApi -// --- -#define RGB(r, g ,b) ((uint32) (((uint8) (r) | ((uint16) (g) << 8)) | (((uint32) (uint8) (b)) << 16))) - - -//------------------------------------------------------------------------------ -// \ru Дать значение красного цвета в пределах 0 - 255. \en Get value of red color in range from 0 to 255. -// --- -inline uint8 GetRValue(COLORREF rgb_color) -{ - return (uint8) (rgb_color); -} - - -//------------------------------------------------------------------------------ -// \ru Дать значение зелёного цвета в пределах 0 - 255. \en Get value of green color in range from 0 to 255. -// --- -inline uint8 GetGValue(COLORREF rgb_color) -{ - return (uint8) (rgb_color >> 8); -} - - -//------------------------------------------------------------------------------ -// \ru Дать значение синего цвета в пределах 0 - 255. \en Get value of blue color in range from 0 to 255. -// --- -inline uint8 GetBValue(COLORREF rgb_color) -{ - return (uint8) (rgb_color >> 16); -} - - -// \ru Вывод сообщений об ошибках пока отключаем в Линуксе \en Output of error messages is temporary disabled in Linux -#define _RPT0(warnlvl, text) -#define _RPT1(a1, a2, a3) // \ru не реализовано \en not implemented -#define _CRT_WARN -#define _CRT_ASSERT - -#else // C3D_WINDOWS - - #define c3d_isnan(d) _isnan(d) - -#ifdef __BORLANDC__ -// \ru Вывод сообщений об ошибках пока отключаем в BORLAND \en Output of error messages is temporary disabled in BORLAND -#define _RPT0(warnlvl, text) -#define _RPT1(a1, a2, a3) // \ru не реализовано \en not implemented -#define _CRT_WARN -#define _CRT_ASSERT - -#else // __BORLANDC__ - -// \ru Одновременное определение _MBCS и _UNICODE недопустимо! \en Simultaneous definition of _MBCS and _UNICODE is not acceptable! -#if defined(_MBCS) && defined(_UNICODE) - #error Multibyte Character Sets (MBCS) not supported -#endif // _MBCS -#endif // __BORLANDC__ - -#endif // C3D_WINDOWS - -#include -#ifdef STANDARD_C11 -#include -// #error is not supported when compiling with /clr or /clr:pure. -// #include -#endif - -#ifndef threadLocal -# if __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__ -# define threadLocal _Thread_local -# elif defined C3D_WINDOWS && ( \ - defined _MSC_VER || \ - defined __ICL || \ - defined __DMC__ || \ - defined __BORLANDC__ ) -# define threadLocal __declspec(thread) -/* note that ICC (linux) and Clang are covered by __GNUC__ */ -# elif defined __GNUC__ || \ - defined __SUNPRO_C || \ - defined __xlC__ -# define threadLocal __thread -# else -# error "Cannot define thread_local" -# endif -#endif - - -//------------------------------------------------------------------------------ -/** \brief \ru Высокоточный таймер. - \en High resolution timer. \~ - \details \ru Высокоточный таймер. - \en High resolution timer. \~ -\ingroup Base_Tools -*/ -// --- -class MbAccurateTimer { -#ifdef STANDARD_C11 - typedef std::chrono::high_resolution_clock::time_point TimePoint; -#endif -protected: - double lastTime; ///< \ru Время в секундах. \en Time in seconds. -private: -#ifdef STANDARD_C11 - TimePoint begTime; ///< \ru Засечка времени. \en Time stamp. -#endif - -public: - /// \ru Конструктор. \en Constructor. - MbAccurateTimer() : lastTime( 0.0 ) {} - virtual ~MbAccurateTimer() {} - - /// \ru Сброшен ли таймер. \en Is empty timer? - virtual bool IsEmpty () const { return !(lastTime > 0.0); } - /// \ru Сбросить таймер. \en Reset timer. - virtual void SetEmpty() { lastTime = 0.0; } - /// \ru Добавить значение. \en Add time value. - virtual bool SetTime ( double t ) { lastTime = t; return (t >= 0); } - /// \ru Начать отсчет. \en Start time measurement. - virtual void Begin (); - /// \ru Закончить отсчет. \en Finish time measurement. - virtual void End (); - /// \ru Получить значение времени. \en Get time value. - double GetLast() const { return lastTime; } - /// \ru Получить значение частоты. \en Get frequency value. - double PerSec () const { return (lastTime > 0.0 ? 1.0 / lastTime : 0.0); } -}; - - -//------------------------------------------------------------------------------ -// \ru Начать отсчет. \en Start time measurement. -//--- -inline void MbAccurateTimer::Begin() -{ -#ifdef STANDARD_C11 - begTime = std::chrono::high_resolution_clock::now(); -#endif -} - -//------------------------------------------------------------------------------ -// \ru Закончить отсчет. \en Finish time measurement. -// --- -inline void MbAccurateTimer::End() -{ -#ifdef STANDARD_C11 - TimePoint endTime = std::chrono::high_resolution_clock::now(); - double t = std::chrono::duration_cast< std::chrono::duration >(endTime - begTime).count(); - SetTime( t ); -#endif -} - - -//------------------------------------------------------------------------------ -/** \brief \ru Таймер со статистикой. - \en Average timer. \~ - \details \ru Таймер со статистикой. - \en Average timer. \~ -\ingroup Base_Tools -*/ -// --- -class MbAverageTimer : public MbAccurateTimer -{ - double avgTime; ///< \ru Среднее время в секундах. \en Average time in seconds. - double minTime; ///< \ru Минимальное время в секундах. \en Min. time in seconds. - double maxTime; ///< \ru Максимальное время в секундах. \en Max. time in seconds. - uint runCount; ///< \ru Количество запусков. \en Number of samples. - -public: - /// \ru Конструктор. \en Constructor. - MbAverageTimer() : avgTime( 0.0 ), minTime( 0.0 ), maxTime ( 0.0 ), runCount( 0 ) {} - - virtual bool IsEmpty() const { return (runCount == 0); } - virtual void SetEmpty(); - virtual bool SetTime( double ); - double GetAvg() const { return avgTime; } - double GetMin() const { return minTime; } - double GetMax() const { return maxTime; } -}; - -//------------------------------------------------------------------------------ -// \ru Сбросить таймер. \en Reset timer. -//--- -inline void MbAverageTimer::SetEmpty() -{ - MbAccurateTimer::SetEmpty(); - avgTime = minTime = maxTime = 0.0; - runCount = 0; -} - -//------------------------------------------------------------------------------ -// \ru Добавить значение. \en Add value. -//--- -inline bool MbAverageTimer::SetTime( double t ) -{ - bool res = MbAccurateTimer::SetTime( t ); - - if ( IsEmpty() ) - avgTime = minTime = maxTime = t; - else - { - avgTime += (t - avgTime) / (runCount + 1); - minTime = (std::min)( minTime, t ); - maxTime = (std::max)( maxTime, t ); - } - - runCount++; - return res; -} - -#endif // __SYSTEM_DEPENDENCY_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Системозависимые функции. + \en System-dependent functions. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __SYSTEM_DEPENDENCY_H +#define __SYSTEM_DEPENDENCY_H + +#include +#include +#include + +#ifdef C3D_WINDOWS //_MSC_VER +#define LOG_PATH _T("C:\\Logs\\") +#else // C3D_WINDOWS +#define LOG_PATH _T("") +#endif // C3D_WINDOWS + +#ifndef C3D_WINDOWS //_MSC_VER + +#include +#include +#include +#include +#include +#include +#include + + +#if (C3D_GLIBC_VERSION >= 2023) // C3D_GLIBC_VERSION + #include + #define c3d_isnan(d) std::isnan(d) +#else // C3D_GLIBC_VERSION + #include + #define c3d_isnan(d) isnan(d) +#endif // C3D_GLIBC_VERSION + +#define _finite std::isfinite + + +//------------------------------------------------------------------------------ +// \ru RGB-цвет. \en RGB color. +// --- +typedef uint32 COLORREF; + + +//------------------------------------------------------------------------------ +// RGBs of int colors from WinApi +// --- +#define RGB(r, g ,b) ((uint32) (((uint8) (r) | ((uint16) (g) << 8)) | (((uint32) (uint8) (b)) << 16))) + + +//------------------------------------------------------------------------------ +// \ru Дать значение красного цвета в пределах 0 - 255. \en Get value of red color in range from 0 to 255. +// --- +inline uint8 GetRValue(COLORREF rgb_color) +{ + return (uint8) (rgb_color); +} + + +//------------------------------------------------------------------------------ +// \ru Дать значение зелёного цвета в пределах 0 - 255. \en Get value of green color in range from 0 to 255. +// --- +inline uint8 GetGValue(COLORREF rgb_color) +{ + return (uint8) (rgb_color >> 8); +} + + +//------------------------------------------------------------------------------ +// \ru Дать значение синего цвета в пределах 0 - 255. \en Get value of blue color in range from 0 to 255. +// --- +inline uint8 GetBValue(COLORREF rgb_color) +{ + return (uint8) (rgb_color >> 16); +} + + +// \ru Вывод сообщений об ошибках пока отключаем в Линуксе \en Output of error messages is temporary disabled in Linux +#define _RPT0(warnlvl, text) +#define _RPT1(a1, a2, a3) // \ru не реализовано \en not implemented +#define _CRT_WARN +#define _CRT_ASSERT + +#else // C3D_WINDOWS + + #define c3d_isnan(d) _isnan(d) + +// \ru Одновременное определение _MBCS и _UNICODE недопустимо! \en Simultaneous definition of _MBCS and _UNICODE is not acceptable! +#if defined(_MBCS) && defined(_UNICODE) + #error Multibyte Character Sets (MBCS) not supported +#endif // _MBCS + +#endif // C3D_WINDOWS + +#include + +#ifdef C3D_STANDARD_CXX_11_PARTIAL + #include + // #error is not supported when compiling with /clr or /clr:pure. + // #include +#endif + +#ifndef threadLocal +# if (__STDC_VERSION__ >= 201112) && !defined(__STDC_NO_THREADS__) +# define threadLocal _Thread_local +# elif defined C3D_WINDOWS && ( \ + defined _MSC_VER || \ + defined __ICL || \ + defined __DMC__ ) +# define threadLocal __declspec(thread) +/* note that ICC (linux) and Clang are covered by __GNUC__ */ +# elif defined __GNUC__ || \ + defined __SUNPRO_C || \ + defined __xlC__ +# define threadLocal __thread +# else +# error "Cannot define thread_local" +# endif +#endif + + +//------------------------------------------------------------------------------ +/** \brief \ru Высокоточный таймер. + \en High resolution timer. \~ + \details \ru Высокоточный таймер. + \en High resolution timer. \~ +\ingroup Base_Tools +*/ +// --- +class MbAccurateTimer { +#ifdef C3D_STANDARD_CXX_11_PARTIAL + typedef std::chrono::high_resolution_clock::time_point TimePoint; +#endif +protected: + double lastTime; ///< \ru Время в секундах. \en Time in seconds. +private: +#ifdef C3D_STANDARD_CXX_11_PARTIAL + TimePoint begTime; ///< \ru Засечка времени. \en Time stamp. +#endif + +public: + /// \ru Конструктор. \en Constructor. + MbAccurateTimer() : lastTime( 0.0 ) {} + virtual ~MbAccurateTimer() {} + + /// \ru Сброшен ли таймер. \en Is empty timer? + virtual bool IsEmpty () const { return !(lastTime > 0.0); } + /// \ru Сбросить таймер. \en Reset timer. + virtual void SetEmpty() { lastTime = 0.0; } + /// \ru Добавить значение. \en Add time value. + virtual bool SetTime ( double t ) { lastTime = t; return (t >= 0); } + /// \ru Начать отсчет. \en Start time measurement. + virtual void Begin (); + /// \ru Закончить отсчет. \en Finish time measurement. + virtual void End (); + /// \ru Получить значение времени. \en Get time value. + double GetLast() const { return lastTime; } + /// \ru Получить значение частоты. \en Get frequency value. + double PerSec () const { return (lastTime > 0.0 ? 1.0 / lastTime : 0.0); } +}; + + +//------------------------------------------------------------------------------ +// \ru Начать отсчет. \en Start time measurement. +//--- +inline void MbAccurateTimer::Begin() +{ +#ifdef C3D_STANDARD_CXX_11_PARTIAL + begTime = std::chrono::high_resolution_clock::now(); +#endif +} + +//------------------------------------------------------------------------------ +// \ru Закончить отсчет. \en Finish time measurement. +// --- +inline void MbAccurateTimer::End() +{ +#ifdef C3D_STANDARD_CXX_11_PARTIAL + TimePoint endTime = std::chrono::high_resolution_clock::now(); + double t = std::chrono::duration_cast< std::chrono::duration >(endTime - begTime).count(); + SetTime( t ); +#endif +} + + +//------------------------------------------------------------------------------ +/** \brief \ru Таймер со статистикой. + \en Average timer. \~ + \details \ru Таймер со статистикой. + \en Average timer. \~ +\ingroup Base_Tools +*/ +// --- +class MbAverageTimer : public MbAccurateTimer +{ + double avgTime; ///< \ru Среднее время в секундах. \en Average time in seconds. + double minTime; ///< \ru Минимальное время в секундах. \en Min. time in seconds. + double maxTime; ///< \ru Максимальное время в секундах. \en Max. time in seconds. + uint runCount; ///< \ru Количество запусков. \en Number of samples. + +public: + /// \ru Конструктор. \en Constructor. + MbAverageTimer() : avgTime( 0.0 ), minTime( 0.0 ), maxTime ( 0.0 ), runCount( 0 ) {} + + virtual bool IsEmpty() const { return (runCount == 0); } + virtual void SetEmpty(); + virtual bool SetTime( double ); + double GetAvg() const { return avgTime; } + double GetMin() const { return minTime; } + double GetMax() const { return maxTime; } +}; + +//------------------------------------------------------------------------------ +// \ru Сбросить таймер. \en Reset timer. +//--- +inline void MbAverageTimer::SetEmpty() +{ + MbAccurateTimer::SetEmpty(); + avgTime = minTime = maxTime = 0.0; + runCount = 0; +} + +//------------------------------------------------------------------------------ +// \ru Добавить значение. \en Add value. +//--- +inline bool MbAverageTimer::SetTime( double t ) +{ + bool res = MbAccurateTimer::SetTime( t ); + + if ( IsEmpty() ) + avgTime = minTime = maxTime = t; + else + { + avgTime += (t - avgTime) / (runCount + 1); + minTime = (std::min)( minTime, t ); + maxTime = (std::max)( maxTime, t ); + } + + runCount++; + return res; +} + +#endif // __SYSTEM_DEPENDENCY_H diff --git a/C3d/Include/system_types.h b/C3d/Include/system_types.h index a63100a..deeb7cc 100644 --- a/C3d/Include/system_types.h +++ b/C3d/Include/system_types.h @@ -78,10 +78,10 @@ typedef uint32 VERSION; ///< \ru Версия. \en Version. \~ // \ru Умный указатель, единолично владеющий объектом. // \en Smart pointer that solely owns the object. //--- -#ifdef STANDARD_C11 -#define std_unique_ptr std::unique_ptr +#ifdef C3D_STANDARD_CXX_11_PARTIAL + #define std_unique_ptr std::unique_ptr #else -#define std_unique_ptr std::auto_ptr + #define std_unique_ptr std::auto_ptr #endif //------------------------------------------------------------------------------ @@ -116,46 +116,46 @@ typedef ptrdiff_t refcount_t; // Возвращаемый тип счет #if defined(PLATFORM_64) /// \ru Максимальное значение uint. \en Maximum value of uint. \~ \ingroup Base_Tools -const uint SYS_MAX_UINT = 0xffffffff; // ((uint)-1) //-V112 +const_expr uint SYS_MAX_UINT = 0xffffffff; // ((uint)-1) //-V112 /// \ru Максимальное значение size_t. \en Maximum value of size_t. \~ \ingroup Base_Tools -const size_t SYS_MAX_T = 0xffffffffffffffff; // ((size_t)-1) //-V112 +const_expr size_t SYS_MAX_T = 0xffffffffffffffff; // ((size_t)-1) //-V112 /// \ru Максимальное значение ptrdiff_t. \en Maximum value of ptrdiff_t. \~ \ingroup Base_Tools -const ptrdiff_t SYS_MAX_ST = 0x7fffffffffffffff; //-V112 +const_expr ptrdiff_t SYS_MAX_ST = 0x7fffffffffffffff; //-V112 /// \ru Минимальное значение ptrdiff_t. \en Minimum value of ptrdiff_t. \~ \ingroup Base_Tools -const ptrdiff_t SYS_MIN_ST = 0x8000000000000000; //-V112 +const_expr ptrdiff_t SYS_MIN_ST = 0x8000000000000000; //-V112 #else // PLATFORM_64 /// \ru Максимальное значение uint. \en Maximum value of uint. \~ \ingroup Base_Tools -const uint SYS_MAX_UINT = 0xffffffff; // ((uint)-1) //-V112 +const_expr uint SYS_MAX_UINT = 0xffffffff; // ((uint)-1) //-V112 /// \ru Максимальное значение size_t. \en Maximum value of size_t. \~ \ingroup Base_Tools -const size_t SYS_MAX_T = 0xffffffff; // ((size_t)-1) //-V112 +const_expr size_t SYS_MAX_T = 0xffffffff; // ((size_t)-1) //-V112 /// \ru Максимальное значение ptrdiff_t. \en Maximum value of ptrdiff_t. \~ \ingroup Base_Tools -const ptrdiff_t SYS_MAX_ST = 0x7fffffff; //-V112 +const_expr ptrdiff_t SYS_MAX_ST = 0x7fffffff; //-V112 /// \ru Минимальное значение ptrdiff_t. \en Minimum value of ptrdiff_t. \~ \ingroup Base_Tools -const ptrdiff_t SYS_MIN_ST = 0x80000000; //-V112 +const_expr ptrdiff_t SYS_MIN_ST = 0x80000000; //-V112 #endif // PLATFORM_64 /// \ru Максимальное значение uint8. \en Maximum value of uint8. \~ \ingroup Base_Tools -const uint8 SYS_MAX_UINT8 = 0xFF; //-V112 +const_expr uint8 SYS_MAX_UINT8 = 0xFF; //-V112 /// \ru Максимальное значение uint16. \en Maximum value of uint16. \~ \ingroup Base_Tools -const uint16 SYS_MAX_UINT16 = 0xFFFF; //-V112 +const_expr uint16 SYS_MAX_UINT16 = 0xFFFF; //-V112 /// \ru Максимальное значение uint32. \en Maximum value of uint32. \~ \ingroup Base_Tools -const uint32 SYS_MAX_UINT32 = 0xFFFFFFFF; //-V112 +const_expr uint32 SYS_MAX_UINT32 = 0xFFFFFFFF; //-V112 /// \ru Максимальное значение uint64. \en Maximum value of uint64. \~ \ingroup Base_Tools -const uint64 SYS_MAX_UINT64 = 0xFFFFFFFFFFFFFFFF; //-V112 +const_expr uint64 SYS_MAX_UINT64 = 0xFFFFFFFFFFFFFFFF; //-V112 /// \ru Максимальное значение int16. \en Maximum value of int16. \~ \ingroup Base_Tools -const int16 SYS_MAX_INT16 = 0x7FFF; //-V112 +const_expr int16 SYS_MAX_INT16 = 0x7FFF; //-V112 /// \ru Максимальное значение int32. \en Maximum value of int32. \~ \ingroup Base_Tools -const int32 SYS_MAX_INT32 = 0x7FFFFFFF; //-V112 +const_expr int32 SYS_MAX_INT32 = 0x7FFFFFFF; //-V112 /// \ru Максимальное значение int64. \en Maximum value of int64. \~ \ingroup Base_Tools -const int64 SYS_MAX_INT64 = 0x7FFFFFFFFFFFFFFF; //-V112 +const_expr int64 SYS_MAX_INT64 = 0x7FFFFFFFFFFFFFFF; //-V112 /// \ru Минимальное значение int16. \en Minimum value of int16. \~ \ingroup Base_Tools -const int16 SYS_MIN_INT16 = (int16)(uint16)0x8000; //-V112 +const_expr int16 SYS_MIN_INT16 = (int16)(uint16)0x8000; //-V112 /// \ru Минимальное значение int32. \en Minimum value of int32. \~ \ingroup Base_Tools -const int32 SYS_MIN_INT32 = (int32)(uint32)0x80000000; //-V112 +const_expr int32 SYS_MIN_INT32 = (int32)(uint32)0x80000000; //-V112 /// \ru Минимальное значение int64. \en Minimum value of int64. \~ \ingroup Base_Tools -const int64 SYS_MIN_INT64 = (int64)(uint64)0x8000000000000000; //-V112 +const_expr int64 SYS_MIN_INT64 = (int64)(uint64)0x8000000000000000; //-V112 //#endif // NOMINMAX @@ -202,12 +202,12 @@ inline int8 HiInt8 ( uint16 u16 ) { return int8(u16 >> 8); } //-V //------------------------------------------------------------------------------ /// \ru Неопределенный размер. \en Undefined size. \~ \ingroup Base_Tools //--- -const size_t NSIZE = SYS_MAX_T; //OV_x64 (size_t)-1; +const_expr size_t NSIZE = SYS_MAX_T; //OV_x64 (size_t)-1; //------------------------------------------------------------------------------ /// \ru Неопределенная позиция (для 32 битных данных). \en Undefined position (for 32-bit data). \~ \ingroup Base_Tools //--- -const uint NPOS32 = (uint)SYS_MAX_UINT32; // \ru КВН x64 -1 для работы с таблицами \en КВН x64 -1 for dealing with tables +const_expr uint NPOS32 = (uint)SYS_MAX_UINT32; // \ru КВН x64 -1 для работы с таблицами \en КВН x64 -1 for dealing with tables //------------------------------------------------------------------------------ @@ -218,7 +218,7 @@ const uint NPOS32 = (uint)SYS_MAX_UINT32; // \ru КВН x64 -1 для работ \ingroup Base_Tools */ //--- -const size_t SIZE_OF_POINTER = sizeof(char *); +const_expr size_t SIZE_OF_POINTER = sizeof(char *); //------------------------------------------------------------------------------ @@ -243,7 +243,7 @@ SignedType abs_t( const SignedType x ) { return ((x >= 0) ? x : -x); } //KYA K13 #pragma warning(disable: 4251) //AP class '1' needs to have dll-interface to be used by clients of class '2' (using non-exported in exported) // have to be disabled -#pragma warning(disable: 4018) // W3: comparison : signed/unsigned mismatch +//#pragma warning(disable: 4018) // W3: comparison : signed/unsigned mismatch #pragma warning(disable: 4201) // W4: nonstandard extension used : nameless struct/union #pragma warning(disable: 4245) // W4: const conversion, signed/unsigned mismatch diff --git a/C3d/Include/templ_array2.h b/C3d/Include/templ_array2.h index a205d26..e1c1156 100644 --- a/C3d/Include/templ_array2.h +++ b/C3d/Include/templ_array2.h @@ -19,11 +19,6 @@ #include -#ifdef __DEBUG_MEMORY_ALLOCATE_FREE_ -#include -#endif // __DEBUG_MEMORY_ALLOCATE_FREE_ - - class reader; class writer; @@ -84,11 +79,11 @@ public: // Общие методы матриц (двумерных массив bool SetSize( size_t n ) { return SetSize( n, n ); } ///< \ru Установить размер. \en Set size. /// \ru Получить элемент массива. \en Get an element of the array. - const Type & GetElem( size_t ln, size_t cn ) const { PRECONDITION( !!parr && ln < l && cn < c ); return parr[ln][cn]; } + const Type & GetElem( size_t ln, size_t cn ) const; /// \ru Установить элемент массива. \en Set an element of the array. - void SetElem( size_t ln, size_t cn, const Type & v ) { PRECONDITION( !!parr && ln < l && cn < c ); parr[ln][cn] = v; } + void SetElem( size_t ln, size_t cn, const Type & v ); /// \ru Оператор доступа по индексам. \en Access by indices operator. - const Type & operator () ( size_t i, size_t j ) const { PRECONDITION( i < l && j < c ); return parr[i][j]; } + const Type & operator () ( size_t i, size_t j ) const; /// \ru Расписать массив нулями. \en Assign zeros to array. Array2 & SetZero() { zero_array( *this ); return *this; } /// \ru Функция присваивания. \en An assignment function. @@ -100,13 +95,13 @@ public: // Общие методы матриц (двумерных массив public: /// \ru Оператор доступа по индексам. \en Access by indices operator. - Type & operator () ( size_t i, size_t j ) { PRECONDITION( i < l && j < c ); return parr[i][j]; } + Type & operator () ( size_t i, size_t j ); /// \ru Выдать адрес начала строки. \en Get an address of the row start. - const Type * GetLine( size_t i = 0 ) const { PRECONDITION( i < l ); return parr[i]; } + const Type * GetLine( size_t i = 0 ) const; /// \ru Выдать адрес начала строки. \en Get an address of the row start. - Type * SetLine( size_t i = 0 ) { PRECONDITION( i < l ); return parr[i]; } + Type * SetLine( size_t i = 0 ); /// \ru Инициировать элемент. \en Initiate an element. - void Init( size_t ln, size_t cn, const Type & v ) { PRECONDITION( !!parr && ln < l && cn < c ); parr[ln][cn] = v; } + void Init( size_t ln, size_t cn, const Type & v ); /// \ru Функции, выделяющие потенциально большие участки памяти, возвращают результат операции (успех/ошибка). /// \en Functions that allocate potentially large memory, return the operation result (success/error). @@ -247,6 +242,74 @@ inline bool Array2::SetSize( size_t lSize, size_t cSize ) { return ::set_array_size( *this, lSize, cSize ); } +//------------------------------------------------------------------------------ +// \ru Получить элемент массива. \en Get an element of the array. +// --- +template +const Type & Array2::GetElem( size_t ln, size_t cn ) const { + PRECONDITION( !!parr && ln < l && cn < c ); + return parr[ln][cn]; +} + +//------------------------------------------------------------------------------ +// \ru Установить элемент массива. \en Set an element of the array. +// --- +template +void Array2::SetElem( size_t ln, size_t cn, const Type & v ) { + PRECONDITION( !!parr && ln < l && cn < c ); + if ( !!parr && ln < l && cn < c ) + parr[ln][cn] = v; +} + +//------------------------------------------------------------------------------ +// \ru Оператор доступа по индексам. \en Access by indices operator. +// --- +template +const Type & Array2::operator() ( size_t i, size_t j ) const { + PRECONDITION( i < l && j < c ); + return parr[i][j]; +} + +/// \ru Оператор доступа по индексам. \en Access by indices operator. +template +Type & Array2::operator() ( size_t i, size_t j ) { + PRECONDITION( i < l && j < c ); + return parr[i][j]; +} + +//------------------------------------------------------------------------------ +// \ru Выдать адрес начала строки. \en Get an address of the row start. +// --- +template +const Type * Array2::GetLine( size_t i ) const { + PRECONDITION( i < l ); + if ( i < l ) + return parr[i]; + return NULL; +} + +//------------------------------------------------------------------------------ +// \ru Выдать адрес начала строки. \en Get an address of the row start. +// --- +template +Type * Array2::SetLine( size_t i ) { + PRECONDITION( i < l ); + if ( i < l ) + return parr[i]; + return NULL; +} + +//------------------------------------------------------------------------------ +// \ru Инициировать элемент. \en Initiate an element. +// --- +template +void Array2::Init( size_t ln, size_t cn, const Type & v ) { + PRECONDITION( !!parr && ln < l && cn < c ); + if ( !!parr && ln < l && cn < c ) + parr[ln][cn] = v; +} + + //------------------------------------------------------------------------------ // \ru Поменять местами строки. \en Swap lines. // --- @@ -396,7 +459,7 @@ inline bool Array2::CatchLinePointers( size_t newCount ) l = newCount; #ifdef __REALLOC_ARRAYS_STATISTIC_ - ::ReallocArrayStatistic( oldParr, oldSize * SIZE_OF_POINTER, parr, newCount * SIZE_OF_POINTER, 2/*Array2*/ ); + REALLOC_ARRAY_STATISTICS( oldParr, oldSize * SIZE_OF_POINTER, parr, newCount * SIZE_OF_POINTER, 2/*Array2*/ ); #endif // __REALLOC_ARRAYS_STATISTIC_ } catch ( const std::bad_alloc & ) { @@ -457,7 +520,7 @@ inline bool realloc_line( Type *& line, size_t oldSize, size_t newSize ) #endif // USE_REALLOC_IN_ARRAYS #ifdef __REALLOC_ARRAYS_STATISTIC_ - ::ReallocArrayStatistic( oldParr, oldSize * sizeOfType, line, newSize * sizeOfType, 2/*Array2*/ ); + REALLOC_ARRAY_STATISTICS( oldParr, oldSize * sizeOfType, line, newSize * sizeOfType, 2/*Array2*/ ); #endif // __REALLOC_ARRAYS_STATISTIC_ } catch ( const std::bad_alloc & ) { diff --git a/C3d/Include/templ_balance_tree.h b/C3d/Include/templ_balance_tree.h index 0287f1b..82517a6 100644 --- a/C3d/Include/templ_balance_tree.h +++ b/C3d/Include/templ_balance_tree.h @@ -319,7 +319,7 @@ inline void BalanceTreeNode::operator delete ( void * ptr, size_t size ) { // \ru конструктор узла дерева \en constructor of node of tree // --- template -inline BalanceTreeNode::BalanceTreeNode( BalanceTree & parent, Type * content ) : +inline BalanceTreeNode::BalanceTreeNode( BalanceTree & parent, Type * content ) : parent_m ( parent ), left_m ( NULL ), right_m ( NULL ), @@ -366,7 +366,7 @@ inline void BalanceTreeNode::SetRight( BalanceTreeNode * p ){ // \ru конструктор дерева \en constructor of tree // --- template -inline BalanceTree::BalanceTree( Compare_t c_t, Compare_v c_v, bool shouldDelete ) : +inline BalanceTree::BalanceTree( Compare_t c_t, Compare_v c_v, bool shouldDelete ) : root_m ( NULL ), allCount_m ( 0 ), owns_m ( shouldDelete ), @@ -513,7 +513,7 @@ inline void BalanceTree::ForEach( IterFunc f, void *pars ) { // \ru двойной RL-поворот \en double RL-rotation // --- template -inline void BalanceTree::DoubleTurningRL ( BalanceTreeNode *& node, +inline void BalanceTree::DoubleTurningRL ( BalanceTreeNode *& node, BalanceTreeNode * p1, BalanceTreeNode *& p2 ) { p2 = p1->left_m; @@ -533,7 +533,7 @@ inline void BalanceTree::DoubleTurningRL ( BalanceTreeNode *& node, // \ru двойной LR-поворот \en double LR-rotation // --- template -inline void BalanceTree::DoubleTurningLR ( BalanceTreeNode *& node, +inline void BalanceTree::DoubleTurningLR ( BalanceTreeNode *& node, BalanceTreeNode * p1, BalanceTreeNode *& p2 ) { p2 = p1->right_m; @@ -553,7 +553,7 @@ inline void BalanceTree::DoubleTurningLR ( BalanceTreeNode *& node, // \ru Построение идеально сбалансированного дерева \en Creation of ideally balanced tree // --- template -inline bool BalanceTree::AddToBalanceTree( Type & content, BalanceTreeNode *& node, +inline bool BalanceTree::AddToBalanceTree( Type & content, BalanceTreeNode *& node, bool & isBranchGrew ) { bool isAdd = false; if ( !node ) { @@ -646,7 +646,7 @@ inline bool BalanceTree::AddToBalanceTree( Type & content, BalanceTreeNode // \ru Балансировка слева \en Balancing at the left // --- template -inline void BalanceTree::BalanceL( BalanceTreeNode *& node, bool & isBranchGrew ) { +inline void BalanceTree::BalanceL( BalanceTreeNode *& node, bool & isBranchGrew ) { switch ( node->balance_m ) { case ts_negative : node->balance_m = ts_neutral; break; case ts_neutral : node->balance_m = ts_positive; isBranchGrew = false; break; @@ -685,7 +685,7 @@ inline void BalanceTree::BalanceL( BalanceTreeNode *& node, bool & i // \ru Балансировка справа \en Balancing at the right // --- template -inline void BalanceTree::BalanceR( BalanceTreeNode *& node, bool & isBranchGrew ) { +inline void BalanceTree::BalanceR( BalanceTreeNode *& node, bool & isBranchGrew ) { switch ( node->balance_m ) { case ts_positive : node->balance_m = ts_neutral; break; case ts_neutral : node->balance_m = ts_negative; isBranchGrew = false; break; @@ -724,7 +724,7 @@ inline void BalanceTree::BalanceR( BalanceTreeNode *& node, bool & i // \ru Исключение из сбалансированного дерева \en Exclude from balanced tree // --- template -inline bool BalanceTree::DelFromBalanceTree( BalanceTreeNode *& q, BalanceTreeNode *& r, bool & isBranchGrew ) { +inline bool BalanceTree::DelFromBalanceTree( BalanceTreeNode *& q, BalanceTreeNode *& r, bool & isBranchGrew ) { bool res = false; if ( r->right_m ) { res = DelFromBalanceTree( q, r->right_m, isBranchGrew ); @@ -748,7 +748,7 @@ inline bool BalanceTree::DelFromBalanceTree( BalanceTreeNode *& q, B // \ru Исключение из сбалансированного дерева \en Exclude from balanced tree // --- template -inline bool BalanceTree::DeleteFromBalanceTree( Type & content, BalanceTreeNode *& node, +inline bool BalanceTree::DeleteFromBalanceTree( Type & content, BalanceTreeNode *& node, bool & isBranchGrew, DelType del ) { bool res = false; if ( node && node->content_m ) { diff --git a/C3d/Include/templ_c_array.h b/C3d/Include/templ_c_array.h index a83e941..e116a25 100644 --- a/C3d/Include/templ_c_array.h +++ b/C3d/Include/templ_c_array.h @@ -20,10 +20,6 @@ #include -#ifdef __DEBUG_MEMORY_ALLOCATE_FREE_ -#include -#endif // __DEBUG_MEMORY_ALLOCATE_FREE_ - FORVARD_DECL_TEMPLATE_TYPENAME( class CcArray ); FORVARD_DECL_TEMPLATE_TYPENAME( void fill_array( CcArray &, const Type & fillData, size_t cnt ) ); @@ -68,12 +64,7 @@ public : /// \ru Освободить память. \en Free memory. void FreeMemory() { SetArraySize( 0 ); } /// \ru Оператор доступа. \en An access operator. - Type & operator []( size_t idx ) const { -#if defined(C3D_DEBUG) - PRECONDITION( idx < count ); -#endif - return parr[idx]; - } + Type & operator []( size_t idx ) const { PRECONDITION( idx < count ); return parr[idx]; } /// \ru Выделена ли память? \en Is memory allocated? bool IsNull () const { return parr == NULL; } /// \ru Выдать адрес начала массива. \en Get address of the beginning of an array. @@ -134,9 +125,7 @@ inline CcArray::CcArray( size_t _count ) template inline void CcArray::Copy( const void * from, size_t cnt, size_t offset ) { -#if defined(C3D_DEBUG) PRECONDITION( (offset + cnt <= count) && (cnt ? from != NULL : true) ); -#endif memcpy( parr + offset, from, cnt * sizeof(Type) ); } @@ -172,7 +161,7 @@ inline bool CcArray::SetArraySize( size_t newCount ) #if defined(C3D_DEBUG) count = newCount; #ifdef __REALLOC_ARRAYS_STATISTIC_ - ::ReallocArrayStatistic( oldParr, oldSize * sizeof(Type), parr, newCount * sizeof(Type), 4/*CcArray*/ ); + REALLOC_ARRAY_STATISTICS( oldParr, oldSize * sizeof(Type), parr, newCount * sizeof(Type), 4/*CcArray*/ ); #endif // __REALLOC_ARRAYS_STATISTIC_ #endif // C3D_DEBUG } @@ -202,9 +191,7 @@ inline bool CcArray::SetArraySize( size_t newCount ) template void fill_array( CcArray & arr, const Type & fillData, size_t cnt ) { -#if defined(C3D_DEBUG) PRECONDITION( cnt <= arr.count ); -#endif for ( size_t i = 0; i < cnt; i++ ) memcpy( &arr[i], &fillData, sizeof(Type) ); } diff --git a/C3d/Include/templ_csp_array.h b/C3d/Include/templ_csp_array.h index 3005fc3..79d43d5 100644 --- a/C3d/Include/templ_csp_array.h +++ b/C3d/Include/templ_csp_array.h @@ -16,8 +16,8 @@ FORVARD_DECL_TEMPLATE_TYPENAME( class CSPArray ); FORVARD_DECL_TEMPLATE_TYPENAME( void qp_sort( CSPArray & arr ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( reader& CALL_DECLARATION operator >> ( reader& in, CSPArray & ref ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( writer& CALL_DECLARATION operator << ( writer& out, const CSPArray & ref ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( reader & CALL_DECLARATION operator >> ( reader & in, CSPArray & ref ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( writer & CALL_DECLARATION operator << ( writer & out, const CSPArray & ref ) ); //------------------------------------------------------------------------------ @@ -85,8 +85,8 @@ public: Type * RemoveObj( Type * delObject, DelType del ); ///< \ru Удалить элемент. \en Delete element. TEMPLATE_FRIEND void qp_sort TEMPLATE_SUFFIX ( CSPArray & arr ); - TEMPLATE_FRIEND reader& CALL_DECLARATION operator >> TEMPLATE_SUFFIX ( reader& in, CSPArray & ref ); - TEMPLATE_FRIEND writer& CALL_DECLARATION operator << TEMPLATE_SUFFIX ( writer& out, const CSPArray & ref ); + TEMPLATE_FRIEND reader & CALL_DECLARATION operator >> TEMPLATE_SUFFIX ( reader & in, CSPArray & ref ); + TEMPLATE_FRIEND writer & CALL_DECLARATION operator << TEMPLATE_SUFFIX ( writer & out, const CSPArray & ref ); private: /// \ru Сортировка с выбором удаляемого элемента из двух одинаковых. \en Sort with the choice of the element to remove from the two identical. @@ -100,7 +100,7 @@ private: // \ru добавление объекта в массив \en adding an object to array // --- template -inline Type* CSPArray::Add( Type * el ) +inline Type * CSPArray::Add( Type * el ) { //::qp_sort( *this ); Sort(); @@ -112,7 +112,7 @@ inline Type* CSPArray::Add( Type * el ) // \ru добавление объекта в массив \en adding an object to array // --- template -inline Type* CSPArray::Add( Type * el, size_t & indexEl ) +inline Type * CSPArray::Add( Type * el, size_t & indexEl ) { //::qp_sort( *this ); Sort(); @@ -124,7 +124,7 @@ inline Type* CSPArray::Add( Type * el, size_t & indexEl ) // \ru поиск объекта в массиве \en search of an element in array // --- template -inline size_t /*OV_x64 int*/ CSPArray::Find( const Type * el ) +inline size_t CSPArray::Find( const Type * el ) { // ::qp_sort( *this ); Sort(); @@ -179,7 +179,8 @@ inline void CSPArray::Sort( LessFuncPtr lessFunc ) // \ru удалить элемент из массива (по указателю) \en delete an element from array (by the pointer) // --- template -inline Type* CSPArray::RemoveObj( Type *delObject, DelType del ) { +inline Type * CSPArray::RemoveObj( Type * delObject, DelType del ) +{ C3D_ASSERT( SPArray::nowDeletedElem == 0 ); // \ru Bременно, для отладки \en Temporarily, for debugging. size_t i = Find( delObject ); return ( i != SYS_MAX_T ) ? RemoveInd(i, del) : 0; diff --git a/C3d/Include/templ_dptr.h b/C3d/Include/templ_dptr.h index 94f46ad..1f59775 100644 --- a/C3d/Include/templ_dptr.h +++ b/C3d/Include/templ_dptr.h @@ -1,186 +1,186 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Автоматический указатель на объекты, не имеющие счетчиков ссылок. - \en Smart pointer to objects without reference counters. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __TEMPL_DPTR_H -#define __TEMPL_DPTR_H - - -#ifndef NULL - #define NULL 0 -#endif - - -#include -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Автоматический указатель. - \en Smart pointer. \~ - \details \ru Автоматический указатель на объекты, не имеющие счетчиков ссылок. \n - \en Smart pointer to objects without reference counters. \n \~ - \ingroup Base_Tools_SmartPointers -*/ -// --- -template -class DPtr { -public: - /// \ru Конструктор. \en Constructor. - DPtr(); - /// \ru Конструктор по указателю на объект. \en Constructor by pointer to an object. - DPtr( dtype * obj ); - /// \ru Конструктор по автоматическому указателю на объект. \en Constructor by smart pointer to an object. - DPtr( const DPtr & dptr ); - /// \ru Деструктор. \en Destructor. - ~DPtr(); - -public: - /// \ru Оператор доступа. \en An access operator. - operator dtype* ( void ) const { return m_Ptr; } - /// \ru Оператор доступа. \en An access operator. - dtype & operator * ( void ) const { return *m_Ptr; } - /// \ru Оператор доступа. \en An access operator. - dtype * operator -> ( void ) const { return m_Ptr; } - /// \ru Оператор присваивания. \en The assignment operator. - DPtr & operator = ( dtype * pObj ); - /// \ru Оператор присваивания. \en The assignment operator. - DPtr & operator = ( const DPtr & src ); - /// \ru Оператор равенства. \en The equality operator. - bool operator == ( const DPtr & src ) const { return ( m_Ptr == src.m_Ptr ); } - /// \ru Оператор равенства. \en The equality operator. - bool operator == ( dtype * pObj ) const { return ( m_Ptr == pObj ); } - /// \ru Оператор неравенства. \en The inequality operator. - bool operator != ( const DPtr & src ) const { return ( !(operator == (src )) ); } - /// \ru Оператор неравенства. \en The inequality operator. - bool operator != ( dtype * pObj ) const { return ( !(operator == (pObj)) ); } - -private: - /// \ru Счетчик ссылок на объект. \en A counter of references to an object. - template - struct Owner { - dtype1 * m_Ptr; - uint m_RefCounter; - - Owner( dtype1 * obj ) - : m_Ptr( obj ) - , m_RefCounter(0) - {} - ~Owner() - { - PRECONDITION( m_RefCounter == 0 ); - delete m_Ptr; - } - void Release() - { - if ( m_RefCounter-- == 1 ) - delete this; - } - }; - -private: - dtype * m_Ptr; ///< \ru Указатель на объект. \en A pointer to an object. - Owner * m_Owner; ///< \ru Счетчик ссылок на объект. \en A counter of references to an object. -}; - - -//------------------------------------------------------------------------------- -/// \ru Конструктор. \en Constructor. -// --- -template -DPtr::DPtr() - : m_Ptr ( NULL ) - , m_Owner( NULL ) -{ -} - - -//------------------------------------------------------------------------------- -// \ru Конструктор по указателю на объект. \en Constructor by pointer to an object. -// --- -template -DPtr::DPtr( dtype * obj ) - : m_Ptr ( obj ) - , m_Owner( NULL ) -{ - if ( obj != NULL ) { - m_Owner = new Owner( obj ); - m_Owner->m_RefCounter++; - } -} - - -//------------------------------------------------------------------------------- -// \ru Конструктор копирования. \en Copy constructor. -// --- -template -DPtr::DPtr( const DPtr & dptr ) - : m_Ptr( dptr.m_Ptr ) - , m_Owner( dptr.m_Owner ) -{ - if ( m_Owner != NULL ) - m_Owner->m_RefCounter++; -} - - -//------------------------------------------------------------------------------- -// \ru Оператор присваивания. \en Assignment operator. -// --- -template -DPtr & DPtr::operator = ( dtype * pObj ) -{ - if ( m_Ptr != pObj ) { - m_Ptr = pObj; - if ( m_Owner != NULL ) { - m_Owner->Release(); - m_Owner = NULL; - } - if ( pObj != NULL ) { - m_Owner = new Owner( pObj ); - m_Owner->m_RefCounter++; - } - } - - return *this; -} - - -//------------------------------------------------------------------------------- -// \ru Оператор присваивания. \en Assignment operator. -// --- -template -DPtr & DPtr::operator = ( const DPtr & dptr ) -{ - if ( m_Ptr != dptr.m_Ptr ) { - m_Ptr = dptr.m_Ptr; - if ( m_Owner != NULL ) { - m_Owner->Release(); - m_Owner = NULL; - } - if ( dptr.m_Ptr != NULL ) { - m_Owner = dptr.m_Owner; - m_Owner->m_RefCounter++; - } - } - - return *this; -} - - -//------------------------------------------------------------------------------- -// \ru Деструктор. \en Destructor. -// --- -template -DPtr::~DPtr() -{ - if ( m_Owner ) - m_Owner->Release(); -} - - -#endif // __TEMPL_DPTR_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Автоматический указатель на объекты, не имеющие счетчиков ссылок. + \en Smart pointer to objects without reference counters. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __TEMPL_DPTR_H +#define __TEMPL_DPTR_H + + +#ifndef NULL + #define NULL 0 +#endif + + +#include +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Автоматический указатель. + \en Smart pointer. \~ + \details \ru Автоматический указатель на объекты, не имеющие счетчиков ссылок. \n + \en Smart pointer to objects without reference counters. \n \~ + \ingroup Base_Tools_SmartPointers +*/ +// --- +template +class DPtr { +public: + /// \ru Конструктор. \en Constructor. + DPtr(); + /// \ru Конструктор по указателю на объект. \en Constructor by pointer to an object. + DPtr( dtype * obj ); + /// \ru Конструктор по автоматическому указателю на объект. \en Constructor by smart pointer to an object. + DPtr( const DPtr & dptr ); + /// \ru Деструктор. \en Destructor. + ~DPtr(); + +public: + /// \ru Оператор доступа. \en An access operator. + operator dtype* ( void ) const { return m_Ptr; } + /// \ru Оператор доступа. \en An access operator. + dtype & operator * ( void ) const { return *m_Ptr; } + /// \ru Оператор доступа. \en An access operator. + dtype * operator -> ( void ) const { return m_Ptr; } + /// \ru Оператор присваивания. \en The assignment operator. + DPtr & operator = ( dtype * pObj ); + /// \ru Оператор присваивания. \en The assignment operator. + DPtr & operator = ( const DPtr & src ); + /// \ru Оператор равенства. \en The equality operator. + bool operator == ( const DPtr & src ) const { return ( m_Ptr == src.m_Ptr ); } + /// \ru Оператор равенства. \en The equality operator. + bool operator == ( dtype * pObj ) const { return ( m_Ptr == pObj ); } + /// \ru Оператор неравенства. \en The inequality operator. + bool operator != ( const DPtr & src ) const { return ( !(operator == (src )) ); } + /// \ru Оператор неравенства. \en The inequality operator. + bool operator != ( dtype * pObj ) const { return ( !(operator == (pObj)) ); } + +private: + /// \ru Счетчик ссылок на объект. \en A counter of references to an object. + template + struct Owner { + dtype1 * m_Ptr; + uint m_RefCounter; + + Owner( dtype1 * obj ) + : m_Ptr( obj ) + , m_RefCounter(0) + {} + ~Owner() + { + PRECONDITION( m_RefCounter == 0 ); + delete m_Ptr; + } + void Release() + { + if ( m_RefCounter-- == 1 ) + delete this; + } + }; + +private: + dtype * m_Ptr; ///< \ru Указатель на объект. \en A pointer to an object. + Owner * m_Owner; ///< \ru Счетчик ссылок на объект. \en A counter of references to an object. +}; + + +//------------------------------------------------------------------------------- +/// \ru Конструктор. \en Constructor. +// --- +template +DPtr::DPtr() + : m_Ptr ( NULL ) + , m_Owner( NULL ) +{ +} + + +//------------------------------------------------------------------------------- +// \ru Конструктор по указателю на объект. \en Constructor by pointer to an object. +// --- +template +DPtr::DPtr( dtype * obj ) + : m_Ptr ( obj ) + , m_Owner( NULL ) +{ + if ( obj != NULL ) { + m_Owner = new Owner( obj ); + m_Owner->m_RefCounter++; + } +} + + +//------------------------------------------------------------------------------- +// \ru Конструктор копирования. \en Copy constructor. +// --- +template +DPtr::DPtr( const DPtr & dptr ) + : m_Ptr( dptr.m_Ptr ) + , m_Owner( dptr.m_Owner ) +{ + if ( m_Owner != NULL ) + m_Owner->m_RefCounter++; +} + + +//------------------------------------------------------------------------------- +// \ru Оператор присваивания. \en Assignment operator. +// --- +template +DPtr & DPtr::operator = ( dtype * pObj ) +{ + if ( m_Ptr != pObj ) { + m_Ptr = pObj; + if ( m_Owner != NULL ) { + m_Owner->Release(); + m_Owner = NULL; + } + if ( pObj != NULL ) { + m_Owner = new Owner( pObj ); + m_Owner->m_RefCounter++; + } + } + + return *this; +} + + +//------------------------------------------------------------------------------- +// \ru Оператор присваивания. \en Assignment operator. +// --- +template +DPtr & DPtr::operator = ( const DPtr & dptr ) +{ + if ( m_Ptr != dptr.m_Ptr ) { + m_Ptr = dptr.m_Ptr; + if ( m_Owner != NULL ) { + m_Owner->Release(); + m_Owner = NULL; + } + if ( dptr.m_Ptr != NULL ) { + m_Owner = dptr.m_Owner; + m_Owner->m_RefCounter++; + } + } + + return *this; +} + + +//------------------------------------------------------------------------------- +// \ru Деструктор. \en Destructor. +// --- +template +DPtr::~DPtr() +{ + if ( m_Owner ) + m_Owner->Release(); +} + + +#endif // __TEMPL_DPTR_H diff --git a/C3d/Include/templ_fdp_array.h b/C3d/Include/templ_fdp_array.h index 089ed17..f34f6e9 100644 --- a/C3d/Include/templ_fdp_array.h +++ b/C3d/Include/templ_fdp_array.h @@ -18,10 +18,6 @@ #include -#ifdef __DEBUG_MEMORY_ALLOCATE_FREE_ -#include -#endif // __DEBUG_MEMORY_ALLOCATE_FREE_ - FORVARD_DECL_TEMPLATE_TYPENAME( class FDPArray ); FORVARD_DECL_TEMPLATE_TYPENAME( void destroy_array ( FDPArray & ) ); @@ -109,11 +105,11 @@ public: void operator delete ( void *, size_t ); #endif // __DEBUG_MEMORY_ALLOCATE_FREE_ -#ifdef STANDARD_CPP11_RVALUE_REFERENCES +#ifdef C3D_STANDARD_CXX_11_PARTIAL public: FDPArray( FDPArray && ); ///< \ru Конструктор перемещения массива. \en Constructor of an array moving. FDPArray & operator = ( FDPArray && ); ///< \ru Оператор перемещения массива. \en Operator of an array moving. -#endif // STANDARD_CPP11_RVALUE_REFERENCES +#endif // C3D_STANDARD_CXX_11_PARTIAL }; @@ -135,7 +131,7 @@ inline void FDPArray::operator delete( void * ptr, size_t size ) { } #endif // __DEBUG_MEMORY_ALLOCATE_FREE_ -#ifdef STANDARD_CPP11_RVALUE_REFERENCES +#ifdef C3D_STANDARD_CXX_11_PARTIAL //------------------------------------------------------------------------------ // \ru Конструктор перемещения массива. \en Constructor of an array moving. // --- @@ -164,7 +160,7 @@ FDPArray & FDPArray::operator = ( FDPArray && _Right ) } return (*this); } -#endif // STANDARD_CPP11_RVALUE_REFERENCES +#endif // C3D_STANDARD_CXX_11_PARTIAL //------------------------------------------------------------------------------ // \ru деструктор массива \en destructor of array // --- @@ -222,23 +218,26 @@ template inline Type* FDPArray::RemoveInd( size_t delIndex, DelType del ) { PRECONDITION( delIndex < RPArray::count ); - const Type **d = RPArray::GetAddr() + delIndex; + Type * r = 0; + if ( delIndex < RPArray::count ) { + const Type ** d = RPArray::GetAddr() + delIndex; - Type *r = (Type*) *d; + r = (Type *)*d; - // \ru сначала приведем в порядок массив ... \en put an array in order at first ... - memmove( d, d+1, (RPArray::count - delIndex - 1) * SIZE_OF_POINTER ); - RPArray::count--; + // \ru сначала приведем в порядок массив ... \en put an array in order at first ... + memmove( d, d + 1, (RPArray::count - delIndex - 1) * SIZE_OF_POINTER ); + RPArray::count--; - // \ru ...а потом будем удалять \en ... and then delete - if ( fDestroy && (del==Delete || del==defDelete) ) { - PRECONDITION( !r || nowDeletedElem != r ); - nowDeletedElem = r; + // \ru ...а потом будем удалять \en ... and then delete + if ( fDestroy && ( del == Delete || del == defDelete ) ) { + PRECONDITION( !r || nowDeletedElem != r ); + nowDeletedElem = r; - (*fDestroy)(r); - r = 0; + ( *fDestroy )( r ); + r = 0; - nowDeletedElem = 0; + nowDeletedElem = 0; + } } return r; @@ -264,23 +263,26 @@ template inline Type* FDPArray::DestroyInd( size_t delIndex, typename FDPArray::DestroyFunc fd ) { PRECONDITION( delIndex < RPArray::count ); - const Type **d = RPArray::GetAddr() + delIndex; + Type * r = 0; + if ( delIndex < RPArray::count ) { + const Type ** d = RPArray::GetAddr() + delIndex; - Type *r = (Type*) *d; + r = (Type *)*d; - // \ru сначала приведем в порядок массив ... \en put an array in order at first ... - memmove( d, d+1, (RPArray::count - delIndex - 1) * SIZE_OF_POINTER ); - RPArray::count--; + // \ru сначала приведем в порядок массив ... \en put an array in order at first ... + memmove( d, d + 1, (RPArray::count - delIndex - 1) * SIZE_OF_POINTER ); + RPArray::count--; - // \ru ...а потом будем удалять \en ... and then delete - if ( fd ) { - PRECONDITION( !r || nowDeletedElem != r ); // \ru Bременно, для отладки \en Temporarily, for debugging. - nowDeletedElem = r; + // \ru ...а потом будем удалять \en ... and then delete + if ( fd ) { + PRECONDITION( !r || nowDeletedElem != r ); // \ru Bременно, для отладки \en Temporarily, for debugging. + nowDeletedElem = r; - (*fd)(r); - r = 0; + ( *fd )( r ); + r = 0; - nowDeletedElem = 0; + nowDeletedElem = 0; + } } return r; diff --git a/C3d/Include/templ_ifc_array.h b/C3d/Include/templ_ifc_array.h index 22618be..7138cac 100644 --- a/C3d/Include/templ_ifc_array.h +++ b/C3d/Include/templ_ifc_array.h @@ -37,8 +37,8 @@ class IFC_Array : private RPArray { public: typedef Type * stored_type; ///< \ru Имя для указателя на объект. \en A name of the pointer to the object. - typedef Type * & reference; - typedef Type * const & const_reference; + typedef Type * & reference; + typedef Type * const & const_reference; /// \ru Константный итератор (новые функции можно добавлять по мере необходимости). \en A constant iterator (new functions can be added as necessary) class iterator @@ -152,11 +152,11 @@ private: TEMPLATE_FRIEND reader & CALL_DECLARATION operator >> TEMPLATE_SUFFIX ( reader & in, IFC_Array & ref ); TEMPLATE_FRIEND writer & CALL_DECLARATION operator << TEMPLATE_SUFFIX ( writer & out, const IFC_Array & ref ); -#ifdef STANDARD_CPP11_RVALUE_REFERENCES +#ifdef C3D_STANDARD_CXX_11_PARTIAL public: IFC_Array( IFC_Array && ); ///< \ru Конструктор перемещения массива. \en Constructor of an array moving. IFC_Array & operator = ( IFC_Array && ); ///< \ru Оператор перемещения массива. \en Operator of an array moving. -#endif // STANDARD_CPP11_RVALUE_REFERENCES +#endif // C3D_STANDARD_CXX_11_PARTIAL }; //------------------------------------------------------------------------------ @@ -172,7 +172,7 @@ inline void IFCArray_Release( Type * & el ) } } -#ifdef STANDARD_CPP11_RVALUE_REFERENCES +#ifdef C3D_STANDARD_CXX_11_PARTIAL //------------------------------------------------------------------------------ // \ru Конструктор перемещения массива. \en Constructor of an array moving. // --- @@ -195,7 +195,7 @@ IFC_Array & IFC_Array::operator = ( IFC_Array && _Right ) } return (*this); } -#endif // STANDARD_CPP11_RVALUE_REFERENCES +#endif // C3D_STANDARD_CXX_11_PARTIAL //------------------------------------------------------------------------------ // \ru Деструктор массива. \en Destructor of array @@ -258,7 +258,7 @@ inline void IFC_Array::SetAt( stored_type ent, size_t ind ) { //--- template inline void IFC_Array::Exchange( size_t ind1, size_t ind2 ) { - if ( ind1 != ind2 ) { + if ( ind1 != ind2 && ind1 < RPArray::count && ind2 < RPArray::count ) { Type * value = RPArray::operator[](ind2); RPArray::operator[](ind2) = RPArray::operator[](ind1); RPArray::operator[](ind1) = value; @@ -273,14 +273,16 @@ template inline Type * IFC_Array::RemoveInd( size_t delIndex ) { PRECONDITION( delIndex < RPArray::count ); + stored_type r = 0; + if ( delIndex < RPArray::count ) { + stored_type * d = RPArray::begin() + delIndex; - stored_type * d = RPArray::begin() + delIndex; + r = *d; - stored_type r = *d; - - memmove( d, d+1, (RPArray::count - delIndex - 1) * SIZE_OF_POINTER ); - RPArray::count--; - IFCArray_Release( r ); + memmove( d, d + 1, (RPArray::count - delIndex - 1) * SIZE_OF_POINTER ); + RPArray::count--; + IFCArray_Release( r ); + } return r; } diff --git a/C3d/Include/templ_im_array.h b/C3d/Include/templ_im_array.h index 12a79f6..a15a6ae 100644 --- a/C3d/Include/templ_im_array.h +++ b/C3d/Include/templ_im_array.h @@ -133,7 +133,7 @@ inline void IMArray::operator delete ( void * ptr, size_t size ) { // \ru конструктор массива \en constructor of an array // --- template -inline IMArray::IMArray( PArray< Type > & arr, Compare_t c_t, Compare_v c_v, size_t maxCnt, uint16 delt ) +inline IMArray::IMArray( PArray< Type > & arr, Compare_t c_t, Compare_v c_v, size_t maxCnt, uint16 delt ) : SArray< size_t >( maxCnt, delt ), array(arr), compT(c_t), compV(c_v) {} @@ -310,82 +310,86 @@ inline size_t & IMArray::operator ()( size_t myIndex ) const { template Type * add_to_array( IMArray & arr, size_t ind, size_t * myIndex ) { PRECONDITION( ind < arr.array.Count() ); - - Type * el = arr.array[ind]; + + Type * el = 0; size_t mcIArr = SYS_MAX_T; // \ru текущий индекс в IMArray \en the current index in IMArray - if ( el ) { + if ( ind < arr.array.Count() ) { + el = arr.array[ind]; - if ( !arr.count ) { - arr.SArray< size_t >::Add( ind ); - mcIArr = 0; - } - else { - size_t ml = 0; - mcIArr = ml; - size_t mcPArr = arr.GetIndex( mcIArr ); // \ru текущий индекс в PArray \en the current index in PArray - - // \ru проверяем первый эл. \en check the first element - int resL = (el->*arr.compT)( arr.array[mcPArr] ); + if ( el ) { - // \ru если элемент меньше первого, вставляем перед первым \en if an element is less than the first element then place it before the first element - if ( resL < 0 ) - arr.InsertInd( mcIArr, ind ); + if ( !arr.count ) { + arr.SArray< size_t >::Add( ind ); + mcIArr = 0; + } else { - size_t mr = arr.count - 1; - mcIArr = mr; - mcPArr = arr.GetIndex( mcIArr ); + size_t ml = 0; + mcIArr = ml; + size_t mcPArr = arr.GetIndex( mcIArr ); // \ru текущий индекс в PArray \en the current index in PArray - // \ru проверяем последний эл. \en check the last element - int resR = !mcIArr ? resL : (el->*arr.compT)( arr.array[mcPArr] ); + // \ru проверяем первый эл. \en check the first element + int resL = ( el->*arr.compT )( arr.array[mcPArr] ); - // \ru если один объект в массиве или эл. больше или равен последнему, \en if one object in the array or an element is not less than the last element, - // \ru должны попасть сюда \en should be here - if ( !mcIArr || resR >= 0 ) { - arr.SArray< size_t >::Add( ind ); - mcIArr = mr + 1; - } + // \ru если элемент меньше первого, вставляем перед первым \en if an element is less than the first element then place it before the first element + if ( resL < 0 ) + arr.InsertInd( mcIArr, ind ); else { - if ( arr.count == 2 ) - arr.InsertInd( mcIArr, ind ); + size_t mr = arr.count - 1; + mcIArr = mr; + mcPArr = arr.GetIndex( mcIArr ); + + // \ru проверяем последний эл. \en check the last element + int resR = !mcIArr ? resL : ( el->*arr.compT )( arr.array[mcPArr] ); + + // \ru если один объект в массиве или эл. больше или равен последнему, \en if one object in the array or an element is not less than the last element, + // \ru должны попасть сюда \en should be here + if ( !mcIArr || resR >= 0 ) { + arr.SArray< size_t >::Add( ind ); + mcIArr = mr + 1; + } else { + if ( arr.count == 2 ) + arr.InsertInd( mcIArr, ind ); + else { - // \ru сюда попадаем если resL >= 0 и resR < 0 \en this is the case when resL >= 0 and resR < 0 - while ( ml + 1 < mr ) { // \ru пока не нашли - ищем \en seek until do not find + // \ru сюда попадаем если resL >= 0 и resR < 0 \en this is the case when resL >= 0 and resR < 0 + while ( ml + 1 < mr ) { // \ru пока не нашли - ищем \en seek until do not find - if ( !resL ) { - mcIArr = ml; + if ( !resL ) { + mcIArr = ml; -// 68759 do { -// mcIArr++; -// mcPArr = arr.GetIndex( mcIArr ); -// } while ( (el->*arr.compT)( arr.array[mcPArr] ) == 0 ); - mcIArr++; - mcPArr = arr.GetIndex( mcIArr ); + // 68759 do { + // mcIArr++; + // mcPArr = arr.GetIndex( mcIArr ); + // } while ( (el->*arr.compT)( arr.array[mcPArr] ) == 0 ); + mcIArr++; + mcPArr = arr.GetIndex( mcIArr ); - break; - } - else { - size_t md = ( ml + mr ) / 2; - mcPArr = arr.GetIndex( md ); - - int res = (el->*arr.compT)( arr.array[mcPArr] ); - - if ( res > 0 ) - ml = md; - else if ( res < 0 ) { - mr = md; - mcIArr = md; + break; } else { - resL = res; //res = 0 - mcIArr = md + 1; // \ru если ml + 1 < mr не выполнится объект должен добавиться после md \en if ml + 1 is not less than mr then the object should be added after md - ml = md; + size_t md = ( ml + mr ) / 2; + mcPArr = arr.GetIndex( md ); + + int res = ( el->*arr.compT )( arr.array[mcPArr] ); + + if ( res > 0 ) + ml = md; + else if ( res < 0 ) { + mr = md; + mcIArr = md; + } + else { + resL = res; //res = 0 + mcIArr = md + 1; // \ru если ml + 1 < mr не выполнится объект должен добавиться после md \en if ml + 1 is not less than mr then the object should be added after md + ml = md; + } } } - } - arr.InsertInd( mcIArr, ind ); + arr.InsertInd( mcIArr, ind ); + } } } } @@ -502,11 +506,12 @@ size_t find_in_array( const IMArray & arr, const void * val, size_t * myIn template void array_remove_ind( IMArray & arr, size_t delIndex, bool completely ){ PRECONDITION( delIndex < arr.count ); - size_t arrayIndex = arr.GetIndex(delIndex); //arr.parr[ delIndex ]; - arr.SArray< size_t >::RemoveInd( delIndex ); - - if ( completely ) - array_reduction_obj( arr, arrayIndex ); + if ( delIndex < arr.count ) { + size_t arrayIndex = arr.GetIndex( delIndex ); //arr.parr[ delIndex ]; + arr.SArray< size_t >::RemoveInd( delIndex ); + if ( completely ) + array_reduction_obj( arr, arrayIndex ); + } } diff --git a/C3d/Include/templ_iterator.h b/C3d/Include/templ_iterator.h index 065b25a..5292cb1 100644 --- a/C3d/Include/templ_iterator.h +++ b/C3d/Include/templ_iterator.h @@ -1,96 +1,96 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Итератор массива. - \en Iterator of array. \~ -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __TEMPL_ITERATOR_H -#define __TEMPL_ITERATOR_H - -#include - -//------------------------------------------------------------------------------ -/** \brief \ru Интерфейс итератора. - \en Interface of iterator. \~ - \details \ru Итератор итератора. \n - \en Iterator of iterator. \n \~ - \ingroup Base_Tools_Containers -*/ -// --- -template -class PointersIterator { -public: - /// \ru Деструктор. \en Destructor. - virtual ~PointersIterator() {}; - /// \ru Сброс итератора. \en Reset iterator. - virtual void Restart() = 0; - /// \ru Получить текущий элемент и сдвинуть итератор на следующий. \en Get the current element and move an iterator to the next. - virtual Type * operator ++(int) = 0; - /// \ru Получить текущий элемент \en Get the current element - virtual Type * operator() () const = 0; -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Итератор массива. - \en Iterator of array. \~ - \details \ru Итератор массива указателей. \n - \en Iterator of pointers array. \n \~ - \ingroup Base_Tools_Containers -*/ -// --- -template -class PointersArrayIterator : public PointersIterator { -private: - const Array & items; ///< \ru Ссылка на массив. \en A reference to the array. - size_t index; ///< \ru Текущее положение итератора. \en The current position of iterator. - -public: - /// \ru Конструктор итератора. \en Constructor of iterator. - PointersArrayIterator( const Array & arr ) : items( arr ), index( 0 ) {} - /// \ru Сброс итератора. \en Reset iterator. - virtual void Restart() { index = 0; } - /// \ru Получить текущий элемент и сдвинуть итератор на следующий. \en Get the current element and move an iterator to the next. - virtual Type * operator ++(int) { return (index < items.Count()) ? items[index++] : NULL; } - /// \ru Получить текущий элемент \en Get the current element - virtual Type * operator() () const { return (index < items.Count()) ? items[index] : NULL; } - -private: // \ru не реализовано \en not implemented - PointersArrayIterator & operator = ( const PointersArrayIterator & ); - PointersArrayIterator ( const PointersArrayIterator & ); - PointersArrayIterator (); -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Итератор списка. - \en Iterator of list. \~ - \details \ru Итератор списка указателей. \n - \en Iterator of pointers list. \n \~ - \ingroup Base_Tools_Containers -*/ -// --- -template -struct PointersListIterator : public LIterator, public PointersIterator -{ - /// \ru Конструктор итератора. \en Constructor of iterator. - PointersListIterator( const List & l ) : LIterator( l ) {} - /// \ru Конструктор итератора. \en Constructor of iterator. - PointersListIterator() : LIterator() {} - - /// \ru Сброс итератора. \en Reset iterator. - virtual void Restart() { LIterator::Restart(); } - /// \ru Получить текущий элемент и сдвинуть итератор на следующий. \en Get the current element and move an iterator to the next. - virtual Type * operator ++(int) { return LIterator::operator++(int()); } - /// \ru Получить текущий элемент \en Get the current element - virtual Type * operator() () const { return LIterator::operator()(); } - -private: // \ru не реализовано \en not implemented - PointersListIterator & operator = ( const PointersListIterator & ); - PointersListIterator ( const PointersListIterator & ); -}; - - -#endif //__TEMPL_ITERATOR_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Итератор массива. + \en Iterator of array. \~ +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __TEMPL_ITERATOR_H +#define __TEMPL_ITERATOR_H + +#include + +//------------------------------------------------------------------------------ +/** \brief \ru Интерфейс итератора. + \en Interface of iterator. \~ + \details \ru Итератор итератора. \n + \en Iterator of iterator. \n \~ + \ingroup Base_Tools_Containers +*/ +// --- +template +class PointersIterator { +public: + /// \ru Деструктор. \en Destructor. + virtual ~PointersIterator() {}; + /// \ru Сброс итератора. \en Reset iterator. + virtual void Restart() = 0; + /// \ru Получить текущий элемент и сдвинуть итератор на следующий. \en Get the current element and move an iterator to the next. + virtual Type * operator ++(int) = 0; + /// \ru Получить текущий элемент \en Get the current element + virtual Type * operator() () const = 0; +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Итератор массива. + \en Iterator of array. \~ + \details \ru Итератор массива указателей. \n + \en Iterator of pointers array. \n \~ + \ingroup Base_Tools_Containers +*/ +// --- +template +class PointersArrayIterator : public PointersIterator { +private: + const Array & items; ///< \ru Ссылка на массив. \en A reference to the array. + size_t index; ///< \ru Текущее положение итератора. \en The current position of iterator. + +public: + /// \ru Конструктор итератора. \en Constructor of iterator. + PointersArrayIterator( const Array & arr ) : items( arr ), index( 0 ) {} + /// \ru Сброс итератора. \en Reset iterator. + virtual void Restart() { index = 0; } + /// \ru Получить текущий элемент и сдвинуть итератор на следующий. \en Get the current element and move an iterator to the next. + virtual Type * operator ++(int) { return (index < items.Count()) ? items[index++] : NULL; } + /// \ru Получить текущий элемент \en Get the current element + virtual Type * operator() () const { return (index < items.Count()) ? items[index] : NULL; } + +private: // \ru не реализовано \en not implemented + PointersArrayIterator & operator = ( const PointersArrayIterator & ); + PointersArrayIterator ( const PointersArrayIterator & ); + PointersArrayIterator (); +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Итератор списка. + \en Iterator of list. \~ + \details \ru Итератор списка указателей. \n + \en Iterator of pointers list. \n \~ + \ingroup Base_Tools_Containers +*/ +// --- +template +struct PointersListIterator : public LIterator, public PointersIterator +{ + /// \ru Конструктор итератора. \en Constructor of iterator. + PointersListIterator( const List & l ) : LIterator( l ) {} + /// \ru Конструктор итератора. \en Constructor of iterator. + PointersListIterator() : LIterator() {} + + /// \ru Сброс итератора. \en Reset iterator. + virtual void Restart() { LIterator::Restart(); } + /// \ru Получить текущий элемент и сдвинуть итератор на следующий. \en Get the current element and move an iterator to the next. + virtual Type * operator ++(int) { return LIterator::operator++(int()); } + /// \ru Получить текущий элемент \en Get the current element + virtual Type * operator() () const { return LIterator::operator()(); } + +private: // \ru не реализовано \en not implemented + PointersListIterator & operator = ( const PointersListIterator & ); + PointersListIterator ( const PointersListIterator & ); +}; + + +#endif //__TEMPL_ITERATOR_H diff --git a/C3d/Include/templ_kdtree.h b/C3d/Include/templ_kdtree.h index cb97bf5..e05b449 100644 --- a/C3d/Include/templ_kdtree.h +++ b/C3d/Include/templ_kdtree.h @@ -1,506 +1,506 @@ -////////////////////////////////////////////////////////////////////////////////// -/** -\file -\brief \ru К-мерное дерево. - \en K-d tree. \~ - -*/ -// \ru К-мерное дерево - это структура данных с разбиением пространства для упорядочивания точек из K-мерного пространства, -// используемая для поиска k ближайших соседей. -// \en K-d tree is a space-partitioning data structure for organizing points in a k-dimensional space, -// using for the k-nearest neighbors (kNN) search -// -//////////////////////////////////////////////////////////////////////////////// -#ifndef __MB_KDTREE_H -#define __MB_KDTREE_H - -#include -#include +////////////////////////////////////////////////////////////////////////////////// +/** +\file +\brief \ru К-мерное дерево. + \en K-d tree. \~ + +*/ +// \ru К-мерное дерево - это структура данных с разбиением пространства для упорядочивания точек из K-мерного пространства, +// используемая для поиска k ближайших соседей. +// \en K-d tree is a space-partitioning data structure for organizing points in a k-dimensional space, +// using for the k-nearest neighbors (kNN) search +// +//////////////////////////////////////////////////////////////////////////////// +#ifndef __MB_KDTREE_H +#define __MB_KDTREE_H + +#include +#include #include -#include -#include - - -//----------------------------------------------------------------------------- -/** \brief \ru Очередь с приоритетом с использованием кучи. - \en Priority queue using a heap. \~ - \details \ru Очередь с приоритетом с использованием кучи. Размер очереди фиксирован. - Производительность этой реализации выше чем у std::priority_queue. \n - \en Priority queue using a heap. Size of queue is fixed. - This implementation perfomance is better in comparsion with std::priority_queue. \n \~ - \ingroup Base_Tools -*/ -// --- -template -class PriorityQueue -{ -protected: - struct Element ///< \ru Элемент очереди. \en Element of queue. - { - Weight weight; ///< \ru Вес элемента. \en Weight of element. - Index index; ///< \ru Индекс элемента. \en Index of element. - }; - Element * elements; ///< \ru Элементы очереди. \en Elements of queue. - Element * offsetedElements; ///< \ru Смещенные элементы очереди. \en Shifted elements of queue. - size_t count; ///< \ru Актуальное число элементов в очереди. \en Actual count of elements in the queue. - size_t maxSize; ///< \ru Максимальное число элементов в очереди. \en Maximal count of elements in the queue. - -protected: - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. - PriorityQueue( const PriorityQueue & ); - -public: - /// \ru Конструктор. \en Constructor. - PriorityQueue( ); - /// \ru Деструктор. \en Destructor. - ~PriorityQueue(); - -public: - /** \ru \name Функции очереди с приоритетом. - \en \name Functions of priority queue. - \{ */ - /// \ru Инициализировать очередь максимальным количеством элементов в очереди. \en Initialize the queue by number of elements. - inline bool Initialize( size_t _maxSize ); - /// \ru Получить количество элементов в очереди. \en Get elements count in the queue. - inline size_t ElementsCount() const { return count; } - /// \ru Получить вес элемента очереди. \en Get weight of element in the queue. - inline Weight GetWeight( size_t i ) const { return elements[i].weight; } - /// \ru Получить индекс элемента очереди. \en Get index of element in the queue. - inline Index GetIndex( size_t i ) const { return elements[i].index; } - /// \ru Получить вес верхнего элемента очереди. \en Get weight of the top element in the queue. - inline Weight GetTopWeight() const { return elements[0].weight; } - /// \ru Вставить элемент с заданным индексом и весом в очередь. \en Insert element with given index and weight in the queue. - inline void Insert( Index index, Weight weight ); - /** \} */ - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. - void operator = ( const PriorityQueue & ); -}; - - -//----------------------------------------------------------------------------- -/** \brief \ru К-d дерево. - \en K-d tree. \~ - \details \ru К-мерное бинарное дерево. \n - \en K-d binary tree. \n \~ - \ingroup Base_Tools -*/ -// --- -template -class KdTree { -public: - struct Node { ///< \ru Структура узла дерева. \en Structure of node of the tree. - union { - struct { ///< \ru Узел дерева. \en Node of tree. - Scalar splitValue; ///< \ru Разделяющее значение. \en Split value. - size_t firstChildId : 24; ///< \ru Индекс первого потомка. \en Index of first child. - size_t dim : 2; ///< \ru Измерение, вдоль которого происходит сравнение [0 = x, 1 = y, 2 = z]. \en The dimension along which node is splitted [0 = x, 1 = y, 2 = z]. - size_t type : 1; ///< \ru Тип узла дерева (0 - узел, 1 - лист). \en Type of node (0 - node, 1 - leaf). - }; - struct { ///< \ru Лист дерева. \en Leaf of tree. - unsigned int start; ///< \ru Индекс первого элемента листа. \en Index of first element of leaf. - unsigned short size; ///< \ru Количество элементов листа. \en Number of elements in the leaf. - }; - }; - }; - - typedef std::vector NodeList; - typedef std::vector PointList; - typedef std::vector IndexList; - typedef PriorityQueue ScalarPriorityQueue; - -protected: - MbCube box; ///< \ru Ограничивающий куб. \en Bounding box. - NodeList nodes; ///< \ru Узлы дерева. \en Nodes of tree. - PointList points; ///< \ru Множество точек. \en Set of points. - IndexList indices; ///< \ru Индексы точек. \en Indices of points. - size_t targetCellSize; ///< \ru Минимальное количество точек в листе дерева. \en Minimal number of point in a tree leaf. - size_t targetMaxDepth; ///< \ru Максимальная глубина дерева. \en Maximal tree depth. - size_t numLevel; ///< \ru Глубина дерева. \en Tree depth. - bool isBalanced; ///< \ru Cбалансированное дерево или нет. \en Three is balanced or unbalanced. - - struct QueryNode { ///< \ru Структура узла для запроса. \en Structure of query node. - QueryNode() {} - QueryNode( size_t id ) : nodeId( id ) {} - size_t nodeId; ///< \ru Id следующего узла. \en Id of next node. - Scalar sq; ///< \ru Квадрат расстояния до следующего узла. \en Squared distance to next node. - }; - -public: - /** - \brief \ru Конструктор. - \en Constructor. \~ - \details \ru Конструктор k-мерного дерева. - \en Constructor of k-d tree. \~ - \param[in] points - \ru Множество точек. - \en Set of points. \~ - \param[in] minLeafSize - \ru Минимальное количество точек в листе дерева (по умолчанию 16). - \en Minimal number of point in a tree leaf (16 by default). \~ - \param[in] maxDepth - \ru Максимальная глубина дерева (по умолчанию 64). - \en Maximal tree depth (64 by default). \~ - \param[in] balanced - \ru Создать сбалансированное дерево или нет (по умолчанию несбалансированное). - \en Create three balanced or unbalanced (unbalanced by default). \~ - */ - KdTree( const PointList & points, size_t minLeafSize = 16, size_t maxDepth = 64, bool balanced = false ); - /// \ru Деструктор. \en Destructor. - ~KdTree(); - -protected: - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. - KdTree( const KdTree & ); - -public: - /** \ru \name Функции очереди с приоритетом. - \en \name Functions of priority queue. - \{ */ - /** - \brief \ru Найти k ближайших соседей для данной точки. - \en Performs the k-nearest neighbors (kNN) query. \~ - \details \ru Найти k ближайших соседей для данной точки. - \en Performs the k-nearest neighbors (kNN) query. \~ - \param[in] queryPoint - \ru Точка, для которой ищутся соседи. - \en The point for which the neighbors are being searched for . \~ - \param[in] neighborCount - \ru Запрашиваемое число соседей . - \en Number of neighbors requested. \~ - \param[in] neighborQueue - \ru Очередь с результатами поиска, в котором верхний элемент является наиболее удаленным от заданной точки - \en Queue with k-nearest neighbors (kNN) query results, where the topmost element [0] is NOT the nearest but the farthest \~ - */ - void GetKNearestNeighbors( const MbCartPoint3D & queryPoint, size_t neighborCount, ScalarPriorityQueue & neighborQueue ); - /// \ru Получить узлы дерева. \en Get tree nodes. - inline const NodeList & GetNodes() { return nodes; } - /// \ru Получить множество точек. \en Get points set. - inline const PointList & GetPoints() { return points; } - /// \ru Получить глубину дерева. \en Get depth of tree. - inline size_t GetNumLevel() { return numLevel; } - /// \ru Получить ограничивающий куб. \en Get axis aligned bounding box. - inline const MbCube & GetAxisAlignedBox() { return box; } - /** \} */ - -private: - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. - void operator = ( const KdTree & ); - // Используется для построения дерева: разделить множество subset [start..end] согласно dim и splitValue, - // и вернуть индекс первого элемента второго множества. - size_t split( size_t start, size_t end, size_t dim, double splitValue ); - // Построение дерева. - size_t createTree( size_t nodeId, size_t start, size_t end, size_t level ); - -}; - - -//////////////////////////////////////////////////////////////////////////////// -// -// \ru PriorityQueue - очередь с приоритетом. \en PriorityQueue - priority queue. -// -//////////////////////////////////////////////////////////////////////////////// - -//------------------------------------------------------------------------------- -// \ru Конструктор очереди с приоритетом. \en Constructor of priority queue. -// --- -template -PriorityQueue::PriorityQueue() - : elements( 0 ) - , maxSize ( 0 ) -{ -} - - -//------------------------------------------------------------------------------- -// \ru Деструктор очереди с приоритетом. \en Destructor of priority queue. -// --- -template -PriorityQueue::~PriorityQueue() -{ - if ( elements ) - delete[] elements; -} - - -//------------------------------------------------------------------------------- -// \ru Инициализировать очередь максимальным количеством элементов в очереди. \en Initialize the queue by number of elements. -// --- -template -inline bool PriorityQueue::Initialize( size_t _maxSize ) -{ - if ( maxSize != _maxSize ) { - maxSize = _maxSize; - delete[] elements; - try { - elements = new Element[maxSize]; - } - catch ( ... ) { +#include +#include + + +//----------------------------------------------------------------------------- +/** \brief \ru Очередь с приоритетом с использованием кучи. + \en Priority queue using a heap. \~ + \details \ru Очередь с приоритетом с использованием кучи. Размер очереди фиксирован. + Производительность этой реализации выше чем у std::priority_queue. \n + \en Priority queue using a heap. Size of queue is fixed. + This implementation perfomance is better in comparsion with std::priority_queue. \n \~ + \ingroup Base_Tools +*/ +// --- +template +class PriorityQueue +{ +protected: + struct Element ///< \ru Элемент очереди. \en Element of queue. + { + Weight weight; ///< \ru Вес элемента. \en Weight of element. + Index index; ///< \ru Индекс элемента. \en Index of element. + }; + Element * elements; ///< \ru Элементы очереди. \en Elements of queue. + Element * offsetedElements; ///< \ru Смещенные элементы очереди. \en Shifted elements of queue. + size_t count; ///< \ru Актуальное число элементов в очереди. \en Actual count of elements in the queue. + size_t maxSize; ///< \ru Максимальное число элементов в очереди. \en Maximal count of elements in the queue. + +protected: + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. + PriorityQueue( const PriorityQueue & ); + +public: + /// \ru Конструктор. \en Constructor. + PriorityQueue( ); + /// \ru Деструктор. \en Destructor. + ~PriorityQueue(); + +public: + /** \ru \name Функции очереди с приоритетом. + \en \name Functions of priority queue. + \{ */ + /// \ru Инициализировать очередь максимальным количеством элементов в очереди. \en Initialize the queue by number of elements. + inline bool Initialize( size_t _maxSize ); + /// \ru Получить количество элементов в очереди. \en Get elements count in the queue. + inline size_t ElementsCount() const { return count; } + /// \ru Получить вес элемента очереди. \en Get weight of element in the queue. + inline Weight GetWeight( size_t i ) const { if ( i < count ) return elements[i].weight; return Weight(); } + /// \ru Получить индекс элемента очереди. \en Get index of element in the queue. + inline Index GetIndex( size_t i ) const { if ( i < count ) return elements[i].index; return Index(); } + /// \ru Получить вес верхнего элемента очереди. \en Get weight of the top element in the queue. + inline Weight GetTopWeight() const { if ( count > 0 ) return elements[0].weight; return Weight(); } + /// \ru Вставить элемент с заданным индексом и весом в очередь. \en Insert element with given index and weight in the queue. + inline void Insert( Index index, Weight weight ); + /** \} */ + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. + void operator = ( const PriorityQueue & ); +}; + + +//----------------------------------------------------------------------------- +/** \brief \ru К-d дерево. + \en K-d tree. \~ + \details \ru К-мерное бинарное дерево. \n + \en K-d binary tree. \n \~ + \ingroup Base_Tools +*/ +// --- +template +class KdTree { +public: + struct Node { ///< \ru Структура узла дерева. \en Structure of node of the tree. + union { + struct { ///< \ru Узел дерева. \en Node of tree. + Scalar splitValue; ///< \ru Разделяющее значение. \en Split value. + size_t firstChildId : 24; ///< \ru Индекс первого потомка. \en Index of first child. + size_t dim : 2; ///< \ru Измерение, вдоль которого происходит сравнение [0 = x, 1 = y, 2 = z]. \en The dimension along which node is splitted [0 = x, 1 = y, 2 = z]. + size_t type : 1; ///< \ru Тип узла дерева (0 - узел, 1 - лист). \en Type of node (0 - node, 1 - leaf). + }; + struct { ///< \ru Лист дерева. \en Leaf of tree. + unsigned int start; ///< \ru Индекс первого элемента листа. \en Index of first element of leaf. + unsigned short size; ///< \ru Количество элементов листа. \en Number of elements in the leaf. + }; + }; + }; + + typedef std::vector NodeList; + typedef std::vector PointList; + typedef std::vector IndexList; + typedef PriorityQueue ScalarPriorityQueue; + +protected: + MbCube box; ///< \ru Ограничивающий куб. \en Bounding box. + NodeList nodes; ///< \ru Узлы дерева. \en Nodes of tree. + PointList points; ///< \ru Множество точек. \en Set of points. + IndexList indices; ///< \ru Индексы точек. \en Indices of points. + size_t targetCellSize; ///< \ru Минимальное количество точек в листе дерева. \en Minimal number of point in a tree leaf. + size_t targetMaxDepth; ///< \ru Максимальная глубина дерева. \en Maximal tree depth. + size_t numLevel; ///< \ru Глубина дерева. \en Tree depth. + bool isBalanced; ///< \ru Cбалансированное дерево или нет. \en Three is balanced or unbalanced. + + struct QueryNode { ///< \ru Структура узла для запроса. \en Structure of query node. + QueryNode() {} + QueryNode( size_t id ) : nodeId( id ) {} + size_t nodeId; ///< \ru Id следующего узла. \en Id of next node. + Scalar sq; ///< \ru Квадрат расстояния до следующего узла. \en Squared distance to next node. + }; + +public: + /** + \brief \ru Конструктор. + \en Constructor. \~ + \details \ru Конструктор k-мерного дерева. + \en Constructor of k-d tree. \~ + \param[in] points - \ru Множество точек. + \en Set of points. \~ + \param[in] minLeafSize - \ru Минимальное количество точек в листе дерева (по умолчанию 16). + \en Minimal number of point in a tree leaf (16 by default). \~ + \param[in] maxDepth - \ru Максимальная глубина дерева (по умолчанию 64). + \en Maximal tree depth (64 by default). \~ + \param[in] balanced - \ru Создать сбалансированное дерево или нет (по умолчанию несбалансированное). + \en Create three balanced or unbalanced (unbalanced by default). \~ + */ + KdTree( const PointList & points, size_t minLeafSize = 16, size_t maxDepth = 64, bool balanced = false ); + /// \ru Деструктор. \en Destructor. + ~KdTree(); + +protected: + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. + KdTree( const KdTree & ); + +public: + /** \ru \name Функции очереди с приоритетом. + \en \name Functions of priority queue. + \{ */ + /** + \brief \ru Найти k ближайших соседей для данной точки. + \en Performs the k-nearest neighbors (kNN) query. \~ + \details \ru Найти k ближайших соседей для данной точки. + \en Performs the k-nearest neighbors (kNN) query. \~ + \param[in] queryPoint - \ru Точка, для которой ищутся соседи. + \en The point for which the neighbors are being searched for . \~ + \param[in] neighborCount - \ru Запрашиваемое число соседей . + \en Number of neighbors requested. \~ + \param[in] neighborQueue - \ru Очередь с результатами поиска, в котором верхний элемент является наиболее удаленным от заданной точки + \en Queue with k-nearest neighbors (kNN) query results, where the topmost element [0] is NOT the nearest but the farthest \~ + */ + void GetKNearestNeighbors( const MbCartPoint3D & queryPoint, size_t neighborCount, ScalarPriorityQueue & neighborQueue ); + /// \ru Получить узлы дерева. \en Get tree nodes. + inline const NodeList & GetNodes() { return nodes; } + /// \ru Получить множество точек. \en Get points set. + inline const PointList & GetPoints() { return points; } + /// \ru Получить глубину дерева. \en Get depth of tree. + inline size_t GetNumLevel() { return numLevel; } + /// \ru Получить ограничивающий куб. \en Get axis aligned bounding box. + inline const MbCube & GetAxisAlignedBox() { return box; } + /** \} */ + +private: + // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation, to prevent an assignment by default. + void operator = ( const KdTree & ); + // Используется для построения дерева: разделить множество subset [start..end] согласно dim и splitValue, + // и вернуть индекс первого элемента второго множества. + size_t split( size_t start, size_t end, size_t dim, double splitValue ); + // Построение дерева. + size_t createTree( size_t nodeId, size_t start, size_t end, size_t level ); + +}; + + +//////////////////////////////////////////////////////////////////////////////// +// +// \ru PriorityQueue - очередь с приоритетом. \en PriorityQueue - priority queue. +// +//////////////////////////////////////////////////////////////////////////////// + +//------------------------------------------------------------------------------- +// \ru Конструктор очереди с приоритетом. \en Constructor of priority queue. +// --- +template +PriorityQueue::PriorityQueue() + : elements( 0 ) + , maxSize ( 0 ) +{ +} + + +//------------------------------------------------------------------------------- +// \ru Деструктор очереди с приоритетом. \en Destructor of priority queue. +// --- +template +PriorityQueue::~PriorityQueue() +{ + if ( elements ) + delete[] elements; +} + + +//------------------------------------------------------------------------------- +// \ru Инициализировать очередь максимальным количеством элементов в очереди. \en Initialize the queue by number of elements. +// --- +template +inline bool PriorityQueue::Initialize( size_t _maxSize ) +{ + if ( maxSize != _maxSize ) { + maxSize = _maxSize; + delete[] elements; + try { + elements = new Element[maxSize]; + } + catch ( ... ) { elements = NULL; - maxSize = count = 0; + maxSize = count = 0; C3D_CONTROLED_THROW; return false; - } - offsetedElements = ( elements - 1 ); - } - count = 0; - return true; -} - - -//------------------------------------------------------------------------------- -// \ru Вставить элемент с заданным индексом и весом в очередь. \en Insert element with given index and weight in the queue. -// --- -template -inline void PriorityQueue::Insert( Index index, Weight weight ) -{ - if ( count == maxSize ) { - if ( weight < elements[0].weight ) { - size_t j, k; - j = 1; - k = 2; - while ( k <= maxSize ) { - Element* z = &( offsetedElements[k] ); - if ( (k < maxSize) && (z->weight < offsetedElements[k+1].weight) ) - z = &( offsetedElements[++k] ); - - if( weight >= z->weight ) - break; - offsetedElements[j] = *z; - j = k; - k = 2 * j; - } - offsetedElements[j].weight = weight; - offsetedElements[j].index = index; - } - } - else { - size_t i, j; - i = ++count; - while ( i >= 2 ) { - j = i >> 1; - Element& y = offsetedElements[j]; - if( weight <= y.weight ) - break; - offsetedElements[i] = y; - i = j; - } - offsetedElements[i].index = index; - offsetedElements[i].weight = weight; - } -} - - -//////////////////////////////////////////////////////////////////////////////// -// -// \ru К-d дерево. \en PriorityQueue - priority queue. -// -//////////////////////////////////////////////////////////////////////////////// - -//------------------------------------------------------------------------------- -// \ru Конструктор дерева. \en K-d tree. -// --- -template -KdTree::KdTree( const PointList & _points, size_t nofPointsPerCell, size_t maxDepth, bool balanced ) - : points ( _points.size() ) - , indices( _points.size() ) -{ - points[0] = _points[0]; - box.SetEmpty(); - for( size_t i = 1; i < points.size(); ++i ) { - points[i] = _points[i]; - indices[i] = i; - box |= ( points[i] ); - } - - targetMaxDepth = maxDepth; - targetCellSize = nofPointsPerCell; - isBalanced = balanced; - - // Добавление первого узла. Остальные добавляются при вызове createTree (рекурсия). - nodes.resize( 1 ); - nodes.back().type = 0; - numLevel = createTree( 0, 0, points.size(), 1 ); -} - - -//------------------------------------------------------------------------------- -// \ru Деструктор дерева. \en Destructor of tree. -// --- -template -KdTree::~KdTree() -{ -} - - -//------------------------------------------------------------------------------- -// \ru Найти k ближайших соседей для данной точки. Результат операции хранится -// в виде стека neighborQueue, в котором верхний элемент является наиболее -// удаленным от заданной точки. -// (его содержимое не сортировано, но упорядоченно расположено в куче) -// \en Performs the k-nearest neighbors (kNN) query. The result of the query, -// the k-nearest neighbors, are stored into the stack neighborQueue, where the -// topmost element [0] is NOT the nearest but the farthest! -// (they are not sorted but arranged into a heap) -// --- -template -void KdTree::GetKNearestNeighbors( const MbCartPoint3D & queryPoint, size_t k, ScalarPriorityQueue & neighborQueue ) -{ - if ( !neighborQueue.Initialize( k ) ) - return; - - std::vector nodeStack( numLevel + 1 ); - nodeStack[0].nodeId = 0; - nodeStack[0].sq = 0.0; - size_t count = 1; - - while( count ) { - QueryNode & qnode = nodeStack[count - 1]; // Последний вставленный в стек узел. - - // При проходе вниз по дереву qnode.nodeId является ближайшим поддеревом, т.е. - // qnode.nodeId является другим поддеревом, которое будет обрабатываться, - // если фактический ближайший узел будет больше, чем делящее расстояние (split distance). - Node & node = nodes[qnode.nodeId]; // - - // Если расстояние меньше чем верхний элемент neighborQueue, то это может быть один из k ближайших соседей. - if( neighborQueue.ElementsCount() < k || neighborQueue.GetTopWeight() ) { - if( node.type ) { // Достигли листа дерева. - --count; - - // Индекс последнего элемента листа в points. - size_t end = node.start + node.size; - // Добавление элемента листа в очередь. - for( size_t i = node.start; i < end; ++i ) - neighborQueue.Insert( indices[i], ( queryPoint - points[i] )*( queryPoint - points[i] ) ); - } - else { // Не лист дерева. - // Расстояние между найденной точкой и фактической координатой деления. - double new_off = queryPoint[node.dim] - node.splitValue; - - // Левое поддерево. - if( new_off < 0. ) { - nodeStack[count].nodeId = node.firstChildId; - // В родительском nodeId хранится индекс другого поддерева (для прохода в обратном направлении). - qnode.nodeId = node.firstChildId + 1; - } - // Правое поддерево. - else { - nodeStack[count].nodeId = node.firstChildId + 1; - qnode.nodeId = node.firstChildId; - } - // Расстояние наследуется от родителя(при спуске по дереву оно равно 0). - nodeStack[count].sq = qnode.sq; - // Расстояние от родителя - это квадрат расстояния от плоскости деления. - qnode.sq = new_off * new_off; - ++count; - } - } - else { - --count; - } - } -} - - -//------------------------------------------------------------------------------- -// \ru Разделить часть массива между индексами start и end на две части, одна из которых меньше -// чем splitValue, другое с элементами больше или равными чем splitValue. Сравнение элементов -// производится с помощью координаты dim [0 = x, 1 = y, 2 = z]. -// \en Split the array part between start and end in two part, one with the elements less than splitValue, -// the other with the elements greater or equal than splitValue. The elements are compared -// using the "dim" coordinate [0 = x, 1 = y, 2 = z]. -// --- -template -size_t KdTree::split( size_t start, size_t end, size_t dim, double splitValue ) -{ - size_t l( start ), r( end - 1 ); - for( ; l < r; ++l, --r ) { - while( l < end && points[l][dim] < splitValue ) - l++; - while( r >= start && points[r][dim] >= splitValue ) - r--; - if( l > r ) - break; - std::swap( points[l], points[r] ); - std::swap( indices[l], indices[r] ); - } - return ( points[l][dim] < splitValue ? l + 1 : l ); // Вернуть индекс первого элемента во второй части. -} - - -//------------------------------------------------------------------------------- -// \ru Построить k-мерное дерево (рекурсивно). Если количество точек узла меньше чем -// targetCellsize, то делаем этот узел листом, иначе рассчитываем ограничивающий -// куб для точек узла и делим его по среднему значению наибольшего габарита куба. -// \en Build the kdtree recursively. If the number of points in the node is lower than -// targetCellsize then mark this node as leaf, else compute the bounding box of the points -// of the node and split it at the middle of the largest bounding box dimension. -// --- -template -size_t KdTree::createTree( size_t nodeId, size_t start, size_t end, size_t level ) -{ - Node & node = nodes[nodeId]; // Первый узел - MbCube cube; - - for( size_t i = start + 1; i < end; ++i ) - cube |= points[i] ; - - - MbVector3D diag = cube.pmax - cube.pmin; // Диагональ габаритного куба. - - size_t dim; - if( diag.x > diag.y ) - dim = diag.x > diag.z ? 0 : 2; - else - dim = diag.y > diag.z ? 1 : 2; - - node.dim = dim; - if( isBalanced ) // Разделить точки используя среднее значение вдоль направления dim. - { - std::vector tempVector; - for( size_t i = start + 1; i < end; ++i ) - tempVector.push_back( (points[i])[dim] ); - std::sort( tempVector.begin(), tempVector.end() ); - node.splitValue = ( tempVector[int(tempVector.size() / 2.0)] + tempVector[int(tempVector.size() / 2.0) + 1] ) / 2.0; - } - else // Разделить ограничивающий куб на две части на основе среднего значение вдоль направления dim. - node.splitValue = Scalar( 0.5*( cube.pmax[dim] + cube.pmin[dim] ) ); - - size_t midId = split( start, end, dim, node.splitValue ); // Индекс первого элемента во второй части. - - node.firstChildId = nodes.size(); - nodes.resize( nodes.size() + 2 ); - bool flag = ( midId == start ) || ( midId == end ); - size_t leftLevel, rightLevel; - { - // Левый потомок. - size_t childId = nodes[nodeId].firstChildId; - Node & child = nodes[childId]; - if( flag || ( midId - start ) <= targetCellSize || level >= targetMaxDepth ) { - child.type = 1; - child.start = (unsigned int)start; - child.size = (unsigned short)(midId - start); - leftLevel = level; - } - else { - child.type = 0; - leftLevel = createTree( childId, start, midId, level + 1 ); - } - } - - { - // Правый потомок. - size_t childId = nodes[nodeId].firstChildId + 1; - Node & child = nodes[childId]; - if( flag || ( end - midId ) <= targetCellSize || level >= targetMaxDepth ) { - child.type = 1; - child.start = (unsigned int)midId; - child.size = (unsigned short)(end - midId); - rightLevel = level; - } - else { - child.type = 0; - rightLevel = createTree( childId, midId, end, level + 1 ); - } - } - if( leftLevel > rightLevel ) - return leftLevel; - return rightLevel; -} - - + } + offsetedElements = ( elements - 1 ); + } + count = 0; + return true; +} + + +//------------------------------------------------------------------------------- +// \ru Вставить элемент с заданным индексом и весом в очередь. \en Insert element with given index and weight in the queue. +// --- +template +inline void PriorityQueue::Insert( Index index, Weight weight ) +{ + if ( count == maxSize ) { + if ( weight < elements[0].weight ) { + size_t j, k; + j = 1; + k = 2; + while ( k <= maxSize ) { + Element* z = &( offsetedElements[k] ); + if ( (k < maxSize) && (z->weight < offsetedElements[k+1].weight) ) + z = &( offsetedElements[++k] ); + + if( weight >= z->weight ) + break; + offsetedElements[j] = *z; + j = k; + k = 2 * j; + } + offsetedElements[j].weight = weight; + offsetedElements[j].index = index; + } + } + else { + size_t i, j; + i = ++count; + while ( i >= 2 ) { + j = i >> 1; + Element& y = offsetedElements[j]; + if( weight <= y.weight ) + break; + offsetedElements[i] = y; + i = j; + } + offsetedElements[i].index = index; + offsetedElements[i].weight = weight; + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// \ru К-d дерево. \en PriorityQueue - priority queue. +// +//////////////////////////////////////////////////////////////////////////////// + +//------------------------------------------------------------------------------- +// \ru Конструктор дерева. \en K-d tree. +// --- +template +KdTree::KdTree( const PointList & _points, size_t nofPointsPerCell, size_t maxDepth, bool balanced ) + : points ( _points.size() ) + , indices( _points.size() ) +{ + points[0] = _points[0]; + box.SetEmpty(); + for( size_t i = 1; i < points.size(); ++i ) { + points[i] = _points[i]; + indices[i] = i; + box |= ( points[i] ); + } + + targetMaxDepth = maxDepth; + targetCellSize = nofPointsPerCell; + isBalanced = balanced; + + // Добавление первого узла. Остальные добавляются при вызове createTree (рекурсия). + nodes.resize( 1 ); + nodes.back().type = 0; + numLevel = createTree( 0, 0, points.size(), 1 ); +} + + +//------------------------------------------------------------------------------- +// \ru Деструктор дерева. \en Destructor of tree. +// --- +template +KdTree::~KdTree() +{ +} + + +//------------------------------------------------------------------------------- +// \ru Найти k ближайших соседей для данной точки. Результат операции хранится +// в виде стека neighborQueue, в котором верхний элемент является наиболее +// удаленным от заданной точки. +// (его содержимое не сортировано, но упорядоченно расположено в куче) +// \en Performs the k-nearest neighbors (kNN) query. The result of the query, +// the k-nearest neighbors, are stored into the stack neighborQueue, where the +// topmost element [0] is NOT the nearest but the farthest! +// (they are not sorted but arranged into a heap) +// --- +template +void KdTree::GetKNearestNeighbors( const MbCartPoint3D & queryPoint, size_t k, ScalarPriorityQueue & neighborQueue ) +{ + if ( !neighborQueue.Initialize( k ) ) + return; + + std::vector nodeStack( numLevel + 1 ); + nodeStack[0].nodeId = 0; + nodeStack[0].sq = 0.0; + size_t count = 1; + + while( count ) { + QueryNode & qnode = nodeStack[count - 1]; // Последний вставленный в стек узел. + + // При проходе вниз по дереву qnode.nodeId является ближайшим поддеревом, т.е. + // qnode.nodeId является другим поддеревом, которое будет обрабатываться, + // если фактический ближайший узел будет больше, чем делящее расстояние (split distance). + Node & node = nodes[qnode.nodeId]; // + + // Если расстояние меньше чем верхний элемент neighborQueue, то это может быть один из k ближайших соседей. + if( neighborQueue.ElementsCount() < k || neighborQueue.GetTopWeight() ) { + if( node.type ) { // Достигли листа дерева. + --count; + + // Индекс последнего элемента листа в points. + size_t end = node.start + node.size; + // Добавление элемента листа в очередь. + for( size_t i = node.start; i < end; ++i ) + neighborQueue.Insert( indices[i], ( queryPoint - points[i] )*( queryPoint - points[i] ) ); + } + else { // Не лист дерева. + // Расстояние между найденной точкой и фактической координатой деления. + double new_off = queryPoint[node.dim] - node.splitValue; + + // Левое поддерево. + if( new_off < 0. ) { + nodeStack[count].nodeId = node.firstChildId; + // В родительском nodeId хранится индекс другого поддерева (для прохода в обратном направлении). + qnode.nodeId = node.firstChildId + 1; + } + // Правое поддерево. + else { + nodeStack[count].nodeId = node.firstChildId + 1; + qnode.nodeId = node.firstChildId; + } + // Расстояние наследуется от родителя(при спуске по дереву оно равно 0). + nodeStack[count].sq = qnode.sq; + // Расстояние от родителя - это квадрат расстояния от плоскости деления. + qnode.sq = new_off * new_off; + ++count; + } + } + else { + --count; + } + } +} + + +//------------------------------------------------------------------------------- +// \ru Разделить часть массива между индексами start и end на две части, одна из которых меньше +// чем splitValue, другое с элементами больше или равными чем splitValue. Сравнение элементов +// производится с помощью координаты dim [0 = x, 1 = y, 2 = z]. +// \en Split the array part between start and end in two part, one with the elements less than splitValue, +// the other with the elements greater or equal than splitValue. The elements are compared +// using the "dim" coordinate [0 = x, 1 = y, 2 = z]. +// --- +template +size_t KdTree::split( size_t start, size_t end, size_t dim, double splitValue ) +{ + size_t l( start ), r( end - 1 ); + for( ; l < r; ++l, --r ) { + while( l < end && points[l][dim] < splitValue ) + l++; + while( r >= start && points[r][dim] >= splitValue ) + r--; + if( l > r ) + break; + std::swap( points[l], points[r] ); + std::swap( indices[l], indices[r] ); + } + return ( points[l][dim] < splitValue ? l + 1 : l ); // Вернуть индекс первого элемента во второй части. +} + + +//------------------------------------------------------------------------------- +// \ru Построить k-мерное дерево (рекурсивно). Если количество точек узла меньше чем +// targetCellsize, то делаем этот узел листом, иначе рассчитываем ограничивающий +// куб для точек узла и делим его по среднему значению наибольшего габарита куба. +// \en Build the kdtree recursively. If the number of points in the node is lower than +// targetCellsize then mark this node as leaf, else compute the bounding box of the points +// of the node and split it at the middle of the largest bounding box dimension. +// --- +template +size_t KdTree::createTree( size_t nodeId, size_t start, size_t end, size_t level ) +{ + Node & node = nodes[nodeId]; // Первый узел + MbCube cube; + + for( size_t i = start + 1; i < end; ++i ) + cube |= points[i] ; + + + MbVector3D diag = cube.pmax - cube.pmin; // Диагональ габаритного куба. + + size_t dim; + if( diag.x > diag.y ) + dim = diag.x > diag.z ? 0 : 2; + else + dim = diag.y > diag.z ? 1 : 2; + + node.dim = dim; + if( isBalanced ) // Разделить точки используя среднее значение вдоль направления dim. + { + std::vector tempVector; + for( size_t i = start + 1; i < end; ++i ) + tempVector.push_back( (points[i])[dim] ); + std::sort( tempVector.begin(), tempVector.end() ); + node.splitValue = ( tempVector[int(tempVector.size() / 2.0)] + tempVector[int(tempVector.size() / 2.0) + 1] ) / 2.0; + } + else // Разделить ограничивающий куб на две части на основе среднего значение вдоль направления dim. + node.splitValue = Scalar( 0.5*( cube.pmax[dim] + cube.pmin[dim] ) ); + + size_t midId = split( start, end, dim, node.splitValue ); // Индекс первого элемента во второй части. + + node.firstChildId = nodes.size(); + nodes.resize( nodes.size() + 2 ); + bool flag = ( midId == start ) || ( midId == end ); + size_t leftLevel, rightLevel; + { + // Левый потомок. + size_t childId = nodes[nodeId].firstChildId; + Node & child = nodes[childId]; + if( flag || ( midId - start ) <= targetCellSize || level >= targetMaxDepth ) { + child.type = 1; + child.start = (unsigned int)start; + child.size = (unsigned short)(midId - start); + leftLevel = level; + } + else { + child.type = 0; + leftLevel = createTree( childId, start, midId, level + 1 ); + } + } + + { + // Правый потомок. + size_t childId = nodes[nodeId].firstChildId + 1; + Node & child = nodes[childId]; + if( flag || ( end - midId ) <= targetCellSize || level >= targetMaxDepth ) { + child.type = 1; + child.start = (unsigned int)midId; + child.size = (unsigned short)(end - midId); + rightLevel = level; + } + else { + child.type = 0; + rightLevel = createTree( childId, midId, end, level + 1 ); + } + } + if( leftLevel > rightLevel ) + return leftLevel; + return rightLevel; +} + + #endif //__MB_KDTREE_H \ No newline at end of file diff --git a/C3d/Include/templ_lis_array.h b/C3d/Include/templ_lis_array.h index 0473359..810a74b 100644 --- a/C3d/Include/templ_lis_array.h +++ b/C3d/Include/templ_lis_array.h @@ -13,10 +13,6 @@ #include #include -#ifdef __DEBUG_MEMORY_ALLOCATE_FREE_ // \ru после sys_defs.h !!! \en after sys_defs.h !!! -#include -#endif // __DEBUG_MEMORY_ALLOCATE_FREE_ - FORVARD_DECL_TEMPLATE_TYPENAME( class LiSArray ); FORVARD_DECL_TEMPLATE_TYPENAME( void set_array_size( LiSArray &, size_t newSize ) ); @@ -249,7 +245,7 @@ void set_array_size( LiSArray & arr, size_t newSize ) // \ru !!! здесь count не изменяется!!! \en !!! 'count' is not changed!! #ifdef __REALLOC_ARRAYS_STATISTIC_ - ::ReallocArrayStatistic( oldParr, oldUpper * sizeof(Type), arr.parr, newUpper * sizeof(Type), 3/*LiSArray*/ ); + REALLOC_ARRAY_STATISTICS( oldParr, oldUpper * sizeof(Type), arr.parr, newUpper * sizeof(Type), 3/*LiSArray*/ ); #endif // __REALLOC_ARRAYS_STATISTIC_ } } diff --git a/C3d/Include/templ_multimap.h b/C3d/Include/templ_multimap.h index 4dfad24..0bdf1cd 100644 --- a/C3d/Include/templ_multimap.h +++ b/C3d/Include/templ_multimap.h @@ -1,699 +1,699 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Мультимножество, реализующее основной функционал std::multimap. - \en Multiset implementing the core functionality of std::multimap. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __TEMPL_MULTIMAP_H -#define __TEMPL_MULTIMAP_H - - -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Ассоциативное множество c дубликатами (мультимножество). - \en Associative set with duplicates (multiset). \~ - \details \ru Ассоциативное множество c дубликатами (мультимножество). \n - Реализует основные функциии std::multimap. Требования к типам данных KeyType и ValType такие же, как в SArray. - Мультимножество задает соответствие (ассоциации) объекта-ключа подмножеству объектов-значений. - Для некоторого объекта типа KeyType задается соответствие подмножеству объектов ValType. - \en Associative set with duplicates (multiset). \n - Implements the core functions of is std::multimap. Requirements to data types KeyType and ValType are the same as in SArray. - Multiset sets the mapping (associations) between a key-object and a subset of value-objects. - For some object of the "KeyType" type there is set a mapping to a subset of objects of the "ValType" type. \~ - \ingroup Base_Tools_Containers -*/ -// --- -template -class MultiMap { -public: - - struct Pair - { - const KeyType m_key; - const ValType m_val; - Pair( const KeyType & key, const ValType & val ) : m_key( key ), m_val( val ) {} - bool operator < ( const Pair & p ) const { - return m_key < p.m_key; - } - bool operator == ( const Pair & p ) const { - return m_key == p.m_key && m_val == p.m_val; - } - Pair & operator = ( const Pair & ); // \ru Не реализовано. \en Not implemented. - }; - -public: - MultiMap(); - virtual ~MultiMap() {} - - class Iterator; - -public: - /// \ru Оператор доступа по ключу. \en Access by key operator. - Iterator operator[] ( const KeyType & key ) const; - /// \ru Получить итератор, указывающий на первый элемент. \en Get an iterator pointing to the first element. - Iterator First() const; - /// \ru Добавить элемент с заданным ключом и значением. \en Add an element with specified key and value. - void Associate( const KeyType & key, const ValType & val ); - /// \ru Удалить заданное значение с заданным ключом. \en Remove an element with specified key and value. - void Dissociate( const KeyType & key, const ValType & val ); - /// \ru Удалить элементы в диапазоне [it1, it2). \en Remove elements in the range [it1, it2). - void Dissociate( Iterator & it1, Iterator & it2 ); - /// \ru Удалить элемент, указанный итератором. \en Remove an element specified by an iterator. - Iterator Dissociate( Iterator & it ); - /// \ru Удалить все элементы из контейнера. \en Removes all elements from the container. - void Flush() { m_Pairs.Flush(); } - /// \ru Существует ли элемент с заданными ключом и значением. \en Is there an element with the specified key and value. - bool IsAssociated( const KeyType & key, const ValType & val ) const; - /// \ru Найти элемент с заданными ключом и значением. \en Find an element with the specified key and value. - Iterator Find( const KeyType & key ) const; - // \ru Итератор, указывающий на первый элемент со значением ключа, не меньшим, чем заданный. \en An iterator pointing to the first element not less than the given key. - Iterator LowerBound( const KeyType & key ) const; - // \ru Итератор, указывающий на первый элемент со значением ключа, большим, чем заданный. \en An iterator pointing to the first element greater than the given key. - Iterator UpperBound( const KeyType & key ) const; - // \ru Диапазон, содержащий все элементы с данным ключом в контейнере. \en A range containing all elements with the given key in the container. - std::pair EqualRange( const KeyType & key ) const; - // \ru Количество элементов в контейнере. \en The number of elements in the container. - size_t Count() const { return m_Pairs.Count(); } - - static size_t UpperBoundEx( const SArray & pairs, const KeyType & key ); - static size_t LowerBoundEx( const SArray & pairs, const KeyType & key ); - static std::pair EqualRangeEx( const SArray & pairs, const KeyType & key ); - -private: - SArray m_Pairs; - - private: // \ru Специфицируем нуль для разных типов. \en Specify null for different types. - template - struct Null { // \ru Нуль тривиальных типов (int, float, double и т.д.). \en Null of trivial types (int, float, double etc.). - static inline T val() { return 0; } - }; - template - struct Null { // \ru Нуль указателей. \en Null of pointers. - static inline T* val() { return NULL; } - }; -// \ru LF_Linux: 25.03.11 g++ выдает ошибку на этот код - не использованы KeyType, ValType в полной специализации шаблона. -// Однако непонятно, зачем нужна эта полная специализация - общая частичная специализация для тривиальных типов вполне подойдет. -// \en LF_Linux: 25.03.11 g++ returns an error in this code - types KeyType and ValType are not used in the full specialization of template. -// But it is not clear what for this specialization is required - the common partial specialization for the trivial types is sufficient. -// template<> -// \ru struct Null { // Нуль вещественных чисел \en struct Null { // Null of real numbers -// static inline double val() { return 0.0; } -// }; - -public: - // \ru Итератор по элементам контейнера. \en Iterator for the container elements. - class Iterator { - private: - Pair * m_Ptr; - Pair * m_MaxPtr; - - public: - Iterator() : m_Ptr( NULL ), m_MaxPtr( NULL ) {} - Iterator( const Iterator & iter ) : m_Ptr( iter.m_Ptr ), m_MaxPtr( iter.m_MaxPtr ) {} - Iterator( const SArray & m_Pairs, const Pair & pair ) : m_Ptr( NULL ), m_MaxPtr( NULL ) - { - const size_t count = m_Pairs.Count(); - if ( count > 0 ) { - size_t idx = MultiMap::LowerBoundEx( m_Pairs, pair.m_key ); - size_t temp = idx; - while ( temp < m_Pairs.Count() && m_Pairs[temp].m_key == pair.m_key ) { - if ( m_Pairs[temp] == pair ) { - idx = temp; - break; - } - temp++; - } - m_MaxPtr = &m_Pairs[count-1]; - if ( idx < count ) { - m_Ptr = &m_Pairs[idx]; - } - } - } - Iterator( const SArray & m_Pairs, const Iterator & iter1, const Iterator & iter2 ) // range - : m_Ptr( NULL ), m_MaxPtr( NULL ) - { - const size_t count = m_Pairs.Count(); - if ( count > 0 && iter1.m_Ptr != NULL ) { - size_t idx1 = MultiMap::LowerBoundEx( m_Pairs, iter1.m_Ptr->m_key ); - size_t temp = idx1; - while ( temp < m_Pairs.Count() && m_Pairs[temp].m_key == iter1.m_Ptr->m_key ) { - if ( m_Pairs[temp] == *iter1.m_Ptr ) { - idx1 = temp; - break; - } - temp++; - } - size_t idx2 = SYS_MAX_T; - if ( iter2.m_Ptr != NULL ) { - idx2 = MultiMap::UpperBoundEx( m_Pairs, iter2.m_Ptr->m_key ); - if ( idx2 < m_Pairs.Count() ) { - if ( idx2 > 0) - idx2--; - else - idx2 = SYS_MAX_T; - } - } - if ( idx2 == SYS_MAX_T ) - idx2 = count - 1; - if ( idx1 < count && idx2 < count ) { - m_Ptr = &m_Pairs[idx1]; - m_MaxPtr = &m_Pairs[idx2]; - } - } - } - - public: - /// \ru Получить текущий элемент и сдвинуть итератор на следующий. \en Get the current element and move the iterator to the next. - Iterator operator ++( int ) - { - Iterator iter(*this); - if ( m_Ptr && m_Ptr < m_MaxPtr ) - m_Ptr++; - else - m_Ptr = 0; - return iter; - } - /// \ru Оператор равенства. \en An equality operator. - bool operator == ( const Iterator & itr ) const - { - if ( Empty() && itr.Empty() ) - return true; - if ( ( Empty() && !itr.Empty() ) || ( !Empty() && itr.Empty() ) ) - return false; - return m_Ptr == itr.m_Ptr && m_MaxPtr == itr.m_MaxPtr; - } - /// \ru Оператор "!=". \en An "!=" operator. - bool operator != ( const Iterator & itr ) const - { - if ( Empty() && itr.Empty() ) - return false; - if ( ( Empty() && !itr.Empty() ) || ( !Empty() && itr.Empty() ) ) - return true; - return m_Ptr != itr.m_Ptr || m_MaxPtr != itr.m_MaxPtr; - } - /// \ru Оператор "меньше". \en Operator "less". - bool operator < ( const Iterator& itr ) const - { - return m_Ptr < itr.m_Ptr && m_MaxPtr <= itr.m_MaxPtr; - } - /// \ru Пустой ли итератор. \en Is the iterator empty. - bool Empty() const - { - return !( m_Ptr && m_Ptr <= m_MaxPtr ); - } - // \ru Получить текущий ключ элемента. \en Get the current key of the element. - KeyType Key() const - { - return !Empty() ? m_Ptr->m_key : (KeyType)0; - } - // \ru Получить текущее значение элемента. \en Get the current value of the element. - ValType Value() const - { - return !Empty() ? m_Ptr->m_val : (ValType)0; - } - // \ru Получить пару с текущим ключом и текущим кзначением элемента. \en Get the pair with the current key and the current value of the element. - Pair* GetPair() const - { - return m_Ptr; - } - }; -}; - - -//------------------------------------------------------------------------------ -// -// --- -template -size_t MultiMap::LowerBoundEx( const SArray::Pair> & pairs, const KeyType & key ) -{ - if ( pairs.Count() > 11 ) { - size_t end = pairs.Count() - 1; - size_t last = end; - size_t start = 0; - size_t firstNotLess = SYS_MAX_T; - - while ( start + 1 < end ) { // \ru Ищем, пока не нашли. \en Seek until find. - size_t middle = ( start + end ) / 2; - Pair& mdE = pairs[middle]; - if ( mdE.m_key < key ) { - start = middle; - } - else if ( key <= mdE.m_key ) { - if ( middle < firstNotLess ) - firstNotLess = middle; - end = middle; - } - // \ru Если попадаем сюда, значит некорректно написаны операторы "тождественно" и сравнения. - // \en If we are here, then operators of identity check and comparison are not correct. - else { - PRECONDITION( 0 ); - return SYS_MAX_T; - } - } - - // \ru Проверяем ключ между start и end. \en Check a key between start and end. - if ( start + 1 < pairs.Count() - 1 ) { - size_t middle = start + 1; - Pair& mdE = pairs[middle]; - if ( key <= mdE.m_key && middle < firstNotLess ) - firstNotLess = middle; - } - - if ( key <= pairs[0].m_key ) - return 0; - if ( firstNotLess <= last && firstNotLess >= 0 && key <= pairs[firstNotLess].m_key ) - return firstNotLess; - if ( key == pairs[last].m_key ) - return last; - } - else { - if ( pairs.Count() == 1 ) - return key <= pairs[0].m_key ? 0 : SYS_MAX_T; - else if ( pairs.Count() == 2 ) { - if ( key <= pairs[0].m_key ) - return 0; - if ( key <= pairs[1].m_key ) - return 1; - } - else { // 2 < count <= 11 - for( size_t i = 0; i < pairs.Count(); ++i ) - if ( key <= pairs[i].m_key ) - return i; - } - } - return SYS_MAX_T; -} - -//------------------------------------------------------------------------------ -// \ru Итератор, указывающий на первый элемент со значением ключа, не меньшим, чем заданный. -// \en An iterator pointing to the first element not less than the given key. -// --- -template -inline typename MultiMap::Iterator MultiMap::LowerBound( const KeyType & key ) const -{ - size_t idx = LowerBoundEx ( m_Pairs, key ); - return idx < m_Pairs.Count() ? Iterator ( m_Pairs, m_Pairs[idx] ) : Iterator(); -} - -template -size_t MultiMap::UpperBoundEx( const SArray::Pair> & pairs, const KeyType & key ) -{ - if ( pairs.Count() > 11 ) { - size_t end = pairs.Count() - 1; - size_t last = end; - size_t start = 0; - size_t firstGreater = SYS_MAX_T; - - while ( start + 1 < end ) { // \ru Ищем, пока не нашли. \en Seek until find. - size_t middle = ( start + end ) / 2; - Pair& mdE = pairs[middle]; - if ( mdE.m_key <= key ) { - start = middle; - } - else if ( key < mdE.m_key ) { - if ( middle < firstGreater ) - firstGreater = middle; - end = middle; - } - // \ru Если попадаем сюда, значит некорректно написаны операторы "тождественно" и сравнения. - // \en If we are here, then operators of identity check and comparison are not correct. - else { - PRECONDITION( 0 ); - return SYS_MAX_T; - } - } - - if ( key < pairs[0].m_key ) - return 0; - if ( firstGreater <= end && firstGreater >= 0 && key < pairs[firstGreater].m_key ) - return firstGreater; - if ( key < pairs[last].m_key ) - return last; - } - else { - if ( pairs.Count() == 1 ) - return key < pairs[0].m_key ? 0 :SYS_MAX_T; - else if ( pairs.Count() == 2 ) { - if ( key < pairs[0].m_key ) - return 0; - if ( key < pairs[1].m_key ) - return 1; - } - else { // 2 < count <= 11 - for( size_t i = 0; i < pairs.Count(); ++i ) - if ( key < pairs[i].m_key ) - return i; - } - } - return SYS_MAX_T; -} - - -//------------------------------------------------------------------------------ -// \ru Итератор, указывающий на первый элемент со значением ключа, большим, чем заданный. -// \en An iterator pointing to the first element greater than the given key. -// --- -template -inline typename MultiMap::Iterator MultiMap::UpperBound( const KeyType & key ) const -{ - size_t ind = UpperBoundEx ( m_Pairs, key ); - return ind != SYS_MAX_T ? Iterator ( m_Pairs, m_Pairs[ind] ) : Iterator(); -} - - -//------------------------------------------------------------------------------ -// -// --- -template -std::pair MultiMap::EqualRangeEx( const SArray::Pair> & pairs, const KeyType & key ) -{ - if ( pairs.Count() > 11 ) { - size_t end = pairs.Count() - 1; - size_t lastRight = end; - size_t start = 0; - size_t firstNotLess = SYS_MAX_T; - size_t firstGreater = SYS_MAX_T; - - // \ru Проверяем последний элемент. \en Check the last element. - if ( key > pairs[pairs.Count() - 1].m_key ) - return std::pair (SYS_MAX_T, SYS_MAX_T); - - // \ru Проверяем первый элемент. \en Check the first element. - if ( key < pairs[0].m_key ) - return std::pair (0, 0); - - if ( key != pairs[0].m_key ) { - while ( start + 1 < end ) { // \ru Ищем, пока не нашли. \en Seek until find. - size_t middle = ( start + end ) / 2; - Pair& mdE = pairs[middle]; - if ( mdE.m_key < key ) { - start = middle; - } - else if ( key <= mdE.m_key ) { - if ( key < mdE.m_key && lastRight > middle ) - lastRight = middle; - if ( middle < firstNotLess ) - firstNotLess = middle; - end = middle; - } - // \ru Если попадаем сюда, значит некорректно написаны операторы "тождественно" и сравнения. - // \en If we are here, then operators of identity check and comparison are not correct. - else { - PRECONDITION( 0 ); - return std::pair (SYS_MAX_T, SYS_MAX_T); - } - } - } - else - firstNotLess = 0; // key == pairs[0].m_key - - // \ru Проверяем ключ между start и end. \en Check a key between start and end. - if ( start + 1 == end ) { - size_t middle = start + 1; - Pair& mdE = pairs[middle]; - if ( key <= mdE.m_key && middle < firstNotLess ) - firstNotLess = middle; - } - - // \ru Проверяем последний элемент. \en Check the last element. - if ( firstNotLess == SYS_MAX_T && key == pairs[pairs.Count() - 1].m_key ) - return std::pair (pairs.Count() - 1, SYS_MAX_T); - - if ( firstNotLess == SYS_MAX_T ) - return std::pair (SYS_MAX_T, SYS_MAX_T); - - // \ru Теперь начинаем правый поиск. \en Now start right search. - end = lastRight; - start = firstNotLess; - - if ( start == end && pairs[firstNotLess].m_key != key ) - return std::pair ( firstNotLess, firstNotLess ); - - while ( start + 1 < end ) { // \ru Ищем, пока не нашли. \en Seek until find. - size_t middle = ( start + end ) / 2; - Pair& mdE = pairs[middle]; - if ( mdE.m_key <= key ) { - start = middle; - } - else if ( key < mdE.m_key ) { - if ( middle < firstGreater ) - firstGreater = middle; - end = middle; - } - // \ru Если попадаем сюда, значит некорректно написаны операторы "тождественно" и сравнения. - // \en If we are here, then operators of identity check and comparison are not correct. - else { - PRECONDITION( 0 ); - return std::pair ( SYS_MAX_T, SYS_MAX_T ); - } - } - - if ( firstGreater == SYS_MAX_T ) - { - if ( pairs[firstNotLess].m_key != key ) - firstGreater = firstNotLess; - else if ( lastRight > 0 && pairs[lastRight - 1].m_key == key && pairs[lastRight].m_key != key ) - firstGreater = lastRight; - else if ( firstNotLess + 1 < pairs.Count() && pairs[firstNotLess + 1].m_key != key ) - firstGreater = firstNotLess + 1; - } - return std::pair ( firstNotLess, firstGreater ); - } - else { - if ( pairs.Count() == 0 ) - return std::pair ( SYS_MAX_T, SYS_MAX_T ); - if ( pairs.Count() == 1 ) - return key == pairs[0].m_key ? std::pair ( 0, SYS_MAX_T ) : - ( key < pairs[0].m_key ? std::pair ( 0, 0 ) : std::pair ( SYS_MAX_T, SYS_MAX_T ) ); - if ( pairs.Count() == 2 ) { - size_t first = SYS_MAX_T; - if ( key < pairs[0].m_key ) - return std::pair(0, 0); - if ( key > pairs[1].m_key ) - return std::pair(SYS_MAX_T, SYS_MAX_T); - if ( key == pairs[0].m_key ) - first = 0; - else if ( key == pairs[1].m_key ) - first = 1; - else // ( key > pairs[0].m_key && key < pairs[1].m_key ) - return std::pair ( 1, 1 ); - if ( key == pairs[1].m_key ) - return std::pair ( first, SYS_MAX_T ); - return std::pair (first, 1); - } - else { // 2 < count <= 11 - size_t first = SYS_MAX_T, second = SYS_MAX_T; - if ( key < pairs[0].m_key ) - return std::pair ( 0, 0 ); - if ( key > pairs[pairs.Count() - 1].m_key ) - return std::pair ( first, second ); - for( size_t i = 0; i < pairs.Count(); ++i ) { - if ( key == pairs[i].m_key ) { - if ( first == SYS_MAX_T ) - first = i; - } - else { - if ( key < pairs[i].m_key ) { - second = i; - break; - } - } - } - if ( first == SYS_MAX_T ) - first = second; - return std::pair ( first, second ); - } - } -} - - -//------------------------------------------------------------------------------ -// \ru Диапазон, содержащий все элементы с данным ключом в контейнере. -// \en A range containing all elements with the given key in the container. -// --- -template -inline std::pair::Iterator,typename MultiMap::Iterator> - MultiMap::EqualRange ( const KeyType & key ) const -{ - std::pair idx = EqualRangeEx ( m_Pairs, key ); - Iterator first, second; - if ( idx.first < m_Pairs.Count() ) - first = Iterator ( m_Pairs, m_Pairs[idx.first]); - if ( idx.second < m_Pairs.Count() ) - second = Iterator ( m_Pairs, m_Pairs[idx.second]); - return std::pair ( first, second ); -} - - -//------------------------------------------------------------------------------ -/// \ru Найти элемент с заданными ключом и значением. -// \en Find an element with the specified key and value. -// --- -template -inline typename MultiMap::Iterator MultiMap::Find( const KeyType & key ) const -{ - if ( m_Pairs.Count() > 11 ) { - size_t mx = m_Pairs.Count() - 1; - size_t mxc = mx; - size_t mn = 0; - - while ( mn + 1 < mx ) { // \ru Ищем, пока не нашли. \en Seek until find. - size_t md = ( mn + mx ) / 2; - Pair& mdE = m_Pairs[md]; - if ( mdE.m_key < key ) { - mn = md; - } - else if ( key < mdE.m_key ) { - mx = md; - } - else if ( mdE.m_key == key ) - return Iterator ( m_Pairs, mdE ); - // \ru Если попадаем сюда, значит некорректно написаны операторы "тождественно" и сравнения. - // \en If we are here, then operators of identity check and comparison are not correct. - else { - PRECONDITION( 0 ); - return Iterator(); - } - } - - if ( key == m_Pairs[0].m_key ) - return Iterator ( m_Pairs, m_Pairs[0] ); - if ( key == m_Pairs[mxc].m_key ) - return Iterator ( m_Pairs, m_Pairs[mxc] ); - } - else { - if ( m_Pairs.Count() == 1 ) - return key == m_Pairs[0].m_key ? Iterator ( m_Pairs, m_Pairs[0] ) : Iterator(); - else if ( m_Pairs.Count() == 2 ) { - if ( key == m_Pairs[0].m_key ) - return Iterator ( m_Pairs, m_Pairs[0] ); - if ( key == m_Pairs[1].m_key ) - return Iterator ( m_Pairs, m_Pairs[1] ); - return Iterator(); - } - else { // 2 < count <= 11 - for( size_t i = 0; i < m_Pairs.Count(); ++i ) - if ( key == m_Pairs[i].m_key ) - return Iterator ( m_Pairs, m_Pairs[i] ); - } - } - return Iterator(); -} - - -//------------------------------------------------------------------------------- -// \ru Конструктор. \en Contructor. -// --- -template -MultiMap::MultiMap() - : m_Pairs( 0, 1 ) -{} - - -//------------------------------------------------------------------------------- -/// \ru Добавить элемент с заданным ключом и значением. -// \en Add an element with specified key and value. -// --- -template -inline void MultiMap::Associate( const KeyType & key, const ValType & val ) { - Pair pair( key, val ); - std::pair idx = EqualRangeEx ( m_Pairs, key ); - if ( idx.second < m_Pairs.Count() ) - m_Pairs.InsertInd ( idx.second, pair ); - else - m_Pairs.Add ( pair ); -} - - -//------------------------------------------------------------------------------- -// \ru Существует ли элемент с заданными ключом и значением. -// \en Is there an element with the specified key and value. -// --- -template -inline bool MultiMap::IsAssociated( const KeyType & key, const ValType & val ) const { - return m_Pairs.FindIt( Pair(key, val) ) < m_Pairs.Count(); -} - - -//------------------------------------------------------------------------------- -/// \ru Удалить заданное значение с заданным ключом. \en Remove an element with specified key and value. -// --- -template -inline void MultiMap::Dissociate( const KeyType & key, const ValType & val ) { - Pair pair( key, val ); - size_t idx = m_Pairs.FindIt( pair ); - if ( idx < m_Pairs.Count() ) { - m_Pairs.RemoveInd( idx ); - } -} - - -//------------------------------------------------------------------------------- -/// \ru Удалить элемент, указанный итератором. \en Remove an element specified by an iterator. -// --- -template -inline typename MultiMap::Iterator MultiMap::Dissociate( - typename MultiMap::Iterator & it ) -{ - size_t idx = m_Pairs.FindIt( *it.GetPair() ); - if ( idx < m_Pairs.Count() ) { - m_Pairs.RemoveInd( idx ); - if ( idx < m_Pairs.Count() ) - return Iterator ( m_Pairs, m_Pairs[idx]); - } - return Iterator(); -} - - -//------------------------------------------------------------------------------- -/// \ru Удалить элементы в диапазоне [it1, it2). \en Remove elements in the range [it1, it2). -// --- -template -inline void MultiMap::Dissociate( - typename MultiMap::Iterator & it1, - typename MultiMap::Iterator & it2 ) -{ - if ( m_Pairs.Count() == 0 || it1.Empty() ) - return; - if ( it2.Empty() ) { - // \ru Удаляем с it1 до конца. \en Remove from it1 up to the end. - it2 = Iterator ( m_Pairs, m_Pairs[m_Pairs.Count() - 1] ); - if ( it1 == it2 ) - Dissociate ( it1.Key(), it1.Value() ); // \ru Просто удаляем последний элемент. \en Just remove the last element. - else if ( it1 < it2 ) { - size_t idx = m_Pairs.FindIt( *it1.GetPair() ); - if ( idx != SYS_MAX_T ) - m_Pairs.RemoveInd ( idx, m_Pairs.Count () ); - } - } - else if ( it1 < it2 ) - m_Pairs.Remove ( it1.GetPair(), it2.GetPair() ); -} - - -//------------------------------------------------------------------------------- -/// \ru Оператор доступа по ключу. \en Access by key operator. -// --- -template -inline typename MultiMap::Iterator MultiMap::operator[] ( const KeyType & key ) const { - return Iterator( m_Pairs, Pair(key, Null::val()) ); -} - - -//------------------------------------------------------------------------------- -/// \ru Получить итератор, указывающий на первый элемент. \en Get an iterator pointing to the first element. -// --- -template -inline typename MultiMap::Iterator MultiMap::First() const { - if ( m_Pairs.Count() ) - return Iterator ( m_Pairs, m_Pairs[0] ); - return Iterator(); -} - - -#endif // __TEMPL_MULTIMAP_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Мультимножество, реализующее основной функционал std::multimap. + \en Multiset implementing the core functionality of std::multimap. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __TEMPL_MULTIMAP_H +#define __TEMPL_MULTIMAP_H + + +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Ассоциативное множество c дубликатами (мультимножество). + \en Associative set with duplicates (multiset). \~ + \details \ru Ассоциативное множество c дубликатами (мультимножество). \n + Реализует основные функциии std::multimap. Требования к типам данных KeyType и ValType такие же, как в SArray. + Мультимножество задает соответствие (ассоциации) объекта-ключа подмножеству объектов-значений. + Для некоторого объекта типа KeyType задается соответствие подмножеству объектов ValType. + \en Associative set with duplicates (multiset). \n + Implements the core functions of is std::multimap. Requirements to data types KeyType and ValType are the same as in SArray. + Multiset sets the mapping (associations) between a key-object and a subset of value-objects. + For some object of the "KeyType" type there is set a mapping to a subset of objects of the "ValType" type. \~ + \ingroup Base_Tools_Containers +*/ +// --- +template +class MultiMap { +public: + + struct Pair + { + const KeyType m_key; + const ValType m_val; + Pair( const KeyType & key, const ValType & val ) : m_key( key ), m_val( val ) {} + bool operator < ( const Pair & p ) const { + return m_key < p.m_key; + } + bool operator == ( const Pair & p ) const { + return m_key == p.m_key && m_val == p.m_val; + } + Pair & operator = ( const Pair & ); // \ru Не реализовано. \en Not implemented. + }; + +public: + MultiMap(); + virtual ~MultiMap() {} + + class Iterator; + +public: + /// \ru Оператор доступа по ключу. \en Access by key operator. + Iterator operator[] ( const KeyType & key ) const; + /// \ru Получить итератор, указывающий на первый элемент. \en Get an iterator pointing to the first element. + Iterator First() const; + /// \ru Добавить элемент с заданным ключом и значением. \en Add an element with specified key and value. + void Associate( const KeyType & key, const ValType & val ); + /// \ru Удалить заданное значение с заданным ключом. \en Remove an element with specified key and value. + void Dissociate( const KeyType & key, const ValType & val ); + /// \ru Удалить элементы в диапазоне [it1, it2). \en Remove elements in the range [it1, it2). + void Dissociate( Iterator & it1, Iterator & it2 ); + /// \ru Удалить элемент, указанный итератором. \en Remove an element specified by an iterator. + Iterator Dissociate( Iterator & it ); + /// \ru Удалить все элементы из контейнера. \en Removes all elements from the container. + void Flush() { m_Pairs.Flush(); } + /// \ru Существует ли элемент с заданными ключом и значением. \en Is there an element with the specified key and value. + bool IsAssociated( const KeyType & key, const ValType & val ) const; + /// \ru Найти элемент с заданными ключом и значением. \en Find an element with the specified key and value. + Iterator Find( const KeyType & key ) const; + // \ru Итератор, указывающий на первый элемент со значением ключа, не меньшим, чем заданный. \en An iterator pointing to the first element not less than the given key. + Iterator LowerBound( const KeyType & key ) const; + // \ru Итератор, указывающий на первый элемент со значением ключа, большим, чем заданный. \en An iterator pointing to the first element greater than the given key. + Iterator UpperBound( const KeyType & key ) const; + // \ru Диапазон, содержащий все элементы с данным ключом в контейнере. \en A range containing all elements with the given key in the container. + std::pair EqualRange( const KeyType & key ) const; + // \ru Количество элементов в контейнере. \en The number of elements in the container. + size_t Count() const { return m_Pairs.Count(); } + + static size_t UpperBoundEx( const SArray & pairs, const KeyType & key ); + static size_t LowerBoundEx( const SArray & pairs, const KeyType & key ); + static std::pair EqualRangeEx( const SArray & pairs, const KeyType & key ); + +private: + SArray m_Pairs; + + private: // \ru Специфицируем нуль для разных типов. \en Specify null for different types. + template + struct Null { // \ru Нуль тривиальных типов (int, float, double и т.д.). \en Null of trivial types (int, float, double etc.). + static inline T val() { return 0; } + }; + template + struct Null { // \ru Нуль указателей. \en Null of pointers. + static inline T* val() { return NULL; } + }; +// \ru LF_Linux: 25.03.11 g++ выдает ошибку на этот код - не использованы KeyType, ValType в полной специализации шаблона. +// Однако непонятно, зачем нужна эта полная специализация - общая частичная специализация для тривиальных типов вполне подойдет. +// \en LF_Linux: 25.03.11 g++ returns an error in this code - types KeyType and ValType are not used in the full specialization of template. +// But it is not clear what for this specialization is required - the common partial specialization for the trivial types is sufficient. +// template<> +// \ru struct Null { // Нуль вещественных чисел \en struct Null { // Null of real numbers +// static inline double val() { return 0.0; } +// }; + +public: + // \ru Итератор по элементам контейнера. \en Iterator for the container elements. + class Iterator { + private: + Pair * m_Ptr; + Pair * m_MaxPtr; + + public: + Iterator() : m_Ptr( NULL ), m_MaxPtr( NULL ) {} + Iterator( const Iterator & iter ) : m_Ptr( iter.m_Ptr ), m_MaxPtr( iter.m_MaxPtr ) {} + Iterator( const SArray & m_Pairs, const Pair & pair ) : m_Ptr( NULL ), m_MaxPtr( NULL ) + { + const size_t count = m_Pairs.Count(); + if ( count > 0 ) { + size_t idx = MultiMap::LowerBoundEx( m_Pairs, pair.m_key ); + size_t temp = idx; + while ( temp < m_Pairs.Count() && m_Pairs[temp].m_key == pair.m_key ) { + if ( m_Pairs[temp] == pair ) { + idx = temp; + break; + } + temp++; + } + m_MaxPtr = &m_Pairs[count-1]; + if ( idx < count ) { + m_Ptr = &m_Pairs[idx]; + } + } + } + Iterator( const SArray & m_Pairs, const Iterator & iter1, const Iterator & iter2 ) // range + : m_Ptr( NULL ), m_MaxPtr( NULL ) + { + const size_t count = m_Pairs.Count(); + if ( count > 0 && iter1.m_Ptr != NULL ) { + size_t idx1 = MultiMap::LowerBoundEx( m_Pairs, iter1.m_Ptr->m_key ); + size_t temp = idx1; + while ( temp < m_Pairs.Count() && m_Pairs[temp].m_key == iter1.m_Ptr->m_key ) { + if ( m_Pairs[temp] == *iter1.m_Ptr ) { + idx1 = temp; + break; + } + temp++; + } + size_t idx2 = SYS_MAX_T; + if ( iter2.m_Ptr != NULL ) { + idx2 = MultiMap::UpperBoundEx( m_Pairs, iter2.m_Ptr->m_key ); + if ( idx2 < m_Pairs.Count() ) { + if ( idx2 > 0) + idx2--; + else + idx2 = SYS_MAX_T; + } + } + if ( idx2 == SYS_MAX_T ) + idx2 = count - 1; + if ( idx1 < count && idx2 < count ) { + m_Ptr = &m_Pairs[idx1]; + m_MaxPtr = &m_Pairs[idx2]; + } + } + } + + public: + /// \ru Получить текущий элемент и сдвинуть итератор на следующий. \en Get the current element and move the iterator to the next. + Iterator operator ++( int ) + { + Iterator iter(*this); + if ( m_Ptr && m_Ptr < m_MaxPtr ) + m_Ptr++; + else + m_Ptr = 0; + return iter; + } + /// \ru Оператор равенства. \en An equality operator. + bool operator == ( const Iterator & itr ) const + { + if ( Empty() && itr.Empty() ) + return true; + if ( ( Empty() && !itr.Empty() ) || ( !Empty() && itr.Empty() ) ) + return false; + return m_Ptr == itr.m_Ptr && m_MaxPtr == itr.m_MaxPtr; + } + /// \ru Оператор "!=". \en An "!=" operator. + bool operator != ( const Iterator & itr ) const + { + if ( Empty() && itr.Empty() ) + return false; + if ( ( Empty() && !itr.Empty() ) || ( !Empty() && itr.Empty() ) ) + return true; + return m_Ptr != itr.m_Ptr || m_MaxPtr != itr.m_MaxPtr; + } + /// \ru Оператор "меньше". \en Operator "less". + bool operator < ( const Iterator& itr ) const + { + return m_Ptr < itr.m_Ptr && m_MaxPtr <= itr.m_MaxPtr; + } + /// \ru Пустой ли итератор. \en Is the iterator empty. + bool Empty() const + { + return !( m_Ptr && m_Ptr <= m_MaxPtr ); + } + // \ru Получить текущий ключ элемента. \en Get the current key of the element. + KeyType Key() const + { + return !Empty() ? m_Ptr->m_key : (KeyType)0; + } + // \ru Получить текущее значение элемента. \en Get the current value of the element. + ValType Value() const + { + return !Empty() ? m_Ptr->m_val : (ValType)0; + } + // \ru Получить пару с текущим ключом и текущим кзначением элемента. \en Get the pair with the current key and the current value of the element. + Pair* GetPair() const + { + return m_Ptr; + } + }; +}; + + +//------------------------------------------------------------------------------ +// +// --- +template +size_t MultiMap::LowerBoundEx( const SArray::Pair> & pairs, const KeyType & key ) +{ + if ( pairs.Count() > 11 ) { + size_t end = pairs.Count() - 1; + size_t last = end; + size_t start = 0; + size_t firstNotLess = SYS_MAX_T; + + while ( start + 1 < end ) { // \ru Ищем, пока не нашли. \en Seek until find. + size_t middle = ( start + end ) / 2; + Pair& mdE = pairs[middle]; + if ( mdE.m_key < key ) { + start = middle; + } + else if ( key <= mdE.m_key ) { + if ( middle < firstNotLess ) + firstNotLess = middle; + end = middle; + } + // \ru Если попадаем сюда, значит некорректно написаны операторы "тождественно" и сравнения. + // \en If we are here, then operators of identity check and comparison are not correct. + else { + PRECONDITION( 0 ); + return SYS_MAX_T; + } + } + + // \ru Проверяем ключ между start и end. \en Check a key between start and end. + if ( start + 1 < pairs.Count() - 1 ) { + size_t middle = start + 1; + Pair& mdE = pairs[middle]; + if ( key <= mdE.m_key && middle < firstNotLess ) + firstNotLess = middle; + } + + if ( key <= pairs[0].m_key ) + return 0; + if ( firstNotLess <= last && firstNotLess >= 0 && key <= pairs[firstNotLess].m_key ) + return firstNotLess; + if ( key == pairs[last].m_key ) + return last; + } + else { + if ( pairs.Count() == 1 ) + return key <= pairs[0].m_key ? 0 : SYS_MAX_T; + else if ( pairs.Count() == 2 ) { + if ( key <= pairs[0].m_key ) + return 0; + if ( key <= pairs[1].m_key ) + return 1; + } + else { // 2 < count <= 11 + for( size_t i = 0; i < pairs.Count(); ++i ) + if ( key <= pairs[i].m_key ) + return i; + } + } + return SYS_MAX_T; +} + +//------------------------------------------------------------------------------ +// \ru Итератор, указывающий на первый элемент со значением ключа, не меньшим, чем заданный. +// \en An iterator pointing to the first element not less than the given key. +// --- +template +inline typename MultiMap::Iterator MultiMap::LowerBound( const KeyType & key ) const +{ + size_t idx = LowerBoundEx ( m_Pairs, key ); + return idx < m_Pairs.Count() ? Iterator ( m_Pairs, m_Pairs[idx] ) : Iterator(); +} + +template +size_t MultiMap::UpperBoundEx( const SArray::Pair> & pairs, const KeyType & key ) +{ + if ( pairs.Count() > 11 ) { + size_t end = pairs.Count() - 1; + size_t last = end; + size_t start = 0; + size_t firstGreater = SYS_MAX_T; + + while ( start + 1 < end ) { // \ru Ищем, пока не нашли. \en Seek until find. + size_t middle = ( start + end ) / 2; + Pair& mdE = pairs[middle]; + if ( mdE.m_key <= key ) { + start = middle; + } + else if ( key < mdE.m_key ) { + if ( middle < firstGreater ) + firstGreater = middle; + end = middle; + } + // \ru Если попадаем сюда, значит некорректно написаны операторы "тождественно" и сравнения. + // \en If we are here, then operators of identity check and comparison are not correct. + else { + PRECONDITION( 0 ); + return SYS_MAX_T; + } + } + + if ( key < pairs[0].m_key ) + return 0; + if ( firstGreater <= end && firstGreater >= 0 && key < pairs[firstGreater].m_key ) + return firstGreater; + if ( key < pairs[last].m_key ) + return last; + } + else { + if ( pairs.Count() == 1 ) + return key < pairs[0].m_key ? 0 :SYS_MAX_T; + else if ( pairs.Count() == 2 ) { + if ( key < pairs[0].m_key ) + return 0; + if ( key < pairs[1].m_key ) + return 1; + } + else { // 2 < count <= 11 + for( size_t i = 0; i < pairs.Count(); ++i ) + if ( key < pairs[i].m_key ) + return i; + } + } + return SYS_MAX_T; +} + + +//------------------------------------------------------------------------------ +// \ru Итератор, указывающий на первый элемент со значением ключа, большим, чем заданный. +// \en An iterator pointing to the first element greater than the given key. +// --- +template +inline typename MultiMap::Iterator MultiMap::UpperBound( const KeyType & key ) const +{ + size_t ind = UpperBoundEx ( m_Pairs, key ); + return ind != SYS_MAX_T ? Iterator ( m_Pairs, m_Pairs[ind] ) : Iterator(); +} + + +//------------------------------------------------------------------------------ +// +// --- +template +std::pair MultiMap::EqualRangeEx( const SArray::Pair> & pairs, const KeyType & key ) +{ + if ( pairs.Count() > 11 ) { + size_t end = pairs.Count() - 1; + size_t lastRight = end; + size_t start = 0; + size_t firstNotLess = SYS_MAX_T; + size_t firstGreater = SYS_MAX_T; + + // \ru Проверяем последний элемент. \en Check the last element. + if ( key > pairs[pairs.Count() - 1].m_key ) + return std::pair (SYS_MAX_T, SYS_MAX_T); + + // \ru Проверяем первый элемент. \en Check the first element. + if ( key < pairs[0].m_key ) + return std::pair (0, 0); + + if ( key != pairs[0].m_key ) { + while ( start + 1 < end ) { // \ru Ищем, пока не нашли. \en Seek until find. + size_t middle = ( start + end ) / 2; + Pair& mdE = pairs[middle]; + if ( mdE.m_key < key ) { + start = middle; + } + else if ( key <= mdE.m_key ) { + if ( key < mdE.m_key && lastRight > middle ) + lastRight = middle; + if ( middle < firstNotLess ) + firstNotLess = middle; + end = middle; + } + // \ru Если попадаем сюда, значит некорректно написаны операторы "тождественно" и сравнения. + // \en If we are here, then operators of identity check and comparison are not correct. + else { + PRECONDITION( 0 ); + return std::pair (SYS_MAX_T, SYS_MAX_T); + } + } + } + else + firstNotLess = 0; // key == pairs[0].m_key + + // \ru Проверяем ключ между start и end. \en Check a key between start and end. + if ( start + 1 == end ) { + size_t middle = start + 1; + Pair& mdE = pairs[middle]; + if ( key <= mdE.m_key && middle < firstNotLess ) + firstNotLess = middle; + } + + // \ru Проверяем последний элемент. \en Check the last element. + if ( firstNotLess == SYS_MAX_T && key == pairs[pairs.Count() - 1].m_key ) + return std::pair (pairs.Count() - 1, SYS_MAX_T); + + if ( firstNotLess == SYS_MAX_T ) + return std::pair (SYS_MAX_T, SYS_MAX_T); + + // \ru Теперь начинаем правый поиск. \en Now start right search. + end = lastRight; + start = firstNotLess; + + if ( start == end && pairs[firstNotLess].m_key != key ) + return std::pair ( firstNotLess, firstNotLess ); + + while ( start + 1 < end ) { // \ru Ищем, пока не нашли. \en Seek until find. + size_t middle = ( start + end ) / 2; + Pair& mdE = pairs[middle]; + if ( mdE.m_key <= key ) { + start = middle; + } + else if ( key < mdE.m_key ) { + if ( middle < firstGreater ) + firstGreater = middle; + end = middle; + } + // \ru Если попадаем сюда, значит некорректно написаны операторы "тождественно" и сравнения. + // \en If we are here, then operators of identity check and comparison are not correct. + else { + PRECONDITION( 0 ); + return std::pair ( SYS_MAX_T, SYS_MAX_T ); + } + } + + if ( firstGreater == SYS_MAX_T ) + { + if ( pairs[firstNotLess].m_key != key ) + firstGreater = firstNotLess; + else if ( lastRight > 0 && pairs[lastRight - 1].m_key == key && pairs[lastRight].m_key != key ) + firstGreater = lastRight; + else if ( firstNotLess + 1 < pairs.Count() && pairs[firstNotLess + 1].m_key != key ) + firstGreater = firstNotLess + 1; + } + return std::pair ( firstNotLess, firstGreater ); + } + else { + if ( pairs.Count() == 0 ) + return std::pair ( SYS_MAX_T, SYS_MAX_T ); + if ( pairs.Count() == 1 ) + return key == pairs[0].m_key ? std::pair ( 0, SYS_MAX_T ) : + ( key < pairs[0].m_key ? std::pair ( 0, 0 ) : std::pair ( SYS_MAX_T, SYS_MAX_T ) ); + if ( pairs.Count() == 2 ) { + size_t first = SYS_MAX_T; + if ( key < pairs[0].m_key ) + return std::pair(0, 0); + if ( key > pairs[1].m_key ) + return std::pair(SYS_MAX_T, SYS_MAX_T); + if ( key == pairs[0].m_key ) + first = 0; + else if ( key == pairs[1].m_key ) + first = 1; + else // ( key > pairs[0].m_key && key < pairs[1].m_key ) + return std::pair ( 1, 1 ); + if ( key == pairs[1].m_key ) + return std::pair ( first, SYS_MAX_T ); + return std::pair (first, 1); + } + else { // 2 < count <= 11 + size_t first = SYS_MAX_T, second = SYS_MAX_T; + if ( key < pairs[0].m_key ) + return std::pair ( 0, 0 ); + if ( key > pairs[pairs.Count() - 1].m_key ) + return std::pair ( first, second ); + for( size_t i = 0; i < pairs.Count(); ++i ) { + if ( key == pairs[i].m_key ) { + if ( first == SYS_MAX_T ) + first = i; + } + else { + if ( key < pairs[i].m_key ) { + second = i; + break; + } + } + } + if ( first == SYS_MAX_T ) + first = second; + return std::pair ( first, second ); + } + } +} + + +//------------------------------------------------------------------------------ +// \ru Диапазон, содержащий все элементы с данным ключом в контейнере. +// \en A range containing all elements with the given key in the container. +// --- +template +inline std::pair::Iterator,typename MultiMap::Iterator> + MultiMap::EqualRange ( const KeyType & key ) const +{ + std::pair idx = EqualRangeEx ( m_Pairs, key ); + Iterator first, second; + if ( idx.first < m_Pairs.Count() ) + first = Iterator ( m_Pairs, m_Pairs[idx.first]); + if ( idx.second < m_Pairs.Count() ) + second = Iterator ( m_Pairs, m_Pairs[idx.second]); + return std::pair ( first, second ); +} + + +//------------------------------------------------------------------------------ +/// \ru Найти элемент с заданными ключом и значением. +// \en Find an element with the specified key and value. +// --- +template +inline typename MultiMap::Iterator MultiMap::Find( const KeyType & key ) const +{ + if ( m_Pairs.Count() > 11 ) { + size_t mx = m_Pairs.Count() - 1; + size_t mxc = mx; + size_t mn = 0; + + while ( mn + 1 < mx ) { // \ru Ищем, пока не нашли. \en Seek until find. + size_t md = ( mn + mx ) / 2; + Pair& mdE = m_Pairs[md]; + if ( mdE.m_key < key ) { + mn = md; + } + else if ( key < mdE.m_key ) { + mx = md; + } + else if ( mdE.m_key == key ) + return Iterator ( m_Pairs, mdE ); + // \ru Если попадаем сюда, значит некорректно написаны операторы "тождественно" и сравнения. + // \en If we are here, then operators of identity check and comparison are not correct. + else { + PRECONDITION( 0 ); + return Iterator(); + } + } + + if ( key == m_Pairs[0].m_key ) + return Iterator ( m_Pairs, m_Pairs[0] ); + if ( key == m_Pairs[mxc].m_key ) + return Iterator ( m_Pairs, m_Pairs[mxc] ); + } + else { + if ( m_Pairs.Count() == 1 ) + return key == m_Pairs[0].m_key ? Iterator ( m_Pairs, m_Pairs[0] ) : Iterator(); + else if ( m_Pairs.Count() == 2 ) { + if ( key == m_Pairs[0].m_key ) + return Iterator ( m_Pairs, m_Pairs[0] ); + if ( key == m_Pairs[1].m_key ) + return Iterator ( m_Pairs, m_Pairs[1] ); + return Iterator(); + } + else { // 2 < count <= 11 + for( size_t i = 0; i < m_Pairs.Count(); ++i ) + if ( key == m_Pairs[i].m_key ) + return Iterator ( m_Pairs, m_Pairs[i] ); + } + } + return Iterator(); +} + + +//------------------------------------------------------------------------------- +// \ru Конструктор. \en Contructor. +// --- +template +MultiMap::MultiMap() + : m_Pairs( 0, 1 ) +{} + + +//------------------------------------------------------------------------------- +/// \ru Добавить элемент с заданным ключом и значением. +// \en Add an element with specified key and value. +// --- +template +inline void MultiMap::Associate( const KeyType & key, const ValType & val ) { + Pair pair( key, val ); + std::pair idx = EqualRangeEx ( m_Pairs, key ); + if ( idx.second < m_Pairs.Count() ) + m_Pairs.InsertInd ( idx.second, pair ); + else + m_Pairs.Add ( pair ); +} + + +//------------------------------------------------------------------------------- +// \ru Существует ли элемент с заданными ключом и значением. +// \en Is there an element with the specified key and value. +// --- +template +inline bool MultiMap::IsAssociated( const KeyType & key, const ValType & val ) const { + return m_Pairs.FindIt( Pair(key, val) ) < m_Pairs.Count(); +} + + +//------------------------------------------------------------------------------- +/// \ru Удалить заданное значение с заданным ключом. \en Remove an element with specified key and value. +// --- +template +inline void MultiMap::Dissociate( const KeyType & key, const ValType & val ) { + Pair pair( key, val ); + size_t idx = m_Pairs.FindIt( pair ); + if ( idx < m_Pairs.Count() ) { + m_Pairs.RemoveInd( idx ); + } +} + + +//------------------------------------------------------------------------------- +/// \ru Удалить элемент, указанный итератором. \en Remove an element specified by an iterator. +// --- +template +inline typename MultiMap::Iterator MultiMap::Dissociate( + typename MultiMap::Iterator & it ) +{ + size_t idx = m_Pairs.FindIt( *it.GetPair() ); + if ( idx < m_Pairs.Count() ) { + m_Pairs.RemoveInd( idx ); + if ( idx < m_Pairs.Count() ) + return Iterator ( m_Pairs, m_Pairs[idx]); + } + return Iterator(); +} + + +//------------------------------------------------------------------------------- +/// \ru Удалить элементы в диапазоне [it1, it2). \en Remove elements in the range [it1, it2). +// --- +template +inline void MultiMap::Dissociate( + typename MultiMap::Iterator & it1, + typename MultiMap::Iterator & it2 ) +{ + if ( m_Pairs.Count() == 0 || it1.Empty() ) + return; + if ( it2.Empty() ) { + // \ru Удаляем с it1 до конца. \en Remove from it1 up to the end. + it2 = Iterator ( m_Pairs, m_Pairs[m_Pairs.Count() - 1] ); + if ( it1 == it2 ) + Dissociate ( it1.Key(), it1.Value() ); // \ru Просто удаляем последний элемент. \en Just remove the last element. + else if ( it1 < it2 ) { + size_t idx = m_Pairs.FindIt( *it1.GetPair() ); + if ( idx != SYS_MAX_T ) + m_Pairs.RemoveInd ( idx, m_Pairs.Count () ); + } + } + else if ( it1 < it2 ) + m_Pairs.Remove ( it1.GetPair(), it2.GetPair() ); +} + + +//------------------------------------------------------------------------------- +/// \ru Оператор доступа по ключу. \en Access by key operator. +// --- +template +inline typename MultiMap::Iterator MultiMap::operator[] ( const KeyType & key ) const { + return Iterator( m_Pairs, Pair(key, Null::val()) ); +} + + +//------------------------------------------------------------------------------- +/// \ru Получить итератор, указывающий на первый элемент. \en Get an iterator pointing to the first element. +// --- +template +inline typename MultiMap::Iterator MultiMap::First() const { + if ( m_Pairs.Count() ) + return Iterator ( m_Pairs, m_Pairs[0] ); + return Iterator(); +} + + +#endif // __TEMPL_MULTIMAP_H diff --git a/C3d/Include/templ_p_array.h b/C3d/Include/templ_p_array.h index 0b9f935..518df40 100644 --- a/C3d/Include/templ_p_array.h +++ b/C3d/Include/templ_p_array.h @@ -17,10 +17,6 @@ #include -#ifdef __DEBUG_MEMORY_ALLOCATE_FREE_ -#include -#endif // __DEBUG_MEMORY_ALLOCATE_FREE_ - // \ru Реализация методов для чтения/записи массива лежит в templ_p_array_rw.h \en Implementation of methods for reading/writing of array is located in templ_p_array_rw.h @@ -262,22 +258,26 @@ template inline Type * PArray::RemoveInd( size_t delIndex, DelType del ) { PRECONDITION( delIndex < RPArray::count ); - const Type **d = RPArray::GetAddr() + delIndex; - Type *r = (Type*)*d; + Type * r = 0; - // \ru сначала приведем в порядок массив ... \en put an array in order at first ... - memmove( d, d+1, (RPArray::count - delIndex - 1) * SIZE_OF_POINTER ); - RPArray::count--; + if ( delIndex < RPArray::count ) { + const Type ** d = RPArray::GetAddr() + delIndex; + r = (Type *)*d; - // \ru ... а теперь будем удалять \en ... and now we will delete - if ( del==Delete || (del==defDelete && owns) ) { - PRECONDITION( !r || nowDeletedElem != r ); - nowDeletedElem = r; + // \ru сначала приведем в порядок массив ... \en put an array in order at first ... + memmove( d, d + 1, (RPArray::count - delIndex - 1) * SIZE_OF_POINTER ); + RPArray::count--; - delete r; - r = 0; + // \ru ... а теперь будем удалять \en ... and now we will delete + if ( del == Delete || ( del == defDelete && owns ) ) { + PRECONDITION( !r || nowDeletedElem != r ); + nowDeletedElem = r; - nowDeletedElem = 0; + delete r; + r = 0; + + nowDeletedElem = 0; + } } return r; @@ -366,7 +366,7 @@ bool set_Parray_size( PArray & arr, size_t newSize, bool clear ) { // --- template inline void PIArray::ForEachI( ParIteratorFunc func, void * pars ) const { - C3D_ASSERT( PArray::nowDeletedElem == 0 ); + PRECONDITION( PArray::nowDeletedElem == 0 ); for_each_in_array( *this, func, pars ); } @@ -376,7 +376,7 @@ inline void PIArray::ForEachI( ParIteratorFunc func, void * pars ) const { // --- template inline size_t PIArray::FirstThatI( CompareFunc func, void * pars, size_t from ) const { - C3D_ASSERT( PArray::nowDeletedElem == 0 ); + PRECONDITION( PArray::nowDeletedElem == 0 ); return first_that_in_array( *this, func, pars, from ); } @@ -398,7 +398,7 @@ inline size_t PIArray::FirstThatI( CompareFunc func, void * pars, size_t f // --- template inline void PMIArray::ForEach( ParIteratorMemFunc func, void * pars ) const { - C3D_ASSERT( PArray::nowDeletedElem == 0 ); + PRECONDITION( PArray::nowDeletedElem == 0 ); for_each_in_array( *this, func, pars ); } @@ -408,7 +408,7 @@ inline void PMIArray::ForEach( ParIteratorMemFunc func, void * pars ) cons // --- template inline size_t PMIArray::FirstThat( CompareMemFunc func, void * pars, size_t from ) const { - C3D_ASSERT( PArray::nowDeletedElem == 0 ); + PRECONDITION( PArray::nowDeletedElem == 0 ); return first_that_in_array( *this, func, pars, from ); } diff --git a/C3d/Include/templ_pointer.h b/C3d/Include/templ_pointer.h index 01b7469..dbcd2f3 100644 --- a/C3d/Include/templ_pointer.h +++ b/C3d/Include/templ_pointer.h @@ -61,7 +61,7 @@ private: // СМВ К15 MVS 2012 private: TPointerBase( const TPointerBase & other ); -#ifdef STANDARD_CPP11_RVALUE_REFERENCES +#ifdef C3D_STANDARD_CXX_11_PARTIAL public: TPointerBase( TPointerBase && _Right ): P( _Right.P ) { _Right.P = nullptr; } TPointerBase & operator = ( TPointerBase && _Right ) @@ -70,7 +70,7 @@ public: { P = _Right.P; _Right.P = nullptr; } return *this; } -#endif // STANDARD_CPP11_RVALUE_REFERENCES +#endif // C3D_STANDARD_CXX_11_PARTIAL }; @@ -104,7 +104,7 @@ public: return *this; } T * operator ->() { return TPointerBase::P; } // Could throw exception if P==0 - const T * operator ->() const { return TPointerBase::P; } // Could throw exception if P==0 + const T * operator ->() const { return TPointerBase::P; } // Could throw exception if P==0 // СМВ К15 MVS 2012 #ifndef __MOBILE_VERSION__ @@ -112,7 +112,7 @@ private: #endif // __MOBILE_VERSION__ TPointer( const TPointer & other ); -#ifdef STANDARD_CPP11_RVALUE_REFERENCES +#ifdef C3D_STANDARD_CXX_11_PARTIAL public: TPointer( TPointer && _Right ) : TPointerBase( std::move(_Right) ) @@ -127,7 +127,7 @@ public: } return *this; } -#endif // STANDARD_CPP11_RVALUE_REFERENCES +#endif // C3D_STANDARD_CXX_11_PARTIAL }; @@ -173,7 +173,7 @@ public: private: TOwnPointer( const TOwnPointer & other ); TOwnPointer & operator = ( const TOwnPointer & _Right ); -#ifdef STANDARD_CPP11_RVALUE_REFERENCES +#ifdef C3D_STANDARD_CXX_11_PARTIAL public: TOwnPointer( TOwnPointer && _Right ) : TPointerBase( std::move(_Right) ) @@ -191,7 +191,7 @@ public: } return *this; } -#endif // STANDARD_CPP11_RVALUE_REFERENCES +#endif // C3D_STANDARD_CXX_11_PARTIAL }; @@ -231,7 +231,7 @@ public: // СМВ К15 MVS 2012 //private: // g++4.7 KUbuntu TAPointer( const TAPointer & other ); -#ifdef STANDARD_CPP11_RVALUE_REFERENCES +#ifdef C3D_STANDARD_CXX_11_PARTIAL public: TAPointer(TAPointer && _Right) : TPointerBase( std::move(_Right) ) @@ -246,7 +246,7 @@ public: } return *this; } -#endif // STANDARD_CPP11_RVALUE_REFERENCES +#endif // C3D_STANDARD_CXX_11_PARTIAL }; @@ -273,7 +273,7 @@ public: char * operator = ( char src[] ) { delete[] P; return P = src; } char * operator = ( const TPointer & src ) { delete[] P; return P = src.P; } char & operator []( size_t i ) { return P[i]; } -#ifdef STANDARD_CPP11_RVALUE_REFERENCES +#ifdef C3D_STANDARD_CXX_11_PARTIAL public: TPointer( TPointer && _Right ) : TPointerBase( std::move(_Right) ) @@ -288,7 +288,7 @@ public: } return *this; } -#endif // STANDARD_CPP11_RVALUE_REFERENCES +#endif // C3D_STANDARD_CXX_11_PARTIAL }; diff --git a/C3d/Include/templ_psrt_array.h b/C3d/Include/templ_psrt_array.h index 0828801..bf87551 100644 --- a/C3d/Include/templ_psrt_array.h +++ b/C3d/Include/templ_psrt_array.h @@ -294,7 +294,7 @@ template inline void PArraySort::CatchMemory() { PRECONDITION( PArray::nowDeletedElem == 0 ); // \ru ЯТ - временно \en ЯТ - temporarily - if ( PArray::upper == PArray::count ) + if ( PArray::upper == PArray::count ) set_Parray_size( *this, PArray::upper + CalculateDelta(), false/*clear*/ ); } diff --git a/C3d/Include/templ_rp_array.h b/C3d/Include/templ_rp_array.h index 730c59c..bbf8b7e 100644 --- a/C3d/Include/templ_rp_array.h +++ b/C3d/Include/templ_rp_array.h @@ -18,10 +18,6 @@ #include -#ifdef __DEBUG_MEMORY_ALLOCATE_FREE_ -#include -#endif // __DEBUG_MEMORY_ALLOCATE_FREE_ - FORVARD_DECL_TEMPLATE_TYPENAME( class RPArray ); FORVARD_DECL_TEMPLATE_TYPENAME( bool set_Rarray_size( RPArray &, size_t newSize ) ); @@ -107,7 +103,7 @@ public: void Sort ( CompFunc comp ); ///< \ru Сортировать массив. \en Sort the array. /// \ru Оператор доступа по индексу. \en Access by index operator. - Type *& operator []( size_t loc ) const { PRECONDITION( loc < count ); return parr[loc]; } + Type *& operator []( size_t loc ) const; /// \ru Получить адрес последнего элемента в массиве. \en Get the address of the last element in the array. Type * GetLast() const { return ((count > 0) ? parr[count-1] : (Type*)NULL); } @@ -133,12 +129,12 @@ public: // \ru унификация с вектором STL \en unification with TPtr * end() { return parr + count; } ///< \ru Получить указатель на участок памяти после массива. \en Get the pointer to the piece of memory after the array. const TPtr * cbegin() const { return parr; } ///< \ru Получить указатель на первый элемент массива. \en Get the pointer to the first array element. const TPtr * cend() const { return parr + count; } ///< \ru Получить указатель на участок памяти после массива. \en Get the pointer to the piece of memory after the array. - const value_type front() const { PRECONDITION(!empty()); return *parr; } - value_type & front() { PRECONDITION(!empty()); return *parr; } - const value_type back() const { PRECONDITION(!empty()); return *(parr + count - 1); } - value_type & back() { PRECONDITION(!empty()); return *(parr + count - 1); } - const_reference at( size_t idx ) const { PRECONDITION( idx < count ); return parr[idx]; } - reference at( size_t idx ) { PRECONDITION( idx < count ); return parr[idx]; } + const value_type front() const; + value_type & front(); + const value_type back() const; + value_type & back(); + const_reference at( size_t idx ) const; + reference at( size_t idx ); protected: const Type ** GetAddr() const { return (const Type **)parr; } ///< \ru Получить указатель на первый элемент массива. \en Get the pointer to the first array element. @@ -171,11 +167,11 @@ public: void operator delete ( void *, size_t ); #endif // __DEBUG_MEMORY_ALLOCATE_FREE_ -#ifdef STANDARD_CPP11_RVALUE_REFERENCES +#ifdef C3D_STANDARD_CXX_11_PARTIAL public: RPArray( RPArray && ); ///< \ru Конструктор перемещения массива. \en Constructor of an array moving. RPArray & operator = ( RPArray && ); ///< \ru Оператор перемещения массива. \en Operator of an array moving. -#endif // STANDARD_CPP11_RVALUE_REFERENCES +#endif // C3D_STANDARD_CXX_11_PARTIAL }; @@ -231,7 +227,7 @@ inline RPArray::RPArray( size_t i_upper, uint16 i_delta )//, bool shouldNu } -#ifdef STANDARD_CPP11_RVALUE_REFERENCES +#ifdef C3D_STANDARD_CXX_11_PARTIAL //------------------------------------------------------------------------------ // \ru Конструктор перемещения массива. \en Constructor of an array moving. // --- @@ -261,7 +257,7 @@ RPArray & RPArray::operator = ( RPArray && _Right ) } return (*this); } -#endif // STANDARD_CPP11_RVALUE_REFERENCES +#endif // C3D_STANDARD_CXX_11_PARTIAL //------------------------------------------------------------------------------ @@ -272,6 +268,72 @@ inline RPArray::~RPArray() { set_Rarray_size( *this, 0 ); //delete [] parr; } +//------------------------------------------------------------------------------ +// \ru Оператор доступа по индексу. \en Access by index operator. +// --- +template +inline Type *& RPArray::operator []( size_t loc ) const +{ + return parr[loc]; +} + +//------------------------------------------------------------------------------ +// \ru Доступ к первому элементу. \en Access to the first element. +// --- +template +inline const typename RPArray::value_type RPArray::front() const { + PRECONDITION( !empty() ); + return *parr; +} + +//------------------------------------------------------------------------------- +// \ru Доступ к первому элементу. \en Access to the first element. +// --- +template +inline typename RPArray::value_type & RPArray::front() { + PRECONDITION( !empty() ); + return *parr; +} + +//------------------------------------------------------------------------------- +// \ru Доступ к последнему элементу. \en Access to the last element. +// --- +template +inline const typename RPArray::value_type RPArray::back() const +{ + PRECONDITION( !empty() ); + return *( parr + count - 1 ); +} + +//------------------------------------------------------------------------------- +// \ru Доступ к последнему элементу. \en Access to the last element. +// --- +template +inline typename RPArray::value_type & RPArray::back() +{ + PRECONDITION( !empty() ); + return *( parr + count - 1 ); +} + +//------------------------------------------------------------------------------- +// \ru Доступ по индексу. \en Access by index operator. +// --- +template +inline typename RPArray::const_reference RPArray::at( size_t idx ) const +{ + PRECONDITION( idx < count ); + return parr[idx]; +} + +//------------------------------------------------------------------------------- +// \ru Доступ по индексу. \en Access by index operator. +// --- +template +inline typename RPArray::reference RPArray::at( size_t idx ) +{ + PRECONDITION( idx < count ); + return parr[idx]; +} //------------------------------------------------------------------------------- // \ru обнулить количество элементов \en set the number of elements to null @@ -443,8 +505,7 @@ template inline size_t RPArray::FindIt( const Type * el ) const { // \ru MA Linux return find_in_array( *this, el ); // ошибка при сопоставлении шаблонов для RPArray \en MA Linux return find_in_array( *this, el ); // an error in matching of templates for RPArray - if ( parr ) - { + if ( parr ) { TPtr * iterLast = parr+count; TPtr * iter = std::find( parr, iterLast, el ); if ( iter != iterLast ) @@ -475,8 +536,10 @@ inline Type * RPArray::DetachInd( size_t delIndex ) if ( parr ) { PRECONDITION( delIndex < count ); - r = parr[delIndex]; - memmove( parr+delIndex, parr+delIndex+1, (count-- - delIndex - 1)*SIZE_OF_POINTER ); + if ( delIndex < count ) { + r = parr[delIndex]; + memmove( parr + delIndex, parr + delIndex + 1, ( count-- - delIndex - 1 ) * SIZE_OF_POINTER ); + } } return r; @@ -496,7 +559,7 @@ void RPArray::insert( Iterator pos, const Type * e ) } if ( begin() ) { const ptrdiff_t k = std::distance( (Iterator)begin(), pos ); - if ( k >= 0 && k <= count ) + if ( k >= 0 && k <= (ptrdiff_t)count ) Insert( k, const_cast(e) ); } } @@ -511,7 +574,7 @@ void RPArray::erase( Iterator pos ) { if ( begin() ) { const ptrdiff_t k = std::distance( (Iterator)begin(), pos ); - if ( k >= 0 && k < count ) + if ( k >= 0 && k < (ptrdiff_t)count ) RemoveInd( k ); } } @@ -528,7 +591,7 @@ void RPArray::erase( Iterator first, Iterator last ) const ptrdiff_t k1 = std::distance( (Iterator)begin(), first ); const ptrdiff_t k2 = std::distance( (Iterator)begin(), last ); - if ( k1 >= 0 && k1 < k2 && k2 <= count ) { + if ( k1 >= 0 && k1 < k2 && k2 <= (ptrdiff_t)count ) { for ( ptrdiff_t k = k2-1; k >= k1; k-- ) { RemoveInd( k ); } @@ -619,7 +682,7 @@ bool set_Rarray_size( RPArray & arr, size_t newSize ) arr.count = newSize < arr.count ? newSize : arr.count; #ifdef __REALLOC_ARRAYS_STATISTIC_ - ::ReallocArrayStatistic( oldParr, oldSize * SIZE_OF_POINTER, arr.parr, newSize * SIZE_OF_POINTER, 1/*RParray*/ ); + REALLOC_ARRAY_STATISTICS( oldParr, oldSize * SIZE_OF_POINTER, arr.parr, newSize * SIZE_OF_POINTER, 1/*RParray*/ ); #endif // __REALLOC_ARRAYS_STATISTIC_ } catch ( const std::bad_alloc & ) { diff --git a/C3d/Include/templ_rp_stack.h b/C3d/Include/templ_rp_stack.h index 7507e9f..0f01451 100644 --- a/C3d/Include/templ_rp_stack.h +++ b/C3d/Include/templ_rp_stack.h @@ -27,7 +27,7 @@ template class RPStack: private RPArray { public: - RPStack( size_t i_upper, uint16 i_delta = 1 ): RPArray( i_upper, i_delta ) {} + RPStack( size_t i_upper, uint16 i_delta = 1 ): RPArray( i_upper, i_delta ) {} public: void Push( Type & obj ); ///< \ru Добавить элемент в стек. \en Add an element to the stack. diff --git a/C3d/Include/templ_s_array.h b/C3d/Include/templ_s_array.h index c173f1b..7696137 100644 --- a/C3d/Include/templ_s_array.h +++ b/C3d/Include/templ_s_array.h @@ -21,25 +21,16 @@ #include #include -#if defined( __BORLANDC__ ) -#include -#endif - - -#ifdef __DEBUG_MEMORY_ALLOCATE_FREE_ -#include -#endif // __DEBUG_MEMORY_ALLOCATE_FREE_ - FORVARD_DECL_TEMPLATE_TYPENAME( class SArray ); -FORVARD_DECL_TEMPLATE_TYPENAME( bool set_array_size ( SArray &, size_t newSize, bool clear ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( Type * add_n_to_array ( SArray &, size_t n ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( bool set_array_size ( SArray &, size_t newSize, bool clear ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( Type * add_n_to_array ( SArray &, size_t n ) ); FORVARD_DECL_TEMPLATE_TYPENAME( size_t find_in_array ( const SArray &, const Type & object ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( bool fill_array ( SArray &, size_t fillCount, const Type & fillData ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( bool fill_array_zero( SArray &, size_t fillCount, size_t startIndex ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( reader & CALL_DECLARATION operator >> ( reader & in, SArray & ref ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( bool fill_array ( SArray &, size_t fillCount, const Type & fillData ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( bool fill_array_zero( SArray &, size_t fillCount, size_t startIndex ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( reader & CALL_DECLARATION operator >> ( reader & in, SArray & ref ) ); FORVARD_DECL_TEMPLATE_TYPENAME( writer & CALL_DECLARATION operator << ( writer & out, const SArray & ref ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( reader & CALL_DECLARATION operator >> ( reader & in, SArray *& ptr ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( reader & CALL_DECLARATION operator >> ( reader & in, SArray *& ptr ) ); FORVARD_DECL_TEMPLATE_TYPENAME( writer & CALL_DECLARATION operator << ( writer & out, const SArray * ptr ) ); @@ -138,13 +129,13 @@ public: bool operator == ( const SArray& w ) const; ///< \ru Оператор равенства. \en Equality operator. /// \ru Оператор доступа по индексу. \en Access by index operator. - Type & operator []( size_t loc ) const { PRECONDITION( loc < upper ); return parr[loc]; } + Type & operator []( size_t loc ) const; typedef int (*CompFunc)( const Type *, const Type * ); ///< \ru Шаблон функции сортировки. \en A template of sorting function. void Sort ( CompFunc comp = CompareSArrayItems ); ///< \ru Сортировать массив. По умолчанию сортирует в порядке возрастания. \en Sort the array. Sort in ascending order by default. - const Type * GetAddr() const { return parr; } ///< \ru Выдать адрес начала массива. \en Get the address of the beginning of the array. - const Type * GetEndAddr() const { return parr + count; } ///< \ru Выдать указатель конца (следующим за крайним). \en Get a pointer of the end (which follows the last element). + const Type * GetAddr() const { return parr; } ///< \ru Выдать адрес начала массива. \en Get the address of the beginning of the array. + const Type * GetEndAddr() const { return parr + count; } ///< \ru Выдать указатель конца (следующим за крайним). \en Get a pointer of the end (which follows the last element). /** \name \ru Унификация с контейнерами STL. @@ -172,16 +163,17 @@ public: template void assign ( Iterator first, Iterator last ); ///< \ru Присвоить массиву новое содержимое, заменив его текущее содержимое. \en Assign new contents to the array, replacing its current contents. void assign ( size_t n, const Type & val ) { resize( n, val ); } ///< \ru Присвоить массиву новое содержимое, заменив его текущее содержимое. \en Assign new contents to the array, replacing its current contents. - const Type * begin() const { return parr; } ///< \ru Получить указатель на первый элемент массива. \en Get the pointer to the first array element. - Type * begin() { return parr; } ///< \ru Получить указатель на первый элемент массива. \en Get the pointer to the first array element. - const Type * end() const { return parr + count; } ///< \ru Получить указатель на участок памяти после массива. \en Get the pointer to the piece of memory after the array. - Type * end() { return parr + count; } ///< \ru Получить указатель на участок памяти после массива. \en Get the pointer to the piece of memory after the array. - const Type * cbegin() const { return parr; } ///< \ru Получить указатель на первый элемент массива. \en Get the pointer to the first array element. - const Type * cend() const { return parr + count; } ///< \ru Получить указатель на участок памяти после массива. \en Get the pointer to the piece of memory after the array. - const Type & front() const { PRECONDITION(!empty()); return *parr; } - Type & front() { PRECONDITION(!empty()); return *parr; } - const Type & back() const { PRECONDITION(!empty()); return *(parr+count-1); } - Type & back() { PRECONDITION(!empty()); return *(parr+count-1); } + + const Type * begin() const { return parr; } ///< \ru Получить указатель на первый элемент массива. \en Get the pointer to the first array element. + Type * begin() { return parr; } ///< \ru Получить указатель на первый элемент массива. \en Get the pointer to the first array element. + const Type * end() const { return parr + count; } ///< \ru Получить указатель на участок памяти после массива. \en Get the pointer to the piece of memory after the array. + Type * end() { return parr + count; } ///< \ru Получить указатель на участок памяти после массива. \en Get the pointer to the piece of memory after the array. + const Type * cbegin() const { return parr; } ///< \ru Получить указатель на первый элемент массива. \en Get the pointer to the first array element. + const Type * cend() const { return parr + count; } ///< \ru Получить указатель на участок памяти после массива. \en Get the pointer to the piece of memory after the array. + const Type & front() const; + Type & front(); + const Type & back() const; + Type & back(); /** \} @@ -193,22 +185,22 @@ protected : size_t AutoDelta() const { return ::KsAutoDelta( count, delta ); } ///< \ru Вычислить автоприращение. \en Calculate autoincrement. /// \ru Перезахватить память. \en Reallocate memory. - TEMPLATE_FRIEND bool set_array_size TEMPLATE_SUFFIX ( SArray &, size_t newSize, bool clear ); + TEMPLATE_FRIEND bool set_array_size TEMPLATE_SUFFIX ( SArray &, size_t newSize, bool clear ); /// \ru Добавить памяти под n элментов массива и вернуть указатель на начало выделеного участка памяти. \en Add memory for n elements of the array and return a pointer to the beginning of the selected piece of memory. - TEMPLATE_FRIEND Type * add_n_to_array TEMPLATE_SUFFIX ( SArray &, size_t n ); + TEMPLATE_FRIEND Type * add_n_to_array TEMPLATE_SUFFIX ( SArray &, size_t n ); /// \ru Найти элемент в массиве. \en Find an element in the array. - TEMPLATE_FRIEND size_t find_in_array TEMPLATE_SUFFIX ( const SArray &, const Type &object ); + TEMPLATE_FRIEND size_t find_in_array TEMPLATE_SUFFIX ( const SArray &, const Type & object ); /// \ru Заполнить fillCount элементов массива копиями объекта fillData. \en Fill fillCount elements of the array by copies of the object fillData. - TEMPLATE_FRIEND bool fill_array TEMPLATE_SUFFIX ( SArray &, size_t fillCount, const Type & fillData ); + TEMPLATE_FRIEND bool fill_array TEMPLATE_SUFFIX ( SArray &, size_t fillCount, const Type & fillData ); /// \ru Заполнить fillCount элементов массива нулями. \en Fill fillCount elements of the array by nulls. - TEMPLATE_FRIEND bool fill_array_zero TEMPLATE_SUFFIX ( SArray &, size_t fillCount, size_t startIndex ); + TEMPLATE_FRIEND bool fill_array_zero TEMPLATE_SUFFIX ( SArray &, size_t fillCount, size_t startIndex ); /// \ru Оператор чтения. \en Read operator. - TEMPLATE_FRIEND reader & CALL_DECLARATION operator >> TEMPLATE_SUFFIX ( reader & in, SArray & ref ); + TEMPLATE_FRIEND reader & CALL_DECLARATION operator >> TEMPLATE_SUFFIX ( reader & in, SArray & ref ); /// \ru Оператор записи. \en Write operator. TEMPLATE_FRIEND writer & CALL_DECLARATION operator << TEMPLATE_SUFFIX ( writer & out, const SArray & ref ); /// \ru Оператор чтения. \en Read operator. - TEMPLATE_FRIEND reader & CALL_DECLARATION operator >> TEMPLATE_SUFFIX ( reader & in, SArray *& ptr ); + TEMPLATE_FRIEND reader & CALL_DECLARATION operator >> TEMPLATE_SUFFIX ( reader & in, SArray *& ptr ); /// \ru Оператор записи. \en Write operator. TEMPLATE_FRIEND writer & CALL_DECLARATION operator << TEMPLATE_SUFFIX ( writer & out, const SArray * ptr ); @@ -298,6 +290,55 @@ inline SArray::SArray( const std::vector & o ) } } +//------------------------------------------------------------------------------ +// \ru Оператор доступа по индексу. \en Access by index operator. +// --- +template +inline Type & SArray::operator []( size_t loc ) const +{ + return parr[loc]; +} + +//------------------------------------------------------------------------------ +// \ru Доступ к первому элементу. \en Access to th first element. +// --- +template +inline const Type & SArray::front() const +{ + PRECONDITION( !empty() ); + return *parr; +} + +//------------------------------------------------------------------------------ +// \ru Доступ к первому элементу. \en Access to th first element. +// --- +template +inline Type & SArray::front() +{ + PRECONDITION( !empty() ); + return *parr; +} + + +//------------------------------------------------------------------------------ +// \ru Доступ к последнему элементу. \en Access to th last element. +// --- +template +inline const Type & SArray::back() const +{ + PRECONDITION( !empty() ); + return *( parr + count - 1 ); +} + +//------------------------------------------------------------------------------ +// \ru Доступ к последнему элементу. \en Access to th last element. +// --- +template +inline Type &SArray::back() +{ + PRECONDITION( !empty() ); + return *( parr + count - 1 ); +} //------------------------------------------------------------------------------ // \ru Указать новый размер массива. \en Set the new size of an array. @@ -407,7 +448,7 @@ inline Type * SArray::Add( const Type & ent ) template inline Type * SArray::AddAfter( const Type & ent, size_t index ) { PRECONDITION( index < count ); - if ( CatchMemory() ) { + if ( index < count && CatchMemory() ) { memmove( parr + index + 2, parr + index + 1, sizeof(Type)*(count - index - 1) ); count++; @@ -424,7 +465,7 @@ inline Type * SArray::AddAfter( const Type & ent, size_t index ) { template inline Type * SArray::InsertInd( size_t index, const Type & ent ) { PRECONDITION( index <= count ); - if ( CatchMemory() ) { // \ru добавить памяти, если все использовано \en add memory if whole allocated memory is used + if ( index <= count && CatchMemory() ) { // \ru добавить памяти, если все использовано \en add memory if whole allocated memory is used if ( index >= count ) index = count; @@ -446,7 +487,7 @@ inline Type * SArray::InsertInd( size_t index, const Type & ent ) { template inline Type * SArray::InsertInd( size_t index ) { PRECONDITION( index <= count ); - if ( CatchMemory() ) { // \ru добавить памяти, если все использовано \en add memory if whole allocated memory is used + if ( index <= count && CatchMemory() ) { // \ru добавить памяти, если все использовано \en add memory if whole allocated memory is used // \ru передвинем вправо все элементы массива с последнего до указанного \en move to the right all elements of the array from the last to the specified one memmove( parr + index + 1, parr + index, (count - index) * sizeof(Type) ); @@ -506,7 +547,7 @@ template inline void SArray::RemoveInd( size_t idx ) { // RemoveInd( idx, idx+1 ); - if ( parr ) { + if ( parr && idx < count ) { Type * ptr = parr + idx; Remove( ptr, ptr+1 ); } @@ -526,7 +567,7 @@ void SArray::insert( Iterator pos, const Type & e ) } if ( begin() ) { const ptrdiff_t k = std::distance( (Iterator)begin(), pos ); - if ( k >= 0 && k <= count ) + if ( k >= 0 && k <= (ptrdiff_t)count ) InsertInd( k, e ); } } @@ -541,7 +582,7 @@ void SArray::erase( Iterator pos ) { if ( begin() ) { const ptrdiff_t k = std::distance( (Iterator)begin(), pos ); - if ( k >= 0 && k < count ) + if ( k >= 0 && k < (ptrdiff_t)count ) RemoveInd( k ); } } @@ -557,7 +598,7 @@ void SArray::erase( Iterator first, Iterator last ) const ptrdiff_t k1 = std::distance( (Iterator)begin(), first ); const ptrdiff_t k2 = std::distance( (Iterator)begin(), last ); - if ( k1 >= 0 && k1 < k2 && k2 <= count ) { + if ( k1 >= 0 && k1 < k2 && k2 <= (ptrdiff_t)count ) { RemoveInd( k1, k2 ); } } @@ -581,12 +622,14 @@ template inline void SArray::Remove( Type * firstItr, Type * lastItr ) { PRECONDITION( firstItr >= parr && firstItr < lastItr && (lastItr - parr) <= (ptrdiff_t)count ); - ptrdiff_t cpyCount = (parr + count) - lastItr; - if ( cpyCount > 0 ) { - memmove( static_cast(firstItr), static_cast(lastItr), cpyCount*sizeof(Type) ); + if ( firstItr >= parr && firstItr < lastItr ) { + ptrdiff_t copyCount = ( parr + count ) - lastItr; + if ( copyCount >= 0 ) { + if ( copyCount > 0 ) + memmove( static_cast( firstItr ), static_cast( lastItr ), copyCount * sizeof( Type ) ); + count -= ( lastItr - firstItr ); + } } - if ( lastItr - firstItr > 0 ) - count -= (lastItr-firstItr); } @@ -735,7 +778,7 @@ void SArray::assign( Iterator first, Iterator last ) { const ptrdiff_t newCount = std::distance( first, last ); if ( set_array_size(*this, newCount, true) ) { - PRECONDITION( newCount <= upper && count == 0 ); + PRECONDITION( newCount <= (ptrdiff_t)upper && count == 0 ); for ( ; first != last; ++first, ++count ) { parr[count] = *first; } @@ -798,7 +841,7 @@ bool set_array_size( SArray & arr, size_t newSize, bool clear ) arr.upper = newSize; #ifdef __REALLOC_ARRAYS_STATISTIC_ - ::ReallocArrayStatistic( oldParr, oldSize * sizeOfType, arr.parr, newSize * sizeOfType, 0/*SArray*/ ); + REALLOC_ARRAY_STATISTICS( oldParr, oldSize * sizeOfType, arr.parr, newSize * sizeOfType, 0/*SArray*/ ); #endif // __REALLOC_ARRAYS_STATISTIC_ } catch ( const std::bad_alloc & ) { @@ -883,17 +926,33 @@ size_t find_in_array( const SArray & arr, const Type & object ) { //------------------------------------------------------------------------------- -// \ru оператор сравнения двух массивов \en an operator of two arrays comparison +// \ru Оператор сравнения двух массивов. \en An operator of two arrays comparison. +/** + \details + \ru Осуществляется поэлементной стравнение с возможностью перегрузки + операции сравния с помощью метода IsEqualSArrayItems. + \en An element-by-element comparison is performed with the ability to redefine the + comparison operation using the overloading global function IsEqualSArrayItems. +*/ // --- template -inline bool SArray::operator == ( const SArray & w ) const { +inline bool SArray::operator == ( const SArray & w ) const +{ if ( count != w.count ) return false; + /* + При размещении в памяти с выравниванием не равным 1, между элементами массива + возможно появление "дырок" заполненного случайным мусором, т.к. сравнивать этот мусор + нам незачем, будем сравнивать содержимое массивов поэлементно (через оператор == или + путем перегрузки IsEqualSArrayItems). - // \ru OV K6 При размещении в памяти с выравниванием не равным 1, между элементами массива \en OV K6 While the memory allocation with alignment which is not equal 1 between elements of the array - // \ru возможно появление "дырок" заполненного случайным мусором, т.к. сравнивать этот мусор \en may appear "holes" filled by random trash, since there is not reason to compare this trash - // \ru нам незачем, будем сравнивать содержимое массивов поэлементно (через оператор == объекта) \en we will compare the content of arrays element by element (using the operator == of an object) - for ( size_t i = 0; i < count; i++ ) { + While the memory allocation with alignment which is not equal 1 between elements of + the array may appear "holes" filled by random trash, since there is not reason to + compare this trash we will compare the content of arrays element by element + (using the operator == of an object or by overloading the func IsEqualSArrayItems). + */ + for ( size_t i = 0; i < count; i++ ) + { if ( !::IsEqualSArrayItems( (*this)[i], w[i] ) ) return false; } diff --git a/C3d/Include/templ_s_list.h b/C3d/Include/templ_s_list.h index f8d56ec..77af271 100644 --- a/C3d/Include/templ_s_list.h +++ b/C3d/Include/templ_s_list.h @@ -14,12 +14,9 @@ #include #include #include +#include -#ifdef __DEBUG_MEMORY_ALLOCATE_FREE_ -#include -#endif //__DEBUG_MEMORY_ALLOCATE_FREE_ - //----------------------------------------------------------------------------- /** \brief \ru Элемент списка. @@ -149,7 +146,7 @@ public: PRECONDITION( nowDelItem == 0 && nowDelElem == 0 ); return first == 0; } // \ru проверить, пустой ли список \en check whether the list is empty - bool IsExist( const Type *d ) const { return is_exist_in_list(*this, d);} // \ru найти элемент по равенству указателей \en find an element by the equality of pointers + bool IsExist( const Type * d ) const { return is_exist_in_list(*this, d);} // \ru найти элемент по равенству указателей \en find an element by the equality of pointers Type * GetFirstData() const { PRECONDITION( nowDelItem == 0 && nowDelElem == 0 ); @@ -319,9 +316,9 @@ inline void List::Add( Type* data, const Type* after ) { // \ru добавить элемент в конец списка с проверкой на существование \en add an element in the end of the list with existence validation //--- template -inline void List::Add( Type* data, bool /*check*/ ) { +inline void List::Add( Type * data, bool /*check*/ ) +{ PRECONDITION( nowDelItem == 0 && nowDelElem == 0 ); - if ( !IsExist(data) ) Add( data ); } @@ -331,9 +328,9 @@ inline void List::Add( Type* data, bool /*check*/ ) { // \ru добавить элемент в конец списка \en add an element to the end of the list //--- template -inline void List::Add( ListItem &item ) { +inline void List::Add( ListItem & item ) +{ PRECONDITION( nowDelItem == 0 && nowDelElem == 0 ); - item.next = 0; if ( last ) @@ -351,7 +348,8 @@ inline void List::Add( ListItem &item ) { // \ru list после добавления становится пустым! \en a list becomes empty after the adding of it! //--- template -inline void List::AddAndEat( List &list ) { +inline void List::AddAndEat( List & list ) +{ PRECONDITION( nowDelItem == 0 && nowDelElem == 0 ); if ( list.first ) { @@ -376,7 +374,8 @@ inline void List::AddAndEat( List &list ) { // \ru вставить элемент в начало списка \en insert an element to the beginning of the list //--- template -inline void List::Insert( Type* data ) { +inline void List::Insert( Type * data ) +{ PRECONDITION( nowDelItem == 0 && nowDelElem == 0 ); Insert( *new ListItem(data) ); } @@ -386,7 +385,8 @@ inline void List::Insert( Type* data ) { // \ru вставить элемент в начало списка \en insert an element to the beginning of the list //--- template -inline void List::Insert( ListItem &item ) { +inline void List::Insert( ListItem & item ) +{ PRECONDITION( nowDelItem == 0 && nowDelElem == 0 ); ListItem* old = first; @@ -405,7 +405,8 @@ inline void List::Insert( ListItem &item ) { // \ru съесть список list в начало данного списка \en destroy and add a list to the beginning of the given list //--- template -inline void List::InsertAndEat( List &list ) { +inline void List::InsertAndEat( List & list ) +{ PRECONDITION( nowDelItem == 0 && nowDelElem == 0 ); if ( list.first ) { @@ -430,7 +431,8 @@ inline void List::InsertAndEat( List &list ) { // \ru удалить один элемент списка \en delete one element of the list //--- template -inline bool List::Remove( Type *del, DelType shdl ) { +inline bool List::Remove( Type * del, DelType shdl ) +{ PRECONDITION( nowDelItem == 0 && nowDelElem == 0 ); if ( Detach(del) ) { @@ -451,9 +453,9 @@ inline bool List::Remove( Type *del, DelType shdl ) { // \ru замкнуть список \en close the list //--- template -inline void List::Close() { +inline void List::Close() +{ PRECONDITION( nowDelItem == 0 && nowDelElem == 0 ); - if ( last ) last->next = first; } @@ -463,9 +465,9 @@ inline void List::Close() { // \ru разомкнуть список \en split the list //--- template -inline void List::Split() { +inline void List::Split() +{ PRECONDITION( nowDelItem == 0 && nowDelElem == 0 ); - if ( last ) last->next = 0; } @@ -481,11 +483,12 @@ inline void List::Split() { // \ru взять данные и продвинуть итератор \en take the data and move the iterator. //--- template -inline Type* LIterator::GetDataAndGo() { +inline Type * LIterator::GetDataAndGo() +{ PRECONDITION( list && list->nowDelItem == 0 && list->nowDelElem == 0 ); if ( curr ) { - Type* ret = curr->data; + Type * ret = curr->data; prev = curr; curr = curr->next; return ret; @@ -499,17 +502,20 @@ inline Type* LIterator::GetDataAndGo() { // \ru добавить элемент после текущего элемента \en add an element after the current element //--- template -inline void LIterator::Add( Type *data ) { +inline void LIterator::Add( Type * data ) +{ PRECONDITION( list && list->nowDelItem == 0 && list->nowDelElem == 0 ); - if ( curr ) { - ListItem *newItem = new ListItem( data, *curr ); // \ru поставит себя после curr \en inserts itself after 'curr' - if ( list->last == curr ) - list->last = newItem; - list->count++; + if ( list ) { + if ( curr ) { + ListItem * newItem = new ListItem( data, *curr ); // \ru поставит себя после curr \en inserts itself after 'curr' + if ( list->last == curr ) + list->last = newItem; + list->count++; + } + else + list->Add( data ); } - else - list->Add( data ); } @@ -517,10 +523,11 @@ inline void LIterator::Add( Type *data ) { // \ru съесть list после текущего элемента \en destroy a list and add it after the current element //--- template -inline void LIterator::AddAndEat( List& l ) { +inline void LIterator::AddAndEat( List & l ) +{ PRECONDITION( list && list->nowDelItem == 0 && list->nowDelElem == 0 ); - if ( l.first ) { + if ( list && l.first ) { if ( curr ) { PRECONDITION( l.last ); @@ -544,20 +551,23 @@ inline void LIterator::AddAndEat( List& l ) { // \ru вставить элемент перед текущим \en insert an element before the specified one //--- template -inline void LIterator::Insert( Type *data ) { +inline void LIterator::Insert( Type * data ) +{ PRECONDITION( list && list->nowDelItem == 0 && list->nowDelElem == 0 ); - if ( prev ) { - ListItem *newItem = new ListItem( data, *prev ); // \ru ставит себя после prev \en inserts itself after 'prev' + if ( list ) { + if ( prev ) { + ListItem * newItem = new ListItem( data, *prev ); // \ru ставит себя после prev \en inserts itself after 'prev' - if ( list->last == prev ) - list->last = newItem; + if ( list->last == prev ) + list->last = newItem; - prev = newItem; - list->count++; + prev = newItem; + list->count++; + } + else + list->Insert( data ); // \ru вставить элемент в начало списка \en insert an element to the beginning of the list } - else - list->Insert( data ); // \ru вставить элемент в начало списка \en insert an element to the beginning of the list } @@ -565,10 +575,11 @@ inline void LIterator::Insert( Type *data ) { // \ru съесть list перед моим текущим элементом \en destroy a list and add it before mine current element //--- template -inline void LIterator::InsertAndEat( List& l ) { +inline void LIterator::InsertAndEat( List & l ) +{ PRECONDITION( list && list->nowDelItem == 0 && list->nowDelElem == 0 ); - if ( l.first ) { + if ( list && l.first ) { if ( prev ) { PRECONDITION( l.last ); @@ -591,10 +602,11 @@ inline void LIterator::InsertAndEat( List& l ) { // \ru удалить текущий элемент итератора \en delete the current element of the iterator //--- template -inline void LIterator::Remove( DelType shdl ) { +inline void LIterator::Remove( DelType shdl ) +{ PRECONDITION( list && list->nowDelItem == 0 && list->nowDelElem == 0 ); - if ( curr ) { + if ( list && curr ) { if ( shdl==Delete || (shdl==defDelete && list->owns) ) { list->nowDelElem = curr->data; delete curr->data; @@ -610,10 +622,11 @@ inline void LIterator::Remove( DelType shdl ) { // \ru отсоединить элемент списка \en detach an element from the list //--- template -inline void LIterator::Detach() { +inline void LIterator::Detach() +{ PRECONDITION( list && list->nowDelItem == 0 && list->nowDelElem == 0 ); - if ( curr ) { + if ( list && curr ) { ListItem* next = curr->next; if ( prev ) @@ -637,7 +650,8 @@ inline void LIterator::Detach() { //------------------------------------------------------------------------------ template -void add_to_list( List &list, Type *data, const Type *after ) { +void add_to_list( List & list, Type * data, const Type * after ) +{ PRECONDITION( list.nowDelItem == 0 && list.nowDelElem == 0 ); if ( after ) { @@ -662,7 +676,8 @@ void add_to_list( List &list, Type *data, const Type *after ) { //------------------------------------------------------------------------------ template -void add_to_list( List &to, List &from ) { +void add_to_list( List & to, List & from ) +{ PRECONDITION( to.nowDelItem == 0 && to.nowDelElem == 0 ); PRECONDITION( from.nowDelItem == 0 && from.nowDelElem == 0 ); @@ -679,7 +694,8 @@ void add_to_list( List &to, List &from ) { //------------------------------------------------------------------------------ template -void insert_to_list( List &to, List &from ) { +void insert_to_list( List & to, List & from ) +{ PRECONDITION( to.nowDelItem == 0 && to.nowDelElem == 0 ); PRECONDITION( from.nowDelItem == 0 && from.nowDelElem == 0 ); @@ -698,7 +714,8 @@ void insert_to_list( List &to, List &from ) { // \ru Очистить лист с возможным удаленим данных \en Clear the list with data deletion // --- template -void remove_from_list( List &list, DelType shdl ) { +void remove_from_list( List & list, DelType shdl ) +{ PRECONDITION( list.nowDelItem == 0 && list.nowDelElem == 0 ); bool del = shdl==Delete || (shdl==defDelete && list.owns); @@ -728,7 +745,8 @@ void remove_from_list( List &list, DelType shdl ) { // \ru Очистить лист с возможным удаленим данных \en Clear the list with data deletion // --- template -void remove_from_list_release( List &list ) { +void remove_from_list_release( List & list ) +{ PRECONDITION( list.nowDelItem == 0 && list.nowDelElem == 0 ); ListItem *first = list.first; @@ -755,7 +773,8 @@ void remove_from_list_release( List &list ) { // \ru Отцепить один список от другого с возможным удалением данных \en Detach one list from another with data deletion // --- template -size_t remove_from_list( List &list, List &deList, DelType shdl ) { +size_t remove_from_list( List & list, List & deList, DelType shdl ) +{ PRECONDITION( &list != &deList ); PRECONDITION( list.nowDelItem == 0 && list.nowDelElem == 0 ); PRECONDITION( deList.nowDelItem == 0 && deList.nowDelElem == 0 ); @@ -849,7 +868,8 @@ size_t remove_from_list( List &list, List &deList, DelType shdl ) { //------------------------------------------------------------------------------ template -bool detach_from_list( List& from, const Type* del ) { +bool detach_from_list( List & from, const Type * del ) +{ PRECONDITION( from.nowDelItem == 0 && from.nowDelElem == 0 ); ListItem* curr = from.first; @@ -885,7 +905,8 @@ bool detach_from_list( List& from, const Type* del ) { //------------------------------------------------------------------------------ template -size_t recalc_list( List &list ) { +size_t recalc_list( List & list ) +{ PRECONDITION( list.nowDelItem == 0 && list.nowDelElem == 0 ); list.count = 0; @@ -900,12 +921,12 @@ size_t recalc_list( List &list ) { //------------------------------------------------------------------------------ template -bool is_exist_in_list( const List& list, const Type* what ) { +bool is_exist_in_list( const List & list, const Type * what ) +{ PRECONDITION( list.nowDelItem == 0 && list.nowDelElem == 0 ); - bool exist = false; - ListItem *curr = list.first; + ListItem * curr = list.first; while ( curr && !exist ) { exist = ( curr->data == what ); curr = curr->next; @@ -917,7 +938,8 @@ bool is_exist_in_list( const List& list, const Type* what ) { //------------------------------------------------------------------------------ template -ListItem* find_prev_in_list( const List& list, ListItem* now ) { +ListItem * find_prev_in_list( const List & list, ListItem * now ) +{ PRECONDITION( list.nowDelItem == 0 && list.nowDelElem == 0 ); if ( now ) { diff --git a/C3d/Include/templ_s_queue.h b/C3d/Include/templ_s_queue.h index 895f737..100802c 100644 --- a/C3d/Include/templ_s_queue.h +++ b/C3d/Include/templ_s_queue.h @@ -77,15 +77,15 @@ public: /// \ru Последний элемент очереди. \en The last element of the queue. const Type & Back() const; /// \ru Первый элемент очереди. \en The first element of the queue. - Type & Front() { PRECONDITION(Capacity() > 0 && !Empty() ); return *qp1; } + Type & Front(); /// \ru Первый элемент очереди. \en The first element of the queue. - const Type & Front() const { PRECONDITION(Capacity() > 0 && !Empty() ); return *qp1; } + const Type & Front() const; /// \ru Свойство пустого множества. \en A property of the empty set. bool Empty() const { return qp1 == qp2; } /// \ru Свойство исчерпанной памяти. \en An expended memory property. bool IsFull() const; /// \ru Самый первый, выходящий из очереди (корректно работает только для непустой очереди). \en The very first one outgoing from the queue (it works correctly only for nonempty queue). - Type & First() const { PRECONDITION( qp1 <= qlast && qp1 >= data && qp1 != qp2 ); return *qp1; } + Type & First() const; /// \ru Добавить в очередь и нарастить буфер выделенной памяти при необходимости. \en Add to the queue and increase the buffer of the allocated memory if it is necessary. void Push( const Type & obj ); /// \ru Вывести из очереди. \en Move out from queue. @@ -155,20 +155,49 @@ SQueue::~SQueue() // --- template inline const Type & SQueue::Back() const -{ +{ PRECONDITION( !Empty() && Capacity() > 0 ); - return qp2 == data ? *qlast : *(qp2-1); + return qp2 == data ? *qlast : *( qp2 - 1 ); } - //------------------------------------------------------------------------------- /// \ru Кто крайний? (корректно работает только для непустой очереди) \en Which is the last? (it works correctly only for nonempty queue) // --- template inline Type & SQueue::Back() -{ +{ PRECONDITION( !Empty() && Capacity() > 0 ); - return qp2 == data ? *qlast : *(qp2-1); + return qp2 == data ? *qlast : *( qp2 - 1 ); +} + +//------------------------------------------------------------------------------- +// \ru Первый элемент очереди. \en The first element of the queue. +// --- +template +inline Type & SQueue::Front() +{ + PRECONDITION( Capacity() > 0 && !Empty() ); + return *qp1; +} + +//------------------------------------------------------------------------------- +// \ru Первый элемент очереди. \en The first element of the queue. +// --- +template +inline const Type & SQueue::Front() const +{ + PRECONDITION( Capacity() > 0 && !Empty() ); + return *qp1; +} + +//------------------------------------------------------------------------------- +// \ru Самый первый, выходящий из очереди (корректно работает только для непустой очереди). \en The very first one outgoing from the queue (it works correctly only for nonempty queue). +// --- +template +inline Type & SQueue::First() const +{ + PRECONDITION( qp1 <= qlast && qp1 >= data && qp1 != qp2 ); + return *qp1; } @@ -183,7 +212,7 @@ inline Type & SQueue::Back() template inline bool SQueue::IsFull() const { - return _IncPtr( qp2 ) == qp1 || data == NULL; + return data == NULL || _IncPtr( qp2 ) == qp1; } @@ -207,10 +236,12 @@ template inline void SQueue::_Push( const Type & obj ) { PRECONDITION( !IsFull() && Size() < Capacity() ); - memcpy( qp2, &obj, sizeof(Type) ); - qp2 = _IncPtr( qp2 ); - PRECONDITION( !Empty() ); // \ru после добавления очередь не может быть пустой \en the queue may bacome empty after the adding - PRECONDITION( qp2 >= data && qp2 <= qlast ); + if ( !IsFull() && Size() < Capacity() ) { + memcpy( qp2, &obj, sizeof( Type ) ); + qp2 = _IncPtr( qp2 ); + PRECONDITION( !Empty() ); // \ru после добавления очередь не может быть пустой \en the queue may bacome empty after the adding + PRECONDITION( qp2 >= data && qp2 <= qlast ); + } } @@ -242,10 +273,12 @@ void SQueue::Push( const Type & obj ) template inline void SQueue::Pop( Type & obj ) { - PRECONDITION( qp2 != qp1 ); // \ru перед извлечением очередь не может быть пустой \en the queue may bacome empty before the extraction - memcpy( &obj, qp1, sizeof(Type) ); - qp1 = _IncPtr( qp1 ); - PRECONDITION( qp1 <= qlast && qp1 >= data ); + PRECONDITION( qp2 != qp1 ); // \ru перед извлечением очередь не может быть пустой \en the queue can't be empty before the extraction + if ( qp2 != qp1 ) { + memcpy( &obj, qp1, sizeof( Type ) ); + qp1 = _IncPtr( qp1 ); + PRECONDITION( qp1 <= qlast && qp1 >= data ); + } } @@ -255,9 +288,11 @@ inline void SQueue::Pop( Type & obj ) template inline void SQueue::Pop() { - PRECONDITION( qp2 != qp1 ); // \ru перед извлечением очередь не может быть пустой \en the queue may bacome empty before the extraction - qp1 = _IncPtr( qp1 ); - PRECONDITION( qp1 <= qlast && qp1 >= data ); + PRECONDITION( qp2 != qp1 ); // \ru перед извлечением очередь не может быть пустой \en the queue can't be empty before the extraction + if ( qp2 != qp1 ) { + qp1 = _IncPtr( qp1 ); + PRECONDITION( qp1 <= qlast && qp1 >= data ); + } } diff --git a/C3d/Include/templ_sfdp_array.h b/C3d/Include/templ_sfdp_array.h index 8a8ea3c..8afd418 100644 --- a/C3d/Include/templ_sfdp_array.h +++ b/C3d/Include/templ_sfdp_array.h @@ -138,7 +138,7 @@ using RPArray::back; \return \ru Вернет -1, если ближайший элемент, или индекс найденного элемента. \en Returns -1, if this is the nearest element, or the index of the same element.\~ */ - size_t FindNearest( const Type &el, Type *&found ) const; + size_t FindNearest( const Type & el, Type *& found ) const; /** \brief \ru Найти индекс элемента, используя функцию сравнения. @@ -148,7 +148,7 @@ using RPArray::back; \return \ru Вернет точно найденный элемент или NULL, если элемент не найден. \en Returns the found element or NULL, if element not found. */ - Type * FindExact ( const Type &el ) const; + Type * FindExact ( const Type & el ) const; /** \brief \ru Вернет true, если элемент найден, или false в противном случае. @@ -156,9 +156,9 @@ using RPArray::back; \details \ru Вернет true, если элемент найден, или false в противном случае. \en Returns true, if the element was found, or false otherwise. */ - bool IsExist ( const Type &el ) const; - Type* RemoveObj( Type *delObject, DelType=defDelete ); - bool DetachObj( const Type *delObject ); + bool IsExist ( const Type & el ) const; + Type* RemoveObj( Type * delObject, DelType=defDelete ); + bool DetachObj( const Type * delObject ); /** \brief \ru Найти индекс элемента, используя функцию поиска. @@ -170,21 +170,21 @@ using RPArray::back; \return \ru Вернет -1, если ближайший элемент, или индекс найденного элемента. \en Returns -1, if this is the nearest, or the index of the same element. */ - size_t SearchIt( size_t , SearchFunc, Type *&found ) const; + size_t SearchIt( size_t , SearchFunc, Type *& found ) const; /** \brief \ru Сортировать массив, используя функцию сравнения. \en Sort an array using the comparison function. */ - void Sort( size_t /*OV_x64 int*/ minInd = SYS_MAX_T/*OV_x64 -1*/, size_t /*OV_x64 int*/ maxInd = SYS_MAX_T/*OV_x64 -1*/ ); + void Sort( size_t minInd = SYS_MAX_T, size_t maxInd = SYS_MAX_T ); private: SFDPArray( const SFDPArray & ); // \ru запрещено !!! \en forbidden !!! SFDPArray& operator = ( const SFDPArray & ); // \ru запрещено !!! \en forbidden !!! - TEMPLATE_FRIEND size_t add_to_array TEMPLATE_SUFFIX ( SFDPArray& arr, Type& el, Type *&found, bool & added ); + TEMPLATE_FRIEND size_t add_to_array TEMPLATE_SUFFIX ( SFDPArray &, Type &, Type *& found, bool & added ); // \ru в found будет лежать найденный, или ближайший к искомому \en in the 'found' object the found element or the nearest to the required element will be stored - TEMPLATE_FRIEND size_t find_in_array TEMPLATE_SUFFIX ( const SFDPArray&, const Type&, Type *&found ); + TEMPLATE_FRIEND size_t find_in_array TEMPLATE_SUFFIX ( const SFDPArray &, const Type &, Type *& found ); // \ru Т.к. наследование от базового класса сделано private, то делаю доступ к операторам базового класса. // \en Since there is a private inheritance from the base class, I give an access to the operators of the base class. diff --git a/C3d/Include/templ_sfp_array.h b/C3d/Include/templ_sfp_array.h index 160ad1f..cb8f157 100644 --- a/C3d/Include/templ_sfp_array.h +++ b/C3d/Include/templ_sfp_array.h @@ -22,12 +22,12 @@ // //////////////////////////////////////////////////////////////////////////////// FORVARD_DECL_TEMPLATE_TYPENAME( class SFPArray ); -FORVARD_DECL_TEMPLATE_TYPENAME( Type * add_to_array ( SFPArray &, Type * ent, size_t & indexEnt ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( size_t find_from_array ( SFPArray &, const Type * ent ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( Type * find_from_array_by_key( SFPArray &, void * key, size_t & index ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( void qp_sort ( SFPArray &, bool always ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( reader& CALL_DECLARATION operator >> ( reader& in, SFPArray & ref ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( writer& CALL_DECLARATION operator << ( writer& out, const SFPArray & ref ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( Type * add_to_array ( SFPArray &, Type * ent, size_t & indexEnt ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( size_t find_from_array ( const SFPArray &, const Type * ent ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( Type * find_from_array_by_key( const SFPArray &, void * key, size_t & index ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( void qp_sort ( SFPArray &, bool always ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( reader & CALL_DECLARATION operator >> ( reader & in, SFPArray & ref ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( writer & CALL_DECLARATION operator << ( writer & out, const SFPArray & ref ) ); template @@ -79,7 +79,7 @@ public : Type * Add( Type *, size_t & indexEnt );// \ru добавить элемент с упорядочиванием по массиву, возвращает индекс \en add element with sorting, returns index of the element. void AddSimple( Type * ent ) { m_sort = false; PArray::Add( ent ); } // \ru Доступ к функции базового класса - добавить элемент в конец массива \en An access to the function of the base class - add an element to the end of the array // \ru найти элемент в упорядоченном массиве \en find an element in ordered array - size_t /*OV_x64 int*/ Find( const Type * ); + size_t Find( const Type * ); // \ru найти элемент по ключевым полям \en find an element by the key fields Type * FindByKey( void * key, size_t & index ); // \ru удалить элемент из массива \en delete an element from array @@ -91,21 +91,21 @@ public : // \ru проверить на соответствие сортировке элемент массива \en check that an element of the array corresponds to the sorting // \ru если меняли значение полей Type - условие сортировки может быть не выполнено \en if the values of fields Type have been changed then the comparison condition may be not performed // \ru если это так - флаг сортировки у массива снимается \en if it is so then the flag of sorting is switched off - void SortCheckByIndex( size_t /*OV_x64 int*/ index ); + void SortCheckByIndex( size_t index ); - TEMPLATE_FRIEND Type * add_to_array TEMPLATE_SUFFIX ( SFPArray &, Type * ent, size_t & indexEnt ); - TEMPLATE_FRIEND size_t find_from_array TEMPLATE_SUFFIX ( SFPArray &, const Type * ent ); - TEMPLATE_FRIEND Type * find_from_array_by_key TEMPLATE_SUFFIX ( SFPArray &, void * key, size_t & index ); - TEMPLATE_FRIEND void qp_sort TEMPLATE_SUFFIX ( SFPArray &, bool always ); + TEMPLATE_FRIEND Type * add_to_array TEMPLATE_SUFFIX ( SFPArray &, Type * ent, size_t & indexEnt ); + TEMPLATE_FRIEND size_t find_from_array TEMPLATE_SUFFIX ( const SFPArray &, const Type * ent ); + TEMPLATE_FRIEND Type * find_from_array_by_key TEMPLATE_SUFFIX ( const SFPArray &, void * key, size_t & index ); + TEMPLATE_FRIEND void qp_sort TEMPLATE_SUFFIX ( SFPArray &, bool always ); private: OBVIOUS_PRIVATE_COPY( SFPArray ) // \ru Т.к. наследование private, то сделаем здесь операторы чтения-записи \en Since there is a private inheritance, then make here operators of reading/writing //ID K8 KNOWN_OBJECTS_RW_REF_OPERATORS( SFPArray ) - // \ru OV_x64 но коду не нашел реализации операторров чтения/записи \en OV_x64 implementation of reading/witing operators was not found - TEMPLATE_FRIEND reader& CALL_DECLARATION operator >> TEMPLATE_SUFFIX ( reader& in, SFPArray & ref ); - TEMPLATE_FRIEND writer& CALL_DECLARATION operator << TEMPLATE_SUFFIX ( writer& out, const SFPArray & ref ); + // \ru OV_x64 но коду не нашел реализации операторов чтения/записи \en OV_x64 implementation of reading/writing operators was not found + TEMPLATE_FRIEND reader & CALL_DECLARATION operator >> TEMPLATE_SUFFIX ( reader & in, SFPArray & ref ); + TEMPLATE_FRIEND writer & CALL_DECLARATION operator << TEMPLATE_SUFFIX ( writer & out, const SFPArray & ref ); #ifdef __DEBUG_MEMORY_ALLOCATE_FREE_ public: @@ -345,7 +345,7 @@ Type * add_to_array( SFPArray & arr, Type * el, size_t & indexEl ) { // \ru поиск ведется методом половинных делений \en a search is performed by the bisection method // --- template -size_t find_from_array( SFPArray &arr, const Type* el ) +size_t find_from_array( const SFPArray & arr, const Type * el ) { C3D_ASSERT( arr.fCompare_m ); // \ru без функции сравнения массив бессмысленнен \en the array is useless without comparison function // \ru общий случай - элементов больше двух \en the common case - the number of elements is more than two @@ -400,7 +400,8 @@ size_t find_from_array( SFPArray &arr, const Type* el ) // // --- template -Type * find_from_array_by_key( SFPArray & arr, void * key, size_t & index ) { +Type * find_from_array_by_key( const SFPArray & arr, void * key, size_t & index ) +{ PRECONDITION( arr.fSearch_m && key ); // \ru без функции сравнения массив бессмысленнен \en the array is useless without comparison function // \ru общий случай - элементов больше двух \en the common case - the number of elements is more than two int res = 0; @@ -454,7 +455,8 @@ Type * find_from_array_by_key( SFPArray & arr, void * key, size_t & index // \ru Н.Вирт "Алгоритмы и структуры данных" 2е издание, Санкт-Петербург, 2001г., стр.111 \en see N.Wirth "Algorithms and Data Structures" // --- template -void qp_sort_r( SFPArray & arr, ptrdiff_t minInd, ptrdiff_t maxInd ) { +void qp_sort_r( SFPArray & arr, ptrdiff_t minInd, ptrdiff_t maxInd ) +{ //OV_x64 ===================== if ( arr.Count() > 1 ) { @@ -494,7 +496,8 @@ void qp_sort_r( SFPArray & arr, ptrdiff_t minInd, ptrdiff_t maxInd ) { // // --- template -void qp_sort( SFPArray & arr, bool always ) { +void qp_sort( SFPArray & arr, bool always ) +{ if ( !arr.m_sort || always ) { //OV_x64 ===================== if ( arr.Count() > 1 ) diff --git a/C3d/Include/templ_sp_array.h b/C3d/Include/templ_sp_array.h index f12a6a2..b8213d0 100644 --- a/C3d/Include/templ_sp_array.h +++ b/C3d/Include/templ_sp_array.h @@ -23,11 +23,11 @@ //////////////////////////////////////////////////////////////////////////////// FORVARD_DECL_TEMPLATE_TYPENAME( class SPArray ); -FORVARD_DECL_TEMPLATE_TYPENAME( Type * add_to_array( SPArray&, Type* ent, size_t & indexEnt ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( size_t find_from_array( SPArray&, const Type* ent ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( size_t find_from_array_spec( SPArray&, const Type* ent, bool& isPresent ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( reader& CALL_DECLARATION operator >> ( reader& in, SPArray & ref ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( writer& CALL_DECLARATION operator << ( writer& out, const SPArray & ref ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( Type * add_to_array ( SPArray &, Type * ent, size_t & indexEnt ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( size_t find_from_array ( const SPArray &, const Type * ent ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( size_t find_from_array_spec( const SPArray &, const Type * ent, bool & isPresent ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( reader & CALL_DECLARATION operator >> ( reader & in, SPArray & ref ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( writer & CALL_DECLARATION operator << ( writer & out, const SPArray & ref ) ); template class SPArray : protected PArray { @@ -50,24 +50,25 @@ public : using PArray::GetLast; using PArray::FindIt; - Type * Add( Type * ); // \ru добавить элемент с упорядочиванием по массиву \en add element with sorting + Type * Add( Type * ); // \ru добавить элемент с упорядочиванием по массиву \en add element with sorting Type * Add( Type *, size_t & indexEnt );// \ru добавить элемент с упорядочиванием по массиву, возвращает индекс \en add element with sorting, returns index of the element // \ru После "простого" добавления нет возможности восстановить сортированность массива \en After a "simple" adding there is no possibility to recover the sorting of the array void AddSimple ( Type * ent ) { PArray::Add( ent ); } // \ru Доступ к функции базового класса - добавить элемент в конец массива \en An access to the function of the base class - add an element to the end of the array // \ru Доступ к функции базового класса - добавить массив в конец массива \en An access to the function of the base class - add an array to the end of the array bool AddArraySimple( const RPArray & arr ) { return PArray::AddArray( arr ); } - Type * RemoveObj( Type *delObject, DelType = defDelete ); // \ru удалить элемент из массива \en delete an element from array + Type * RemoveObj( Type * delObject, DelType = defDelete ); // \ru удалить элемент из массива \en delete an element from array // \ru Доступ к функции базового класса - удалить элемент из массива \en An access to the function of the base class - delete an element from the array - Type * RemoveObjSimple( Type *delObject, DelType delType = defDelete ) { return PArray::RemoveObj( delObject, delType ); } ; // \ru удалить элемент из массива \en delete an element from array - size_t /*OV_x64 int*/ Find( const Type * ); // \ru найти элемент в упорядоченном массиве \en find an element in ordered array - size_t /*OV_x64 int*/ PossibleIndex( const Type *, bool& isPresent ); // \ru найти место в массиве, куда будет добавлен элемент ( без добавления ) \en find a place in the array for adding ann element (adding is not performed) - // \ru на выходе : isPresent == true - элемент уже в массиве \en in output : isPresent == true - the element is already in the array - bool IsExist( const Type * ); // \ru true если элемент найден \en true if the element was found + Type * RemoveObjSimple( Type * delObject, DelType delType = defDelete ) { return PArray::RemoveObj( delObject, delType ); } ; // \ru удалить элемент из массива \en delete an element from array - TEMPLATE_FRIEND Type * add_to_array TEMPLATE_SUFFIX ( SPArray&, Type* ent, size_t & indexEnt ); - TEMPLATE_FRIEND size_t /*OV_x64 int*/ find_from_array TEMPLATE_SUFFIX ( SPArray&, const Type* ent ); - TEMPLATE_FRIEND size_t /*OV_x64 int*/ find_from_array_spec TEMPLATE_SUFFIX ( SPArray&, const Type* ent, bool& isPresent ); + size_t Find( const Type * ) const; // \ru найти элемент в упорядоченном массиве \en find an element in ordered array + size_t PossibleIndex( const Type *, bool & isPresent ) const; // \ru найти место в массиве, куда будет добавлен элемент ( без добавления ) \en find a place in the array for adding an element (adding is not performed) + // \ru на выходе : isPresent == true - элемент уже в массиве \en in output : isPresent == true - the element is already in the array + bool IsExist( const Type * ) const; // \ru true если элемент найден \en true if the element was found + + TEMPLATE_FRIEND Type * add_to_array TEMPLATE_SUFFIX ( SPArray &, Type * ent, size_t & indexEnt ); + TEMPLATE_FRIEND size_t find_from_array TEMPLATE_SUFFIX ( const SPArray &, const Type * ent ); + TEMPLATE_FRIEND size_t find_from_array_spec TEMPLATE_SUFFIX ( const SPArray &, const Type * ent, bool & isPresent ); private: SPArray( const SPArray & ); // \ru запрещено !!! \en forbidden !!! @@ -75,8 +76,8 @@ private: // \ru Т.к. наследование private, то сделаем здесь операторы чтения-записи \en Since there is a private inheritance, then make here operators of reading/writing //ID K8 KNOWN_OBJECTS_RW_REF_OPERATORS( SPArray ) - TEMPLATE_FRIEND reader& CALL_DECLARATION operator >> TEMPLATE_SUFFIX ( reader& in, SPArray & ref ); - TEMPLATE_FRIEND writer& CALL_DECLARATION operator << TEMPLATE_SUFFIX ( writer& out, const SPArray & ref ); + TEMPLATE_FRIEND reader & CALL_DECLARATION operator >> TEMPLATE_SUFFIX ( reader & in, SPArray & ref ); + TEMPLATE_FRIEND writer & CALL_DECLARATION operator << TEMPLATE_SUFFIX ( writer & out, const SPArray & ref ); #ifdef __DEBUG_MEMORY_ALLOCATE_FREE_ public: @@ -109,7 +110,8 @@ inline void SPArray::operator delete( void * ptr, size_t size ) { // \ru добавить элемент с упорядочиванием по массиву \en add element with sorting // --- template -inline Type* SPArray::Add( Type* el ) { +inline Type * SPArray::Add( Type * el ) +{ size_t index; return add_to_array( *this, el, index ); } @@ -119,8 +121,7 @@ inline Type* SPArray::Add( Type* el ) { // \ru добавить элемент с упорядочиванием по массиву, возвращает индекс \en add element with sorting, returns index of the element // --- template -inline Type* SPArray::Add( Type* el, size_t & indexEl ) -{ +inline Type* SPArray::Add( Type * el, size_t & indexEl ) { return add_to_array( *this, el, indexEl ); } @@ -129,8 +130,7 @@ inline Type* SPArray::Add( Type* el, size_t & indexEl ) // \ru поиск объекта в массиве \en search of an element in array // --- template -inline size_t /*OV_x64 int*/ SPArray::Find( const Type * el ) -{ +inline size_t SPArray::Find( const Type * el ) const { return find_from_array( *this, el ); } @@ -139,7 +139,7 @@ inline size_t /*OV_x64 int*/ SPArray::Find( const Type * el ) // \ru Есть ли в массиве такой указатель \en Whether such pointer exists in the array // --- template -inline bool SPArray::IsExist( const Type *el ) { +inline bool SPArray::IsExist( const Type * el ) const { return find_from_array( *this, el ) != SYS_MAX_T; } @@ -161,7 +161,7 @@ inline Type * SPArray::RemoveObj( Type * delObject, DelType del ) // \ru на выходе : isPresent == true - элемент уже в массиве \en in output : isPresent == true - the element is already in the array // --- template -inline size_t SPArray::PossibleIndex( const Type* el, bool& isPresent ) { +inline size_t SPArray::PossibleIndex( const Type * el, bool & isPresent ) const { return find_from_array_spec( *this, el, isPresent ); } @@ -192,7 +192,7 @@ Type * add_to_array( SPArray & arr, Type * el, size_t & indexEl ) // \ru по логике правильнее было бы проверять сначала меньше, потом тождественно, а затем, уже \en it would be better to check at first whether it is less, then whether it is equal and only after this // \ru без сравнения - делать вывод что больше. \en conclude which is greater without comparison. // \ru НО! оператор сравнения, как правило более быстрый, чем оператор тождественности, \en BUT! the comparison operator is generally faster than identity operator, - // \ru и заведомо более часто используется( тождественно - финишная опреация в поиске ) \en and it is used more often (identity check is the last operation in search) + // \ru и заведомо более часто используется( тождественно - финишная операция в поиске ) \en and it is used more often (identity check is the last operation in search) // \ru и если поставить проверку тождественности впереди "больше" - можно получить торможение \en and if the identity check will be placed before the "greater" check then there may occur an inhibition // \ru на тяжелых операторах \en on heavy operators // \ru Кроме того, все три проверки делаются, дабы не отказывать программистам в их праве делать \en - @@ -234,7 +234,7 @@ Type * add_to_array( SPArray & arr, Type * el, size_t & indexEl ) // \ru специальные случаи \en special cases // \ru массив пустой - просто добавляем \en the array is empty - simply add - // \ru проверку на пустоту массива нельзя переносить за проверку границ - \en a check for array emptyness should not be moved outside a boundary check - + // \ru проверку на пустоту массива нельзя переносить за проверку границ - \en a check for array emptiness should not be moved outside a boundary check - // \ru можно вылететь (из-за mxc = -1) \en the error may occur (because of mxc = -1) if ( !arr.count ) { arr.PArray::Add( el ); @@ -282,7 +282,7 @@ Type * add_to_array( SPArray & arr, Type * el, size_t & indexEl ) // \ru поиск ведется методом половинных делений \en a search is performed by the bisection method // --- template -size_t find_from_array_spec( SPArray & arr, const Type * el, bool & isPresent ) +size_t find_from_array_spec( const SPArray & arr, const Type * el, bool & isPresent ) { isPresent = false; @@ -320,13 +320,13 @@ size_t find_from_array_spec( SPArray & arr, const Type * el, bool & isPres isPresent = true; return mn; } - else - if (*el == *arr/*.parr*/[mx]) { + else { + if ( *el == *arr/*.parr*/[mx] ) { isPresent = true; return mx; } else { - size_t md = ( mn + mx ) / 2; + size_t md = (mn + mx) / 2; if ( *arr/*.parr*/[md] < *el ) mn = md; else if ( *el < *arr/*.parr*/[md] ) @@ -336,6 +336,7 @@ size_t find_from_array_spec( SPArray & arr, const Type * el, bool & isPres return md; } } + } } return mx; @@ -352,7 +353,7 @@ size_t find_from_array_spec( SPArray & arr, const Type * el, bool & isPres // \ru Индекс объекта в массиве. Если объект не был найден, то возращается SYS_MAX_T. \en index of an object in array If the object was not found then SYS_MAX_T is returned. // --- template -size_t find_from_array( SPArray & arr, const Type * el ) +size_t find_from_array( const SPArray & arr, const Type * el ) { if ( el == NULL ) // \ru LF_Linux: добавил проверку на NULL \en LF_Linux: added a check for NULL return SYS_MAX_T; @@ -372,7 +373,7 @@ size_t find_from_array( SPArray & arr, const Type * el ) // \ru по логике правильнее было бы проверять сначала меньше, потом тождественно, а затем, уже \en it would be better to check at first whether it is less, then whether it is equal and only after this // \ru без сравнения - делать вывод что больше. \en conclude which is greater without comparison. // \ru НО! оператор сравнения, как правило более быстрый, чем оператор тождественности, \en BUT! the comparison operator is generally faster than identity operator, - // \ru и заведомо более часто используется( тождественно - финишная опреация в поиске ) \en and it is used more often (identity check is the last operation in search) + // \ru и заведомо более часто используется( тождественно - финишная операция в поиске ) \en and it is used more often (identity check is the last operation in search) // \ru и если поставить проверку тождественности впереди "больше" - можно получить торможение \en and if the identity check will be placed before the "greater" check then there may occur an inhibition // \ru на тяжелых операторах \en on heavy operators // \ru Кроме того, все три проверки делаются, дабы не отказывать программистам в их праве делать \en - diff --git a/C3d/Include/templ_sptr.h b/C3d/Include/templ_sptr.h index 9bd40ce..9fa0882 100644 --- a/C3d/Include/templ_sptr.h +++ b/C3d/Include/templ_sptr.h @@ -22,7 +22,7 @@ \ingroup Base_Tools_SmartPointers */ // --- -#define NULL_CHECK PRECONDITION( m_pI != NULL ); +#define NULL_CHECK PRECONDITION( m_pI != C3D_NULL_PTR ); //------------------------------------------------------------------------------ @@ -42,20 +42,20 @@ class SPtr public: /// \ru Конструктор. \en Constructor. - SPtr () : m_pI( NULL ) {} + SPtr () : m_pI( C3D_NULL_PTR ) {} /// \ru Конструктор по указателю. \en Constructor by pointer. explicit SPtr ( T * elem ) { - if ( (m_pI = elem) != NULL ) + if ( (m_pI = elem) != C3D_NULL_PTR ) m_pI->AddRef(); } /// \ru Конструктор копирования. \en Copy constructor. - SPtr( const SPtr & ptr ) : m_pI( NULL ) { assign(ptr.m_pI); } + SPtr( const SPtr & ptr ) : m_pI( C3D_NULL_PTR ) { assign(ptr.m_pI); } /// \ru Конструктор по совместимому указателю \en Constructor by compatible pointer template - SPtr( const SPtr<_T> & ptr ) : m_pI( ptr.get() ) { if ( m_pI != NULL ) { m_pI->AddRef();} } + SPtr( const SPtr<_T> & ptr ) : m_pI( ptr.get() ) { if ( m_pI != C3D_NULL_PTR ) { m_pI->AddRef();} } /// \ru Деструктор. \en Destructor. - ~SPtr() { if( m_pI != NULL ) m_pI->Release(); } + ~SPtr() { if( m_pI != C3D_NULL_PTR ) m_pI->Release(); } public: // \ru Перегрузка операторов \en Operators overloading /// \ru Оператор преобразования к типу T* . \en An operator for conversion to the type T*. @@ -102,16 +102,16 @@ public: // \ru Перегрузка операторов \en Operators overloadi public: /// \ru Функция присваивания указателем. \en A function of assignment by pointer. SPtr & assign( T * elem ); - /// \ru Фунция освобождения объекта. \en A function of release an object. - SPtr & reset( void ) { if( m_pI != NULL ) { m_pI->Release(); m_pI = NULL; } return *this; } + /// \ru Функция освобождения объекта. \en A function of release an object. + SPtr & reset( void ) { if( m_pI != C3D_NULL_PTR ) { m_pI->Release(); m_pI = C3D_NULL_PTR; } return *this; } /// \ru Функция доступа к элементу данных. \en A function of access to data element. T * get() const { return m_pI; } /// \ru Функция отсоединяет объект. \en A function detaches an object. - T * detach() { T * obj = m_pI; m_pI = NULL; if ( obj != NULL ) obj->DecRef(); return obj; } + T * detach() { T * obj = m_pI; m_pI = C3D_NULL_PTR; if ( obj != C3D_NULL_PTR ) obj->DecRef(); return obj; } /// \ru Нулевой указатель? \en Is null pointer? - bool is_null() const { return (( NULL == m_pI ) ? true : false ); } + bool is_null() const { return (( C3D_NULL_PTR == m_pI ) ? true : false ); } -#ifdef STANDARD_CPP11_RVALUE_REFERENCES +#ifdef C3D_STANDARD_CXX_11_PARTIAL public: /// \ru Конструктор перемещения. \en Moving constructor. SPtr( SPtr && src ) : m_pI ( src.m_pI ) @@ -126,7 +126,7 @@ public: src.m_pI = tmp; return *this; } -#endif // STANDARD_CPP11_RVALUE_REFERENCES +#endif // C3D_STANDARD_CXX_11_PARTIAL }; @@ -138,8 +138,8 @@ inline SPtr & SPtr::assign( T * elem ) { if ( m_pI != elem ) { - if ( elem != NULL ) { elem->AddRef(); } - if ( m_pI != NULL ) { m_pI->Release(); } + if ( elem != C3D_NULL_PTR ) { elem->AddRef(); } + if ( m_pI != C3D_NULL_PTR ) { m_pI->Release(); } m_pI = elem; } return *this; @@ -170,7 +170,7 @@ public: m_pI->AddRef(); } /// \ru Конструктор копирования. \en Copy constructor. - SRef( const SRef & src ): m_pI( src.m_pI ) + SRef( const SRef & src ): m_pI( src.m_pI ) { m_pI->AddRef(); } @@ -187,7 +187,7 @@ public: /// \ru Оператор доступа. \en An access operator. T & operator()() const { return *m_pI; } /// \ru Оператор проверки на равенство. \en An operator for equality check. - bool operator == ( const SRef & src ) const { return ( m_pI == src.m_pI ); } + bool operator == ( const SRef & src ) const { return ( m_pI == src.m_pI ); } /// \ru Оператор проверки на равенство. \en An operator for equality check. bool operator == ( T & pObj ) const { return ( m_pI == &pObj ); } /// \ru Оператор проверки на неравенство. \en An operator for inequality check. @@ -234,5 +234,67 @@ struct SPtrPair SPtrPair & operator = ( const SPtrPair & sPair ) { first = sPair.first; second = sPair.second; } }; +namespace c3d { + +#ifdef C3D_STANDARD_CXX_11 +//------------------------------------------------------------------------------ +// \ru Создать объект по параметрам и вернуть автоматический указатель на него. +// \en Create an object by parameters and return a smart pointer to it. +//--- +template +inline SPtr make_sptr( Args && ... args ) +{ + return SPtr( new T(std::forward( args )...) ); +} +#else +//------------------------------------------------------------------------------ +// \ru Создать объект по параметрам и вернуть автоматический указатель на него (может быть неэффективной в отсутствие C++11). +// \en Create an object by parameters and return a smart pointer to it (might be ineffective in the absence of C++11).. +//--- +template +inline SPtr make_sptr() +{ + return SPtr( new T() ); +} + +template +inline SPtr make_sptr( Arg1 & arg1 ) +{ + return SPtr( new T( arg1 ) ); +} + +template +inline SPtr make_sptr( Arg1 & arg1, Arg2 & arg2 ) +{ + return SPtr( new T( arg1, arg2 ) ); +} + +template +inline SPtr make_sptr( Arg1 & arg1, Arg2 & arg2, Arg3 & arg3 ) +{ + return SPtr( new T( arg1, arg2, arg3 ) ); +} + +template +inline SPtr make_sptr( Arg1 & arg1, Arg2 & arg2, Arg3 & arg3, Arg4 & arg4 ) +{ + return SPtr( new T( arg1, arg2, arg3, arg4 ) ); +} + +template +inline SPtr make_sptr( Arg1 & arg1, Arg2 & arg2, Arg3 & arg3, Arg4 & arg4, Arg5 & arg5 ) +{ + return SPtr( new T( arg1, arg2, arg3, arg4, arg5 ) ); +} + +template +inline SPtr make_sptr( Arg1 & arg1, Arg2 & arg2, Arg3 & arg3, Arg4 & arg4, Arg5 & arg5, Arg6 & arg6 ) +{ + return SPtr( new T( arg1, arg2, arg3, arg4, arg5, arg6 ) ); +} + +#endif + +} // namespace c3d #endif // __TEMPL_SPTR_H diff --git a/C3d/Include/templ_ss_array.h b/C3d/Include/templ_ss_array.h index 11eb29d..7c6cc2a 100644 --- a/C3d/Include/templ_ss_array.h +++ b/C3d/Include/templ_ss_array.h @@ -15,11 +15,11 @@ FORVARD_DECL_TEMPLATE_TYPENAME( class SSArray ); -FORVARD_DECL_TEMPLATE_TYPENAME( Type * add_to_array ( SSArray & arr, const Type & el, size_t & indexEl ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( size_t find_in_array ( const SSArray &, const Type & el ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( size_t find_from_array_spec ( const SSArray &, const Type & el, bool & isPresent ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( reader& CALL_DECLARATION operator >> ( reader& in, SSArray & ref ) ); -FORVARD_DECL_TEMPLATE_TYPENAME( writer& CALL_DECLARATION operator << ( writer& out, const SSArray & ref ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( Type * add_to_array ( SSArray &, const Type & el, size_t & indexEl ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( size_t find_in_array ( const SSArray &, const Type & el ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( size_t find_from_array_spec( const SSArray &, const Type & el, bool & isPresent ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( reader & CALL_DECLARATION operator >> ( reader & in, SSArray & ref ) ); +FORVARD_DECL_TEMPLATE_TYPENAME( writer & CALL_DECLARATION operator << ( writer & out, const SSArray & ref ) ); //------------------------------------------------------------------------------ /** \brief \ru Упорядоченный массив. @@ -69,10 +69,10 @@ public: Type * Add ( const Type &, size_t & indexEnt ); // \ru добавить элемент с упорядочиванием по массиву, возвращает индекс \en add element with sorting, returns index of the element // \ru используя эту функцию, пользователь несет всю ответственность за дальнейшую работу сортировки \en When using this function the user is fully responsible for the further work of the sorting process // \ru - сортировка может работать неправильно. \en - sorting may work incorrectly. - void AddSimple( const Type &ent ) { SArray::Add( ent ); } // \ru Доступ к функции базового класса - добавить элемент в конец массива \en An access to the function of the base class - add an element to the end of the array + void AddSimple( const Type & ent ) { SArray::Add( ent ); } // \ru Доступ к функции базового класса - добавить элемент в конец массива \en An access to the function of the base class - add an element to the end of the array size_t Find( const Type & ) const; // \ru найти элемент в упорядоченном массиве \en find an element in ordered array - size_t RemoveObj( const Type& delObject ); + size_t RemoveObj( const Type & delObject ); bool operator == ( const SSArray & ) const; // \ru сравнить два массива \en compare two arrays bool operator != ( const SSArray & ) const; // \ru сравнить два массива \en compare two arrays @@ -82,15 +82,15 @@ public: // \ru преобразование к базовому классу \en Convert to the base class const SArray & BaseClass() const { return *this; } - size_t PossibleIndex( const Type& ent, bool& isPresent ) const; // \ru найти место в массиве, куда будет добавлен элемент ( без добавления ) \en find a place in the array for adding ann element (adding is not performed) - // \ru на выходе : isPresent == true - элемент уже в массиве \en in output : isPresent == true - the element is already in the array + size_t PossibleIndex( const Type & ent, bool & isPresent ) const; // \ru найти место в массиве, куда будет добавлен элемент ( без добавления ) \en find a place in the array for adding ann element (adding is not performed) + // \ru на выходе : isPresent == true - элемент уже в массиве \en in output : isPresent == true - the element is already in the array - TEMPLATE_FRIEND Type * add_to_array TEMPLATE_SUFFIX ( SSArray & arr, const Type & el, size_t & indexEl ); + TEMPLATE_FRIEND Type * add_to_array TEMPLATE_SUFFIX ( SSArray &, const Type & el, size_t & indexEl ); TEMPLATE_FRIEND size_t find_in_array TEMPLATE_SUFFIX ( const SSArray &, const Type & el ); TEMPLATE_FRIEND size_t find_from_array_spec TEMPLATE_SUFFIX ( const SSArray &, const Type & el, bool & isPresent ); - TEMPLATE_FRIEND reader& CALL_DECLARATION operator >> TEMPLATE_SUFFIX ( reader& in, SSArray & ref ); - TEMPLATE_FRIEND writer& CALL_DECLARATION operator << TEMPLATE_SUFFIX ( writer& out, const SSArray & ref ); + TEMPLATE_FRIEND reader & CALL_DECLARATION operator >> TEMPLATE_SUFFIX ( reader & in, SSArray & ref ); + TEMPLATE_FRIEND writer & CALL_DECLARATION operator << TEMPLATE_SUFFIX ( writer & out, const SSArray & ref ); #ifdef __DEBUG_MEMORY_ALLOCATE_FREE_ public: @@ -190,7 +190,8 @@ inline Type * SSArray::Add( const Type & el, size_t & indexEl ) { // \ru удалить элемент из массива \en delete an element from array // --- template -inline size_t SSArray::RemoveObj( const Type & delObject ) { +inline size_t SSArray::RemoveObj( const Type & delObject ) +{ size_t ind = Find( delObject ); if ( ind != SYS_MAX_T ) RemoveInd( ind ); @@ -347,7 +348,7 @@ size_t find_in_array( const SSArray & arr, const Type & el ) // \ru по логике правильнее было бы проверять сначала меньше, потом тождественно, а затем, уже \en it would be better to check at first whether it is less, then whether it is equal and only after this // \ru без сравнения - делать вывод что больше. \en conclude which is greater without comparison. // \ru НО! оператор сравнения, как правило более быстрый, чем оператор тождественности, \en BUT! the comparison operator is generally faster than identity operator, - // \ru и заведомо более часто используется( тождественно - финишная опреация в поиске ) \en and it is used more often (identity check is the last operation in search) + // \ru и заведомо более часто используется( тождественно - финишная операция в поиске ) \en and it is used more often (identity check is the last operation in search) // \ru и если поставить проверку тождественности впереди "больше" - можно получить торможение \en and if the identity check will be placed before the "greater" check then there may occur an inhibition // \ru на тяжелых операторах \en on heavy operators // \ru Кроме того, все три проверки делаются, дабы не отказывать программистам в их праве делать \en - @@ -395,7 +396,8 @@ size_t find_in_array( const SSArray & arr, const Type & el ) // \ru поиск ведется методом половинных делений \en a search is performed by the bisection method // --- template -size_t find_from_array_spec( const SSArray & arr, const Type & el, bool & isPresent ) { +size_t find_from_array_spec( const SSArray & arr, const Type & el, bool & isPresent ) +{ isPresent = false; if ( !arr.count || el < arr/*.parr*/[0] ) diff --git a/C3d/Include/templ_stack.h b/C3d/Include/templ_stack.h index 3822185..8b0011f 100644 --- a/C3d/Include/templ_stack.h +++ b/C3d/Include/templ_stack.h @@ -1,86 +1,86 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Стек объектов. - \en A stack of objects. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - - -#ifndef __TEMPL_SSTACK_H -#define __TEMPL_SSTACK_H - - -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Стек объектов. - \en A stack of objects. \~ - \details \ru Стек объектов. \n - Для организации стека используем в качестве строителя SArray, - и отсекаем лишнее с помощью приватного наследования. \n - \en A stack of objects. \n - To organize stack SArray is used as the builder, - and all redundant is cut by using a private inheritance. \n \~ - \ingroup Base_Tools_Containers -*/ -// --- -template -class SStack: private SArray { -public: - /// \ru Конструктор. \en Constructor. - SStack( size_t i_upper = 0, uint16 i_delta = 1 ) - : SArray( i_upper, i_delta ) - {} -public: - void Push( const Type & obj ); ///< \ru Добавить элемент в стек. \en Add an element to the stack. - Type & Pop(); ///< \ru Извлечь один элемент стека, если возвращаетя NULL, значит достигнуто дно стека. \en Retrieve one element from the stack, if NULL is returned then the bottom of stack is reached. - Type & Top() const; ///< \ru Верхний элемент стека (последний внесенный). \en The top element of the stack (the last added). - - // \ru Оставить доступными следующие методы: \en Leave an access to the next methods: - using SArray::Flush; ///< \ru Очистить стек. \en Clear the stack. - using SArray::Count; ///< \ru Количество элементов, содержащихся в стеке. \en The number of elements in stack. - using SArray::IsExist; ///< \ru Существует ли элемент. \en Whether an element exists. - using SArray::operator[]; ///< \ru Оператор прямого доступа - работает, как для массива. \en An operator of a direct access - it works as for an array. - -private: - SStack( const SStack & ); ///< \ru (!) Без реализации \en (!) There is no implementation - void operator =( const SStack & ); ///< \ru (!) Без реализации \en (!) There is no implementation -}; - - -//------------------------------------------------------------------------------ -/// \ru Добавить элемент в стек \en Add an element to the stack -//--- -template -void SStack::Push( const Type & obj ) { - SArray::Add( obj ); -} - - -//------------------------------------------------------------------------------ -/// \ru Извлечь один элемент стека \en Retrieve one element from the stack -//--- -template -Type & SStack::Pop() { - if ( SArray::count > 0 ) { - Type & ret = (*this)[SArray::count-1]; - SArray::count--; - return ret; - } - return (*this)[0]; -} - - -//------------------------------------------------------------------------------ -/// \ru Верхний элемент стека \en The top element of the stack -//--- -template -Type & SStack::Top() const { - return (*this)[SArray::count-1]; -} - - -#endif // __TEMPL_SSTACK_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Стек объектов. + \en A stack of objects. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef __TEMPL_SSTACK_H +#define __TEMPL_SSTACK_H + + +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Стек объектов. + \en A stack of objects. \~ + \details \ru Стек объектов. \n + Для организации стека используем в качестве строителя SArray, + и отсекаем лишнее с помощью приватного наследования. \n + \en A stack of objects. \n + To organize stack SArray is used as the builder, + and all redundant is cut by using a private inheritance. \n \~ + \ingroup Base_Tools_Containers +*/ +// --- +template +class SStack: private SArray { +public: + /// \ru Конструктор. \en Constructor. + SStack( size_t i_upper = 0, uint16 i_delta = 1 ) + : SArray( i_upper, i_delta ) + {} +public: + void Push( const Type & obj ); ///< \ru Добавить элемент в стек. \en Add an element to the stack. + Type & Pop(); ///< \ru Извлечь один элемент стека, если возвращаетя NULL, значит достигнуто дно стека. \en Retrieve one element from the stack, if NULL is returned then the bottom of stack is reached. + Type & Top() const; ///< \ru Верхний элемент стека (последний внесенный). \en The top element of the stack (the last added). + + // \ru Оставить доступными следующие методы: \en Leave an access to the next methods: + using SArray::Flush; ///< \ru Очистить стек. \en Clear the stack. + using SArray::Count; ///< \ru Количество элементов, содержащихся в стеке. \en The number of elements in stack. + using SArray::IsExist; ///< \ru Существует ли элемент. \en Whether an element exists. + using SArray::operator[]; ///< \ru Оператор прямого доступа - работает, как для массива. \en An operator of a direct access - it works as for an array. + +private: + SStack( const SStack & ); ///< \ru (!) Без реализации \en (!) There is no implementation + void operator =( const SStack & ); ///< \ru (!) Без реализации \en (!) There is no implementation +}; + + +//------------------------------------------------------------------------------ +/// \ru Добавить элемент в стек \en Add an element to the stack +//--- +template +void SStack::Push( const Type & obj ) { + SArray::Add( obj ); +} + + +//------------------------------------------------------------------------------ +/// \ru Извлечь один элемент стека \en Retrieve one element from the stack +//--- +template +Type & SStack::Pop() { + if ( SArray::count > 0 ) { + Type & ret = (*this)[SArray::count-1]; + SArray::count--; + return ret; + } + return (*this)[0]; +} + + +//------------------------------------------------------------------------------ +/// \ru Верхний элемент стека \en The top element of the stack +//--- +template +Type & SStack::Top() const { + return (*this)[SArray::count-1]; +} + + +#endif // __TEMPL_SSTACK_H diff --git a/C3d/Include/tool_cstring.h b/C3d/Include/tool_cstring.h index b201ab8..93f1052 100644 --- a/C3d/Include/tool_cstring.h +++ b/C3d/Include/tool_cstring.h @@ -244,16 +244,9 @@ #endif #define _acstoi strtol // char* -> int32 -#ifdef __BORLANDC__ - #define _acstoi64 strtoll // char* -> int64 (long long) - #define _acstoui64 strtoull // char* -> uint64 (unsigned long long) - #define _tcstoi64 wcstoll // TCHAR* -> int64 (long long) - #define _tcstoui64 wcstoull // TCHAR* -> uint64 (unsigned long long) - //#define _tcstod // TCHAR* -> double -#else #define _acstoi64 _strtoi64 // char* -> int64 #define _acstoui64 _strtoui64 // char* -> uint64 -#endif + #define _acstoui strtoul // char* -> uint32 #define _acstod strtod // char* -> double diff --git a/C3d/Include/tool_enabler.h b/C3d/Include/tool_enabler.h index 58c8372..5a7151f 100644 --- a/C3d/Include/tool_enabler.h +++ b/C3d/Include/tool_enabler.h @@ -1,104 +1,104 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Включатель модулей ядра. - \en Kernel modules enabler \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef _TOOL_ENABLER_H_ -#define _TOOL_ENABLER_H_ - -#include - - -//------------------------------------------------------------------------------ -/** \brief \ru Включить модули ядра. - \en Enable kernel modules. \~ - \details \ru Включить соответствующие модули ядра. - \en Enable the corresponding kernel modules. \~ - \ingroup Base_Tools -*/ -// --- -MATH_FUNC (void) EnableMathModules( const char * name, int nameLength, const char * key, int keyLength ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить ключ активации на валидность. - \en Verify key. \~ - \details \ru Проверить ключ активации на валидность. - \en Verify key. \~ - \ingroup Base_Tools -*/ -// --- -MATH_FUNC (bool) VerifyLicenseKey( const char * name, const char * key, const char * pub_key ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить контроллер защиты моделировщика. - \en Check the controller of the Modeler. \~ - \details \ru Проверить контроллер защиты моделировщика. - \en Check the controller of the Modeler. \~ - \ingroup Base_Tools -*/ -// --- -MATH_FUNC (bool) IsMathModelerEnable(); - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить контроллер защиты конвертеров. - \en Check the controller of the Converter. \~ - \details \ru Проверить контроллер защиты конвертеров. - \en Check the controller of the Converter. \~ - \ingroup Base_Tools -*/ -// --- -MATH_FUNC (bool) IsMathConverterEnable(); - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить контроллер защиты решателя. - \en Check the controller of the Solver. \~ - \details \ru Проверить контроллер защиты решателя. - \en Check the controller of the Solver. \~ - \ingroup Base_Tools -*/ -// --- -MATH_FUNC (bool) IsMathSolverEnable(); - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить контроллер защиты визуализатора. - \en Check the controller of the Vision. \~ - \details \ru Проверить контроллер защиты визуализатора. - \en Check the controller of the Vision. \~ - \ingroup Base_Tools -*/ -// --- -MATH_FUNC (bool) IsMathVisionEnable(); - - -//------------------------------------------------------------------------------ -/** \brief \ru Проверить контроллер защиты преобразователя сеток в BRep. - \en Check the controller of the BShaper. \~ - \details \ru Проверить контроллер защиты преобразователя сеток в BRep. - \en Check the controller of the BShaper. \~ - \ingroup Base_Tools -*/ -// --- -MATH_FUNC (bool) IsMathBShaperEnable(); - - -//------------------------------------------------------------------------------ -/** \brief \ru Отпустить контролера работы модулей ядра. - \en Free the controller of the kernel modules work. \~ - \details \ru Отпустить контролера работы модулей ядра. - \en Free the controller of the kernel modules work. \~ - \ingroup Base_Tools -*/ -// --- -MATH_FUNC (void) FreeMathModulesChecker(); - - -#endif // _TOOL_ENABLER_H_ +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Включатель модулей ядра. + \en Kernel modules enabler \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _TOOL_ENABLER_H_ +#define _TOOL_ENABLER_H_ + +#include + + +//------------------------------------------------------------------------------ +/** \brief \ru Включить модули ядра. + \en Enable kernel modules. \~ + \details \ru Включить соответствующие модули ядра. + \en Enable the corresponding kernel modules. \~ + \ingroup Base_Tools +*/ +// --- +MATH_FUNC (void) EnableMathModules( const char * name, int nameLength, const char * key, int keyLength ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить ключ активации на валидность. + \en Verify key. \~ + \details \ru Проверить ключ активации на валидность. + \en Verify key. \~ + \ingroup Base_Tools +*/ +// --- +MATH_FUNC (bool) VerifyLicenseKey( const char * name, const char * key, const char * pub_key ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить контроллер защиты моделировщика. + \en Check the controller of the Modeler. \~ + \details \ru Проверить контроллер защиты моделировщика. + \en Check the controller of the Modeler. \~ + \ingroup Base_Tools +*/ +// --- +MATH_FUNC (bool) IsMathModelerEnable(); + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить контроллер защиты конвертеров. + \en Check the controller of the Converter. \~ + \details \ru Проверить контроллер защиты конвертеров. + \en Check the controller of the Converter. \~ + \ingroup Base_Tools +*/ +// --- +MATH_FUNC (bool) IsMathConverterEnable(); + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить контроллер защиты решателя. + \en Check the controller of the Solver. \~ + \details \ru Проверить контроллер защиты решателя. + \en Check the controller of the Solver. \~ + \ingroup Base_Tools +*/ +// --- +MATH_FUNC (bool) IsMathSolverEnable(); + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить контроллер защиты визуализатора. + \en Check the controller of the Vision. \~ + \details \ru Проверить контроллер защиты визуализатора. + \en Check the controller of the Vision. \~ + \ingroup Base_Tools +*/ +// --- +MATH_FUNC (bool) IsMathVisionEnable(); + + +//------------------------------------------------------------------------------ +/** \brief \ru Проверить контроллер защиты преобразователя сеток в BRep. + \en Check the controller of the BShaper. \~ + \details \ru Проверить контроллер защиты преобразователя сеток в BRep. + \en Check the controller of the BShaper. \~ + \ingroup Base_Tools +*/ +// --- +MATH_FUNC (bool) IsMathBShaperEnable(); + + +//------------------------------------------------------------------------------ +/** \brief \ru Отпустить контролера работы модулей ядра. + \en Free the controller of the kernel modules work. \~ + \details \ru Отпустить контролера работы модулей ядра. + \en Free the controller of the kernel modules work. \~ + \ingroup Base_Tools +*/ +// --- +MATH_FUNC (void) FreeMathModulesChecker(); + + +#endif // _TOOL_ENABLER_H_ diff --git a/C3d/Include/tool_err_handling.h b/C3d/Include/tool_err_handling.h index 40f98ba..96c30f9 100644 --- a/C3d/Include/tool_err_handling.h +++ b/C3d/Include/tool_err_handling.h @@ -1,73 +1,137 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Сервис для обработки ошибок. - \en Error-handling services. \~ -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __TOOL_ERR_HANDLING_H -#define __TOOL_ERR_HANDLING_H - -#include - - -//------------------------------------------------------------------------------ -/** - \brief \ru Определение режима обработки исключений. - \en Definition of mode for exception handling. \~ - \details \ru Определяет, пробрасывать ли исключение дальше после его обработки. По умолчанию исключения подавляются. \n - \en Defines, whether to throw exception further after its processing. By default, exceptions are suppressed. \~ - \ingroup Base_Tools -*/ -//--- -class MATH_CLASS ExceptionMode -{ - static bool sExptEnabled; -public: - // \ru Установить режим обработки исключений: true, чтобы пробрасывать исключения дальше; false, чтобы подавлять исключения. - // Возвращает предыдущий режим. - // \en Set mode for exception handling: true - to throw exceptions further; false - to suppress exceptions. - // Return the previous mode. - static bool Enable( bool enabled = true ); - - // \ru Получить текущий режим обработки исключений (true - пробрасывать исключение дальше; false - нет). - // \en Get current exception handling mode (true - to throw exception further; false - to not throw). - static bool IsEnabled(); -}; - -//------------------------------------------------------------------------------ -/** - \brief \ru Меняет режим обработки исключений в области видимости. - \en Alter mode for exception handling in a scope. \~ - \ingroup Base_Tools -*/ -//--- -class MATH_CLASS ScopedExceptionMode -{ - bool m_oldMode; -public: - ScopedExceptionMode( bool mode = true ) { m_oldMode = ExceptionMode::Enable( mode ); } - ~ScopedExceptionMode() { ExceptionMode::Enable( m_oldMode ); } -}; - - -//------------------------------------------------------------------------------ -/** - \brief \ru Бросить указанное исключение, если режим позволяет. - \en Throw the specified exception if allowed by the exception mode. \~ - \ingroup Base_Tools -*/ -//--- -#define C3D_CONTROLED_THROW_EX(expt) if( ExceptionMode::IsEnabled() ) throw expt; - -//------------------------------------------------------------------------------ -/** - \brief \ru Бросить исключение, если режим позволяет. - \en Throw exception if allowed by the mode for exception handling. \~ - \ingroup Base_Tools -*/ -//--- -#define C3D_CONTROLED_THROW if( ExceptionMode::IsEnabled() ) throw; - -#endif // __TOOL_ERR_HANDLING_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Сервис для обработки ошибок. + \en Error-handling services. \~ +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __TOOL_ERR_HANDLING_H +#define __TOOL_ERR_HANDLING_H + +#include +#include +#include +#include + +#include // \ru Внимание! Читаем внимательно! Если вытереть отсюда, то подключить везде, где есть функции из этого файла! \en Attention! Read carefully! If remove this from here, then it should be included anywhere where the functions from this file exist! + + +//------------------------------------------------------------------------------ +/** + \brief \ru Определение режима обработки исключений. + \en Definition of mode for exception handling. \~ + \details \ru Определяет, пробрасывать ли исключение дальше после его обработки. По умолчанию исключения подавляются. \n + \en Defines, whether to throw exception further after its processing. By default, exceptions are suppressed. \~ + \ingroup Base_Tools +*/ +//--- +class MATH_CLASS ExceptionMode +{ + static bool sExptEnabled; +public: + /** \brief \ru Установить режим обработки исключений: true, чтобы пробрасывать исключения дальше; false, чтобы подавлять исключения. + Возвращает предыдущий режим. + \en Set mode for exception handling: true - to throw exceptions further; false - to suppress exceptions. + Return the previous mode. + */ + static bool Enable( bool enabled = true ); + + /** \brief \ru Получить текущий режим обработки исключений (true - пробрасывать исключение дальше; false - нет). + \en Get current exception handling mode (true - to throw exception further; false - to not throw). + */ + static bool IsEnabled(); +}; + +//------------------------------------------------------------------------------ +/** + \brief \ru Меняет режим обработки исключений в области видимости. + \en Alter mode for exception handling in a scope. \~ + \ingroup Base_Tools +*/ +//--- +class MATH_CLASS ScopedExceptionMode +{ + bool m_oldMode; +public: + ScopedExceptionMode( bool mode = true ) { m_oldMode = ExceptionMode::Enable( mode ); } + ~ScopedExceptionMode() { ExceptionMode::Enable( m_oldMode ); } +}; + + +//------------------------------------------------------------------------------ +/** + \brief \ru Бросить указанное исключение, если режим позволяет. + \en Throw the specified exception if allowed by the exception mode. \~ + \ingroup Base_Tools +*/ +//--- +#define C3D_CONTROLED_THROW_EX(expt) if( ExceptionMode::IsEnabled() ) throw expt; + +//------------------------------------------------------------------------------ +/** + \brief \ru Бросить исключение, если режим позволяет. + \en Throw exception if allowed by the mode for exception handling. \~ + \ingroup Base_Tools +*/ +//--- +#define C3D_CONTROLED_THROW if( ExceptionMode::IsEnabled() ) throw; + +//------------------------------------------------------------------------------ +/** + \brief \ru Обработчик фатальных ошибок. + \en Handler of fatal errors. \~ + \details \ru Обработчик фатальных ошибок. + Перед началом мониторинга должна вызываться функция Init(). + При выходе из проблемной области должна вызываться функция Recover(). \n + \en Handler of fatal errors. + Before starting error monitoring the Init() function should be called. + On exit a faulty region, the Recover() function should be called. \~ + \ingroup Base_Tools +*/ +//--- +class MATH_CLASS FatalErrorHandler +{ +public: + + /** \brief \ru Получить последнюю ошибку. \en Get the last error. + */ + static MbResultType GetError(); + + /** \brief \ru Есть ли фатальная ошибка. \en Is there a fatal error. + */ + static bool HasError(); + + /** \brief \ru Зарегистрировать фатальную ошибку. Возвращает эту ошибку. + \en Register a fatal error. Return this error. + */ + static MbResultType SetError( MbResultType ); + + /** \brief \ru Инициализировать обработчик перед началом мониторинга ошибок. + Возвращает true, если обработчик стартовал, или false, если обработчик уже работает. + Парная функция Recover() должна вызываться, только если Init() вернул true. + \en Initialize handler before starting error monitoring. + Returns true if the handler is started or false if the handler is already working. + The paired function Recover() should be called only if Init() returnes true. + */ + static bool Init(); + + /** \brief \ru Восстановиться, если обнаружена фатальная ошибка. + \en Recover if a fatal error is detected. + */ + static void Recover(); + + /** \brief \ru Достаточно ли памяти для работы. \en Whether is enough memory to work. + */ + static bool IsEnoughMemory(); + + /** \brief \ru Установить допустимый порог памяти для работы (Mb). \en Set an acceptable memory threshold for work (Mb). + */ + static void SetMemoryThreshold( double ); + + /** \brief \ru Установить порог памяти по умолчанию. \en Set default memory threshold. + */ + static void ResetMemoryThreshold(); +}; + +#endif // __TOOL_ERR_HANDLING_H diff --git a/C3d/Include/tool_log.h b/C3d/Include/tool_log.h index 7001f1c..55854c0 100644 --- a/C3d/Include/tool_log.h +++ b/C3d/Include/tool_log.h @@ -1,124 +1,124 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Логирование информации. - \en Information logging. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __TOOL_LOG_H -#define __TOOL_LOG_H - -#include -#include -#include - -//------------------------------------------------------------------------------ -// \ru Потокобезопасные интерфейсы для ведение журнала сообщений и записи его в файл. -// Для каждого потока ведется отдельный лог, который записывается в отдельный файл. -// Доступны в дебаге. -// \en Thread-safe interfaces for logging messages and writing the log to the file. -// Keep a separate log for each thread which is saved to a separate file. -// Available in debug configuration. -// -/* \ru Пример использования. \en Usage sample. - int k = 0; - START_LOGGING; // \ru Начинаем логирование. \en Start logging. - LOG_MSG( _T("Samplelog") ); // \ru Добавляем указанную строку в лог. \en Put a specified string to the log. - ... - // \ru Форматируем строку лога (оператор << Logger::Endl добавляет ее в лог). \en Format a log line (operator << Logger::Endl puts it to the log). - Logger::Get() << _T("Value ") << k << Logger::Endl; - ... - WRITE_LOG_FILE( _T("sample.log") ); // \ru Записываем лог в файл. \en Write the log to the file. - END_LOGGING; // \ru Заканчиваем логирование. \en Stop logging. -*/ -// --- -#ifdef C3D_DEBUG - -//------------------------------------------------------------------------------ -// \ru Класс позволяет форматировать строку для лога и добавлять ее в лог. -// \en Class allows to format a line to the log and put it to the log. -// --- -class MATH_CLASS Logger -{ -public: - // \ru Получить логгер. \en Get the logger. - static Logger& Get(); - - // \ru Следующие методы позволяют форматировать строку для лога. Работают с текущей строкой лога. - // \en Next methods allow to format a line to the log. Work with the current line of the log. - - // \ru Добавить строку в текущую строку лога. \en Add a string to the current line of the log. - virtual Logger& operator << ( const TCHAR * ) = 0; - // \ru Добавить integer в текущую строку лога. \en Add integer to the current line of the log. - virtual Logger& operator << ( const int & ) = 0; -#if defined(PLATFORM_64) // \ru x32 совпадение типов ptrdiff_t и int \en x32 coincidence of ptrdiff_t and int types - // \ru Добавить ptrdiff_t в текущую строку лога. \en Add ptrdiff_t to the current line of the log. - virtual Logger& operator << ( const ptrdiff_t & ) = 0; -#endif - // \ru Добавить size_t в текущую строку лога. \en Add size_t to the current line of the log. - virtual Logger& operator << ( const size_t & ) = 0; - // \ru Добавить double в текущую строку лога. \en Add double to the current line of the log. - virtual Logger& operator << ( const double & ) = 0; - // \ru Завершить форматирование текущей строки и добавить ее в лог. Следующий вызов оператора << создаст новую текущую строку. - // \en Finish formatting of the current line and add it to the log. Next call to the operator << will create new current line. - virtual Logger& operator << ( Logger& (*man)( Logger& ) ) = 0; - - // \ru Манипулятор-признак завершения форматирования текущей строки лога. - // \en Manipulator-indicator of finishing formatting of the current line of the log. - static Logger& Endl( Logger& ); -}; - -// \ru Начать или закончить логирование. \en Start or stop logging. -MATH_FUNC(void) SetLogging( bool allow ); - -// \ru Записать лог в файл. Лог для каждого потока записывается в отдельный файл. -// \en Write the log to the file. Log of each thread writes to a separate file. -MATH_FUNC(void) WriteLog( const TCHAR *fileName ); - -// \ru Записать указанную строку в лог. \en Write a specified string to the log. -MATH_FUNC(void) LogMessage( const c3d::string_t &msg ); - -// \ru Макросы для операций логирования. \en Logging macros. - -// \ru Начать логирование. \en Start logging. -#define START_LOGGING SetLogging( true ); -// \ru Закончить логирование. \en Stop logging. -#define END_LOGGING SetLogging( false ); -// \ru Положить форматированную строку в лог. \en Put a formatted string to the log. -#define LOG_MSG(msg) LogMessage( msg ); -// \ru Записать лог в файл. Лог для каждого потока записывается в отдельный файл. -// \en Write the log to the file. Log of each thread writes to a separate file. -#define WRITE_LOG_FILE(fileName) WriteLog( fileName ); - -#else -inline void CALL_DECLARATION SetLogging( bool ){} -inline void CALL_DECLARATION WriteLog( const TCHAR * ){} -inline void CALL_DECLARATION LogMessage( const c3d::string_t & ){} - -#define START_LOGGING -#define END_LOGGING -#define LOG_MSG(msg) -#define WRITE_LOG_FILE(fileName) - -#endif - - -namespace c3d //namespace c3d -{ - -//------------------------------------------------------------------------------ -/** \brief \ru Включить контроль утечек памяти. - \en Enable memory leakage control. \~ - \details \ru Включить контроль утечек памяти. - \en Enable memory leakage control. \~ - \ingroup Base_Tools -*/ -// --- -MATH_FUNC(void) EnableMemoryLeakDump(); - -} //namespace c3d - - -#endif // __TOOL_LOG_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Логирование информации. + \en Information logging. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __TOOL_LOG_H +#define __TOOL_LOG_H + +#include +#include +#include + +//------------------------------------------------------------------------------ +// \ru Потокобезопасные интерфейсы для ведение журнала сообщений и записи его в файл. +// Для каждого потока ведется отдельный лог, который записывается в отдельный файл. +// Доступны в дебаге. +// \en Thread-safe interfaces for logging messages and writing the log to the file. +// Keep a separate log for each thread which is saved to a separate file. +// Available in debug configuration. +// +/* \ru Пример использования. \en Usage sample. + int k = 0; + START_LOGGING; // \ru Начинаем логирование. \en Start logging. + LOG_MSG( _T("Samplelog") ); // \ru Добавляем указанную строку в лог. \en Put a specified string to the log. + ... + // \ru Форматируем строку лога (оператор << Logger::Endl добавляет ее в лог). \en Format a log line (operator << Logger::Endl puts it to the log). + Logger::Get() << _T("Value ") << k << Logger::Endl; + ... + WRITE_LOG_FILE( _T("sample.log") ); // \ru Записываем лог в файл. \en Write the log to the file. + END_LOGGING; // \ru Заканчиваем логирование. \en Stop logging. +*/ +// --- +#ifdef C3D_DEBUG + +//------------------------------------------------------------------------------ +// \ru Класс позволяет форматировать строку для лога и добавлять ее в лог. +// \en Class allows to format a line to the log and put it to the log. +// --- +class MATH_CLASS Logger +{ +public: + // \ru Получить логгер. \en Get the logger. + static Logger& Get(); + + // \ru Следующие методы позволяют форматировать строку для лога. Работают с текущей строкой лога. + // \en Next methods allow to format a line to the log. Work with the current line of the log. + + // \ru Добавить строку в текущую строку лога. \en Add a string to the current line of the log. + virtual Logger& operator << ( const TCHAR * ) = 0; + // \ru Добавить integer в текущую строку лога. \en Add integer to the current line of the log. + virtual Logger& operator << ( const int & ) = 0; +#if defined(PLATFORM_64) // \ru x32 совпадение типов ptrdiff_t и int \en x32 coincidence of ptrdiff_t and int types + // \ru Добавить ptrdiff_t в текущую строку лога. \en Add ptrdiff_t to the current line of the log. + virtual Logger& operator << ( const ptrdiff_t & ) = 0; +#endif + // \ru Добавить size_t в текущую строку лога. \en Add size_t to the current line of the log. + virtual Logger& operator << ( const size_t & ) = 0; + // \ru Добавить double в текущую строку лога. \en Add double to the current line of the log. + virtual Logger& operator << ( const double & ) = 0; + // \ru Завершить форматирование текущей строки и добавить ее в лог. Следующий вызов оператора << создаст новую текущую строку. + // \en Finish formatting of the current line and add it to the log. Next call to the operator << will create new current line. + virtual Logger& operator << ( Logger& (*man)( Logger& ) ) = 0; + + // \ru Манипулятор-признак завершения форматирования текущей строки лога. + // \en Manipulator-indicator of finishing formatting of the current line of the log. + static Logger& Endl( Logger& ); +}; + +// \ru Начать или закончить логирование. \en Start or stop logging. +MATH_FUNC(void) SetLogging( bool allow ); + +// \ru Записать лог в файл. Лог для каждого потока записывается в отдельный файл. +// \en Write the log to the file. Log of each thread writes to a separate file. +MATH_FUNC(void) WriteLog( const TCHAR *fileName ); + +// \ru Записать указанную строку в лог. \en Write a specified string to the log. +MATH_FUNC(void) LogMessage( const c3d::string_t &msg ); + +// \ru Макросы для операций логирования. \en Logging macros. + +// \ru Начать логирование. \en Start logging. +#define START_LOGGING SetLogging( true ); +// \ru Закончить логирование. \en Stop logging. +#define END_LOGGING SetLogging( false ); +// \ru Положить форматированную строку в лог. \en Put a formatted string to the log. +#define LOG_MSG(msg) LogMessage( msg ); +// \ru Записать лог в файл. Лог для каждого потока записывается в отдельный файл. +// \en Write the log to the file. Log of each thread writes to a separate file. +#define WRITE_LOG_FILE(fileName) WriteLog( fileName ); + +#else +inline void CALL_DECLARATION SetLogging( bool ){} +inline void CALL_DECLARATION WriteLog( const TCHAR * ){} +inline void CALL_DECLARATION LogMessage( const c3d::string_t & ){} + +#define START_LOGGING +#define END_LOGGING +#define LOG_MSG(msg) +#define WRITE_LOG_FILE(fileName) + +#endif + + +namespace c3d //namespace c3d +{ + +//------------------------------------------------------------------------------ +/** \brief \ru Включить контроль утечек памяти. + \en Enable memory leakage control. \~ + \details \ru Включить контроль утечек памяти. + \en Enable memory leakage control. \~ + \ingroup Base_Tools +*/ +// --- +MATH_FUNC(void) EnableMemoryLeakDump(); + +} //namespace c3d + + +#endif // __TOOL_LOG_H diff --git a/C3d/Include/tool_memory_debug.h b/C3d/Include/tool_memory_debug.h index 9f6d8b9..fb2d0a7 100644 --- a/C3d/Include/tool_memory_debug.h +++ b/C3d/Include/tool_memory_debug.h @@ -1,421 +1,271 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Контроль выделения памяти под отладкой. - \en Memory allocation control during the debugging process. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef __MEMORY_DEBUG_H -#define __MEMORY_DEBUG_H - -//#define __DEBUG_MEMORY_ALLOCATE_FREE_ -//#define __MEMSET_USED_FREE_HEAP_HEAR__ -//#define __REALLOC_ARRAYS_STATISTIC_ - -#include -#include -#include - -#ifdef C3D_DEBUG - #if defined(C3D_MacOS) // mac - #include - #elif defined(C3D_FreeBSD) - #include - #else - #include - #endif // mac - - #ifndef __DISABLE_MEMORY_CONTROL__ // \ru Чтобы можно было отключать в других проектах \en To allow to disable it in other projects - #ifndef __BORLANDC__ - #define USE_REALLOC_IN_ARRAYS // no _aligned_realloc in bcc32c - #endif - #endif // __DISABLE_MEMORY_CONTROL__ -#endif // C3D_DEBUG - -#ifdef __REALLOC_ARRAYS_STATISTIC_ -//#include -#include -#include -#endif - - -#ifdef __DEBUG_MEMORY_ALLOCATE_FREE_ - -//------------------------------------------------------------------------------ -/// \ru Проверить указатель и значение. \en Check pointer and value. \~ \ingroup Base_Tools -// --- -inline void CheckPointerAndValue( void * ptr, size_t size ) -{ -#if defined (C3D_WINDOWS) && !defined(ALL_WARNINGS) //_MSC_VER // Set warnings level -#pragma warning(disable: 4312) -#endif - C3D_ASSERT( ptr != (ptrdiff_t *)0xEEEEEEEE ); - C3D_ASSERT( ptr != (ptrdiff_t *)0xFFFFFFFF ); -#if defined (C3D_WINDOWS) && !defined(ALL_WARNINGS) //_MSC_VER // Set warnings level -#pragma warning(default: 4312) -#endif - - if ( ptr ) { - // \ru Надо доработать для 64-бит \en It should be adapted for the 64-bit version - if ( size > 11 ) { - uint32* _ptr = (uint32 *)ptr; - uint32 value1 = *_ptr++; - uint32 value2 = *_ptr++; - uint32 value3 = *_ptr; - C3D_ASSERT( value1 != 0xEEEEEEEE || value2 != 0xEEEEEEEE || value3 != 0xEEEEEEEE ); - C3D_ASSERT( value1 != 0xFFFFFFFF || value2 != 0xFFFFFFFF || value3 != 0xFFFFFFFF ); - } - else if ( size > 7 ) { - uint32 * _ptr = (uint32 *)ptr; - uint32 value1 = *_ptr++; - uint32 value2 = *_ptr; - C3D_ASSERT( value1 != 0xEEEEEEEE && value2 != 0xEEEEEEEE ); - C3D_ASSERT( value1 != 0xFFFFFFFF || value2 != 0xFFFFFFFF ); - } - else if ( size > 3 ) { - uint32 value = *(uint32 *)ptr; - C3D_ASSERT( value != 0xEEEEEEEE ); - C3D_ASSERT( value != 0xFFFFFFFF ); - } - else if ( size > 1 ) { - uint16 value = *(uint16 *)ptr; - C3D_ASSERT( value != 0xEEEE ); - C3D_ASSERT( value != 0xFFFF ); - } - } -} - -//------------------------------------------------------------------------------ -/// \ru Выделить память указанного размера. \en Allocate memory of the given size. \~ \ingroup Base_Tools -// --- -inline void * Allocate( size_t size, const char * ) // className ) -{ - void * ptr = ::malloc( size ); - // \ru Дабы работал _msize: void * ptr = ::operator new( size ); \en For working of _msize: void * ptr = ::operator new( size ); - - if ( ptr ) { - ::memset( ptr, 0xFF, size ); - } - return ptr; -} - -//------------------------------------------------------------------------------ -/// \ru Выделить память указанного размера под массив. \en Allocate memory of the given size for an array. \~ \ingroup Base_Tools -// --- -inline void * AllocateArray( size_t size, const char * ) // className ) -{ - void * ptr = ::malloc( size ); - // \ru Дабы работал _msize: void *ptr = ::operator new [] ( size ); \en For working of _msize: void *ptr = ::operator new [] ( size ); - - if ( ptr ) { -#ifdef __MEMSET_USED_FREE_HEAP_HEAR__ - ::memset( ptr, 0xFFFFFFFF, size ); // \ru OV - надо доработать для 64-бит \en OV - it should be adapted for the 64-bit version -#endif // __MEMSET_USED_FREE_HEAP_HEAR__ - } - return ptr; -} - -//------------------------------------------------------------------------------ -/// \ru Освободить память указанного размера. \en Free memory of the given size. \~ \ingroup Base_Tools -// --- -inline void Free( void * ptr, size_t size, const char * ) // className ) -{ - if ( ptr ) { - ::CheckPointerAndValue( ptr, size ); - -#ifdef __MEMSET_USED_FREE_HEAP_HEAR__ - size_t ptr_size = ::_msize( ptr ); - C3D_ASSERT( ptr_size > 0 && ptr_size < 0xFFFFFFFF ); // \ru Надо доработать для 64-бит. \en It should be adapted for the 64-bit version. - if ( ptr_size ) { - C3D_ASSERT( size <= ptr_size ); - ::memset( ptr, 0xEE, ptr_size ); - } -#endif // __MEMSET_USED_FREE_HEAP_HEAR__ - - ::free( ptr ); - } -} - -//------------------------------------------------------------------------------ -/// \ru Освободить память, выделенную под массив. \en Free the memory allocated for the array. \~ \ingroup Base_Tools -// --- -// \ru ЯТ можно перегрузить в классах operator delete [] ( void *, size_t ) и передать \en ЯТ it is pertinent to overload the operator delete [] ( void *, size_t ) and pass -// \ru в эту функцию size_t size, но это будет не размер массива, а размер Type, \en to this function size_t size, but this will be not the size of an array but the size of 'Type', -// \ru массив которых распределялся. То есть эта информация здесь не нужна (делать \en an array of which was not distribute. I.e. this information is not needed here ( -// \ru ::memset не нее НЕЛЬЗЯ!) \en it is forbidden to do ::memset here) -// --- -inline void FreeArray( void * ptr, const char * ) // className ) -{ - if ( ptr ) { - ::CheckPointerAndValue( ptr, 0/*size*/ ); - -#ifdef __MEMSET_USED_FREE_HEAP_HEAR__ - size_t size = ::_msize( ptr ); - C3D_ASSERT( size > 0 && size < 0xFFFFFFFF ); // \ru OV - надо доработать для 64-бит \en OV - it should be adapted for the 64-bit version - ::CheckPointerAndValue( ptr, size ); - if ( size ) - ::memset( ptr, 0xEE, size ); -#endif // __MEMSET_USED_FREE_HEAP_HEAR__ - - ::free( ptr ); - } -} - -#endif // __DEBUG_MEMORY_ALLOCATE_FREE_ - - -#ifdef __REALLOC_ARRAYS_STATISTIC_ - -struct OneArrayTypeStatistic { -public: - size_t reallocCountSuccess; // \ru кол-во успешных перераспределений памяти \en A number of successfully reallocations of the memory - size_t firstAlloc ; // \ru кол-во первичных распределений \en the number of primary allocations - size_t lastAlloc ; // \ru кол-во освобождений \en the number of releases - size_t realAllocIncremet ; // \ru кол-во запросов на увеличение \en the number of requests for an increment - size_t realAllocDecremet ; // \ru кол-во запросов на уменьшение \en the number of requests for a decrement - size_t fullLength ; // \ru суммарный размер в байтах \en the total size in bites - size_t maxOneArrayLength ; // \ru максимальный размер одного массива \en the minimal size of one array - size_t sumCurrLength ; // \ru суммарный размер памяти во всех массивах этого типа в данный момент \en the total size of the memory in all arrays of this type at this moment - size_t maxOneTimeLength ; // \ru максимальный одновременный размер памяти во всех массивах этого типа \en the maximum simultaneous size of the memory in all arrays of this type - size_t minDelta ; // \ru минимальное приращение \en minimum increment - size_t maxDelta ; // \ru максимальное приращение \en maximum increment -public: - OneArrayTypeStatistic() { Clear(); } - void Clear() { - reallocCountSuccess = 0; - firstAlloc = 0; - lastAlloc = 0; - realAllocIncremet = 0; - realAllocDecremet = 0; - fullLength = 0; - maxOneArrayLength = 0; - sumCurrLength = 0; - maxOneTimeLength = 0; - minDelta = SYS_MAX_T; // \ru минимальное приращение \en minimum increment - maxDelta = 0; // \ru максимальное приращение \en maximum increment - } -}; - -const size_t STAT_ARRAY_COUNT = 6; -static OneArrayTypeStatistic statisticArray[STAT_ARRAY_COUNT] - = { OneArrayTypeStatistic(), OneArrayTypeStatistic(), OneArrayTypeStatistic(), - OneArrayTypeStatistic(), OneArrayTypeStatistic(), OneArrayTypeStatistic() }; - -static size_t allReallocCount = 0; - -//------------------------------------------------------------------------------ -// \ru Уменьшение uint с проверкой \en A decrement of uint with the check -// --- -inline void DecrementUint( size_t & val, size_t delta ) { - val = (delta < val) ? (val - delta) : 0; -} - -//------------------------------------------------------------------------------ -/// \ru Статистика изменений размера массива. \en Statistics of array size changes. \~ \ingroup Base_Tools -// arrayType : -// \ru 0 - SArray (или наследники), \en 0 -SAray (or inheritors) -// \ru 1 - RParray (или наследники), \en 1 -RPAray (or inheritors) -// 2 - Array2, -// 3 - LiSArray, -// 4 - CcArray, -// \ru 5 - неопознанные (вообще-то, такого не должно быть) \en 5 - not defined (it should not happen) -// --- -inline void ReallocArrayStatistic( void * oldParr, size_t oldSize, - void * newParr, size_t newSize, - uint arrayType ) -{ - if ( oldParr || newSize ) { - allReallocCount++; - - // \ru найдем статистическую запись про данный тип массива \en find a statistic record about the given type of array - size_t index = (size_t)arrayType; - if ( index >= STAT_ARRAY_COUNT ) - index = STAT_ARRAY_COUNT - 1; - OneArrayTypeStatistic & stat = statisticArray[index]; - - if ( newSize > stat.maxOneArrayLength ) - stat.maxOneArrayLength = newSize; - - if ( !oldParr ) { // \ru первичное распределение \en primary allocation - stat.firstAlloc++; - stat.fullLength += newSize; // \ru общее кол-во байт в этом типе массива \en the total number of bites in this type of an array - stat.sumCurrLength += newSize; // \ru суммарный размер памяти во всех массивах этого типа в данный момент \en the total size of the memory in all arrays of this type at this moment - } - - if ( oldParr && !newSize ) { // \ru полное освобождение \en full release - stat.lastAlloc++; - // \ru максимальный одновременный размер памяти во всех массивах этого типа \en the maximum simultaneous size of the memory in all arrays of this type - if ( stat.sumCurrLength > stat.maxOneTimeLength ) - stat.maxOneTimeLength = stat.sumCurrLength; - // \ru суммарный размер памяти во всех массивах этого типа в данный момент \en the total size of the memory in all arrays of this type at this moment - ::DecrementUint( stat.sumCurrLength, oldSize ); - } - - if ( oldParr && newSize > oldSize ) { // \ru запрос на увеличение \en a request to increase - stat.realAllocIncremet++; - size_t delta = newSize - oldSize; - stat.fullLength += delta; // \ru общее кол-во байт в этом типе массива \en the total number of bites in this type of an array - stat.sumCurrLength += delta; // \ru суммарный размер памяти во всех массивах этого типа в данный момент \en the total size of the memory in all arrays of this type at this moment - - if ( delta < stat.minDelta ) - stat.minDelta = delta; // \ru минимальное приращение \en minimum increment - if ( delta > stat.minDelta ) - stat.maxDelta = delta; // \ru максимальное приращение \en maximum increment - } - - if ( oldParr && newSize && newSize < oldSize ) { // \ru запрос на уменьшение \en a request to decrease - stat.realAllocDecremet++; - // \ru максимальный одновременный размер памяти во всех массивах этого типа \en the maximum simultaneous size of the memory in all arrays of this type - if ( stat.sumCurrLength > stat.maxOneTimeLength ) - stat.maxOneTimeLength = stat.sumCurrLength; - // \ru суммарный размер памяти во всех массивах этого типа в данный момент \en the total size of the memory in all arrays of this type at this moment - ::DecrementUint( stat.sumCurrLength, oldSize - newSize ); - } - -#ifdef USE_REALLOC_IN_ARRAYS - if ( oldParr && oldParr == newParr && newSize != oldSize ) - stat.reallocCountSuccess++; -#endif // USE_REALLOC_IN_ARRAYS - } -} - - -//------------------------------------------------------------------------------ -/// \ru Отчет по статистике изменений размера массива. \en A report by the statistics of array size changes. \~ \ingroup Base_Tools -// --- -inline void ReallocReport( bool clear, const char * title = NULL ) -{ - std::string text( title ); - - if ( text.length() > 0 ) - text.append( "\n" ); - - char buf[256]; - ::sprintf( buf, "Общее кол-во realloc %d", ::LoUint32( allReallocCount ) ); - text.append( buf ); - - for ( size_t i = 0; i < STAT_ARRAY_COUNT; i++ ) { - OneArrayTypeStatistic & stat = statisticArray[i]; - - // \ru если данных нет, то и писать про них не будем \en if there is no data then we will not write about it - if ( stat.firstAlloc || stat.lastAlloc || stat.realAllocIncremet || stat.realAllocDecremet ) { - - switch ( i ) { - case 0 : text.append( "\n\n SArray (или наследники)" ); break; - case 1 : text.append( "\n\n RParray (или наследники)" ); break; - case 2 : text.append( "\n\n Array2" ); break; - case 3 : text.append( "\n\n LiSArray (или наследники)" ); break; - case 4 : text.append( "\n\n CcArray" ); break; - case 5 : text.append( "\n\n неопознанные" ); break; - } - - ::sprintf( buf, "\n всего байт \t %d", ::LoUint32( stat.fullLength ) ); - text.append( buf ); - ::sprintf( buf, "\n макс.размер \t %d", ::LoUint32( stat.maxOneArrayLength ) ); - text.append( buf ); - ::sprintf( buf, "\n первичных \t %d", ::LoUint32( stat.firstAlloc ) ); - text.append( buf ); - ::sprintf( buf, "\n освобождений \t %d", ::LoUint32( stat.lastAlloc ) ); - text.append( buf ); - if ( stat.realAllocIncremet ) { - ::sprintf( buf, "\n на увеличение \t %d", ::LoUint32( stat.realAllocIncremet ) ); - text.append( buf ); - } - if ( stat.realAllocDecremet ) { - ::sprintf( buf, "\n на уменьшение \t %d", ::LoUint32( stat.realAllocDecremet ) ); - text.append( buf ); - } -#ifdef USE_REALLOC_IN_ARRAYS - if ( stat.reallocCountSuccess ) { - ::sprintf( buf, "\n удачных realloc \t %d", ::LoUint32( stat.reallocCountSuccess ) ); - text.append( buf ); - } -#endif - ::sprintf( buf, "\n max одновременно %d", ::LoUint32( stat.maxOneTimeLength ) ); - text.append( buf ); - if ( stat.realAllocIncremet ) { - ::sprintf( buf, "\n Delta (min, max) \t %d, %d", ::LoUint32( stat.minDelta ), ::LoUint32( stat.maxDelta ) ); - text.append( buf ); - } - } - } - - { - const char * outName = "C:\\Logs\\ADRAFT_TTEST.txt"; - std::ofstream out( outName, std::ios::out|std::ios::app ); - out << "\n Статистика использования памяти \n"; - //out << text; - out << "\n"; - } - - if ( clear ) { - // \ru очистить, чтобы в следующий раз цифры были новые, а не накопленные \en clear to renew numbers - allReallocCount = 0; - for ( size_t i = 0; i < STAT_ARRAY_COUNT; i++ ) - statisticArray[i].Clear(); - } -} - -#endif // __REALLOC_ARRAYS_STATISTIC_ - - -//------------------------------------------------------------------------------ -// \ru Использовать realloc для изменения размера массивов \en Use realloc to change arrays sizes -// \ru (если не определено, то по-старому, через new и delete) \en (if it is not defined then use new and delete operators) -//--- -#ifdef USE_REALLOC_IN_ARRAYS - -#ifdef C3D_DEBUG - -#ifdef __MEMSET_USED_FREE_HEAP_HEAR__ -//------------------------------------------------------------------------------ -/// \ru Функция перезахватов памяти в массивах. \en Function of memory reallocation in arrays. \~ \ingroup Base_Tools -// --- -inline void * ReallocArraySize( void * arr_parr, size_t newBytesCount, bool clear ) -#else // __MEMSET_USED_FREE_HEAP_HEAR__ -//------------------------------------------------------------------------------ -/// \ru Функция перезахватов памяти в массивах. \en Function of memory reallocation in arrays. \~ \ingroup Base_Tools -// --- -inline void * ReallocArraySize( void * arr_parr, size_t newBytesCount, bool ) -#endif // __MEMSET_USED_FREE_HEAP_HEAR__ -{ -#ifdef __MEMSET_USED_FREE_HEAP_HEAR__ - if ( newBytesCount == 0 || clear ) { - size_t arr_parr_size = ::_msize( arr_parr ); - C3D_ASSERT( arr_parr ? (arr_parr_size > 0 && arr_parr_size < 0xFFFFFFFF) : true ); // \ru OV - надо доработать для 64-бит \en OV - it should be adapted for the 64-bit version - if ( arr_parr_size ) - ::memset( arr_parr, 0xEE, arr_parr_size ); - } -#endif // __MEMSET_USED_FREE_HEAP_HEAR__ - -#if defined( _AFXDLL ) && defined( C3D_DEBUG ) - void * tmp_parr = _realloc_dbg( arr_parr, newBytesCount, _NORMAL_BLOCK, __FILE__, __LINE__ ); -#else - void * tmp_parr = ::realloc( arr_parr, newBytesCount ); -#endif - - PRECONDITION( newBytesCount == 0 || tmp_parr != NULL ); // \ru проверка на нехватку памяти в массивах \en check the memory deficit in arrays - -#ifdef __MEMSET_USED_FREE_HEAP_HEAR__ - if ( clear ) { - size_t tmp_parr_size = ::_msize( tmp_parr ); - C3D_ASSERT( tmp_parr ? (tmp_parr_size > 0 && tmp_parr_size < 0xFFFFFFFF) : true ); // \ru OV - надо доработать для 64-бит \en OV - it should be adapted for the 64-bit version - if ( tmp_parr_size ) - ::memset( tmp_parr, 0xEE, tmp_parr_size ); - } -#endif // __MEMSET_USED_FREE_HEAP_HEAR__ - - return tmp_parr; -} - -#define REALLOC_ARRAY_SIZE(p,s,c) ::ReallocArraySize((p),(s),(c)) -#else // C3D_DEBUG -#define REALLOC_ARRAY_SIZE(p,s,c) ::realloc((p),(s)) -#endif // C3D_DEBUG - -#endif // USE_REALLOC_IN_ARRAYS - - -#endif // __MEMORY_DEBUG_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Контроль выделения памяти под отладкой. + \en Memory allocation control during the debugging process. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __MEMORY_DEBUG_H +#define __MEMORY_DEBUG_H + +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// \ru Макросы для переключения контроля выделения памяти находятся в math_cfg.h +// \en Macros for switching control of memory allocation are in math_cfg.h +//////////////////////////////////////////////////////////////////////////////// + +#ifdef C3D_DEBUG + #if defined(C3D_MacOS) // mac + #include + #elif defined(C3D_FreeBSD) + #include + #else + #include + #endif // mac + + #ifndef __DISABLE_MEMORY_CONTROL__ // \ru Чтобы можно было отключать в других проектах \en To allow to disable it in other projects + #define USE_REALLOC_IN_ARRAY + #endif // __DISABLE_MEMORY_CONTROL__ +#endif // C3D_DEBUG + + +#ifdef __REALLOC_ARRAYS_STATISTIC_ +#include +#include + +//------------------------------------------------------------------------------ +/** + \brief \ru Класс для контроля выделения памяти под отладкой. + \en Class for controling memory allocation during the debugging process.\~ + \ingroup Base_Tools +*/ +//--- +class MATH_CLASS MemoryChecker +{ +public: + + /// \ru Получить указатель на синглтон класса. \en Get pointer to the class singleton. + static MemoryChecker * Get(); + + /// \ru Перезапуск сбора статистики. Очистить, чтобы в следующий раз цифры были новые, а не накопленные. + /// \en Restart collecting statistics. Clear to renew numbers. + virtual void RestartStatistic() = 0; + + /// \ru Статистика изменений размера массива. \en Statistics of array size changes. + // arrayType : + // \ru 0 - SArray (или наследники), \en 0 -SAray (or inheritors) + // \ru 1 - RParray (или наследники), \en 1 -RPAray (or inheritors) + // 2 - Array2, + // 3 - LiSArray, + // 4 - CcArray, + // \ru 5 - неопознанные (вообще-то, такого не должно быть) \en 5 - not defined (it should not happen) + virtual void ReallocArrayStatistic( void * oldParr, size_t oldSize, void * newParr, size_t newSize, uint arrayType ) = 0; + + /// \ru Отчет по статистике изменений размера массива. \en A report by the statistics of array size changes. \~ \ingroup Base_Tools + virtual void ReallocReport( bool clear, const char * title = NULL ) = 0; + +}; + +//------------------------------------------------------------------------------ +/// \ru Дополнить статистику изменений размера массива. \en Add statistics of array size changes. +//--- +#define REALLOC_ARRAY_STATISTICS(oldParr,oldSize,newParr,newSize,arrayType) \ +MemoryChecker::Get()->ReallocArrayStatistic( oldParr, oldSize, newParr, newSize, arrayType ); + +#endif + +#if defined(__DEBUG_MEMORY_ALLOCATE_FREE_) || defined(USE_REALLOC_IN_ARRAYS) + #define C3D_INVALID_UINT32_1 SYS_MAX_UINT32 + #define C3D_INVALID_UINT32_2 0xEEEEEEEE + #define C3D_INVALID_UINT16_1 SYS_MAX_UINT16 + #define C3D_INVALID_UINT16_2 0xEEEE + #define C3D_MEMORY_FILL_VALUE 0xEE + #ifdef PLATFORM_64 + #define C3D_INVALID_ADDR_1 0xFFFFFFFFFFFFFFFF + #define C3D_INVALID_ADDR_2 0xEEEEEEEEEEEEEEEE + #else // PLATFORM_64 + #define C3D_INVALID_ADDR_1 0xFFFFFFFF + #define C3D_INVALID_ADDR_2 0xEEEEEEEE + #endif // PLATFORM_64 +#endif // (_DEBUG_MEMORY_ALLOCATE_FREE_ || USE_REALLOC_IN_ARRAYS) + +#ifdef __DEBUG_MEMORY_ALLOCATE_FREE_ + +//------------------------------------------------------------------------------ +/// \ru Проверить указатель и значение. \en Check pointer and value. \~ \ingroup Base_Tools +// --- +inline void CheckPointerAndValue( void * ptr, size_t size ) +{ +#if defined (C3D_WINDOWS) && !defined(ALL_WARNINGS) // Set warnings level +#pragma warning(disable: 4312) +#endif + PRECONDITION( ptr != (ptrdiff_t *)C3D_INVALID_ADDR_2 ); + PRECONDITION( ptr != (ptrdiff_t *)C3D_INVALID_ADDR_1 ); +#if defined (C3D_WINDOWS) && !defined(ALL_WARNINGS) // Set warnings level +#pragma warning(default: 4312) +#endif + + if ( ptr ) { + if ( size > 11 ) { + uint32 * _ptr = (uint32 *)ptr; + uint32 value1 = *_ptr++; + uint32 value2 = *_ptr++; + uint32 value3 = *_ptr; + PRECONDITION( value1 != C3D_INVALID_UINT32_2 || value2 != C3D_INVALID_UINT32_2 || value3 != C3D_INVALID_UINT32_2 ); + PRECONDITION( value1 != C3D_INVALID_UINT32_1 || value2 != C3D_INVALID_UINT32_1 || value3 != C3D_INVALID_UINT32_1 ); + } + else if ( size > 7 ) { + uint32 * _ptr = (uint32 *)ptr; + uint32 value1 = *_ptr++; + uint32 value2 = *_ptr; + PRECONDITION( value1 != C3D_INVALID_UINT32_2 && value2 != C3D_INVALID_UINT32_2 ); + PRECONDITION( value1 != C3D_INVALID_UINT32_1 || value2 != C3D_INVALID_UINT32_1 ); + } + else if ( size > 3 ) { + uint32 value = *(uint32 *)ptr; + PRECONDITION( value != C3D_INVALID_UINT32_2 ); + PRECONDITION( value != C3D_INVALID_ADDR_1 ); + } + else if ( size > 1 ) { + uint16 value = *(uint16 *)ptr; + PRECONDITION( value != C3D_INVALID_UINT16_2 ); + PRECONDITION( value != C3D_INVALID_UINT16_1 ); + } + } +} + + +//------------------------------------------------------------------------------ +/// \ru Выделить память указанного размера. \en Allocate memory of the given size. \~ \ingroup Base_Tools +// --- +inline void * Allocate( size_t size, const char * ) +{ + void * ptr = ::malloc( size ); + // \ru Дабы работал _msize: void * ptr = ::operator new( size ); \en For working of _msize: void * ptr = ::operator new( size ); + + if ( ptr ) { + ::memset( ptr, 0xFF, size ); + } + return ptr; +} + +//------------------------------------------------------------------------------ +/// \ru Выделить память указанного размера под массив. \en Allocate memory of the given size for an array. \~ \ingroup Base_Tools +// --- +inline void * AllocateArray( size_t size, const char * ) +{ + void * ptr = ::malloc( size ); + // \ru Дабы работал _msize: void *ptr = ::operator new [] ( size ); \en For working of _msize: void *ptr = ::operator new [] ( size ); + + if ( ptr ) { +#ifdef __MEMSET_USED_FREE_HEAP_HEAR__ + ::memset( ptr, C3D_INVALID_UINT32_1, size ); +#endif // __MEMSET_USED_FREE_HEAP_HEAR__ + } + return ptr; +} + +//------------------------------------------------------------------------------ +/// \ru Освободить память указанного размера. \en Free memory of the given size. \~ \ingroup Base_Tools +// --- +inline void Free( void * ptr, size_t size, const char * ) +{ + if ( ptr ) { + ::CheckPointerAndValue( ptr, size ); + +#ifdef __MEMSET_USED_FREE_HEAP_HEAR__ + size_t ptr_size = ::_msize( ptr ); + PRECONDITION( ptr_size > 0 && ptr_size < SYS_MAX_T ); + if ( ptr_size ) { + PRECONDITION( size <= ptr_size ); + ::memset( ptr, C3D_MEMORY_FILL_VALUE, ptr_size ); + } +#endif // __MEMSET_USED_FREE_HEAP_HEAR__ + + ::free( ptr ); + } +} + +//------------------------------------------------------------------------------ +/// \ru Освободить память, выделенную под массив. \en Free the memory allocated for the array. \~ \ingroup Base_Tools +// --- +// \ru ЯТ можно перегрузить в классах operator delete [] ( void *, size_t ) и передать \en ЯТ it is pertinent to overload the operator delete [] ( void *, size_t ) and pass +// \ru в эту функцию size_t size, но это будет не размер массива, а размер Type, \en to this function size_t size, but this will be not the size of an array but the size of 'Type', +// \ru массив которых распределялся. То есть эта информация здесь не нужна (делать \en an array of which was not distribute. I.e. this information is not needed here ( +// \ru ::memset не нее НЕЛЬЗЯ!) \en it is forbidden to do ::memset here) +// --- +inline void FreeArray( void * ptr, const char * ) +{ + if ( ptr ) { + ::CheckPointerAndValue( ptr, 0/*size*/ ); + +#ifdef __MEMSET_USED_FREE_HEAP_HEAR__ + size_t size = ::_msize( ptr ); + PRECONDITION( size > 0 && size < SYS_MAX_T ); + ::CheckPointerAndValue( ptr, size ); + if ( size ) + ::memset( ptr, C3D_MEMORY_FILL_VALUE, size ); +#endif // __MEMSET_USED_FREE_HEAP_HEAR__ + + ::free( ptr ); + } +} +#endif // __DEBUG_MEMORY_ALLOCATE_FREE_ + + +#ifdef USE_REALLOC_IN_ARRAYS +//------------------------------------------------------------------------------ +/// \ru Функция перезахватов памяти в массивах. \en Function of memory reallocation in arrays. \~ \ingroup Base_Tools +// --- +#ifdef __MEMSET_USED_FREE_HEAP_HEAR__ +inline void * ReallocArraySize( void * arr_parr, size_t newBytesCount, bool clear ) +#else // __MEMSET_USED_FREE_HEAP_HEAR__ +inline void * ReallocArraySize( void * arr_parr, size_t newBytesCount, bool ) +#endif // __MEMSET_USED_FREE_HEAP_HEAR__ +{ +#ifdef __MEMSET_USED_FREE_HEAP_HEAR__ + if ( newBytesCount == 0 || clear ) { + size_t arr_parr_size = arr_parr ? ::_msize( arr_parr ) : 0; + PRECONDITION( arr_parr ? ( arr_parr_size > 0 && arr_parr_size < SYS_MAX_T ) : true ); + if ( arr_parr_size ) + ::memset( arr_parr, C3D_MEMORY_FILL_VALUE, arr_parr_size ); + } +#endif // __MEMSET_USED_FREE_HEAP_HEAR__ + +#if defined( _AFXDLL ) && defined( C3D_DEBUG ) + void * tmp_parr = _realloc_dbg( arr_parr, newBytesCount, _NORMAL_BLOCK, __FILE__, __LINE__ ); +#else + void * tmp_parr = ::realloc( arr_parr, newBytesCount ); +#endif + + PRECONDITION( newBytesCount == 0 || tmp_parr != NULL ); // \ru проверка на нехватку памяти в массивах \en check the memory deficit in arrays + +#ifdef __MEMSET_USED_FREE_HEAP_HEAR__ + if ( clear ) { + size_t tmp_parr_size = tmp_parr ? ::_msize( tmp_parr ) : 0; + PRECONDITION( tmp_parr ? ( tmp_parr_size > 0 && tmp_parr_size < SYS_MAX_T ) : true ); + if ( tmp_parr_size ) + ::memset( tmp_parr, C3D_MEMORY_FILL_VALUE, tmp_parr_size ); + } +#endif // __MEMSET_USED_FREE_HEAP_HEAR__ + + return tmp_parr; +} + +//------------------------------------------------------------------------------ +// \ru Использовать realloc для изменения размера массивов \en Use realloc to change arrays sizes +// \ru (если не определено, то по-старому, через new и delete) \en (if it is not defined then use new and delete operators) +//--- + +#define REALLOC_ARRAY_SIZE(p,s,c) ::ReallocArraySize((p),(s),(c)) +#else // USE_REALLOC_IN_ARRAYS +#define REALLOC_ARRAY_SIZE(p,s,c) ::realloc((p),(s)) + +#endif // USE_REALLOC_IN_ARRAYS + + +#endif // __MEMORY_DEBUG_H diff --git a/C3d/Include/tool_memory_leaks_check.h b/C3d/Include/tool_memory_leaks_check.h index 15c452e..30bb993 100644 --- a/C3d/Include/tool_memory_leaks_check.h +++ b/C3d/Include/tool_memory_leaks_check.h @@ -1,49 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -/// Слежение за утечками -/** - \file - \brief Содержит класс MemoryLeaksVerifiable - базовый для контролируемых классов -*/ -// -//////////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include - -#if ( defined(STANDARD_C11) && (_MSC_VER > 1800) ) -#define ENABLE_MEMORY_LEAKS_CHECK -#endif - - -namespace c3d // namespace C3D -{ - -#ifdef ENABLE_MEMORY_LEAKS_CHECK - -//------------------------------------------------------------------------------ -/** \brief \ru Базовый класс для контролируемых классов. - \en . \~ - \details \ru На конструкторе объект регистрируется в менеджере утечек, в деструкторе удаляется из списка зарегистрированных. - Информация о всех объектах, которые остались в регистраторе, будет выведена. \n - \en . \n \~ - \ingroup Base_Tools -*/ -// --- -class MATH_CLASS MemoryLeaksVerifiable -{ -protected: - MemoryLeaksVerifiable(); - virtual ~MemoryLeaksVerifiable(); -}; - -#else - -class MATH_CLASS MemoryLeaksVerifiable {}; - -#endif // ENABLE_MEMORY_LEAKS_CHECK - -} // namespace C3D - - - +//////////////////////////////////////////////////////////////////////////////// +/// Слежение за утечками +/** + \file + \brief Содержит класс MemoryLeaksVerifiable - базовый для контролируемых классов +*/ +// +//////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include + +#if ( defined(_MSC_VER) && (_MSC_VER > 1800) ) + #define ENABLE_MEMORY_LEAKS_CHECK +#endif + + +namespace c3d // namespace C3D +{ + +#ifdef ENABLE_MEMORY_LEAKS_CHECK + +//------------------------------------------------------------------------------ +/** \brief \ru Базовый класс для контролируемых классов. + \en . \~ + \details \ru На конструкторе объект регистрируется в менеджере утечек, в деструкторе удаляется из списка зарегистрированных. + Информация о всех объектах, которые остались в регистраторе, будет выведена. \n + \en . \n \~ + \ingroup Base_Tools +*/ +// --- +class MATH_CLASS MemoryLeaksVerifiable +{ +protected: + MemoryLeaksVerifiable(); + virtual ~MemoryLeaksVerifiable(); +}; + +#else + +class MATH_CLASS MemoryLeaksVerifiable {}; + +#endif // ENABLE_MEMORY_LEAKS_CHECK + +} // namespace C3D + + + diff --git a/C3d/Include/tool_memory_leaks_utils.h b/C3d/Include/tool_memory_leaks_utils.h index cf6659d..1becc43 100644 --- a/C3d/Include/tool_memory_leaks_utils.h +++ b/C3d/Include/tool_memory_leaks_utils.h @@ -1,64 +1,64 @@ -//////////////////////////////////////////////////////////////////////////////// -/// Утилиты Слежения за утечками -/** - \file - \brief Содержит интерфейс MemoryLeaksController - контроллер утечек памяти -*/ -// -//////////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include -#include -#include - - -namespace c3d // namespace C3D -{ -#ifdef ENABLE_MEMORY_LEAKS_CHECK - -typedef std::unordered_map MemoryLeaksRegisteredData; -typedef std::unique_ptr MemoryLeaksControllerPtr; - -//---------------------------------------------------------------------------------------- -/** \brief \ru Контроллер утечек памяти. - \en Memory Leaks Controller. \~ - \details \ru Контроллер утечек памяти. При выходе из приложения, вызывается метод OnLeakDetect, в который передается информация об утечках. - \en Memory Leaks Controller. When you exit the application, the OnLeakDetect method is called, to which information about leaks is transmitted. \~ - \ingroup Base_Tools -*/ -//--- -struct MemoryLeaksController -{ - MemoryLeaksController() {} - virtual ~MemoryLeaksController() {} - virtual void OnLeakDetect( const MemoryLeaksRegisteredData & ) const = 0; -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Добавить контроллер утечек памяти. - \en Add memory leaks controller. \~ -\ingroup Base_Tools -*/ -// --- -MATH_FUNC( void ) AddController( MemoryLeaksControllerPtr ); - - -//------------------------------------------------------------------------------ -/** \brief \ru Собрать утечки. Необходимо вызвать перед выходом из приложения, чтобы выполнить именование утекающих объектов. - \en Collect leaks. The function should be called before exiting the application to names the leaking objects. \~ -\ingroup Base_Tools -*/ -// --- -MATH_FUNC( void ) CollectLeaks(); - -#else - -MATH_FUNC( void ) CollectLeaks() {} - -#endif // ENABLE_CHECK_MEMLEAK - -} // namespace c3d - +//////////////////////////////////////////////////////////////////////////////// +/// Утилиты Слежения за утечками +/** + \file + \brief Содержит интерфейс MemoryLeaksController - контроллер утечек памяти +*/ +// +//////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include + + +namespace c3d // namespace C3D +{ +#ifdef ENABLE_MEMORY_LEAKS_CHECK + +typedef std::unordered_map MemoryLeaksRegisteredData; +typedef std::unique_ptr MemoryLeaksControllerPtr; + +//---------------------------------------------------------------------------------------- +/** \brief \ru Контроллер утечек памяти. + \en Memory Leaks Controller. \~ + \details \ru Контроллер утечек памяти. При выходе из приложения, вызывается метод OnLeakDetect, в который передается информация об утечках. + \en Memory Leaks Controller. When you exit the application, the OnLeakDetect method is called, to which information about leaks is transmitted. \~ + \ingroup Base_Tools +*/ +//--- +struct MemoryLeaksController +{ + MemoryLeaksController() {} + virtual ~MemoryLeaksController() {} + virtual void OnLeakDetect( const MemoryLeaksRegisteredData & ) const = 0; +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Добавить контроллер утечек памяти. + \en Add memory leaks controller. \~ +\ingroup Base_Tools +*/ +// --- +MATH_FUNC( void ) AddController( MemoryLeaksControllerPtr ); + + +//------------------------------------------------------------------------------ +/** \brief \ru Собрать утечки. Необходимо вызвать перед выходом из приложения, чтобы выполнить именование утекающих объектов. + \en Collect leaks. The function should be called before exiting the application to names the leaking objects. \~ +\ingroup Base_Tools +*/ +// --- +MATH_FUNC( void ) CollectLeaks(); + +#else + +MATH_FUNC( void ) CollectLeaks() {} + +#endif // ENABLE_CHECK_MEMLEAK + +} // namespace c3d + diff --git a/C3d/Include/tool_multithreading.h b/C3d/Include/tool_multithreading.h index fd270a5..8715b2b 100644 --- a/C3d/Include/tool_multithreading.h +++ b/C3d/Include/tool_multithreading.h @@ -1,490 +1,571 @@ -//////////////////////////////////////////////////////////////////////////////// -/** -\file -\brief \ru Управление параллельной обработкой данных. - \en Managing of parallel data processing. \~ -\details \ru Управление параллельной обработкой данных.\n - \en Managing of parallel data processing. \n \~ -*/ -//////////////////////////////////////////////////////////////////////////////// -#ifndef __TOOL_MULTITHREADING_H -#define __TOOL_MULTITHREADING_H - -#include -#include - -//------------------------------------------------------------------------------ -/** -\brief \ru Режимы многопоточных вычислений. - \en Multithreading modes. \~ -\details \ru Режимы многопоточных вычислений. \n - \en Multithreading modes. \n \~ -\ingroup Data_Structures -*/ -//--- -enum MbeMultithreadedMode { - // \ru Многопоточность ядра отключена. \en Kernel multithreading is off. - mtm_Off = 0, - // \ru Включена многопоточность ядра при обработке независимых объектов (без общих данных). - // \en Kernel multithreading is ON for independent objects (without common data). - mtm_Standard = 1, - // \ru Обеспечивается потокобезопасность объектов типа MbItem. Выключена многопоточность ядра при обработке объектов, имеющих общие данные. - // \en Ensured thread-safety of objects MbItem. Kernel multithreading is OFF for objects with shared data. - mtm_SafeItems = 2, - // \ru Обеспечивается потокобезопасность объектов типа MbItem. Включена многопоточность ядра при обработке объектов с общими данными. - // \en Ensured thread-safety of objects MbItem. Kernel multithreading is ON for objects with shared data. - mtm_Items = 3, - // \ru Включена максимальная многопоточность ядра. \en Maximal kernel multithreading is ON. - mtm_Max = 31 -}; - -//------------------------------------------------------------------------------ -/** - \brief \ru Базовый класс для объектов, требующих сборки мусора. - \en Base class for objects which require a garbage collection. \~ -\details \ru Базовый класс для объектов, требующих сборки мусора. - Класс, наследующий от CacheCleaner, должен реализовать метод ResetCacheData, - который будет вызываться для сборки мусора. - \en Base class for objects which require a garbage collection. - A class, inheriting from CacheCleaner, should implement the method ResetCacheData, - which will be called for garbage collection. \~ -\ingroup Base_Tools -*/ -//--- -class MATH_CLASS CacheCleaner -{ - int subscribed; -public: - CacheCleaner(); - virtual ~CacheCleaner(); - - /** \brief \ru Подписан ли объект на сборку мусора. - \en Whether the object is subscribed for garbage collection. \~ - */ - bool IsSubscribed() { return subscribed > 0; } - - /** \brief \ru Очистить кэшированные данные. Возвращает true, если объект был отписан от сборки мусора. - \en Reset cached data. Return true if the object was unsubscribed from garbage collection.\~ - */ - virtual bool ResetCacheData() = 0; - - /** \brief \ru Подписаться на сборку мусора. - \en Subscribe for garbage collection. \~ - */ - void SubcribeOnCleaning(); - - /** \brief \ru Отписаться от сборки мусора. - \en Unsubscribe from garbage collection. \~ - */ - void UnsubcribeOnCleaning(); -}; - -//------------------------------------------------------------------------------ -/** - \brief \ru Сборщик мусора в объектах, использующих кэширование данных. - \en Garbage collector in objects which use data caching. \~ -\details \ru Сборщик мусора. По требованию очищает кэши в зарегистрированных объектах CacheCleaner, - вызывая метод ResetCacheData каждого объекта. \n - \en Garbage collector. At request clears caches in registered CacheCleaner objects - by calling the method ResetCacheData of each object. \n \~ -\ingroup Base_Tools -*/ -//--- -class MATH_CLASS MbGarbageCollection -{ -public: - - /** \brief \ru Подписать объект на сборку мусора. - \en Subscribe the object for garbage collection. \~ - */ - static void Subscribe( CacheCleaner * obj ); - - /** \brief \ru Отписать объект от сборки мусора. - \en Unsubscribe the object from garbage collection. \~ - */ - static void Unsubscribe( CacheCleaner * obj ); - - /** \brief \ru Выполнить сборку мусора. \en Perform garbage collection. - \details \ru Выполнить сборку мусора. Должна вызываться в последовательном участке кода. - При вызове в параллельном регионе ничего не делает. - \en Perform garbage collection. Should be called in sequential code. - When called in a parallel region, does nothing. - \param[in] force - \ru Если false, то инициируется сборка мусора в кэшах, созданных для потоков, которые уже завершены, - если true, то инициируется принудительная сборка мусора во всех кэшах. - \en If false, then run garbage collection in caches created for threads which are finished, - if true, then force garbage collection in all caches. - \return \ru Возвращает TRUE, если сборка проведена. \en Returns TRUE if the garbage collection is done. \~ - */ - static bool Run( bool force = false ); - - /** \brief \ru Активировать/деактивировать сбор данных для проведения сборки мусора. - По умолчанию, сбор данных для сборки мусора активирован. - \en Enable/disable collecting data for garbage collection. - By default, collecting data for garbage collection is enabled. \~ - */ - static void Enable( bool allow = true ); -}; - -//------------------------------------------------------------------------------ -// \ru Принудительно вернуть освобожденную динамическую память операционной системе. -// Может быть полезна после выполнения операций с интенсивным использованием памяти. -// \en Force to return freed heap memory to the operating system. -// May be useful after performing memory-intensive operations. -// --- -MATH_FUNC( void ) ReleaseMemory(); - -//------------------------------------------------------------------------------ -/** -\brief \ru Родительский класс данных для менеджера параллельной обработки. - \en Parent class of data for manager of parallel processing. \~ -\details \ru Родительский класс для данных, которые могут обрабатываться параллельно - с помощью менеджера кэшей. - \en Parent class for data which could be processed in parallel using the cache manager. \~ - \ingroup Base_Tools -*/ -// --- -class AuxiliaryData { -public: - AuxiliaryData() {} - AuxiliaryData( const AuxiliaryData & ) {} - virtual ~AuxiliaryData() {} - - /** \brief \ru Объединить с указанными данными. - \en Merge with specified data. - \details \ru Функция вызывается Менеджером кэшей для данных основного потока - с данными каждого многопоточного кэша в качестве параметра. - После завершения функции Менеджер кэшей удаляет многопоточный кэш. - \en The function is called by CacheManager for the main thread data with - each multithreaded cache data as a parameter. When the function completed, - the CacheManager deletes the multithreaded cache. \~ - */ - virtual void MergeWith( AuxiliaryData * ) {} -}; - -//#define CACHE_DELETE_LOCK -//------------------------------------------------------------------------------ -/** -\brief \ru Менеджер параллельной обработки данных (менеджер кэшей) с возможностью пост-обработки кэшей потоков. - \en Manager for parallel data processing (the cache manager) with support of caches post-processing. \~ -\details \ru Менеджер кэшей представляет шаблон, содержащий: - longTerm - данные главного потока при последовательном выполнении и - tcache - список кэшей с данными, которые используются при параллельном выполнении. - Каждый поток по идентификатору threadKey использует только свою копию данных. - Для многопоточной обработки зависимых (имеющих общие данные) объектов должен использоваться режим - многопоточных вычислений не ниже mtm_SafeItems. - Менеджер предоставляет функцию Postprocess() для пост-обработки кэшей, которая вызывается после - выхода из параллельных вычислений. Указанная функция итерируется по кэшам, использованным - при параллельных вычислениях, и вызывает функцию longTerm.MergeWith() с данными каждого кэша - в качестве параметра. После завершения работы функции Postprocess() кэши удаляются. \n - \en The cache manager is a template which contains: - longTerm - data of the main thread in sequential execution, and - tcache - a list of caches with data which are used in parallel calculations. - Each thread uses its own copy of data according to threadKey. - For multithreaded processing of dependent (with shared data) objects the multithreading mode mtm_SafeItems - or higher should be used. - The Manager provides a Postprocess() function for caches post-processing which is called - after exiting parallel computing. The specified function iterates through the caches used - in parallel computing and calls the function longTerm.MergeWith() with the data of the each cache - as a parameter. After the function Postprocess() finished the caches are destroyed. \n \~ -*/ -// --- -template -class CacheManager : public CacheCleaner { - struct List - { - unsigned int _id; - T* _data; - List* _next; - bool _valid; - List( unsigned int id, T* data ) : - _id( id ), - _data( data != NULL ? data : new T() ), // Always _data != NULL. - _next( NULL ), - _valid( true ) {} - ~List() { - if ( _data != NULL ) - delete _data; - _data = NULL; - if ( _next != NULL ) // Also deletes linked List. - delete _next; - _next = NULL; - } - private: - List() : _id( 0 ), _data( NULL ), _next( NULL ) {} - }; - -private: - T* longTerm; // \ru Данные главного потока при последовательном выполнении. \en Data of the main thread in sequential execution. - List* tcache; // \ru Данные, которые используются при параллельном выполнении. \en Caches which are used in parallel execution. - CommonMutex* lock; // \ru Блокировка для операций с кэшами. \en Lock for operations with caches. - -public: -#ifdef CACHE_DELETE_LOCK - CacheManager( bool createLock = false ); -#else - CacheManager( bool createLock = true ); -#endif - CacheManager( const CacheManager & ); - ~CacheManager(); - - /** \brief \ru Оператор (). Возвращает указатель на кэш (данные) текущего потока. Всегда возвращает ненулевое значение. - \en Operator (). Returns a pointer to the cache (data) of the current thread. Always returns non-null value. \~ - */ - T * operator ()(); - - /** \brief \ru Удалить данные в кэшах. Если resetLongTerm == true, удалить также данные кэша главного потока. - \en Delete caches data. If resetLongTerm == true, also delete data of the main thread cache. - */ - void Reset ( bool resetLongTerm = false ); - - /** \brief \ru Получить указатель на кэш (данные) главного потока. Всегда возвращает ненулевое значение. - Все операции с кэшем главного потока должны быть защищены блокировкой кэша. - \en Get a pointer to cache (data) of the main thread. Always returns non-null value. - All operations with the main thread cache should be protected by the cache lock. \~ - */ - T * LongTerm (); - - /** \brief \ru Получить указатель на блокировку для операций с кэшем главного потока, учитывая, исполняется ли код параллельно - Может возвращать нулевое значение (удобно для использования с ScopedLock). - \en Get a pointer to the lock for operations with the main thread cache, considering whether the code runs in parallel. - Can return null value (good for use with ScopedLock). \~ - */ - CommonMutex* GetLock() { if ( IsInParallel() ) return GetLockHard(); return lock; } - - /** \brief \ru Функция очистки, используемая сборщиком мусора. - \en Cleaning function, used by the garbage collector. \~ - */ - virtual bool ResetCacheData() { CleanAll(); return true; } - -private: - /** \brief \ru Удалить все кэши и отписаться от сборки мусора. Должна вызываться в последовательном участке кода. - \en Delete all caches and unsubscribe from the garbage collection. Should be called in sequential code. \~ - */ - void CleanAll( bool doPostproc = true ); - - /** \brief \ru Получить указатель на блокировку для операций с кэшем главного потока. Всегда возвращает ненулевое значение. - \en Get a pointer to the lock for operations with the main thread cache. Always returns non-null value. \~ - */ - CommonMutex* GetLockHard(); - - /** \brief \ru Пост-обработка кэшей после выхода из параллельных вычислений. - \en Caches post-processing after exiting the parallel calculations. \~ - */ - void Postprocess(); - - CacheManager & operator = ( const CacheManager & ); // \ru Не разрешен. \en Not allowed. -}; - - -//------------------------------------------------------------------------------ -// \ru Конструктор. \en Constructor. -// --- -template -inline CacheManager::CacheManager( bool createLock ) - : longTerm ( NULL ) - , tcache ( NULL ) - , lock ( NULL ) -{ - if ( createLock ) - lock = new CommonMutex(); -} - - -#define C3D_NULLKEY 0 - -//------------------------------------------------------------------------------ -// \ru Конструктор. \en Constructor. -// --- -template -inline CacheManager::CacheManager( const CacheManager & item ) - : longTerm ( NULL ) - , tcache ( NULL ) - , lock ( NULL ) -{ - if ( item.longTerm != NULL ) - longTerm = new T( *item.longTerm ); -#ifndef CACHE_DELETE_LOCK - lock = new CommonMutex(); -#endif -} - - -//------------------------------------------------------------------------------ -// \ru Деструктор. \en Destructor. -// --- -template -inline CacheManager::~CacheManager() -{ - CleanAll( false ); - if ( longTerm != NULL ) - delete longTerm; - if ( lock != NULL ) - delete lock; -} - -//------------------------------------------------------------------------------ -// \ru Получить указатель на кэш главного потока. Всегда возвращает ненулевое значение. -// Все операции с кэшем главного потока должны быть защищены блокировкой кэша. -// \en Get a pointer to the main thread cache. Always returns non-null value. -// All operations with the main thread cache should be protected by the cache lock. -// --- -template -inline T* CacheManager::LongTerm () -{ - if ( longTerm == NULL ) - longTerm = new T(); - return longTerm; -} - -//------------------------------------------------------------------------------ -// \ru Получить указатель на блокировку для операций с кэшем главного потока. Всегда возвращает ненулевое значение. -// \en Get a pointer to the lock for operations with the main thread cache. Always returns non-null value. -// --- -template -inline CommonMutex* CacheManager::GetLockHard() -{ - if ( lock == NULL ) { - CommonMutex* ll = GetGlobalLock(); - ll->lock(); - if ( lock == NULL ) - lock = new CommonMutex(); - ll->unlock(); - } - return lock; -} - -//------------------------------------------------------------------------------ -// \ru Оператор (). Возвращает указатель на кэш текущего потока (всегда ненулевое значение). -// \en Operator (). Returns a pointer to the current thread cache (always non-null value). -// --- -template -inline T * CacheManager::operator()() -{ -// \ru Создать данные по данным кэша главного потока. \en Create data using the data of the main thread cache. -#define INIT_BY_LONGTERM ( longTerm != NULL ? new T( *longTerm ) : new T() ) - - if ( !IsSafeMultithreading() || !IsInParallel() ) { - CleanAll(); - return LongTerm(); - } - - T * res = NULL; - unsigned int threadKey = GetThreadKey(); - - if ( tcache == NULL ) { - // \ru Подписаться на сборку мусора, так как используются многопоточные кэши. - // \en Subscribe on garbage collection because using multithreaded caches. - SubcribeOnCleaning(); - { - // \ru Используется блокировка при изменении списка кэшей. \en Use lock when changing the cache list. - ScopedLock sl( GetLock(), false ); - if ( tcache == NULL ) { - tcache = new List( threadKey, INIT_BY_LONGTERM ); - return tcache->_data; - } - } - } - - List* entry = tcache; - while( entry != NULL ) { - if ( entry->_id == threadKey ) { - if ( !entry->_valid ) { - delete entry->_data; - entry->_data = INIT_BY_LONGTERM; - entry->_valid = true; - } - return entry->_data; - } - // \ru Если кэш не найден в списке, 'entry' содержит последний (на данный момент) элемент в списке. - // \en If cache not found in the list, 'entry' contains the last element in the list (at that point). - if ( entry->_next == NULL ) - break; - entry = entry->_next; - } - res = INIT_BY_LONGTERM; - List* newList = new List( threadKey, res ); - { - // \ru Используется блокировка при изменении списка кэшей. \en Use lock when changing the cache list. - ScopedLock sl( GetLock(), false ); - // \ru На данный момент, entry может быть не последним элементом в списке. - // \en At that point, entry could be not a last element in the list. - while ( entry->_next != NULL ) { - entry = entry->_next; - } - entry->_next = newList; - } // ScopedLock - - return res; - -} - - -//------------------------------------------------------------------------------ -// \ru Удалить данные в кэшах. Если resetLongTerm == true, удалить также данные кэша главного потока. -// \en Delete caches data. If resetLongTerm == true, also delete data of the main thread cache. -// --- -template -inline void CacheManager::Reset( bool resetLongTerm ) -{ - if ( tcache != NULL ) { - ScopedLock sl( GetLock() ); - List* entry = tcache; - while ( entry != NULL ) { - entry->_valid = false; - entry = entry->_next; - } - } - if ( resetLongTerm ) { - ScopedLock sl( GetLock() ); - delete longTerm; - longTerm = NULL; - // \ru Если нет параллельности, удаляется блокировка. \en If no parallelism, delete the lock. -#ifdef CACHE_DELETE_LOCK - if ( !sl.IsLocked() ) { - if ( lock != NULL ) - delete lock; - lock = NULL; - } -#endif - } -} - -//------------------------------------------------------------------------------ -// \ru Удалить все кэши и отписаться от сборки мусора. Должна вызываться в последовательном участке кода. -// \en Delete all caches and unsubscribe from the garbage collection.Should be called in sequential code. -// --- -template -inline void CacheManager::CleanAll( bool doPostproc ) -{ - if ( tcache != NULL ) { - if ( IsSubscribed() ) - UnsubcribeOnCleaning(); - if ( doPostproc ) - Postprocess(); - delete tcache; - tcache = NULL; - } -#ifdef CACHE_DELETE_LOCK - if ( lock != NULL ) { - delete lock; - lock = NULL; - } -#endif -} - -//------------------------------------------------------------------------------ -// \ru Пост-обработка кэшей после выхода из параллельных вычислений. -// \en Caches post-processing after exiting the parallel calculations. \~ -// --- -template -inline void CacheManager::Postprocess() -{ - T * main = LongTerm(); - List * entry = tcache; - while ( entry != NULL ) { - main->MergeWith( entry->_data );// Incorporate thread data into main thread data. - entry = entry->_next; - } -} - -#endif // __TOOL_MULTITHREADING_H +//////////////////////////////////////////////////////////////////////////////// +/** +\file +\brief \ru Управление параллельной обработкой данных. + \en Managing of parallel data processing. \~ +\details \ru Управление параллельной обработкой данных.\n + \en Managing of parallel data processing. \n \~ +*/ +//////////////////////////////////////////////////////////////////////////////// +#ifndef __TOOL_MULTITHREADING_H +#define __TOOL_MULTITHREADING_H + +#include +#include +#include + +//------------------------------------------------------------------------------ +/** +\brief \ru Режимы многопоточных вычислений. + \en Multithreading modes. \~ +\details \ru Режимы многопоточных вычислений. Режим многопоточности ядра управляет механизмом потокобезопасности объектов ядра и определяет, какие операции ядра будут распараллеливаться. \n + \en Multithreading modes. Multithreading mode of the kernel manages the mechanism of thread-safity of the kernel objects and defines which \n \~ +\ingroup Data_Structures +*/ +//--- +enum MbeMultithreadedMode { + mtm_Off = 0, ///< \ru Многопоточность ядра отключена. \en Kernel multithreading is off. + mtm_Standard = 1, ///< \ru Включена многопоточность ядра при обработке независимых объектов (без общих данных). \en Kernel multithreading is ON for independent objects (without common data). + mtm_SafeItems = 2, ///< \ru Обеспечивается потокобезопасность объектов типа MbItem. Выключена многопоточность ядра при обработке объектов, имеющих общие данные. \en Ensured thread-safety of objects MbItem. Kernel multithreading is OFF for objects with shared data. + mtm_Items = 3, ///< \ru Обеспечивается потокобезопасность объектов типа MbItem. Включена многопоточность ядра при обработке объектов с общими данными. \en Ensured thread-safety of objects MbItem. Kernel multithreading is ON for objects with shared data. + mtm_Max = 31 ///< \ru Включена максимальная многопоточность ядра. \en Maximal kernel multithreading is ON. +}; + +//------------------------------------------------------------------------------ +/** + \brief \ru Базовый класс для объектов, требующих сборки мусора. + \en Base class for objects which require a garbage collection. \~ +\details \ru Базовый класс для объектов, требующих сборки мусора. + Класс, наследующий от CacheCleaner, должен реализовать метод ResetCacheData, + который будет вызываться для сборки мусора. + \en Base class for objects which require a garbage collection. + A class, inheriting from CacheCleaner, should implement the method ResetCacheData, + which will be called for garbage collection. \~ +\ingroup Base_Tools +*/ +//--- +class MATH_CLASS CacheCleaner +{ + int subscribed; +public: + CacheCleaner(); + virtual ~CacheCleaner(); + + /** \brief \ru Подписан ли объект на сборку мусора. + \en Whether the object is subscribed for garbage collection. \~ + */ + bool IsSubscribed() { return subscribed > 0; } + + /** \brief \ru Очистить кэшированные данные. Возвращает true, если объект был отписан от сборки мусора. + \en Reset cached data. Return true if the object was unsubscribed from garbage collection.\~ + */ + virtual bool ResetCacheData() = 0; + + /** \brief \ru Принудительное удаление всех кэшей (используется сборщиком мусора). + \en Forced deletion of all caches (used by the garbage collector). \~ + */ + virtual void HardReset() = 0; + + /** \brief \ru Подписаться на сборку мусора. + \en Subscribe for garbage collection. \~ + */ + void SubcribeOnCleaning(); + + /** \brief \ru Отписаться от сборки мусора. + \en Unsubscribe from garbage collection. \~ + */ + void UnsubcribeOnCleaning(); +}; + +//------------------------------------------------------------------------------ +/** + \brief \ru Сборщик мусора в объектах, использующих кэширование данных. + \en Garbage collector in objects which use data caching. \~ +\details \ru Сборщик мусора. По требованию очищает кэши в зарегистрированных объектах CacheCleaner, + вызывая метод ResetCacheData каждого объекта. \n + \en Garbage collector. At request clears caches in registered CacheCleaner objects + by calling the method ResetCacheData of each object. \n \~ +\ingroup Base_Tools +*/ +//--- +class MATH_CLASS MbGarbageCollection +{ +public: + + /** \brief \ru Подписать объект на сборку мусора. + \en Subscribe the object for garbage collection. \~ + */ + static void Subscribe( CacheCleaner * obj ); + + /** \brief \ru Отписать объект от сборки мусора. + \en Unsubscribe the object from garbage collection. \~ + */ + static void Unsubscribe( CacheCleaner * obj ); + + /** \brief \ru Выполнить сборку мусора. \en Perform garbage collection. + \details \ru Функция должна вызываться в последовательном участке кода. + При вызове в параллельном регионе ничего не делает. + Вызывает метод ResetCacheData каждого объекта. + \en The function should be called in sequential code. + When called in a parallel region, does nothing. + Calls method ResetCacheData of each object. + \param[in] force - \ru Если false, то инициируется сборка мусора в кэшах, созданных для потоков, которые уже завершены, + если true, то инициируется принудительная сборка мусора во всех кэшах. + \en If false, then run garbage collection in caches created for threads which are finished, + if true, then force garbage collection in all caches. + \return \ru Возвращает TRUE, если сборка проведена. \en Returns TRUE if the garbage collection is done. \~ + */ + static bool Run( bool force = false ); + + /** \brief \ru Сбросить все зарегистрированные кэши. \en Reset all registered caches. + \details \ru Сбросить все зарегистрированные кэш после фатальной ошибки. + Должна вызываться в последовательном участке кода. При вызове в параллельном регионе ничего не делает. + Вызывает метод HardReset каждого объекта. + \en Reset all registered caches after fatal error. Should be called in sequential code. + When called in a parallel region, does nothing. + Calls method HardReset of each object. + */ + static void Reset(); + + /** \brief \ru Активировать/деактивировать сбор данных для проведения сборки мусора. + По умолчанию, сбор данных для сборки мусора активирован. + \en Enable/disable collecting data for garbage collection. + By default, collecting data for garbage collection is enabled. \~ + */ + static void Enable( bool allow = true ); +}; + +//------------------------------------------------------------------------------ +// \ru Принудительно вернуть освобожденную динамическую память операционной системе. +// Может быть полезна после выполнения операций с интенсивным использованием памяти. +// \en Force to return freed heap memory to the operating system. +// May be useful after performing memory-intensive operations. +// --- +MATH_FUNC( void ) ReleaseMemory(); + +//------------------------------------------------------------------------------ +/** +\brief \ru Родительский класс данных для менеджера параллельной обработки. + \en Parent class of data for manager of parallel processing. \~ +\details \ru Родительский класс для данных, которые могут обрабатываться параллельно + с помощью менеджера кэшей. + \en Parent class for data which could be processed in parallel using the cache manager. \~ + \ingroup Base_Tools +*/ +// --- +class AuxiliaryData { +public: + AuxiliaryData() {} + AuxiliaryData( const AuxiliaryData & ) {} + virtual ~AuxiliaryData() {} + + /** \brief \ru Объединить с указанными данными. + \en Merge with specified data. + \details \ru Функция вызывается Менеджером кэшей для данных основного потока + с данными каждого многопоточного кэша в качестве параметра. + Должна возвращать true, если MergeWith() не пустая и объединение произошло, + или false в противном случае. + После завершения функции Менеджер кэшей удаляет многопоточный кэш. + \en The function is called by CacheManager for the main thread data with + each multithreaded cache data as a parameter. + Should return true if MergeWith() is not empty and the merge occurred, + or false otherwise. + When the function completed, the CacheManager deletes the multithreaded cache. \~ + */ + virtual bool MergeWith( AuxiliaryData * ) { return false; } +}; + +//#define CACHE_DELETE_LOCK +//------------------------------------------------------------------------------ +/** +\brief \ru Менеджер параллельной обработки данных (менеджер кэшей) с возможностью пост-обработки кэшей потоков. + \en Manager for parallel data processing (the cache manager) with support of caches post-processing. \~ +\details \ru Менеджер кэшей представляет шаблон, содержащий: + longTerm - данные главного потока при последовательном выполнении и + tcache - список кэшей с данными, которые используются при параллельном выполнении. + Каждый поток по идентификатору threadKey использует только свою копию данных. + Для многопоточной обработки зависимых (имеющих общие данные) объектов должен использоваться режим + многопоточных вычислений не ниже mtm_SafeItems. + Менеджер предоставляет функцию Postprocess() для пост-обработки кэшей, которая вызывается после + выхода из параллельных вычислений. Указанная функция итерируется по кэшам, использованным + при параллельных вычислениях, и вызывает функцию longTerm.MergeWith() с данными каждого кэша + в качестве параметра. После завершения работы функции Postprocess() кэши удаляются. \n + \en The cache manager is a template which contains: + longTerm - data of the main thread in sequential execution, and + tcache - a list of caches with data which are used in parallel calculations. + Each thread uses its own copy of data according to threadKey. + For multithreaded processing of dependent (with shared data) objects the multithreading mode mtm_SafeItems + or higher should be used. + The Manager provides a Postprocess() function for caches post-processing which is called + after exiting parallel computing. The specified function iterates through the caches used + in parallel computing and calls the function longTerm.MergeWith() with the data of the each cache + as a parameter. After the function Postprocess() finished the caches are destroyed. \n \~ +*/ +// --- +template +class CacheManager : public CacheCleaner { + struct List + { + unsigned int _id; + T* _data; + List* _next; + bool _valid; + List( unsigned int id, T* data ) : + _id( id ), + _data( data != NULL ? data : new T() ), // Always _data != NULL. + _next( NULL ), + _valid( true ) {} + ~List() { + if ( _data != NULL ) + delete _data; + _data = NULL; + if ( _next != NULL ) // Also deletes linked List. + delete _next; + _next = NULL; + } + private: + List() : _id( 0 ), _data( NULL ), _next( NULL ) {} + }; + +private: + T* longTerm; // \ru Данные главного потока при последовательном выполнении. \en Data of the main thread in sequential execution. + List* tcache; // \ru Данные, которые используются при параллельном выполнении. \en Caches which are used in parallel execution. + CommonMutex* lock; // \ru Блокировка для операций с кэшами. \en Lock for operations with caches. + +public: +#ifdef CACHE_DELETE_LOCK + CacheManager( bool createLock = false ); +#else + CacheManager( bool createLock = true ); +#endif + CacheManager( const CacheManager & ); + ~CacheManager(); + + /** \brief \ru Оператор (). Возвращает указатель на кэш (данные) текущего потока. Всегда возвращает ненулевое значение. + \en Operator (). Returns a pointer to the cache (data) of the current thread. Always returns non-null value. \~ + */ + T * operator ()(); + + /** \brief \ru Удалить данные в кэшах. Если resetLongTerm == true, удалить также данные кэша главного потока. + \en Delete caches data. If resetLongTerm == true, also delete data of the main thread cache. + */ + void Reset ( bool resetLongTerm = false ); + + /** \brief \ru Получить указатель на кэш (данные) главного потока. Всегда возвращает ненулевое значение. + Все операции с кэшем главного потока должны быть защищены блокировкой кэша. + \en Get a pointer to cache (data) of the main thread. Always returns non-null value. + All operations with the main thread cache should be protected by the cache lock. \~ + */ + T * LongTerm (); + + /** \brief \ru Получить указатель на блокировку для операций с кэшем главного потока, учитывая, исполняется ли код параллельно + Может возвращать нулевое значение (удобно для использования с ScopedLock). + \en Get a pointer to the lock for operations with the main thread cache, considering whether the code runs in parallel. + Can return null value (good for use with ScopedLock). \~ + */ + CommonMutex* GetLock() { if ( ::LocksEnabled() ) return GetLockHard(); return lock; } + + /** \brief \ru Функция очистки, используемая сборщиком мусора. + \en Cleaning function, used by the garbage collector. \~ + */ + virtual bool ResetCacheData() { CleanAll(); return true; } + + /** \brief \ru Принудительное удаление всех кэшей (используется сборщиком мусора). + \en Forced deletion of all caches (used by the garbage collector). \~ + */ + virtual void HardReset(); + +private: + /** \brief \ru Удалить все кэши и отписаться от сборки мусора. Должна вызываться в последовательном участке кода. + \en Delete all caches and unsubscribe from the garbage collection. Should be called in sequential code. \~ + */ + void CleanAll( bool doPostproc = true ); + + /** \brief \ru Получить указатель на блокировку для операций с кэшем главного потока. Всегда возвращает ненулевое значение. + \en Get a pointer to the lock for operations with the main thread cache. Always returns non-null value. \~ + */ + CommonMutex* GetLockHard(); + + /** \brief \ru Пост-обработка кэшей после выхода из параллельных вычислений. + \en Caches post-processing after exiting the parallel calculations. \~ + */ + void Postprocess(); + + CacheManager & operator = ( const CacheManager & ); // \ru Не разрешен. \en Not allowed. +}; + + +//------------------------------------------------------------------------------ +// \ru Конструктор. \en Constructor. +// --- +template +inline CacheManager::CacheManager( bool createLock ) + : longTerm ( NULL ) + , tcache ( NULL ) + , lock ( NULL ) +{ + if ( createLock ) { + lock = new CommonMutex(); + longTerm = new T(); + } +} + + + +//------------------------------------------------------------------------------ +// \ru Конструктор. \en Constructor. +// --- +template +inline CacheManager::CacheManager( const CacheManager & item ) + : longTerm ( NULL ) + , tcache ( NULL ) + , lock ( NULL ) +{ + if ( item.longTerm != NULL ) + longTerm = new T( *item.longTerm ); +#ifndef CACHE_DELETE_LOCK + lock = new CommonMutex(); +#endif +} + + +//------------------------------------------------------------------------------ +// \ru Деструктор. \en Destructor. +// --- +template +inline CacheManager::~CacheManager() +{ + CleanAll( false ); + if ( longTerm != NULL ) + delete longTerm; + if ( lock != NULL ) + delete lock; +} + +//------------------------------------------------------------------------------ +// \ru Получить указатель на кэш главного потока. Всегда возвращает ненулевое значение. +// Все операции с кэшем главного потока должны быть защищены блокировкой кэша. +// \en Get a pointer to the main thread cache. Always returns non-null value. +// All operations with the main thread cache should be protected by the cache lock. +// --- +template +inline T* CacheManager::LongTerm () +{ + try { + if ( longTerm == NULL ) + longTerm = new T(); + } + catch ( const std::bad_alloc & ) { + FatalErrorHandler::SetError( rt_NotEnoughMemory ); + } + return longTerm; +} + +//------------------------------------------------------------------------------ +// \ru Получить указатель на блокировку для операций с кэшем главного потока. Всегда возвращает ненулевое значение. +// \en Get a pointer to the lock for operations with the main thread cache. Always returns non-null value. +// --- +template +inline CommonMutex* CacheManager::GetLockHard() +{ + if ( lock == NULL ) { + CommonMutex* ll = GetGlobalLock(); + ll->lock(); + if ( lock == NULL ) + lock = new CommonMutex(); + ll->unlock(); + } + return lock; +} + +//------------------------------------------------------------------------------ +// \ru Оператор (). Возвращает указатель на кэш текущего потока (всегда ненулевое значение). +// \en Operator (). Returns a pointer to the current thread cache (always non-null value). +// --- +template +inline T * CacheManager::operator()() +{ +// \ru Создать данные по данным кэша главного потока. \en Create data using the data of the main thread cache. +#define INIT_BY_LONGTERM ( longTerm != NULL ? new T( *longTerm ) : new T() ) + + if ( !IsSafeMultithreading() || !IsInParallel() ) { + CleanAll(); + return LongTerm(); + } + + T * res = NULL; + unsigned int threadKey = GetThreadKey(); + + if ( FatalErrorHandler::HasError() ) + return LongTerm(); + + if ( tcache == NULL ) { + // \ru Подписаться на сборку мусора, так как используются многопоточные кэши. + // \en Subscribe on garbage collection because using multithreaded caches. + SubcribeOnCleaning(); + { + // \ru Используется блокировка при изменении списка кэшей. \en Use lock when changing the cache list. + ScopedLock sl( GetLock(), false ); + if ( tcache == NULL ) { + try { + tcache = new List( threadKey, INIT_BY_LONGTERM ); + return tcache->_data; + } + catch ( const std::bad_alloc & ) { + FatalErrorHandler::SetError( rt_NotEnoughMemory ); + return LongTerm(); + } + } + } + } + + List* entry = tcache; + while( entry != NULL ) { + if ( entry->_id == threadKey ) { + if ( !entry->_valid ) { + try { + delete entry->_data; + { + ScopedLock sl( GetLock(), false ); + entry->_data = INIT_BY_LONGTERM; + } + entry->_valid = true; + } + catch ( const std::bad_alloc & ) { + FatalErrorHandler::SetError( rt_NotEnoughMemory ); + return LongTerm(); + } + } + return entry->_data; + } + // \ru Если кэш не найден в списке, 'entry' содержит последний (на данный момент) элемент в списке. + // \en If cache not found in the list, 'entry' contains the last element in the list (at that point). + if ( entry->_next == NULL ) + break; + entry = entry->_next; + } + { + // \ru Используется блокировка при изменении списка кэшей. \en Use lock when changing the cache list. + ScopedLock sl( GetLock(), false ); + try { + res = INIT_BY_LONGTERM; + List * newList = new List( threadKey, res ); + // \ru На данный момент, entry может быть не последним элементом в списке. + // \en At that point, entry could be not a last element in the list. + while ( entry->_next != NULL ) { + entry = entry->_next; + } + entry->_next = newList; + } + catch ( const std::bad_alloc & ) { + FatalErrorHandler::SetError( rt_NotEnoughMemory ); + return LongTerm(); + } + } // ScopedLock + + return res; + +} + + +//------------------------------------------------------------------------------ +// \ru Удалить данные в кэшах. Если resetLongTerm == true, удалить также данные кэша главного потока. +// \en Delete caches data. If resetLongTerm == true, also delete data of the main thread cache. +// --- +template +inline void CacheManager::Reset( bool resetLongTerm ) +{ + if ( tcache != NULL ) { + ScopedLock sl( GetLock() ); + List* entry = tcache; + while ( entry != NULL ) { + entry->_valid = false; + entry = entry->_next; + } + } + if ( resetLongTerm ) { + ScopedLock sl( GetLock() ); + delete longTerm; + longTerm = NULL; + // \ru Если нет параллельности, удаляется блокировка. \en If no parallelism, delete the lock. +#ifdef CACHE_DELETE_LOCK + if ( !sl.IsLocked() ) { + if ( lock != NULL ) + delete lock; + lock = NULL; + } +#endif + } +} + +//------------------------------------------------------------------------------ +// \ru Удалить все кэши и отписаться от сборки мусора. Должна вызываться в последовательном участке кода. +// \en Delete all caches and unsubscribe from the garbage collection.Should be called in sequential code. +// --- +template +inline void CacheManager::CleanAll( bool doPostproc ) +{ + if ( CacheCleanupAllowed() ) { + if ( tcache != NULL ) { + if ( doPostproc ) + Postprocess(); + delete tcache; + tcache = NULL; + } +#ifdef CACHE_DELETE_LOCK + if ( lock != NULL ) { + delete lock; + lock = NULL; + } +#endif + if ( IsSubscribed() ) + UnsubcribeOnCleaning(); +#ifdef CACHE_DELETE_LOCK + if ( lock != NULL ) { + delete lock; + lock = NULL; + } +#endif + } +} + +//------------------------------------------------------------------------------ +// \ru Пост-обработка кэшей после выхода из параллельных вычислений. +// \en Caches post-processing after exiting the parallel calculations. \~ +// --- +template +inline void CacheManager::Postprocess() +{ + if ( tcache != NULL ) { + LongTerm(); // Create longTerm + List * entry = tcache; + // Incorporate thread data into main thread data. + while ( entry != NULL && longTerm->MergeWith( entry->_data ) ) { + entry = entry->_next; + } + } +} + +//------------------------------------------------------------------------------ +// \ru Принудительное удаление всех кэшей (используется сборщиком мусора). +// \en Forced deletion of all caches (used by the garbage collector). +//------------------------------------------------------------------------------ +template +inline void CacheManager::HardReset() +{ + if ( tcache != NULL ) { + delete tcache; + tcache = NULL; + } + if ( longTerm != NULL ) { + delete longTerm; + longTerm = NULL; + } + + if ( lock != NULL ) { + delete lock; + lock = NULL; + } +} + +#endif // __TOOL_MULTITHREADING_H diff --git a/C3d/Include/tool_mutex.h b/C3d/Include/tool_mutex.h index 32f8f0c..2695e20 100644 --- a/C3d/Include/tool_mutex.h +++ b/C3d/Include/tool_mutex.h @@ -1,478 +1,560 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Реализация блокировок на базе системных механизмов синхронизации - и OpenMP блокировок. - \en Locks implementation on base of system synchronization mechanisms - and OpenMP locks. \~ - details \ru Реализация блокировок (в том числе блокировки в области видимости) - на базе системных механизмов синхронизации и OpenMP блокировок.\n - \en Implementation of locks (including scoped lock) on base of - system synchronization mechanisms and OpenMP locks.\n \~ -*/ -//////////////////////////////////////////////////////////////////////////////// -#ifndef __TOOL_MUTEX_H -#define __TOOL_MUTEX_H - -#include - - -//////////////////////////////////////////////////////////////////////////////// -// -// \ru Управление блокировками. -// \en Locks management. \~ -// -// \ru Переменная C3D_NATIVE_LOCK включает использование блокировок на базе -// \ru системных механизмов синхронизации вместо OpenMP, -// \ru что позволяет использование механизмов распараллеливания, отличных от OpenMP. -// -// \en The variable C3D_NATIVE_LOCK enables using locks on base of system -// \en synchronization mechanisms instead of OpenMP, that allows -// \en use of parallelization frameworks, other than OpenMP. \~ -// -//////////////////////////////////////////////////////////////////////////////// -#define C3D_NATIVE_LOCK - - -#ifdef C3D_NATIVE_LOCK -class ToolLock; - -//------------------------------------------------------------------------------ -/** \brief \ru Класс блокировки. \en Lock class. \~ - \details \ru Класс блокировки (реализация на базе системных механизмов синхронизации). - \en Lock class (implementation on base of system synchronization mechanisms). \~ - \ingroup Base_Tools -*/ -// --- -class MATH_CLASS CommonMutex -{ - ToolLock* m_lock; -public: - CommonMutex(); - ~CommonMutex(); - - void lock(); - void unlock(); - -private: - // \ru Запрет копирования. \en Copy forbidden. - CommonMutex ( const CommonMutex& ); - CommonMutex& operator = ( const CommonMutex& ); -}; - -//------------------------------------------------------------------------------ -/** \brief \ru Одинаковая реализация CommonMutex и CommonRecursiveMutex. - \en Same implementation of CommonMutex and CommonRecursiveMutex. \~ - \ingroup Base_Tools -*/ -#define CommonRecursiveMutex CommonMutex - -#else // C3D_NATIVE_LOCK - -//------------------------------------------------------------------------------ -/** \brief \ru Класс блокировки. - \en Lock class. \~ - \details \ru Класс блокировки на базе OpenMP lock. - \en Lock class on base of OpenMP lock. \~ - \ingroup Base_Tools -*/ -// --- -class MATH_CLASS CommonMutex -{ - omp_lock_t m_lock; -public: - // For correct work, CommonMutex implementation should be encapsulated in cpp. - CommonMutex(); - ~CommonMutex(); - void lock(); - void unlock(); -}; - -//------------------------------------------------------------------------------ -/** \brief \ru Класс блокировки на базе вложенного OpenMP lock. - \en Wrapper for nested OpenMP lock. \~ - \ingroup Base_Tools -*/ -// --- -class MATH_CLASS CommonRecursiveMutex -{ - omp_nest_lock_t m_lock; -public: - // For correct work, CommonRecursiveMutex implementation should be encapsulated in cpp. - CommonRecursiveMutex(); - ~CommonRecursiveMutex(); - void lock(); - void unlock(); -}; - -#endif // C3D_NATIVE_LOCK - - -//////////////////////////////////////////////////////////////////////////////// -// -// \ru Поддержка многопоточности при использовании произвольного параллельного фреймворка -// \ru в пользовательском приложении. -// \ru Чтобы использовать интерфейсы ядра в нескольких потоках, -// \ru для ядра должен быть установлен режим многопоточных вычислений не ниже mtm_SafeItems. -// -// \ru При использовании параллельных механизмов, отличных от OpenMP, пользовательское приложение -// \ru обязано нотифицировать ядро о входе в каждый параллельный регион и выходе из него. -// \ru Для этого могут быть использованы класс ParallelRegionGuard (защитник параллельного региона -// \ru в области видимости), функции EnterParallelRegion и ExitParallelRegion -// или макросы ENTER_PARALLEL и EXIT_PARALLEL. -// \ru Примеры: -// { -// ParallelRegionGuard l; -// std::thread t1( function1 ); -// std::thread t2( function2 ); -// t1.join(); -// t2.join(); -// } -// { -// EnterParallelRegion(); -// std::thread t1( function1 ); -// std::thread t2( function2 ); -// t1.join(); -// t2.join(); -// ExitParallelRegion(); -// } -// -// -// \en Support of multithreading when using an arbitrary parallel framework in user application. -// -// \en For using the kernel interfaces in several threads, the multithreading mode mtm_SafeItems -// \en or higher should be defined for the kernel. -// -// \en When using a parallel framework other than OpenMP in user code, the application must notify -// \en the kernel about entering and exiting a parallel region. -// \en For that, the class ParallelRegionGuard (a scoped guard of parallel region), -// \en the functions EnterParallelRegion and ExitParallelRegion, -// or macros ENTER_PARALLEL and EXIT_PARALLEL could be used. -// \en Examples: -// { -// ParallelRegionGuard l; -// std::thread t1( function1 ); -// std::thread t2( function2 ); -// t1.join(); -// t2.join(); -// } -// { -// EnterParallelRegion(); -// std::thread t1( function1 ); -// std::thread t2( function2 ); -// t1.join(); -// t2.join(); -// ExitParallelRegion(); -// } -// -//////////////////////////////////////////////////////////////////////////////// - -//------------------------------------------------------------------------------ -/** \brief \ru Защитник параллельного региона в области видимости. - \en Scoped guard of parallel region. \~ - \details \ru Класс защищает регион кода, выполняющийся параллельно. - Работает в области видимости. - Должен использоваться для защиты параллельного кода, - если используются средства распараллеливания, отличные от OpenMP. - Пример использования: - { - ParallelRegionGuard l; - std::thread t1( function1 ); - std::thread t2( function2 ); - t1.join(); - t2.join(); - } - \en The class guards a code region running in parallel. - Works in scope. - Should be used to protect parallel code if parallel framework other than OpenMP is used. - Example of use: - { - ParallelRegionGuard l; - std::thread t1( function1 ); - std::thread t2( function2 ); - t1.join(); - t2.join(); - } - \ingroup Base_Tools -*/ -// --- -class MATH_CLASS ParallelRegionGuard -{ -public: - ParallelRegionGuard(); - ~ParallelRegionGuard(); -}; - -//------------------------------------------------------------------------------ -/** \brief \ru Функция нотифицирует ядро о входе в параллельный блок кода. - Вызов функции должен стоять перед началом параллельного блока. - \en The function notifies the kernel about entering a parallel region. - The function call should be placed before the start of a parallel block -*/ -// --- -MATH_FUNC( void ) EnterParallelRegion(); - -//------------------------------------------------------------------------------ -/** \brief \ru Функция нотифицирует ядро о выходе из параллельного блока кода. - Вызов функции должен стоять после окончания параллельного блока. - \en The function notifies the kernel about exiting a parallel region. - The function call should be placed after the end of the parallel block. -*/ -// --- -MATH_FUNC( void ) ExitParallelRegion(); - - -//------------------------------------------------------------------------------ -// \ru Макросы для нотификации о входе и выходе из параллельного цикла. -// Вызов макросов при использовании OpenMP не обязателен, однако значительно ускоряет -// выполнение параллельного цикла. -// Вызов ENTER_PARALLEL должен стоять перед началом параллельного блока. -// Вызов EXIT_PARALLEL должен стоять после окончания параллельного блока. -// Пример использования: -// bool useParallel = Math::CheckMultithreadedMode( mtm_Items ); -// ENTER_PARALLEL( useParallel ); -// #pragma omp parallel for -// for ( ptrdiff_t i = 0; i < count; ++i ) { -// /* Cycle body */ -// } -// EXIT_PARALLEL( useParallel ); -// -/// \ru Macros for notification of entering and exiting a parallel block. -// Calling the macros when using OpenMP is not required, but significantly speeds up -// the execution of parallel cycle. -// The call ENTER_PARALLEL should be placed before the start of a parallel block. -// The call EXIT_PARALLEL should be placed after the end of the parallel block. -// Example of use: -// bool useParallel = Math::CheckMultithreadedMode( mtm_Items ); -// ENTER_PARALLEL( useParallel ); -// #pragma omp parallel for -// for ( ptrdiff_t i = 0; i < count; ++i ) { -// /* Cycle body */ -// } -// EXIT_PARALLEL( useParallel ); -// --- - -//------------------------------------------------------------------------------ -/** \brief \ru Если useParallel == true, нотифицирует ядро о входе в параллельный блок кода. - \en If useParallel == true, notifies the kernel about entering a parallel region. - \details \ru Если useParallel == true, нотифицирует ядро о входе в параллельный блок кода. - Вызов должен стоять перед началом параллельного блока (перед прагмой OpenMP). - Использование макроса значительно ускоряет параллельные циклы OpenMP. - \en If useParallel == true, notifies the kernel about entering a parallel region. - The call should be placed before the start of a parallel block (before OpenMP pragma). - Using a macro speeds up parallel OpenMP cycles significantly. -*/ -// --- -#define ENTER_PARALLEL(useParallel) if ( useParallel ) EnterParallelRegion(); - -//------------------------------------------------------------------------------ -/** \brief \ru Если useParallel == true, нотифицирует ядро о выходе из параллельного блока кода. - \en If useParallel == true, notifies the kernel about exiting a parallel region. - \details \ru Если useParallel == true, нотифицирует ядро о выходе из параллельного блока кода. - Вызов должен стоять после окончания параллельного блока. - Использование макроса значительно ускоряет параллельные циклы OpenMP. - \en If useParallel == true, notifies the kernel about exiting a parallel region. - The call should be placed after the end of the parallel block. - Using a macro speeds up parallel OpenMP cycles significantly. -*/ -// --- -#define EXIT_PARALLEL(useParallel) if ( useParallel ) ExitParallelRegion(); - -//------------------------------------------------------------------------------ -/** \brief \ru Функция определяет, выполняется ли код параллельно. - \en The function determines whether the code is executed in parallel. -*/ -// --- -MATH_FUNC( bool ) IsInParallel(); - -//////////////////////////////////////////////////////////////////////////////// -// -// \ru Блокировки и другие средства синхронизации. -// \en Locks and other synchronization objects. \~ - -// \ru В качестве блокировок должны использоваться CommonMutex и CommonRecursiveMutex -// \ru (OpenMP lock не должны использоваться напрямую). -// -// \en CommonMutex and CommonRecursiveMutex should be used as locks -// \en (OpenMP locks should not be used directly). -// -//////////////////////////////////////////////////////////////////////////////// - -//------------------------------------------------------------------------------ -/** \brief \ru Блокировка в области видимости. Может принимать нулевой указатель на мьютекс. - Блокировка происходит, если указатель на мьютекс ненулевой и код выполняется параллельно. - \en Scoped lock. Can accept a null pointer to a mutex. - Locking occurs if the pointer to the mutex is nonzero and the code runs in parallel. \~ - \ingroup Base_Tools -*/ -// --- -class MATH_CLASS ScopedLock -{ - CommonMutex* m_mutex; -public: - ScopedLock( CommonMutex* mutex, bool parallelCheck = true ); - ~ScopedLock(); - - // \ru Выполнена ли реальная блокировка. \en Whether a real locking performed. - bool IsLocked(); - -private: - ScopedLock(); - ScopedLock ( const ScopedLock& ); - ScopedLock& operator = ( const ScopedLock& ); -}; - -//------------------------------------------------------------------------------ -/** \brief \ru Рекурсивная блокировка в области видимости. Может принимать нулевой указатель на мьютекс. - Блокировка происходит, если указатель на мьютекс ненулевой и код выполняется параллельно. - \en Recursive scoped lock. Can accept a null pointer to a mutex. - Locking occurs if the pointer to the mutex is nonzero and the code runs in parallel. \~ -\ingroup Base_Tools -*/ -// --- -class MATH_CLASS ScopedRecursiveLock -{ - CommonRecursiveMutex* m_mutex; -public: - ScopedRecursiveLock( CommonRecursiveMutex* mutex, bool parallelCheck = true ); - ~ScopedRecursiveLock(); - - // \ru Выполнена ли реальная блокировка. \en Whether a real locking performed. - bool IsLocked(); - -private: - ScopedRecursiveLock(); - ScopedRecursiveLock ( const ScopedRecursiveLock& ); - ScopedRecursiveLock& operator = ( const ScopedRecursiveLock& ); -}; - -//------------------------------------------------------------------------------ -/** \brief \ru Базовый объект синхронизации с отложенной инициализацией. - \en Base synchronization object with lazy initialization. \~ - \details \ru Базовый объект, предоставляющий средства синхронизации и создающий блокировку при необходимости. \n - \en Base object which provides means of synchronization and creates a lock when needed. \n \~ -\ingroup Base_Tools -*/ -// --- -class MATH_CLASS MbSyncItem { -protected: - mutable CommonMutex * m_comLock; // \ru Критическая секция для монопольного доступа к объекту. \en The critical section for exclusive access to the object. - mutable bool m_locked; - -public: - MbSyncItem(); - virtual ~MbSyncItem(); - - // \ru Включить блокировку (блокировка происходит только при наличии параллельности). - // \en Switch lock on (locking happens only in parallel region). - void Lock() const; - // \ru Снять блокировку, если она была установлена. - // \en Switch lock off if locking has been set. - void Unlock() const; - - // \ru Выдать указатель на объект мьютекса. Возращает NULL, если параллельности нет. Для использования в ScopedLock. - // \en Get a pointer to the mutex object. Return NULL if no parallelism. For use in ScopedLock. - CommonMutex * GetLock() const; -}; - -//------------------------------------------------------------------------------ -/** \brief \ru Базовый объект синхронизации с отложенной инициализацией, поддерживающий множественные блокировки. - \en Base synchronization object with lazy initialization which supports nested locks. \~ - \details \ru Базовый объект синхронизации, поддерживающий множественные блокировки и создающий блокировку при необходимости. \n - \en Base synchronization object with support of nested locks which creates a lock if necessary. \n \~ -\ingroup Base_Tools -*/ -// --- -class MATH_CLASS MbNestSyncItem { -protected: - mutable CommonRecursiveMutex * m_comLock; // \ru Критическая секция для монопольного доступа к объекту. \en The critical section for exclusive access to the object. - mutable bool m_locked; - -public: - MbNestSyncItem(); - virtual ~MbNestSyncItem(); - - // \ru Включить блокировку (блокировка происходит только при наличии параллельности). - // \en Switch lock on (locking happens only in parallel region). - void Lock() const; - // \ru Снять блокировку, если она была установлена. - // \en Switch lock off if locking has been set. - void Unlock() const; - - // \ru Выдать указатель на объект мьютекса. Возращает NULL, если параллельности нет. Для использования в ScopedLock. - // \en Get a pointer to the mutex object. Return NULL if no parallelism. For use in ScopedLock. - CommonRecursiveMutex * GetLock() const; -}; - -//------------------------------------------------------------------------------ -/** \brief \ru Базовый объект, предоставляющий средства синхронизации. - \en Base object providing means of synchronization. \~ - \details \ru Базовый объект, предоставляющий средства синхронизации. \n - \en Base object providing means of synchronization. \n \~ -\ingroup Base_Tools -*/ -// --- -class MATH_CLASS MbPersistentSyncItem { -protected: - mutable CommonMutex m_comLock; // \ru Критическая секция для монопольного доступа к объекту. \en The critical section for exclusive access to the object. - -public: - MbPersistentSyncItem(); - virtual ~MbPersistentSyncItem(); - - void Lock() const; - void Unlock() const; -}; - -//------------------------------------------------------------------------------ -/** \brief \ru Базовый объект синхронизации, поддерживающий множественные блокировки. - \en Base synchronization object with support of nested locks. \~ - \details \ru Базовый объект синхронизации, поддерживающий множественные блокировки. \n - \en Base synchronization object with support of nested locks. \n \~ -\ingroup Base_Tools -*/ -// --- -class MATH_CLASS MbPersistentNestSyncItem { -protected: - mutable CommonRecursiveMutex m_comLock; // \ru Критическая секция для монопольного доступа к объекту. \en The critical section for exclusive access to the object. - -public: - MbPersistentNestSyncItem(); - virtual ~MbPersistentNestSyncItem(); - - void Lock() const; - void Unlock() const; -}; - - -//------------------------------------------------------------------------------ -// \ru Установлен ли режим безопасной многопоточности (используется в CacheManager). -// \en Whether is enabled a safe multithreading mode (used in CacheManager). -// --- -MATH_FUNC(bool) IsSafeMultithreading(); - -//------------------------------------------------------------------------------ -// \ru Получить идентификатор текущего потока. -// \en Get a current thread identifier. -// --- -MATH_FUNC( unsigned int ) GetThreadKey(); - -//------------------------------------------------------------------------------ -// \ru Получить указатель на глобальный мьютекс (используется в CacheManager). -// \en Get a pointer to the global mutex (used in CacheManager). -// --- -MATH_FUNC( CommonMutex* ) GetGlobalLock(); - -//------------------------------------------------------------------------------ -// \ru Получить указатель на глобальный рекурсивный мьютекс (используется для операций выделения и освобождения памяти). -// \en Get a pointer to the global recursive mutex (used for memory allocation and deallocation operations). -// --- -MATH_FUNC( CommonRecursiveMutex* ) GetGlobalRecursiveLock(); - -//------------------------------------------------------------------------------ -// \ru Установить блокировку в области видимости для операций выделения и освобождения памяти. -// \en Set scoped lock for memory allocation and deallocation operations. -// --- -#define SET_MEMORY_SCOPED_LOCK ScopedRecursiveLock memScopedLock( GetGlobalRecursiveLock() ); - -#endif // __TOOL_MUTEX_H +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Реализация блокировок на базе системных механизмов синхронизации + и OpenMP блокировок. + \en Locks implementation on base of system synchronization mechanisms + and OpenMP locks. \~ + details \ru Реализация блокировок (в том числе блокировки в области видимости) + на базе системных механизмов синхронизации и OpenMP блокировок.\n + \en Implementation of locks (including scoped lock) on base of + system synchronization mechanisms and OpenMP locks.\n \~ +*/ +//////////////////////////////////////////////////////////////////////////////// +#ifndef __TOOL_MUTEX_H +#define __TOOL_MUTEX_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// \ru Управление блокировками. +// \en Locks management. \~ +// +// \ru Переменная C3D_NATIVE_LOCK включает использование блокировок на базе +// \ru системных механизмов синхронизации вместо OpenMP, +// \ru что позволяет использование механизмов распараллеливания, отличных от OpenMP. +// +// \en The variable C3D_NATIVE_LOCK enables using locks on base of system +// \en synchronization mechanisms instead of OpenMP, that allows +// \en use of parallelization frameworks, other than OpenMP. \~ +// +//////////////////////////////////////////////////////////////////////////////// +#define C3D_NATIVE_LOCK + + +#ifdef C3D_NATIVE_LOCK +class ToolLock; + +//------------------------------------------------------------------------------ +/** \brief \ru Класс блокировки. \en Lock class. \~ + \details \ru Класс блокировки (реализация на базе системных механизмов синхронизации). + \en Lock class (implementation on base of system synchronization mechanisms). \~ + \ingroup Base_Tools +*/ +// --- +class MATH_CLASS CommonMutex +{ + ToolLock* m_lock; +public: + CommonMutex(); + ~CommonMutex(); + + /** \brief \ru Установить блокировку. \en Set a lock. \~ + */ + void lock(); + /** \brief \ru Снять блокировку. \en Unset a lock. \~ + */ + void unlock(); + +private: + // \ru Запрет копирования. \en Copy forbidden. + CommonMutex ( const CommonMutex& ); + CommonMutex& operator = ( const CommonMutex& ); +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Одинаковая реализация CommonMutex и CommonRecursiveMutex. + \en Same implementation of CommonMutex and CommonRecursiveMutex. \~ + \ingroup Base_Tools +*/ +#define CommonRecursiveMutex CommonMutex + +#else // C3D_NATIVE_LOCK + +//------------------------------------------------------------------------------ +/** \brief \ru Класс блокировки. + \en Lock class. \~ + \details \ru Класс блокировки на базе OpenMP lock. + \en Lock class on base of OpenMP lock. \~ + \ingroup Base_Tools +*/ +// --- +class MATH_CLASS CommonMutex +{ + omp_lock_t m_lock; +public: + // For correct work, CommonMutex implementation should be encapsulated in cpp. + CommonMutex(); + ~CommonMutex(); + void lock(); + void unlock(); +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Класс блокировки на базе вложенного OpenMP lock. + \en Wrapper for nested OpenMP lock. \~ + \ingroup Base_Tools +*/ +// --- +class MATH_CLASS CommonRecursiveMutex +{ + omp_nest_lock_t m_lock; +public: + // For correct work, CommonRecursiveMutex implementation should be encapsulated in cpp. + CommonRecursiveMutex(); + ~CommonRecursiveMutex(); + void lock(); + void unlock(); +}; + +#endif // C3D_NATIVE_LOCK + + +//////////////////////////////////////////////////////////////////////////////// +// +// \ru Поддержка многопоточности при использовании произвольного параллельного фреймворка +// \ru в пользовательском приложении. +// \ru Чтобы использовать интерфейсы ядра в нескольких потоках, +// \ru для ядра должен быть установлен режим многопоточных вычислений не ниже mtm_SafeItems. +// +// \ru При использовании параллельных механизмов, отличных от OpenMP, пользовательское приложение +// \ru обязано нотифицировать ядро о входе в каждый параллельный регион и выходе из него. +// \ru Для этого могут быть использованы класс ParallelRegionGuard (защитник параллельного региона +// \ru в области видимости), функции EnterParallelRegion и ExitParallelRegion +// или макросы ENTER_PARALLEL и EXIT_PARALLEL. +// \ru Примеры: +// { +// ParallelRegionGuard l; +// std::thread t1( function1 ); +// std::thread t2( function2 ); +// t1.join(); +// t2.join(); +// } +// { +// EnterParallelRegion(); +// std::thread t1( function1 ); +// std::thread t2( function2 ); +// t1.join(); +// t2.join(); +// ExitParallelRegion(); +// } +// +// +// \en Support of multithreading when using an arbitrary parallel framework in user application. +// +// \en For using the kernel interfaces in several threads, the multithreading mode mtm_SafeItems +// \en or higher should be defined for the kernel. +// +// \en When using a parallel framework other than OpenMP in user code, the application must notify +// \en the kernel about entering and exiting a parallel region. +// \en For that, the class ParallelRegionGuard (a scoped guard of parallel region), +// \en the functions EnterParallelRegion and ExitParallelRegion, +// or macros ENTER_PARALLEL and EXIT_PARALLEL could be used. +// \en Examples: +// { +// ParallelRegionGuard l; +// std::thread t1( function1 ); +// std::thread t2( function2 ); +// t1.join(); +// t2.join(); +// } +// { +// EnterParallelRegion(); +// std::thread t1( function1 ); +// std::thread t2( function2 ); +// t1.join(); +// t2.join(); +// ExitParallelRegion(); +// } +// +//////////////////////////////////////////////////////////////////////////////// + +//------------------------------------------------------------------------------ +/** \brief \ru Защитник параллельного региона в области видимости. + \en Scoped guard of parallel region. \~ + \details \ru Класс защищает регион кода, выполняющийся параллельно. + Работает в области видимости. + Должен использоваться для защиты параллельного кода, + если используются средства распараллеливания, отличные от OpenMP. + Пример использования: + { + ParallelRegionGuard l; + std::thread t1( function1 ); + std::thread t2( function2 ); + t1.join(); + t2.join(); + } + \en The class guards a code region running in parallel. + Works in scope. + Should be used to protect parallel code if parallel framework other than OpenMP is used. + Example of use: + { + ParallelRegionGuard l; + std::thread t1( function1 ); + std::thread t2( function2 ); + t1.join(); + t2.join(); + } + \ingroup Base_Tools +*/ +// --- +class MATH_CLASS ParallelRegionGuard +{ +public: + ParallelRegionGuard(); + ~ParallelRegionGuard(); +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Функция нотифицирует ядро о входе в параллельный блок кода. + Вызов функции должен стоять перед началом параллельного блока. + \en The function notifies the kernel about entering a parallel region. + The function call should be placed before the start of a parallel block +*/ +// --- +MATH_FUNC( void ) EnterParallelRegion(); + +//------------------------------------------------------------------------------ +/** \brief \ru Функция нотифицирует ядро о выходе из параллельного блока кода. + Вызов функции должен стоять после окончания параллельного блока. + \en The function notifies the kernel about exiting a parallel region. + The function call should be placed after the end of the parallel block. +*/ +// --- +MATH_FUNC( void ) ExitParallelRegion(); + + +//------------------------------------------------------------------------------ +// \ru Макросы для нотификации о входе и выходе из параллельного цикла. +// Вызов макросов при использовании OpenMP не обязателен, однако значительно ускоряет +// выполнение параллельного цикла. +// Вызов ENTER_PARALLEL должен стоять перед началом параллельного блока. +// Вызов EXIT_PARALLEL должен стоять после окончания параллельного блока. +// Пример использования: +// bool useParallel = Math::CheckMultithreadedMode( mtm_Items ); +// ENTER_PARALLEL( useParallel ); +// #pragma omp parallel for +// for ( ptrdiff_t i = 0; i < count; ++i ) { +// /* Cycle body */ +// } +// EXIT_PARALLEL( useParallel ); +// +/// \ru Macros for notification of entering and exiting a parallel block. +// Calling the macros when using OpenMP is not required, but significantly speeds up +// the execution of parallel cycle. +// The call ENTER_PARALLEL should be placed before the start of a parallel block. +// The call EXIT_PARALLEL should be placed after the end of the parallel block. +// Example of use: +// bool useParallel = Math::CheckMultithreadedMode( mtm_Items ); +// ENTER_PARALLEL( useParallel ); +// #pragma omp parallel for +// for ( ptrdiff_t i = 0; i < count; ++i ) { +// /* Cycle body */ +// } +// EXIT_PARALLEL( useParallel ); +// --- + +//------------------------------------------------------------------------------ +/** \brief \ru Если useParallel == true, нотифицирует ядро о входе в параллельный блок кода. + \en If useParallel == true, notifies the kernel about entering a parallel region. + \details \ru Если useParallel == true, нотифицирует ядро о входе в параллельный блок кода. + Вызов должен стоять перед началом параллельного блока (перед прагмой OpenMP). + Использование макроса значительно ускоряет параллельные циклы OpenMP. + \en If useParallel == true, notifies the kernel about entering a parallel region. + The call should be placed before the start of a parallel block (before OpenMP pragma). + Using a macro speeds up parallel OpenMP cycles significantly. +*/ +// --- +#define ENTER_PARALLEL(useParallel) if ( useParallel ) EnterParallelRegion(); + +//------------------------------------------------------------------------------ +/** \brief \ru Если useParallel == true, нотифицирует ядро о выходе из параллельного блока кода. + \en If useParallel == true, notifies the kernel about exiting a parallel region. + \details \ru Если useParallel == true, нотифицирует ядро о выходе из параллельного блока кода. + Вызов должен стоять после окончания параллельного блока. + Использование макроса значительно ускоряет параллельные циклы OpenMP. + \en If useParallel == true, notifies the kernel about exiting a parallel region. + The call should be placed after the end of the parallel block. + Using a macro speeds up parallel OpenMP cycles significantly. +*/ +// --- +#define EXIT_PARALLEL(useParallel) if ( useParallel ) ExitParallelRegion(); + +//------------------------------------------------------------------------------ +/** \brief \ru Нотифицирует ядро о входе в параллельный блок кода. + \en Notifies the kernel about entering a parallel region. + \details \ru Нотифицирует ядро о входе в параллельный блок кода. + Вызов должен стоять перед началом параллельного блока (перед прагмой OpenMP). + Использование макроса значительно ускоряет параллельные циклы OpenMP. + \en Notifies the kernel about entering a parallel region. + The call should be placed before the start of a parallel block (before OpenMP pragma). + Using a macro speeds up parallel OpenMP cycles significantly. +*/ +// --- +#define EXIT_PARALLEL_FORCED ExitParallelRegion(); + +//------------------------------------------------------------------------------ +/** \brief \ru Нотифицирует ядро о выходе из параллельного блока кода. + \en Notifiesotifies the kernel about exiting a parallel region. + \details \ru Нотифицирует ядро о выходе из параллельного блока кода. + Вызов должен стоять после окончания параллельного блока. + Использование макроса значительно ускоряет параллельные циклы OpenMP. + \en Notifies the kernel about exiting a parallel region. + The call should be placed after the end of the parallel block. + Using a macro speeds up parallel OpenMP cycles significantly. +*/ +// --- +#define ENTER_PARALLEL_FORCED EnterParallelRegion(); + +//------------------------------------------------------------------------------ +/** \brief \ru Функция определяет, выполняется ли код параллельно. + \en The function determines whether the code is executed in parallel. +*/ +// --- +MATH_FUNC( bool ) IsInParallel(); + +//------------------------------------------------------------------------------ +/** \brief \ru Функция определяет, разрешена ли очистка кэшей. + \en The function determines whether the caches cleanup is allowed. +*/ +// --- +MATH_FUNC( bool ) CacheCleanupAllowed(); + + +//////////////////////////////////////////////////////////////////////////////// +// +// \ru Блокировки и другие средства синхронизации. +// \en Locks and other synchronization objects. \~ + +// \ru В качестве блокировок должны использоваться CommonMutex и CommonRecursiveMutex +// \ru (OpenMP lock не должны использоваться напрямую). +// +// \en CommonMutex and CommonRecursiveMutex should be used as locks +// \en (OpenMP locks should not be used directly). +// +//////////////////////////////////////////////////////////////////////////////// + +//------------------------------------------------------------------------------ +/** \brief \ru Блокировка в области видимости. Может принимать нулевой указатель на мьютекс. + Блокировка происходит, если указатель на мьютекс ненулевой и код выполняется параллельно. + \en Scoped lock. Can accept a null pointer to a mutex. + Locking occurs if the pointer to the mutex is nonzero and the code runs in parallel. \~ + \ingroup Base_Tools +*/ +// --- +class MATH_CLASS ScopedLock +{ + CommonMutex* m_mutex; +public: + ScopedLock( CommonMutex* mutex, bool parallelCheck = true ); + ~ScopedLock(); + + /** \brief \ru Выполнена ли реальная блокировка. \en Whether a real locking performed. \~ + */ + bool IsLocked(); + +private: + ScopedLock(); + ScopedLock ( const ScopedLock& ); + ScopedLock& operator = ( const ScopedLock& ); +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Рекурсивная блокировка в области видимости. Может принимать нулевой указатель на мьютекс. + Блокировка происходит, если указатель на мьютекс ненулевой и код выполняется параллельно. + \en Recursive scoped lock. Can accept a null pointer to a mutex. + Locking occurs if the pointer to the mutex is nonzero and the code runs in parallel. \~ +\ingroup Base_Tools +*/ +// --- +class MATH_CLASS ScopedRecursiveLock +{ + CommonRecursiveMutex* m_mutex; +public: + ScopedRecursiveLock( CommonRecursiveMutex* mutex, bool parallelCheck = true ); + ~ScopedRecursiveLock(); + + /** \brief \ru Выполнена ли реальная блокировка. \en Whether a real locking performed. \~ + */ + bool IsLocked(); + +private: + ScopedRecursiveLock(); + ScopedRecursiveLock ( const ScopedRecursiveLock& ); + ScopedRecursiveLock& operator = ( const ScopedRecursiveLock& ); +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Базовый объект синхронизации с отложенной инициализацией. + \en Base synchronization object with lazy initialization. \~ + \details \ru Базовый объект, предоставляющий средства синхронизации и создающий блокировку при необходимости. \n + \en Base object which provides means of synchronization and creates a lock when needed. \n \~ +\ingroup Base_Tools +*/ +// --- +class MATH_CLASS MbSyncItem { +protected: + mutable CommonMutex * m_comLock; // \ru Критическая секция для монопольного доступа к объекту. \en The critical section for exclusive access to the object. + mutable int m_locked; + +public: + MbSyncItem(); + virtual ~MbSyncItem(); + + /** \brief \ru Включить блокировку (блокировка происходит только при наличии параллельности). + \en Switch lock on (locking happens only in parallel region). + */ + void Lock() const; + + /** \brief \ru Снять блокировку, если она была установлена. + \en Switch lock off if locking has been set. + */ + void Unlock() const; + + // \ru Выдать указатель на объект мьютекса. Возращает NULL, если параллельности нет. Для использования в ScopedLock. + // \en Get a pointer to the mutex object. Return NULL if no parallelism. For use in ScopedLock. + CommonMutex * GetLock() const; +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Базовый объект синхронизации с отложенной инициализацией, поддерживающий множественные блокировки. + \en Base synchronization object with lazy initialization which supports nested locks. \~ + \details \ru Базовый объект синхронизации, поддерживающий множественные блокировки и создающий блокировку при необходимости. \n + \en Base synchronization object with support of nested locks which creates a lock if necessary. \n \~ +\ingroup Base_Tools +*/ +// --- +class MATH_CLASS MbNestSyncItem { +protected: + mutable CommonRecursiveMutex * m_comLock; // \ru Критическая секция для монопольного доступа к объекту. \en The critical section for exclusive access to the object. + mutable int m_locked; + +public: + MbNestSyncItem(); + virtual ~MbNestSyncItem(); + + /** \brief \ru Включить блокировку (блокировка происходит только при наличии параллельности). + \en Switch lock on (locking happens only in parallel region). + */ + void Lock() const; + + /** \brief \ru Снять блокировку, если она была установлена. + \en Switch lock off if locking has been set. + */ + void Unlock() const; + + /** \brief \ru Выдать указатель на объект мьютекса. Возращает NULL, если параллельности нет. Для использования в ScopedLock. + \en Get a pointer to the mutex object. Return NULL if no parallelism. For use in ScopedLock. + */ + CommonRecursiveMutex * GetLock() const; +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Базовый объект, предоставляющий средства синхронизации. + \en Base object providing means of synchronization. \~ + \details \ru Базовый объект, предоставляющий средства синхронизации. \n + \en Base object providing means of synchronization. \n \~ +\ingroup Base_Tools +*/ +// --- +class MATH_CLASS MbPersistentSyncItem { +protected: + mutable CommonMutex m_comLock; // \ru Критическая секция для монопольного доступа к объекту. \en The critical section for exclusive access to the object. + mutable int m_locked; + +public: + MbPersistentSyncItem(); + virtual ~MbPersistentSyncItem(); + + /** \brief \ru Включить блокировку. + \en Switch lock on. + */ + void Lock() const; + + /** \brief \ru Снять блокировку, если она была установлена. + \en Switch lock off if locking has been set. + */ + void Unlock() const; + + /** \brief \ru Выдать указатель на объект мьютекса. + \en Get a pointer to the mutex object. + */ + CommonMutex * GetLock() const; + +}; + +//------------------------------------------------------------------------------ +/** \brief \ru Базовый объект синхронизации, поддерживающий множественные блокировки. + \en Base synchronization object with support of nested locks. \~ + \details \ru Базовый объект синхронизации, поддерживающий множественные блокировки. \n + \en Base synchronization object with support of nested locks. \n \~ +\ingroup Base_Tools +*/ +// --- +class MATH_CLASS MbPersistentNestSyncItem { +protected: + mutable CommonRecursiveMutex m_comLock; // \ru Критическая секция для монопольного доступа к объекту. \en The critical section for exclusive access to the object. + mutable int m_locked; + +public: + MbPersistentNestSyncItem(); + virtual ~MbPersistentNestSyncItem(); + + /** \brief \ru Включить блокировку. \en Switch lock on. + */ + void Lock() const; + + /** \brief \ru Снять блокировку, если она была установлена. + \en Switch lock off if locking has been set. + */ + void Unlock() const; + + /** \brief \ru Выдать указатель на объект мьютекса. + \en Get a pointer to the mutex object. + */ + CommonRecursiveMutex * GetLock() const; + +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Установлен ли режим безопасной многопоточности (используется в CacheManager). + \en Whether is enabled a safe multithreading mode (used in CacheManager). \~ + \ingroup Base_Tools +*/ +MATH_FUNC(bool) IsSafeMultithreading(); + +//------------------------------------------------------------------------------ +/** \brief \ru Включены ли блокировки (режим многопоточности >= mtm_Standard и код выполняется параллельно). + \en Whether locks are enabled (multithreading is on and code is executed in parallel). \~ + \ingroup Base_Tools +*/ +MATH_FUNC( bool ) LocksEnabled(); + +//------------------------------------------------------------------------------ +/** \brief \ru Получить псевдо-идентификатор текущего потока. + \en Get a current thread pseudo-identifier. +*/ +MATH_FUNC( unsigned int ) GetThreadKey(); + +//------------------------------------------------------------------------------ +/** \brief \ru Получить указатель на глобальный мьютекс (используется в CacheManager). + \en Get a pointer to the global mutex (used in CacheManager). +*/ +MATH_FUNC( CommonMutex* ) GetGlobalLock(); + +//------------------------------------------------------------------------------ +/** \brief \ru Получить указатель на глобальный рекурсивный мьютекс (используется для операций выделения и освобождения памяти). + \en Get a pointer to the global recursive mutex (used for memory allocation and deallocation operations). +*/ +MATH_FUNC( CommonRecursiveMutex* ) GetGlobalRecursiveLock(); + +//------------------------------------------------------------------------------ +/** \brief \ru Установить блокировку в области видимости для операций выделения и освобождения памяти. + \en Set scoped lock for memory allocation and deallocation operations. +*/ +#define SET_MEMORY_SCOPED_LOCK ScopedRecursiveLock memScopedLock( GetGlobalRecursiveLock() ); + +#endif // __TOOL_MUTEX_H diff --git a/C3d/Include/tool_time_test.h b/C3d/Include/tool_time_test.h index e0f5c74..6c6ec3e 100644 --- a/C3d/Include/tool_time_test.h +++ b/C3d/Include/tool_time_test.h @@ -224,11 +224,11 @@ MATH_FUNC(void) SortResultMeasuring( TimeTest &, std::vector & ) #define TB_MATH_CONVERTERS _T("MATH_CONVERTERS:") #define TB_TEST _T("Test.exe:") - #define SET_TIME_TEST(allow) ::SetTimeTest ( allow ); - #define CHECK_TIME_TEST() ::CheckTimeTest (); + #define SET_TIME_TEST(allow) ::SetTimeTest( allow ); + #define CHECK_TIME_TEST() ::CheckTimeTest(); - #define BEGIN_TIME1(name,attitude) ::BeginTime ( name, attitude ); // \ru копирование оболочек, методы пересечения \en copying of shells, intersection methods - #define END_TIME1(name) ::EndTime ( name ); + #define BEGIN_TIME1(name,attitude) ::BeginTime( name, attitude ); // \ru копирование оболочек, методы пересечения \en copying of shells, intersection methods + #define END_TIME1(name) ::EndTime( name ); #else // C3D_DEBUG @@ -251,7 +251,7 @@ MATH_FUNC(void) SortResultMeasuring( TimeTest &, std::vector & ) #define SET_TIME_TEST_REL(allow) ::SetTimeTest( allow ); -#define BEGIN_TIME_REL(name,attitude) ::BeginTime( name, attitude ); +#define BEGIN_TIME_REL(name, attitude) ::BeginTime( name, attitude ); #define END_TIME_REL(name) ::EndTime( name ); #define TIME_TEST_REPORT_REL(filename) ::TimeTestReport( filename ); diff --git a/C3d/Include/tool_uuid.h b/C3d/Include/tool_uuid.h index b194937..39f54cc 100644 --- a/C3d/Include/tool_uuid.h +++ b/C3d/Include/tool_uuid.h @@ -1,288 +1,288 @@ -//////////////////////////////////////////////////////////////////////////////// -/** - \file - \brief \ru Глобально уникальный идентификатор. - \en Global unique identifier. \~ - -*/ -//////////////////////////////////////////////////////////////////////////////// - -#ifndef _TOOL_UUID_H_ -#define _TOOL_UUID_H_ - -#include -#include -#include -#include -#include - -class reader; -class writer; - - -const uint8 uuidSize = 16; - - -//------------------------------------------------------------------------------ -/** \brief \ru Глобально уникальный идентификатор. - \en Global unique identifier. \~ - \details \ru Глобально уникальный идентификатор - используется как идентификатор - типа пользовательского атрибута. - \en Global unique identifier - it is used as an identifier - of a type of user attribute. \~ - \ingroup Model_Attributes - */ -struct MbUuid -{ -protected: - uint8 data[uuidSize]; -private: - mutable ThreeStates isEmpty; - -public: - typedef uint8 * iterator; - typedef uint8 const * const_iterator; -public: - MbUuid() : isEmpty( ts_positive ) { ::memset( data, 0, uuidSize ); } - MbUuid( const MbUuid & id ) : isEmpty( id.isEmpty ) { ::memcpy( data, id.data, uuidSize ); } -public: - iterator begin() { isEmpty = ts_neutral; return data; } - iterator end() { isEmpty = ts_neutral; return data + uuidSize; } - - const_iterator cbegin() const { return data; } - const_iterator cend() const { return data + uuidSize; } - - size_t size() const { return uuidSize; } - - bool is_nil() const - { - if ( isEmpty == ts_neutral ) { - isEmpty = ts_positive; - for ( uint8 i = 0; i < uuidSize; i++ ) { - if ( data[i] != 0U ) { - isEmpty = ts_negative; - break; - } - } - } - return (isEmpty == ts_positive); - } - void swap( MbUuid & id ) - { - uint8 temp[16]; - ::memcpy( temp, data, uuidSize ); - ::memcpy( data, id.data, uuidSize ); - ::memcpy( id.data, temp, uuidSize ); - std::swap( isEmpty, id.isEmpty ); - } - -public: - MbUuid & operator = ( const MbUuid & id ) - { - ::memcpy( data, id.data, uuidSize ); - isEmpty = id.isEmpty; - return *this; - } - bool operator == ( const MbUuid & id ) const - { - //return std::equal( cbegin(), cend(), id.cbegin() ); - MbUuid::const_iterator first1 = cbegin(), last1 = cend(), first2 = id.cbegin(); - while ( first1 != last1 ) - { - if ( !(*first1 == *first2) ) - return false; - ++first1; ++first2; - } - return true; - } - bool operator != ( const MbUuid & id ) const { return !(*this == id); } - bool operator < ( const MbUuid & id ) const { return std::lexicographical_compare( cbegin(), cend(), id.cbegin(), id.cend() ); } - bool operator > ( const MbUuid & id ) const { return (id < *this); } - bool operator <= ( const MbUuid & id ) const { return !(id < *this); } - bool operator >= ( const MbUuid & id ) const { return !(*this < id); } - -public: - friend struct string_generator; - friend reader & CALL_DECLARATION operator >> ( reader & in, MbUuid & ref ); - friend writer & CALL_DECLARATION operator << ( writer & out, const MbUuid & ref ); - friend writer & CALL_DECLARATION operator << ( writer & out, MbUuid & ref ) { return operator << ( out, (const MbUuid &)ref ); } -}; - - -//------------------------------------------------------------------------------ -/** \brief \ru Генератор MbUuid из string. - \en Generator of MbUuid from string. \~ - \details \ru Генератор MbUuid из string. Принимает следующие формы: \n - 0123456789abcdef0123456789abcdef, \n - 01234567-89ab-cdef-0123456789abcdef, \n - {01234567-89ab-cdef-0123456789abcdef}, \n - {0123456789abcdef0123456789abcdef}. \n - \en Generator of MbUuid from string. It accepts the next format: \n - 0123456789abcdef0123456789abcdef, \n - 01234567-89ab-cdef-0123456789abcdef, \n - {01234567-89ab-cdef-0123456789abcdef}, \n - {0123456789abcdef0123456789abcdef}. \n - \~ - \ingroup Model_Attributes -*/ -//--- -struct string_generator -{ - template - MbUuid operator()( std::basic_string const & s ) const { - return operator()( s.begin(), s.end() ); - }; - - MbUuid operator()( char const * const s ) const { - return operator()( s, s+std::strlen(s) ); - } - - MbUuid operator()( wchar_t const * const s ) const { - return operator()( s, s+std::wcslen(s) ); - } - - template - MbUuid operator()( CharIterator begin, CharIterator end ) const - { - typedef typename std::iterator_traits::value_type char_type; - - // \ru Проверяем открывающую скобку \en Check an opening parenthesis. - char_type c = get_next_char( begin, end ); - bool has_open_brace = is_open_brace(c); - char_type open_brace_char = c; - if ( has_open_brace ) { - c = get_next_char( begin, end ); - } - - bool has_dashes = false; - - MbUuid u; - bool isEmpty = true; - - int i = 0; - for ( MbUuid::iterator it_byte = u.begin(); it_byte != u.end(); ++it_byte, ++i ) { - if ( it_byte != u.begin() ) { - c = get_next_char( begin, end ); - } - if ( i == 4 ) { - if ( is_dash(c) ) { - c = get_next_char( begin, end ); - has_dashes = true; - } - } - if ( has_dashes ) { - if ( is_dash(c) ) { - c = get_next_char( begin, end ); - } - } - - *it_byte = get_value(c); - - c = get_next_char( begin, end ); - *it_byte <<= 4; - *it_byte |= get_value(c); - if ( *it_byte != 0U ) - isEmpty = false; - } - - // \ru Проверяем закрывающую скобку \en Check a closing parenthesis - if ( has_open_brace ) { - c = get_next_char( begin, end ); - check_close_brace( c, open_brace_char ); - } - - u.isEmpty = isEmpty ? ts_positive : ts_negative; - return u; - } - -private: - template - typename std::iterator_traits::value_type - get_next_char( CharIterator & begin, CharIterator end ) const { - if ( begin == end ) { - _ASSERT( false ); - } - return *begin++; - } - - unsigned char get_value( char c ) const { - static char const*const digits_begin = "0123456789abcdefABCDEF"; - static char const*const digits_end = digits_begin + 22; - - static unsigned char const values[] = - { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,10,11,12,13,14,15 - , static_cast(-1) }; - - char const * d = std::find( digits_begin, digits_end, c ); - return values[d - digits_begin]; - } - - unsigned char get_value( wchar_t c ) const { - static wchar_t const*const digits_begin = L"0123456789abcdefABCDEF"; - static wchar_t const*const digits_end = digits_begin + 22; - - static unsigned char const values[] = - { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,10,11,12,13,14,15 - , static_cast(-1) }; - - wchar_t const * d = std::find( digits_begin, digits_end, c ); - return values[d - digits_begin]; - } - - bool is_dash( char c ) const { return c == '-'; } - bool is_dash( wchar_t c ) const { return c == L'-'; } - - // \ru Возвращаем открывающую скобку \en Return an opening parenthesis. - bool is_open_brace( char c ) const { return (c == '{'); } - bool is_open_brace( wchar_t c ) const { return (c == L'{'); } - - bool check_close_brace( char c, char open_brace ) const { - if ( open_brace == '{' && c == '}' ) - return true; - _ASSERT( false ); - return false; - } - - bool check_close_brace( wchar_t c, wchar_t open_brace ) const { - if ( open_brace == L'{' && c == L'}' ) - return true; - _ASSERT( false ); - return false; - } -}; - - -struct hash8_generator -{ - uint8 operator()( const MbUuid & trg ) const - { - static uint8 rand8[256] = - { - 1, 14, 110, 25, 97, 174, 132, 119, 138, 170, 125, 118, 27, 233, 140, 51, - 87, 197, 177, 107, 234, 169, 56, 68, 30, 7, 173, 73, 188, 40, 36, 65, - 49, 213, 104, 190, 57, 211, 148, 223, 48, 115, 15, 2, 67, 186, 210, 28, - 12, 181, 103, 70, 22, 58, 75, 78, 183, 167, 238, 157, 124, 147, 172, 144, - 176, 161, 141, 86, 60, 66, 128, 83, 156, 241, 79, 46, 168, 198, 41, 254, - 178, 85, 253, 237, 250, 154, 133, 88, 35, 206, 95, 116, 252, 192, 54, 221, - 102, 218, 255, 240, 82, 106, 158, 201, 61, 3, 89, 9, 42, 155, 159, 93, - 166, 80, 50, 34, 175, 195, 100, 99, 26, 150, 16, 145, 4, 33, 8, 189, - 121, 64, 77, 72, 208, 245, 130, 122, 143, 55, 105, 134, 29, 164, 185, 194, - 193, 239, 101, 242, 5, 171, 126, 11, 74, 59, 137, 228, 108, 191, 232, 139, - 6, 24, 81, 20, 127, 17, 91, 92, 251, 151, 225, 207, 21, 98, 113, 112, - 84, 226, 18, 214, 199, 187, 13, 32, 94, 220, 224, 212, 247, 204, 196, 43, - 249, 236, 45, 244, 111, 182, 153, 136, 129, 90, 217, 202, 19, 165, 231, 71, - 230, 142, 96, 227, 62, 179, 246, 114, 162, 53, 160, 215, 205, 180, 47, 109, - 44, 38, 31, 149, 135, 0, 216, 52, 63, 23, 37, 69, 39, 117, 146, 184, - 163, 200, 222, 235, 248, 243, 219, 10, 152, 131, 123, 229, 203, 76, 120, 209 - }; - - uint8 h = 0; - MbUuid::const_iterator curr_iter = trg.cbegin(), end_iter = trg.cend(); - while ( curr_iter != end_iter ) - h = rand8[h ^ *curr_iter++]; - return h; - } -}; - - -#endif // _TOOL_UUID_H_ +//////////////////////////////////////////////////////////////////////////////// +/** + \file + \brief \ru Глобально уникальный идентификатор. + \en Global unique identifier. \~ + +*/ +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _TOOL_UUID_H_ +#define _TOOL_UUID_H_ + +#include +#include +#include +#include +#include + +class reader; +class writer; + + +const_expr uint8 uuidSize = 16; + + +//------------------------------------------------------------------------------ +/** \brief \ru Глобально уникальный идентификатор. + \en Global unique identifier. \~ + \details \ru Глобально уникальный идентификатор - используется как идентификатор + типа пользовательского атрибута. + \en Global unique identifier - it is used as an identifier + of a type of user attribute. \~ + \ingroup Model_Attributes + */ +struct MbUuid +{ +protected: + uint8 data[uuidSize]; +private: + mutable ThreeStates isEmpty; + +public: + typedef uint8 * iterator; + typedef uint8 const * const_iterator; +public: + MbUuid() : isEmpty( ts_positive ) { ::memset( data, 0, uuidSize ); } + MbUuid( const MbUuid & id ) : isEmpty( id.isEmpty ) { ::memcpy( data, id.data, uuidSize ); } +public: + iterator begin() { isEmpty = ts_neutral; return data; } + iterator end() { isEmpty = ts_neutral; return data + uuidSize; } + + const_iterator cbegin() const { return data; } + const_iterator cend() const { return data + uuidSize; } + + size_t size() const { return uuidSize; } + + bool is_nil() const + { + if ( isEmpty == ts_neutral ) { + isEmpty = ts_positive; + for ( uint8 i = 0; i < uuidSize; i++ ) { + if ( data[i] != 0U ) { + isEmpty = ts_negative; + break; + } + } + } + return (isEmpty == ts_positive); + } + void swap( MbUuid & id ) + { + uint8 temp[16]; + ::memcpy( temp, data, uuidSize ); + ::memcpy( data, id.data, uuidSize ); + ::memcpy( id.data, temp, uuidSize ); + std::swap( isEmpty, id.isEmpty ); + } + +public: + MbUuid & operator = ( const MbUuid & id ) + { + ::memcpy( data, id.data, uuidSize ); + isEmpty = id.isEmpty; + return *this; + } + bool operator == ( const MbUuid & id ) const + { + //return std::equal( cbegin(), cend(), id.cbegin() ); + MbUuid::const_iterator first1 = cbegin(), last1 = cend(), first2 = id.cbegin(); + while ( first1 != last1 ) + { + if ( !(*first1 == *first2) ) + return false; + ++first1; ++first2; + } + return true; + } + bool operator != ( const MbUuid & id ) const { return !(*this == id); } + bool operator < ( const MbUuid & id ) const { return std::lexicographical_compare( cbegin(), cend(), id.cbegin(), id.cend() ); } + bool operator > ( const MbUuid & id ) const { return (id < *this); } + bool operator <= ( const MbUuid & id ) const { return !(id < *this); } + bool operator >= ( const MbUuid & id ) const { return !(*this < id); } + +public: + friend struct string_generator; + friend reader & CALL_DECLARATION operator >> ( reader & in, MbUuid & ref ); + friend writer & CALL_DECLARATION operator << ( writer & out, const MbUuid & ref ); + friend writer & CALL_DECLARATION operator << ( writer & out, MbUuid & ref ) { return operator << ( out, (const MbUuid &)ref ); } +}; + + +//------------------------------------------------------------------------------ +/** \brief \ru Генератор MbUuid из string. + \en Generator of MbUuid from string. \~ + \details \ru Генератор MbUuid из string. Принимает следующие формы: \n + 0123456789abcdef0123456789abcdef, \n + 01234567-89ab-cdef-0123456789abcdef, \n + {01234567-89ab-cdef-0123456789abcdef}, \n + {0123456789abcdef0123456789abcdef}. \n + \en Generator of MbUuid from string. It accepts the next format: \n + 0123456789abcdef0123456789abcdef, \n + 01234567-89ab-cdef-0123456789abcdef, \n + {01234567-89ab-cdef-0123456789abcdef}, \n + {0123456789abcdef0123456789abcdef}. \n + \~ + \ingroup Model_Attributes +*/ +//--- +struct string_generator +{ + template + MbUuid operator()( std::basic_string const & s ) const { + return operator()( s.begin(), s.end() ); + }; + + MbUuid operator()( char const * const s ) const { + return operator()( s, s+std::strlen(s) ); + } + + MbUuid operator()( wchar_t const * const s ) const { + return operator()( s, s+std::wcslen(s) ); + } + + template + MbUuid operator()( CharIterator begin, CharIterator end ) const + { + typedef typename std::iterator_traits::value_type char_type; + + // \ru Проверяем открывающую скобку \en Check an opening parenthesis. + char_type c = get_next_char( begin, end ); + bool has_open_brace = is_open_brace(c); + char_type open_brace_char = c; + if ( has_open_brace ) { + c = get_next_char( begin, end ); + } + + bool has_dashes = false; + + MbUuid u; + bool isEmpty = true; + + int i = 0; + for ( MbUuid::iterator it_byte = u.begin(); it_byte != u.end(); ++it_byte, ++i ) { + if ( it_byte != u.begin() ) { + c = get_next_char( begin, end ); + } + if ( i == 4 ) { + if ( is_dash(c) ) { + c = get_next_char( begin, end ); + has_dashes = true; + } + } + if ( has_dashes ) { + if ( is_dash(c) ) { + c = get_next_char( begin, end ); + } + } + + *it_byte = get_value(c); + + c = get_next_char( begin, end ); + *it_byte <<= 4; + *it_byte |= get_value(c); + if ( *it_byte != 0U ) + isEmpty = false; + } + + // \ru Проверяем закрывающую скобку \en Check a closing parenthesis + if ( has_open_brace ) { + c = get_next_char( begin, end ); + check_close_brace( c, open_brace_char ); + } + + u.isEmpty = isEmpty ? ts_positive : ts_negative; + return u; + } + +private: + template + typename std::iterator_traits::value_type + get_next_char( CharIterator & begin, CharIterator end ) const { + if ( begin == end ) { + _ASSERT( false ); + } + return *begin++; + } + + unsigned char get_value( char c ) const { + static char const*const digits_begin = "0123456789abcdefABCDEF"; + static char const*const digits_end = digits_begin + 22; + + static unsigned char const values[] = + { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,10,11,12,13,14,15 + , static_cast(-1) }; + + char const * d = std::find( digits_begin, digits_end, c ); + return values[d - digits_begin]; + } + + unsigned char get_value( wchar_t c ) const { + static wchar_t const*const digits_begin = L"0123456789abcdefABCDEF"; + static wchar_t const*const digits_end = digits_begin + 22; + + static unsigned char const values[] = + { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,10,11,12,13,14,15 + , static_cast(-1) }; + + wchar_t const * d = std::find( digits_begin, digits_end, c ); + return values[d - digits_begin]; + } + + bool is_dash( char c ) const { return c == '-'; } + bool is_dash( wchar_t c ) const { return c == L'-'; } + + // \ru Возвращаем открывающую скобку \en Return an opening parenthesis. + bool is_open_brace( char c ) const { return (c == '{'); } + bool is_open_brace( wchar_t c ) const { return (c == L'{'); } + + bool check_close_brace( char c, char open_brace ) const { + if ( open_brace == '{' && c == '}' ) + return true; + _ASSERT( false ); + return false; + } + + bool check_close_brace( wchar_t c, wchar_t open_brace ) const { + if ( open_brace == L'{' && c == L'}' ) + return true; + _ASSERT( false ); + return false; + } +}; + + +struct hash8_generator +{ + uint8 operator()( const MbUuid & trg ) const + { + static uint8 rand8[256] = + { + 1, 14, 110, 25, 97, 174, 132, 119, 138, 170, 125, 118, 27, 233, 140, 51, + 87, 197, 177, 107, 234, 169, 56, 68, 30, 7, 173, 73, 188, 40, 36, 65, + 49, 213, 104, 190, 57, 211, 148, 223, 48, 115, 15, 2, 67, 186, 210, 28, + 12, 181, 103, 70, 22, 58, 75, 78, 183, 167, 238, 157, 124, 147, 172, 144, + 176, 161, 141, 86, 60, 66, 128, 83, 156, 241, 79, 46, 168, 198, 41, 254, + 178, 85, 253, 237, 250, 154, 133, 88, 35, 206, 95, 116, 252, 192, 54, 221, + 102, 218, 255, 240, 82, 106, 158, 201, 61, 3, 89, 9, 42, 155, 159, 93, + 166, 80, 50, 34, 175, 195, 100, 99, 26, 150, 16, 145, 4, 33, 8, 189, + 121, 64, 77, 72, 208, 245, 130, 122, 143, 55, 105, 134, 29, 164, 185, 194, + 193, 239, 101, 242, 5, 171, 126, 11, 74, 59, 137, 228, 108, 191, 232, 139, + 6, 24, 81, 20, 127, 17, 91, 92, 251, 151, 225, 207, 21, 98, 113, 112, + 84, 226, 18, 214, 199, 187, 13, 32, 94, 220, 224, 212, 247, 204, 196, 43, + 249, 236, 45, 244, 111, 182, 153, 136, 129, 90, 217, 202, 19, 165, 231, 71, + 230, 142, 96, 227, 62, 179, 246, 114, 162, 53, 160, 215, 205, 180, 47, 109, + 44, 38, 31, 149, 135, 0, 216, 52, 63, 23, 37, 69, 39, 117, 146, 184, + 163, 200, 222, 235, 248, 243, 219, 10, 152, 131, 123, 229, 203, 76, 120, 209 + }; + + uint8 h = 0; + MbUuid::const_iterator curr_iter = trg.cbegin(), end_iter = trg.cend(); + while ( curr_iter != end_iter ) + h = rand8[h ^ *curr_iter++]; + return h; + } +}; + + +#endif // _TOOL_UUID_H_ diff --git a/C3d/Include/topology.h b/C3d/Include/topology.h index 3b4955c..e30dd7c 100644 --- a/C3d/Include/topology.h +++ b/C3d/Include/topology.h @@ -56,6 +56,26 @@ typedef std::vector ConstVerticesVector; typedef std::vector VerticesSPtrVector; typedef std::vector ConstVerticesSPtrVector; +typedef std::set VerticesSet; +typedef VerticesSet::iterator VerticesSetIt; +typedef VerticesSet::const_iterator VerticesSetConstIt; +typedef std::pair VerticesSetRet; + +typedef std::set VerticesSPtrSet; +typedef VerticesSPtrSet::iterator VerticesSPtrSetIt; +typedef VerticesSPtrSet::const_iterator VerticesSPtrSetConstIt; +typedef std::pair VerticesSPtrSetRet; + +typedef std::set ConstVerticesSet; +typedef ConstVerticesSet::iterator ConstVerticesSetIt; +typedef ConstVerticesSet::const_iterator ConstVerticesSetConstIt; +typedef std::pair ConstVerticesSetRet; + +typedef std::set ConstVerticesSPtrSet; +typedef ConstVerticesSPtrSet::iterator ConstVerticesSPtrSetIt; +typedef ConstVerticesSPtrSet::const_iterator ConstVerticesSPtrSetConstIt; +typedef std::pair ConstVerticesSPtrSetRet; + // edges typedefs typedef SPtr WireEdgeSPtr; typedef SPtr ConstWireEdgeSPtr; @@ -267,7 +287,7 @@ private: // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. void operator = ( const MbVertex & ); - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbVertex ) +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbVertex ) }; IMPL_PERSISTENT_OPS( MbVertex ) @@ -367,6 +387,10 @@ public : const MbVertex * GetBegVertexPointer() const { return begVertex; } /// \ru Выдать вершину-конец. \en Get the end vertex. const MbVertex * GetEndVertexPointer() const { return endVertex; } + /// \ru Выдать вершину-начало. \en Get the start vertex. + MbVertex * SetBegVertexPointer() { return begVertex; } + /// \ru Выдать вершину-конец. \en Get the end vertex. + MbVertex * SetEndVertexPointer() { return endVertex; } /// \ru Выдать вершину-начало. \en Get the start vertex. const MbVertex & GetBegVertex() const { return *begVertex; } @@ -473,8 +497,8 @@ public : /// \ru Установить свойства объекта. \en Set properties of the object. void SetProperties( const MbProperties & ); - OBVIOUS_PRIVATE_COPY( MbEdge ) - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbEdge ) +OBVIOUS_PRIVATE_COPY( MbEdge ) +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbEdge ) }; IMPL_PERSISTENT_OPS( MbEdge ) @@ -871,9 +895,9 @@ public : bool CutPeriodicEdge( const MbVector3D & eye, SSArray & trimParams, double & delT1, double & delT2 ) const; - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - OBVIOUS_PRIVATE_COPY( MbCurveEdge ) - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCurveEdge ) +// \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. +OBVIOUS_PRIVATE_COPY( MbCurveEdge ) +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbCurveEdge ) }; IMPL_PERSISTENT_OPS( MbCurveEdge ) @@ -1249,9 +1273,9 @@ public : /// \ru Удалить лишнюю память. \en Free the unnecessary memory. void EdgesAdjust () { edgeList.Adjust(); } - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - OBVIOUS_PRIVATE_COPY( MbLoop ) - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbLoop ) +// \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. +OBVIOUS_PRIVATE_COPY( MbLoop ) +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbLoop ) }; // MbLoop IMPL_PERSISTENT_OPS( MbLoop ) @@ -1473,9 +1497,11 @@ public: /// \ru Установить указатели на грань слева или грань справа в ребрах циклов на NULL. \en Set to null the pointers to the face on the left or to the face on the right in edges of loops. void SetNullToLoopsEdges(); /// \ru Обнулить указатели на грань слева или грань справа, указывающие на смежную грань delFace, в ребрах циклов. \en Set to null pointers to the face on the left or to the face on the right which point to the adjacent face delFace in edges of loops. - void SetNullToFace( const MbFace * delFace ); + void SetNullToFace( const MbFace * delFace ); + /// \ru Обнулить указатели на грань слева или грань справа, указывающие на смежную грань delFace, в ребрах циклов. \en Set to null pointers to the face on the left or to the face on the right which point to the adjacent face delFace in edges of loops. + void SetNullToFace( const MbFace * delFace, bool setEdgeChanged ); /// \ru Установить указатели на грань слева или грань справа в ребрах циклов на данную грань и параметры поверхности по данным циклов грани (setBounds = true). \en Set the pointers to the face on the left or to the face on the right to the given face in edges of loops and parameters of surface by loops of face (setBounds = true). - void MakeRight( bool setBounds = false); + void MakeRight( bool setBounds = false ); /// \ru Принадлежит ли вершина грани? \en Does a vertex belong an edge? bool IsVertexOn( const MbVertex * vertex, size_t * indLoop = NULL, size_t * indEdge = NULL ) const; @@ -1541,8 +1567,16 @@ public: bool FindEdgeIndex( const MbCurveEdge & edge, ThreeStates orient, size_t & loopIndex, size_t & edgeIndex ) const; /// \ru Дать ребро по индексам цикла грани и ребра в цикле. \en Get an edge by the indices of a face loop and an edge in the loop. MbCurveEdge * GetEdgeByIndex( size_t loopIndex, size_t edgeIndex ) const; - /// \ru Найти ориентированное ребро по ребру грани. \en Find an oriented edge by the edge of a face. - MbOrientedEdge * GetOrientedEdge( const MbCurveEdge & curveEdge ) const; + /// \ru Найти ориентированное ребро по индексам цикла грани и ребра в цикле. \en Find an oriented edge by the indices of a face loop and an edge in the loop. + const MbOrientedEdge * GetOrientedEdge( size_t loopIndex, size_t edgeIndex ) const; + MbOrientedEdge * SetOrientedEdge( size_t loopIndex, size_t edgeIndex ); + /// \ru Найти ориентированное ребро по ребру грани. Не ищет для швов. \en Find an oriented edge by the edge of a face. Don't seek if an edge is a seam. + const MbOrientedEdge * GetOrientedEdge( const MbCurveEdge & curveEdge ) const; + /// \ru Найти ориентированное ребро по ребру грани. Не ищет для швов. \en Find an oriented edge by the edge of a face. Don't seek if an edge is a seam. + MbOrientedEdge * SetOrientedEdge( const MbCurveEdge & curveEdge ); + /// \ru Удалить ориентированное ребро по ребру грани. \en Remove an oriented edge by the edge of a face. + bool DeleteOrientedEdge( MbCurveEdge * curveEdge ); + /// \ru Найти номера для рёбер. \en Find numbers for the edges. bool FindIndexByEdges( const RPArray & initEdges, SArray & indexes ) const; /// \ru Найти рёбра по номерам. \en Find edges by numbers. @@ -1739,9 +1773,9 @@ public: bool UpdateTemporal() const; - // \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. - OBVIOUS_PRIVATE_COPY( MbFace ) - DECLARE_PERSISTENT_CLASS_NEW_DEL( MbFace ) +// \ru Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. \en The declaration of the assignment operator without implementation to prevent an assignment by default. +OBVIOUS_PRIVATE_COPY( MbFace ) +DECLARE_PERSISTENT_CLASS_NEW_DEL( MbFace ) }; IMPL_PERSISTENT_OPS( MbFace ) diff --git a/C3d/Include/topology_faceset.h b/C3d/Include/topology_faceset.h index 2fa50f8..4197b22 100644 --- a/C3d/Include/topology_faceset.h +++ b/C3d/Include/topology_faceset.h @@ -30,6 +30,7 @@ struct MATH_CLASS MbEdgeFunction; struct MATH_CLASS MbCheckTopologyParams; class MATH_CLASS MbFaceShell; class MATH_CLASS MbShellsDistanceData; +struct MATH_CLASS MbUnitInfo; namespace c3d // namespace C3D { @@ -153,10 +154,12 @@ public : sameShell == cm_Copy - an ordinary copying (the initial shell and its copy have no the common data). \n \~ \param[in] history - \ru История копий граней используется после операции для замены неизменённых копий граней их оригиналами. \en A history of faces copies is used after the operation for the replacement of unchanged copies by their originals. \~ + \param[in] iReg - \ru Регистратор копирования. + \en Copy registrator. \~ \return \ru Копия объекта или оригинал(в случае режима копирования cm_Same). \en Copy of an object or original (in a case of the mode cm_Same). \~ */ - MbFaceShell * Copy( MbeCopyMode sameShell, MbShellHistory * history = NULL ); + MbFaceShell * Copy( MbeCopyMode sameShell, MbShellHistory * history = NULL, MbRegDuplicate * iReg = NULL ); /** \brief \ru Создать копию. \en Create a copy. \~ @@ -201,11 +204,11 @@ public : /// \ru Вставить грань перед гранью с заданным индексом. \en Insert a face before the face with the given index. void InsertFace( size_t index, const MbFace & ); /// \ru Заменить грань с заданным индексом. \en Replace a face with the given index. - void ChangeFace( size_t index, const MbFace & ); + bool ChangeFace( size_t index, const MbFace & ); /// \ru Удалить грань с заданным индексом. \en Delete a face at the given index. - void DeleteFace( size_t index ); + bool DeleteFace( size_t index ); /// \ru Удалить грань. \en Delete a face. - void DeleteFace( const MbFace * ); + bool DeleteFace( const MbFace * ); /// \ru Отсоединить грань от оболочки с заданным индексом. \en Detach a face from a shell with the given index. MbFace * DetachFace( size_t index ); /// \ru Отсоединить грань. \en Detach face. @@ -214,8 +217,25 @@ public : void DeleteFaces(); /// \ru Отсоединить все грани оболочки. \en Detach all faces from shell. void DetachFaces(); + /// \ru Отсоединить все грани оболочки. \en Detach all faces from shell. + template + void DetachFaces( FacesVector & detachFaces ) + { + size_t facesCnt = faceSet.size(); + detachFaces.reserve( detachFaces.size() + facesCnt ); + c3d::FaceSPtr face; + for ( size_t k = 0; k < facesCnt; ++k ) { + face = faceSet[k]; + ::DecRefItem( faceSet[k] ); + faceSet[k] = C3D_NULL_PTR; + detachFaces.push_back( face ); + ::DetachItem( face ); + } + faceSet.clear(); + RemoveTemporal(); + } /// \ru Поменять местами грани оболочки. \en Swap shell faces. - void ExchangeFaces( size_t i1, size_t i2 ); + bool ExchangeFaces( size_t i1, size_t i2 ); /// \ru Установить правильную (текущую) информацию в ребрах о соединяемых ими гранях и параметры поверхностей по данным циклов граней (setBounds = true). \en Set the correct (the current) information in edges about the connected by them faces and parameters of surfaces by loops of faces (setBounds = true). void MakeRight( bool setBounds = false ); /// \ru Верно ли установлены указатели в ребрах на соединяемые ими грани. \en Are the pointers in edges to the connected by them faces correctly set? @@ -599,22 +619,53 @@ public : /// \ru Установить свойства объекта. \en Set properties of the object. void SetProperties( const MbProperties & ); - /// \ru Установить главное имя и вставить старое в индекс копирования. \en Set the main name and insert an old name to the copy index. - void SetMainName ( SimpleName mainName, bool addOldMainName ); + /** \brief \ru Установить главное имя и вставить старое в индекс копирования. + \en Set the main name and insert an old name to the copy index. \~ + \details \ru Установить главное имя и вставить старое в индекс копирования. Объекты с пустыми имена пропускаются. + \en Set the main name and insert an old name to the copy index. Objects with empty names are skipped. \~ + \param[in] newMainName - \ru Новое главное имя. + \en The new main name. \~ + \param[in] addOldMainName - \ru Вставить старое в индекс копирования. + \en Insert an old name to the copy index. \~ + */ + void SetMainName ( SimpleName newMainName, bool addOldMainName ); + /** \brief \ru Вставить индекс копирования. + \en Insert copying index. \~ + \details \ru Вставить индекс копирования. + \en Insert copying index. \~ + \param[in] index - \ru Индекс копирования. + \en Copying index. \~ + */ + void SetNamesCopyIndex( SimpleName index ); + /** \brief \ru Заменить главное имя, вставить старое главное имя и индекс копирования в индексы копирования. + \en Replace main name by new one, insert old main name and given copy index into name copy indices. \~ + \details \ru Заменить главное имя, вставить старое главное имя и индекс копирования в индексы копирования. Объекты с пустыми имена пропускаются. + \en Replace main name by new one, insert old main name and given copy index into name copy indices. Objects with empty names are skipped. \~ + \param[in] index - \ru Индекс копирования. + \en Copying index. \~ + \param[in] newMainName - \ru Новое главное имя. + \en The new main name. \~ + */ + void SetNamesCopyIndex( SimpleName index, const SimpleName & newMainName ); + /// \ru Установить главное имя и модифицировать имена граней, рёбер и вершин для оболочки-копии для предотвращения совпадения имен нескольких копий. \en Set the main name and modify names of faces, edges and vertices for the shell-copy in order to prevent coincidence of several copies names. void MakeNewNames ( SimpleName mainName, SimpleName modifier ); /// \ru Установить главное имя и модифицировать имена граней для оболочки-копии для предотвращения совпадения имен нескольких копий. \en Set the main name and modify names of faces for the shell-copy in order to prevent coincidence of several copies names. void MakeNewNames ( const MbSNameMaker &, SimpleName modifier ); + /// \ru Проименовать грани, рёбра и вершины оболочки. \en Rename faces, edges and vertices of the shell. - void SetShellNames ( const MbSNameMaker & names ); - /// \ru Заменить в имени метку копирования на index. \en Replace in a name a copying label by 'index'. - void SetNamesCopyIndex ( SimpleName index ); - /// \ru Проименовать грани оболочки именами оболочки s. \en Name shell faces by names of the shell 's'. - void SetShellNames ( const MbFaceShell * s ); - /// \ru Очистить все имена в оболочке. \en Clear all shell names. - void ClearShellNames (); + void SetShellNames( const MbSNameMaker & ); + /// \ru Проименовать элементы оболочки именами оболочки s. \en Name shell elements by names of the shell 's'. + void SetShellNames( const MbFaceShell & ); + + /** \brief \ru Очистить все имена в оболочке. + \en Clear all shell names. \~ + \details \ru Очистить имена всех элементов оболочки : граней, ребер и вершин. + \en Clear the names of all shell elements: faces, edges, and vertices. \~ + */ + void ClearShellNames(); /// \ru Очистить имена ребер в оболочке. \en Clear all shell edges names. - void ClearEdgesNames ( bool clearVerticesNames = true ); + void ClearEdgesNames( bool clearVerticesNames = true ); /** \brief \ru Проверка оболочки: вершин (удаление совпадающих и лишних), ребер (со слиянием). \en Validation of the shell: vertices (deletion of coincident and extra), edges (with merge). \~ @@ -630,18 +681,17 @@ public : /// \ru Найти граничные рёбра и сделать граничными их кривые. \en Find the boundary edges, make their curve boundary. bool MakeBoundaryCurve(); /// \ru Получить краевые ребра оболочки. \en Get boundary edges of the shell. - bool GetBoundaryEdges( RPArray & ) const; - /// \ru Получить краевые ребра оболочки. \en Get boundary edges of the shell. - bool GetBoundaryEdges( c3d::ConstEdgesVector & ) const; + template + bool GetBoundaryEdges( ConstEdgesVector & ) const; /// \ru Для множества ребер найти номера ребер и номера ее граней. \en For a set of edges find their indices and indices of their faces. bool FindFacesIndexByEdges( const RPArray & init, SArray & indexes, bool any = false ) const; /// \ru Для множества структур (ребер и функций изменения радиусов) найти номера ребер и номера ее граней. \en For a set of structures (edges and functions of radii changing) find indices of edges and their faces. bool FindFacesIndexByEdges( const SArray & init, - RPArray & functions, SArray & indexes ) const; + RPArray & functions, RPArray & slideways, SArray & indexes ) const; /// \ru Для множества номеров ребер и номеров ее граней найти ребра. \en For a set of edge indices and indices of their faces find edges. - bool FindEdgesByFacesIndex( const SArray & indexes, RPArray * functions, - RPArray & initCurves, RPArray & initFunctions ) const; + bool FindEdgesByFacesIndex( const SArray & indexes, RPArray * functions, RPArray * slideways, + RPArray & initCurves, RPArray & initFunctions, RPArray & initSlideways ) const; /// \ru Найти номера граней по ребру. \en Find faces indices by the edge. bool FindFacesIndexByEdge( const MbCurveEdge & edge, size_t & ind1, size_t & ind2, bool any = false ) const; /// \ru Для множества граней найти множество их номеров. \en For a set of faces find a set of their indices. @@ -688,8 +738,14 @@ public : /// \ru Найти грань по имени. \en Find face by name. MbFace * FindFaceByName ( const MbName & ); - /// \ru Объединить подобные грани. \en Merge similar faces. - bool MergeSimilarFaces(); + /** \brief \ru Объединить подобные грани. + \en Merge similar faces. \~ + \details \ru Объединить подобные грани. + \en Merge similar faces. \~ + \param[in] simMainName - \ru Новое главное имя для объединенных граней (если не равно c3d::SIMPLENAME_MAX). + \en The new primary name for the merged faces (if it is not equal to c3d::SIMPLENAME_MAX). \~ + */ + bool MergeSimilarFaces( SimpleName simMainName = c3d::SIMPLENAME_MAX ); /// \ru Создан ли временный объект сопровождения? \en Is a temporary object for the maintenance created? bool IsTemporal() const { return (temporal != NULL); } @@ -744,6 +800,7 @@ MbFaceShell::MbFaceShell( const Faces & initFaces ) } } + //------------------------------------------------------------------------------ // \ru Выдать множество вершин оболочки. \en Get a set of vertices of the shell. // --- @@ -791,13 +848,14 @@ void MbFaceShell::GetVertices( VerticesVector & vertices ) const } } + //------------------------------------------------------------------------------ // \ru Выдать множество вершин и множество ребер оболочки. \en Get a set of vertices and a set of edges of the shell. // --- template void MbFaceShell::GetItems( VerticesVector & vertices, EdgesVector & edges ) const { - if ( edges.size() == 0 && vertices.size() == 0 ) { + if ( edges.empty() && vertices.empty() ) { size_t maxCount = 1; size_t i, fcount; @@ -808,8 +866,8 @@ void MbFaceShell::GetItems( VerticesVector & vertices, EdgesVector & edges ) con const MbLoop * loop = face->_GetLoop( j ); size_t ecount = loop->GetEdgesCount(); - for ( size_t k = 0; k < ecount; k++ ) { - MbCurveEdge * edge = const_cast(&loop->_GetOrientedEdge( k )->GetCurveEdge()); + for ( size_t k = 0; k < ecount; ++k ) { + const MbCurveEdge * edge = const_cast(&loop->_GetOrientedEdge( k )->GetCurveEdge()); edge->SetOwnLabel( ls_Used ); edge->GetBegVertex().SetOwnLabel( ls_Used ); edge->GetEndVertex().SetOwnLabel( ls_Used ); @@ -833,8 +891,8 @@ void MbFaceShell::GetItems( VerticesVector & vertices, EdgesVector & edges ) con for ( size_t j = 0, lcount = face->GetLoopsCount(); j < lcount; ++j ) { const MbLoop * loop = face->_GetLoop( j ); - SPtr edge; - SPtr vertex; + c3d::EdgeSPtr edge; + c3d::VertexSPtr vertex; for ( size_t k = 0, ecount = loop->GetEdgesCount(); k < ecount; ++k ) { edge = const_cast(&loop->_GetOrientedEdge( k )->GetCurveEdge()); @@ -876,6 +934,7 @@ void MbFaceShell::GetItems( VerticesVector & vertices, EdgesVector & edges ) con } } + //------------------------------------------------------------------------------ // \ru Для множества граней найти множество их комбинированных номеров. \en For a set of faces find a set of their combined indices. // --- @@ -927,6 +986,7 @@ bool MbFaceShell::FindIndexByFaces( const FacesPointersVector & initFaces, ItemI return (indices.size() > 0); } + //------------------------------------------------------------------------------ // \ru Найти множество граней по множеству комбинированных индексов. \en Find a set of faces by a set of combined indices. // --- @@ -947,6 +1007,7 @@ bool MbFaceShell::FindConstFacesByIndex( const ItemIndices & indices, ConstFaces return initFaces.size() > 0; } + //------------------------------------------------------------------------------ // \ru Найти множество граней по множеству комбинированных индексов. \en Find a set of faces by a set of combined indices. // --- @@ -968,6 +1029,39 @@ bool MbFaceShell::FindFacesByIndex( const ItemIndices & indices, FacesPointersVe } +//------------------------------------------------------------------------------ +// \ru Получить краевые ребра оболочки. \en Get boundary edges of the shell. +// --- +template +bool MbFaceShell::GetBoundaryEdges( ConstEdgesVector & boundaryEdges ) const +{ + const size_t boundaryCnt = boundaryEdges.size(); + for ( size_t i = 0, facesCnt = faceSet.size(); i < facesCnt; ++i ) { + const MbFace * face = faceSet[i]; + if ( face == NULL ) + continue; + for ( size_t j = 0, loopsCnt = face->GetLoopsCount(); j < loopsCnt; ++j ) { + const MbLoop * loop = face->_GetLoop( j ); + if ( loop == NULL ) + continue; + for ( size_t k = 0, edgesCnt = loop->GetEdgesCount(); k < edgesCnt; ++k ) { + const MbOrientedEdge * orientEdge = loop->_GetOrientedEdge( k ); + if ( orientEdge != NULL ) { + c3d::ConstEdgeSPtr edge( &orientEdge->GetCurveEdge() ); + if ( edge->IsBoundaryFace() ) + boundaryEdges.push_back( edge ); + } + } + } + } + + if ( boundaryEdges.size() > boundaryCnt ) + return true; + + return false; +} + + //------------------------------------------------------------------------------ /** \brief \ru Параметры функции проверки топологии оболочки. \en Parameters of validation of the shell. \~ @@ -981,6 +1075,7 @@ protected: bool mergeEdges; ///< \ru Флаг слияния ребер. \en Merge flag for edges. bool addNameAttributes; ///< \ru Добавить атрибут имени с именами слитых ребер. \en Add name attribute with names of merged edges. VERSION version; ///< \ru Версия. \en Version. + SimpleName lastMainName; ///< \ru Главное имя именователя последней операции. \en Main name of the last operation name maker. c3d::ConstFacesVector controlFaces; ///< \ru Грани, по которым может быть взведена ошибка. \en Faces where an error may occur. c3d::ConstEdgesVector boundaryEdges; ///< \ru Исходные краевые ребра (до операции). \en Initial boundary edges (before an operation). public: @@ -988,15 +1083,17 @@ public: : mergeEdges ( doMergingEdges ) , addNameAttributes( nameMaker.GetParentNamesAttributes() ) , version ( nameMaker.GetMathVersion() ) + , lastMainName ( c3d::SIMPLENAME_MAX ) // \ru Неизвестно. \en Unknown. , controlFaces ( ) , boundaryEdges ( ) {} explicit MbCheckTopologyParams( bool doMergingEdges, VERSION ver, bool addNameAttrs ) - : mergeEdges ( doMergingEdges ) - , addNameAttributes( addNameAttrs ) - , version ( ver ) - , controlFaces ( ) - , boundaryEdges ( ) + : mergeEdges ( doMergingEdges ) + , addNameAttributes( addNameAttrs ) + , version ( ver ) + , lastMainName ( c3d::SIMPLENAME_MAX ) // \ru Неизвестно. \en Unknown. + , controlFaces ( ) + , boundaryEdges ( ) {} template explicit MbCheckTopologyParams( bool doMergingEdges, const MbSNameMaker & nameMaker, @@ -1004,6 +1101,7 @@ public: : mergeEdges ( doMergingEdges ) , addNameAttributes( nameMaker.GetParentNamesAttributes() ) , version ( nameMaker.GetMathVersion() ) + , lastMainName ( c3d::SIMPLENAME_MAX ) // \ru Неизвестно. \en Unknown. , controlFaces ( ) , boundaryEdges ( ) { @@ -1018,11 +1116,12 @@ public: template explicit MbCheckTopologyParams( bool doMergingEdges, VERSION ver, bool addNameAttrs, const Faces & faces ) - : mergeEdges ( doMergingEdges ) - , addNameAttributes( addNameAttrs ) - , version ( ver ) - , controlFaces ( ) - , boundaryEdges ( ) + : mergeEdges ( doMergingEdges ) + , addNameAttributes( addNameAttrs ) + , version ( ver ) + , lastMainName ( c3d::SIMPLENAME_MAX ) // \ru Неизвестно. \en Unknown. + , controlFaces ( ) + , boundaryEdges ( ) { size_t facesCnt = faces.size(); if ( facesCnt > 0 ) { @@ -1038,6 +1137,7 @@ public: : mergeEdges ( doMergingEdges ) , addNameAttributes( nameMaker.GetParentNamesAttributes() ) , version ( nameMaker.GetMathVersion() ) + , lastMainName ( c3d::SIMPLENAME_MAX ) // \ru Неизвестно. \en Unknown. , controlFaces ( ) , boundaryEdges ( edges ) { @@ -1052,17 +1152,20 @@ public: } ~MbCheckTopologyParams() {} public: - bool MergeEdges () const { return mergeEdges; } - bool AddNameAttributes() const { return addNameAttributes; } - VERSION MathVersion () const { return version; } + bool MergeEdges () const { return mergeEdges; } + bool AddNameAttributes() const { return addNameAttributes; } + VERSION MathVersion () const { return version; } + SimpleName LastMainName() const { return lastMainName; } - void ClearControlFaces() { controlFaces.clear(); } - void SetControlFaces( const c3d::ConstFacesVector & faces ) + void SetLastMainName( const SimpleName & lmn ) { lastMainName = lmn; } + + void ClearControlFaces() { controlFaces.clear(); } + void SetControlFaces( const c3d::ConstFacesVector & faces ) { controlFaces = faces; std::sort( controlFaces.begin(), controlFaces.end() ); } - bool DeleteTheseSortedFaces( const c3d::ConstFacesVector & sortedDelFaces ) + bool DeleteTheseSortedFaces( const c3d::ConstFacesVector & sortedDelFaces ) { bool res = false; if ( controlFaces.size() > 0 && sortedDelFaces.size() > 0 ) { @@ -1088,13 +1191,13 @@ public: //------------------------------------------------------------------------------ -/** \brief \ru Структура для передачи ребра и функции. - \en A structure for edge and function transferring. \~ - \details \ru Структура передаёт информацию о ребре и функции изменения радиуса скругления ребра. +/** \brief \ru Структура для передачи ребра и функции или опорной кривой. + \en A structure for edge and function transferring or supporting curve. \~ + \details \ru Структура передаёт информацию о ребре и функции изменения радиуса скругления ребра или об опорной кривой для скругления. Структура используется в алгоритмах скругления ребер переменным радиусом. \n Начальный параметр функции изменения радиуса соответствует начальной вершине ребра. Конечный параметр функции изменения радиуса соответствует конечной вершине ребра. \n - \en A structure transmits an information about an edge and a function of edge fillet radius changing. + \en A structure transmits an information about an edge and a function of edge fillet radius changing or supporting curve for filliting. A structure is used in algorithms of edge fillet by the variable radius. \n The starting parameter of radius changing function corresponds to the starting vertex of the edge. The ending parameter of radius changing function corresponds to the ending vertex of the edge. \n \~ @@ -1105,24 +1208,31 @@ struct MATH_CLASS MbEdgeFunction { private: const MbCurveEdge * edge; ///< \ru Ребро. \en An edge. const MbFunction * function; ///< \ru Функция изменения радиуса по относительной длине ребра. \en A function of radius changing by relative edge length. + const MbCurve3D * slideway; ///< \ru Опорная кривая скругления. \en A supporting curve for fillet. public: /// \ru Конструктор по умолчанию \en Default constructor - MbEdgeFunction () : edge(NULL), function(NULL) {} + MbEdgeFunction () : edge(NULL), function(NULL), slideway(NULL) {} /// \ru Конструктор по ребру и функции. \en Constructor by an edge and function. - MbEdgeFunction ( const MbCurveEdge * e, const MbFunction * f ) : edge(e), function(f) {} + MbEdgeFunction ( const MbCurveEdge * e, const MbFunction * f ) : edge(e), function(f), slideway(NULL) {} + /// \ru Конструктор по ребру и опорной кривой. \en Constructor by an edge and a supporting curve. + MbEdgeFunction ( const MbCurveEdge * e, const MbCurve3D * c ) : edge(e), function(NULL), slideway(c) {} /// \ru Конструктор по другому ребру с функцией. \en Constructor by other edge with a function. - MbEdgeFunction ( const MbEdgeFunction & other ) : edge(other.edge), function(other.function) {} + MbEdgeFunction ( const MbEdgeFunction & other ) : edge(other.edge), function(other.function), slideway(other.slideway) {} ~MbEdgeFunction() {} public: - /// \ru Функция инициализации по ребру и функции. \en A function of initialization by an edge and a function. - void Init( const MbCurveEdge * e, const MbFunction * f ) { edge = e; function = f; } + /// \ru Инициализация по ребру и функции. \en Initialization by an edge and a function. + void Init( const MbCurveEdge * e, const MbFunction * f ) { edge = e; function = f; slideway = NULL; } + /// \ru Инициализация по ребру и опорной кривой. \en Initialization by an edge and a supporting curve. + void Init( const MbCurveEdge * e, const MbCurve3D * c ) { edge = e; function = NULL; slideway = c; } /// \ru Дать ребро. \en Get an edge. const MbCurveEdge * Edge() const { return edge; } /// \ru Дать функцию изменения радиуса. \en Get a function of radius changing. const MbFunction * Function() const { return function; } + /// \ru Дать опорную кривую скругления. \en Get a supporting curve for fillet. + const MbCurve3D * Slideway() const { return slideway; } /// \ru Оператор присваивания. \en Assignment operator. - void operator = ( const MbEdgeFunction & other ) { edge = other.edge; function = other.function; } + void operator = ( const MbEdgeFunction & other ) { edge = other.edge; function = other.function; slideway = other.slideway; } }; @@ -1237,6 +1347,55 @@ public: } }; + +//------------------------------------------------------------------------------ +/** \brief \ru Информация о произвольном элементе. + \en Information about arbitrary element. \~ + \details \ru В качестве произвольного элемента может выступить элемент массива. \n + \en Array element may be arbitrary element. \n \~ + \ingroup Data_Structures +*/ +//--- +struct MATH_CLASS MbUnitInfo { +public: + MbeItemLocation location; ///< \ru Положение элемента. \en Element location. + size_t index; ///< \ru Индекс элемента. \en Index of element. + +public: + /// \ru Конструктор по умолчанию. \en Default constructor. + MbUnitInfo() + : location( iloc_Undefined ) + , index ( SYS_MAX_T ) + {} + /// \ru Конструктор копирования. \en Copy-constructor. + MbUnitInfo( const MbUnitInfo & obj ) + : location( obj.location ) + , index ( obj.index ) + {} + /// \ru Конструктор по индексу. \en Constructor by index. + MbUnitInfo( size_t ind ) + : location( iloc_InItem ) + , index ( ind ) + {} + /// \ru Конструктор по положению элемента. \en Constructor by element location. + MbUnitInfo( MbeItemLocation loc ) + : location( loc ) + , index ( SYS_MAX_T ) + {} + +public: + /// \ru Сброс параметров. \en Parameter reset. + void Reset() { location = iloc_Undefined; index = SYS_MAX_T; } + /// \ru Оператор присваивания. \en Assignment operator. + MbUnitInfo & operator = ( const MbUnitInfo & unitInfo ) + { + location = unitInfo.location; + index = unitInfo.index; + return (*this); + } +}; + + //------------------------------------------------------------------------------ /** \brief \ru Объект с информацией о положении точки относительно оболочки. \en An object with information about the point location relative to the shell. \~ @@ -1402,13 +1561,15 @@ public: */ // --- inline -void SetMainName( MbName & name, SimpleName mainName, bool addOldMainName ) +bool SetMainName( MbName & name, SimpleName mainName, bool addOldMainName ) { if ( !name.IsEmpty() ) { if ( addOldMainName ) name.SetCopyIndex( name.GetMainName() ); name.SetMainName( mainName ); + return true; } + return false; } diff --git a/C3d/Include/topology_item.h b/C3d/Include/topology_item.h index 84fed69..57be739 100644 --- a/C3d/Include/topology_item.h +++ b/C3d/Include/topology_item.h @@ -42,6 +42,7 @@ enum MbeTopologyType { tt_Undefined = 0, ///< \ru Неизвестный объект. \en Unknown object. tt_TopItem = 1, ///< \ru Топологический объект. \en A topological object. \n + tt_Proxy = 10, ///< \ru Заменитель топологического объекта. \en A topological object proxy. \n tt_Vertex = 101, ///< \ru Вершина. \en A vertex. @@ -82,7 +83,7 @@ enum MbeChangedType { //------------------------------------------------------------------------------ -/** \brief \ru Метка для выполнения операция. +/** \brief \ru Метка для выполнения операций. \en A label for performing of operations. \~ \ingroup Topology_Items */ @@ -157,13 +158,14 @@ public: virtual MbeTopologyType IsA() const = 0; /// \ru Подготовить объект к записи. \en Prepare an object for writing. - void PrepareWrite() { SetRegistrable( GetUseCount() > 1 ? registrable : noRegistrable ); } + void PrepareWrite() const { SetRegistrable( GetUseCount() > 1 ? registrable : noRegistrable ); } bool IsAVertex() const { return (IsA() == tt_Vertex); } ///< \ru Это вершина? \en Is it a vertex? bool IsAWireEdge() const { return (IsA() == tt_Edge); } ///< \ru Это ребро каркаса? \en Is it an edge of wireframe? bool IsAnEdge() const { return (IsA() == tt_CurveEdge); } ///< \ru Это ребро? \en Is it an edge? bool IsAFace() const { return (IsA() == tt_Face); } ///< \ru Это грань? \en Is it a face? bool IsAShell() const { return (IsA() == tt_FaceShell); } ///< \ru Это оболочка? \en Is it a shell? + bool IsAProxy() const { return (IsA() == tt_Proxy); } ///< \ru Это заменитель? \en Is it a proxy? DECLARE_PERSISTENT_CLASS( MbTopItem ) OBVIOUS_PRIVATE_COPY( MbTopItem ) @@ -171,6 +173,7 @@ OBVIOUS_PRIVATE_COPY( MbTopItem ) IMPL_PERSISTENT_OPS( MbTopItem ) + //------------------------------------------------------------------------------ /** \brief \ru Топологический объект с именем. \en Topological object with name. \~ @@ -345,7 +348,8 @@ IMPL_PERSISTENT_OPS( MbTopologyItem ) //------------------------------------------------------------------------------ // \ru Установить флаг, свидетельствующий о том, что объект был (не был) изменен. \en Set flag which indicates that an object has (not) been changed. // --- -inline void MbTopologyItem::SetOwnChanged( MbeChangedType n ) +inline +void MbTopologyItem::SetOwnChanged( MbeChangedType n ) { if ( n != tct_Unchanged ) { changed |= n; @@ -359,7 +363,8 @@ inline void MbTopologyItem::SetOwnChanged( MbeChangedType n ) //------------------------------------------------------------------------------ // \ru Получить флаг, свидетельствующий о том, что объект был создан, переименован, трансформирован или переориентирован. \en Get flag which indicates that an object has been only created, renamed, transformed or reoriented. // --- -inline bool MbTopologyItem::IsOwnChangedWeakly() const +inline +bool MbTopologyItem::IsOwnChangedWeakly() const { uint16 wrkFlag = (tct_Created | tct_Renamed | tct_Transformed | tct_Reoriented | tct_Modified); wrkFlag = ~wrkFlag; @@ -372,7 +377,8 @@ inline bool MbTopologyItem::IsOwnChangedWeakly() const //------------------------------------------------------------------------------ // \ru Копирование данных. \en Data copying. // --- -inline void MbTopologyItem::Assign( const MbTopologyItem & other ) +inline +void MbTopologyItem::Assign( const MbTopologyItem & other ) { AttributesAssign( other ); name.SetName( other.GetName() ); @@ -381,4 +387,40 @@ inline void MbTopologyItem::Assign( const MbTopologyItem & other ) } +//------------------------------------------------------------------------------ +/** \brief \ru Заменитель топологического объекта. + \en Topology object proxy. \~ + \details \ru Заменитель топологического объекта. Используется в алгоритмах именования. + \en Proxy for a topology item. It's used in naming algorithms. \~ + \ingroup Topology_Items +*/ +// --- +class MATH_CLASS MbTopologyProxy : public MbTopologyItem { + friend class MbNameMaker; +protected: + MbTopologyProxy() : MbTopologyItem() {} +public: + /// \ru Тип элемента. \en A type of element. + virtual MbeTopologyType IsA() const; + // \ru Преобразовать согласно матрице. \en Transform according to the matrix. + virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); + // \ru Сдвинуть вдоль вектора. \en Move along a vector. + virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); + //\ru Повернуть вокруг оси. \en Rotate around an axis. + virtual void Rotate ( const MbAxis3D &, double, MbRegTransform * = NULL ); +public: + // \ru Вычислить расстояние до точки. \en Calculate the distance to a point. + virtual double DistanceToPoint( const MbCartPoint3D & ) const; + // \ru Добавить свой габарит в присланный габарит. \en Add your own bounding box into the sent bounding box. + virtual void AddYourGabaritTo( MbCube & ) const; + // \ru Рассчитать габарит в локальной системы координат, заданной матрицей преобразования в эту систему. \en Calculate bounding box in the local coordinate system which is given by the matrix of transformation to this system. + virtual void CalculateLocalGabarit( const MbMatrix3D &, MbCube & ) const; + // \ru Являются ли объекты равными? \en Determine whether objects are equal. + virtual bool IsSame( const MbTopologyItem &, double /*accuracy*/ ) const; + // \ru Построить полигональную копию объекта mesh. \en Construct a polygonal copy of an object mesh). + virtual void CalculateMesh( const MbStepData &, const MbFormNote &, MbMesh & ) const; + +OBVIOUS_PRIVATE_COPY( MbTopologyProxy ) +}; + #endif // __TOPOLOGY_ITEM_H diff --git a/C3d/Include/tri_ball_pivoting.h b/C3d/Include/tri_ball_pivoting.h index e1cbe66..ddd1d0c 100644 --- a/C3d/Include/tri_ball_pivoting.h +++ b/C3d/Include/tri_ball_pivoting.h @@ -1,249 +1,249 @@ -#ifndef __TRI_BALL_PIVOTING_H -#define __TRI_BALL_PIVOTING_H - -#include -#include -#include -#include -#include - - -class MATH_CLASS MbGrid; -class MATH_CLASS MbCollection; - - -//------------------------------------------------------------------------------ -// Облако точек -// --- -class MbPointCloud -{ -protected: - enum { - pnt_Deleted = 0x0001, // Точка удалена из сетки. - pnt_Visited = 0x0010, // Точка используется в сетке (является вершиной построенного треугольника). - pnt_Border = 0x0020, // Точка находится на внешней границе построенной сетки. - pnt_Processed = 0x0100 // Точка уже обработана алгоритмом. - }; - -public: - const std::vector & points; // Множество точек. - const std::vector & normals; // Множество нормалей в точках (согласовано с множеством точек). - std::vector flags; // Множество битовых флагов для точек. - -public: - /// Конструктор. - MbPointCloud( const MbCollection & collection ) - : points ( collection.GetPoints() ) - , normals ( collection.GetNormals() ) - , flags ( collection.PointsCount(), 0 ) - { } - -protected: - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. - MbPointCloud( const MbPointCloud & ); - -public: - /// Выдать количество точек. - size_t PointsCount() const { return points.size(); } - /// Выдать точку по её номеру. - MbCartPoint3D GetPoint ( size_t i ) const { return points[i]; } - /// Выдать нормаль по её номеру. - MbVector3D GetNormal( size_t i ) const { return normals[i]; } - /// Находится ли точка на границе построенной сетки. - bool IsBorder( size_t i ) const { return (flags[i] & pnt_Border) != 0; } - /// Установить признак того, что точка находится на границе построенной сетки. - void SetBorder( size_t i ) { flags[i] |= pnt_Border; } - /// Очистить признак того, что точка находится на границе построенной сетки. - void ClearBorder( size_t i ) { flags[i] &= ~pnt_Border;} - /// Обработана ли точка алгоритмом. - bool IsUsed( size_t i ) const { return (flags[i] & pnt_Processed) != 0; } - /// Установить признак того, что точка обработана алгоритмом. - void SetUsed( size_t i ) { flags[i] |= pnt_Processed; } - /// Очистить признак того, что точка обработана алгоритмом. - void ClearUsed( size_t i ) { flags[i] &= ~pnt_Processed;} - /// Удалена ли точка из сетки. - bool IsDeleted( size_t i ) const { return (flags[i] & pnt_Deleted) != 0; } - /// Установить признак того, что точка удалена из сетки. - void SetDeleted( size_t i ) { flags[i] |= pnt_Deleted; } - /// Является ли точка вершиной уже построенного треугольника. - bool IsVisited( size_t i ) const { return (flags[i] & pnt_Visited) != 0; } - /// Установить признак того, что точка является вершиной построенного треугольника. - void SetVisited( size_t i ) { flags[i] |= pnt_Visited; } - /// Очистить признак того, что точка является вершиной построенного треугольника. - void ClearVisite( size_t i ) { flags[i] &= ~pnt_Visited; } - -private: - // Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. - void operator = ( const MbPointCloud & ); -}; - - -//------------------------------------------------------------------------------ -// Граница фронта. -// --- -class MbFrontEdge -{ -public: - size_t v0, v1, v2; // v0, v1 - описывают отрезок - границу фронта, - // v2 - точка внутри области, ограниченной фронтом. - bool active; // Является ли ребро границей фронта или внутренним ребром. - - // Цикл границ фронта рассматривается как двусвязный список. - std::list::iterator next; - std::list::iterator previous; - -public: - /// Конструктор. - MbFrontEdge() - {} - /// Конструктор по параметрам. - MbFrontEdge( size_t _v0, size_t _v1, size_t _v2 ) - : v0 ( _v0 ) - , v1 ( _v1 ) - , v2 ( _v2 ) - , active( true ) - { - C3D_ASSERT( v0 != v1 && v1 != v2 && v0 != v2 ); - } - /// Оператор сравнения. - bool operator== ( const MbFrontEdge & f ) const - { - return ( (v0 == f.v0) && (v1 == f.v1) && (v2 == f.v2) ); - } -}; - - -//------------------------------------------------------------------------------- -// Алгоритм подвижного фронта (Advancing Front Algorithm, R. Lohner) -// Основной идеей является расширение границ области (фронта) путем -// присоединения точки из набора и построения новых границ области из вершин -// текущей актиной границы до этой точки. Активная граница при этом убирается из -// фронта. -// Наследник этого класса должен определить правила: -// 1) Seed - правило выбора трех точек из набора для построения начального треугольника; -// 2) Place- правило выбора точки из набора для построения новых границ области из вершин -// текущей активной границы до этой точки. -// --- -class MbAdvancingFront -{ -public: - typedef std::list::iterator ListIterator; - - std::list front; // Список границ фронта области. - std::list internal; // Список внутренних границ области. - std::vector nb; // Вектор со значениями, соответствующими числу границ фронта, проходящих через точку с данным индексом. - MbPointCloud pointCloud; // Облако точек. - MbGrid & grid; // Объект триангуляции, который необходимо наполнить. - -public: - /// Конструктор. - MbAdvancingFront( const MbCollection & coll, MbGrid & _grid ) ; - /// Деструктор. - virtual ~MbAdvancingFront(); - -protected: - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. - MbAdvancingFront( const MbAdvancingFront & ); - -public: - /// Построить сетку. - void BuildMesh(); - -protected: - enum ListID { - edge_Front, // Внешняя граница. - edge_Internal // Внутреннее ребро. - }; - - typedef std::pair ResultIterator; - - // Найти точки для исходного(порождающего фронт) треугольника. - virtual bool Seed( size_t & v0, size_t & v1, size_t & v2 ) = 0; - // Найти точку для построения треугольника по заданному ребру. - virtual bool Place( MbFrontEdge & e, ResultIterator & touch, size_t & v ) = 0; - // Построить новый фронт по треугольнику. - bool SeedFace(); - // Расширить фронт путем поиска, присоединения точки и построения треугольника по активному ребру фронта. - bool Advance(); - // Добавить треугольник в сетку. - void AddTriangleToMesh( size_t v0, size_t v1, size_t v2 ); - // Проверить ребро: - // 1. На правильность ориентации, т.е. ребро (v0,v1) может быть включено в другие треугольники только с обратным направлением. - // 2. Ребро существует по краней мере в единственном экземпляре. - bool CheckEdge( size_t v0, size_t v1 ); - // Добавить новое ребро фронта в конец очереди. - ListIterator addNewEdge( const MbFrontEdge & e ); - // Квалифицировать ребро как внутреннюю границу. - void MoveEdgeToInternals( ListIterator e ); - // Удалить ребро. - void EraseEdge( ListIterator e ); - // Перемесить ребро в конец очереди. - void MoveBack( ListIterator e ); - // Перемесить ребро в начало очереди. - void MoveFront( ListIterator e ); - // Проверить, может ли ребро быть сшито с одним из соседей. - bool Glue( ListIterator e ); - // Склеить вместе два ребра, если a.next = b. - bool Glue( ListIterator a, ListIterator b ); - // Разорвать фронт в точке. - void Detach( size_t v ); - -private: - void operator = ( const MbAdvancingFront & ); -}; - - -//------------------------------------------------------------------------------- -// Алгоритм поворотного шара (Ball pivoting algorithm) -// Reference: Bernardini F., Mittleman J., Rushmeier H., Silva C., Taubin G. -// "The ball-pivoting algorithm for surface reconstruction", IEEE TVCG, 1999 -// 1) Точки, использованные в алгоритме маркируются как pnt_Visited; -// 2) Граничные точки сетки маркируются как pnt_Border; -// 3) В векторе nb по индексу вершины хранится количество ребер, проходяших через нее; -// 4) Точки, обработанные в алгоритме маркируются как pnt_Processed. -// --- -class MbBallPivoting: public MbAdvancingFront -{ -public: - double radius; // Радиус поворотного шара (абсолютная величина в единицах измерения сетки). - double minEdge; // Минимальная длина ребра. - double maxEdge; // Максимальная длина ребра. - double maxAngle; // Максимальный угол между двумя гранями сетки (косинус). - -public: - /// Конструктор. - MbBallPivoting( const MbCollection & coll, // Объект с облаком точек, - MbGrid & grid, // триангуляция, которую нужно наполнить/дополнить, - double radBall = 0.0, // радиус поворотного шара, если 0 будет предпринята попытка его автоопределения, - double radMin = 0.2, // радиус кластеризации ( в % от радиуса поворотного шара ), - double angle = M_PI / 2 ); // максимальный угол между двумя элементами сетки. - /// Деструктор. - ~MbBallPivoting(); - -protected: - // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. - MbBallPivoting( const MbBallPivoting & ); - -private: - /// Найти точки начального треугольника по алгоритму поворотного шара. - bool Seed( size_t & v0, size_t & v1, size_t & v2 ); - /// Найти точку для построения треугольника по заданному ребру согласно алгоритму поворотного шара. - bool Place( MbFrontEdge & edge, MbAdvancingFront::ResultIterator & touch, size_t & v ); - /// Найти сферу, проходящую через три заданных точки, такую что нормаль к грани через эти три точки направлена в центр сферы. - bool FindSphere( const MbCartPoint3D & p0, const MbCartPoint3D & p1, const MbCartPoint3D & p2, MbCartPoint3D & center ); - /// Рассчитать угол между векторами, учитывая ориентацию axis. - double OrientedAngleRad( MbVector3D p, MbVector3D q, const MbVector3D & axis ); - /// Пометить точку и ее соседей. - void Mark( size_t idx ); - -private: - size_t last_seed; // Испольуется для поиска нового фронта когда текущий фронт пуст. - MbCartPoint3D baricenter; // Используется для первого поиска. - KdTree * tree; // К-мерное дерево для поиска N ближайших соседей точки. - -private: - void operator = ( const MbBallPivoting & ); -}; - - +#ifndef __TRI_BALL_PIVOTING_H +#define __TRI_BALL_PIVOTING_H + +#include +#include +#include +#include +#include + + +class MATH_CLASS MbGrid; +class MATH_CLASS MbCollection; + + +//------------------------------------------------------------------------------ +// Облако точек +// --- +class MbPointCloud +{ +protected: + enum { + pnt_Deleted = 0x0001, // Точка удалена из сетки. + pnt_Visited = 0x0010, // Точка используется в сетке (является вершиной построенного треугольника). + pnt_Border = 0x0020, // Точка находится на внешней границе построенной сетки. + pnt_Processed = 0x0100 // Точка уже обработана алгоритмом. + }; + +public: + const std::vector & points; // Множество точек. + const std::vector & normals; // Множество нормалей в точках (согласовано с множеством точек). + std::vector flags; // Множество битовых флагов для точек. + +public: + /// Конструктор. + MbPointCloud( const MbCollection & collection ) + : points ( collection.GetPoints() ) + , normals ( collection.GetNormals() ) + , flags ( collection.PointsCount(), 0 ) + { } + +protected: + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. + MbPointCloud( const MbPointCloud & ); + +public: + /// Выдать количество точек. + size_t PointsCount() const { return points.size(); } + /// Выдать точку по её номеру. + MbCartPoint3D GetPoint ( size_t i ) const { return points[i]; } + /// Выдать нормаль по её номеру. + MbVector3D GetNormal( size_t i ) const { return normals[i]; } + /// Находится ли точка на границе построенной сетки. + bool IsBorder( size_t i ) const { return (flags[i] & pnt_Border) != 0; } + /// Установить признак того, что точка находится на границе построенной сетки. + void SetBorder( size_t i ) { flags[i] |= pnt_Border; } + /// Очистить признак того, что точка находится на границе построенной сетки. + void ClearBorder( size_t i ) { flags[i] &= ~pnt_Border;} + /// Обработана ли точка алгоритмом. + bool IsUsed( size_t i ) const { return (flags[i] & pnt_Processed) != 0; } + /// Установить признак того, что точка обработана алгоритмом. + void SetUsed( size_t i ) { flags[i] |= pnt_Processed; } + /// Очистить признак того, что точка обработана алгоритмом. + void ClearUsed( size_t i ) { flags[i] &= ~pnt_Processed;} + /// Удалена ли точка из сетки. + bool IsDeleted( size_t i ) const { return (flags[i] & pnt_Deleted) != 0; } + /// Установить признак того, что точка удалена из сетки. + void SetDeleted( size_t i ) { flags[i] |= pnt_Deleted; } + /// Является ли точка вершиной уже построенного треугольника. + bool IsVisited( size_t i ) const { return (flags[i] & pnt_Visited) != 0; } + /// Установить признак того, что точка является вершиной построенного треугольника. + void SetVisited( size_t i ) { flags[i] |= pnt_Visited; } + /// Очистить признак того, что точка является вершиной построенного треугольника. + void ClearVisite( size_t i ) { flags[i] &= ~pnt_Visited; } + +private: + // Объявление оператора присваивания без реализации, чтобы не было присваивания по умолчанию. + void operator = ( const MbPointCloud & ); +}; + + +//------------------------------------------------------------------------------ +// Граница фронта. +// --- +class MbFrontEdge +{ +public: + size_t v0, v1, v2; // v0, v1 - описывают отрезок - границу фронта, + // v2 - точка внутри области, ограниченной фронтом. + bool active; // Является ли ребро границей фронта или внутренним ребром. + + // Цикл границ фронта рассматривается как двусвязный список. + std::list::iterator next; + std::list::iterator previous; + +public: + /// Конструктор. + MbFrontEdge() + {} + /// Конструктор по параметрам. + MbFrontEdge( size_t _v0, size_t _v1, size_t _v2 ) + : v0 ( _v0 ) + , v1 ( _v1 ) + , v2 ( _v2 ) + , active( true ) + { + C3D_ASSERT( v0 != v1 && v1 != v2 && v0 != v2 ); + } + /// Оператор сравнения. + bool operator== ( const MbFrontEdge & f ) const + { + return ( (v0 == f.v0) && (v1 == f.v1) && (v2 == f.v2) ); + } +}; + + +//------------------------------------------------------------------------------- +// Алгоритм подвижного фронта (Advancing Front Algorithm, R. Lohner) +// Основной идеей является расширение границ области (фронта) путем +// присоединения точки из набора и построения новых границ области из вершин +// текущей актиной границы до этой точки. Активная граница при этом убирается из +// фронта. +// Наследник этого класса должен определить правила: +// 1) Seed - правило выбора трех точек из набора для построения начального треугольника; +// 2) Place- правило выбора точки из набора для построения новых границ области из вершин +// текущей активной границы до этой точки. +// --- +class MbAdvancingFront +{ +public: + typedef std::list::iterator ListIterator; + + std::list front; // Список границ фронта области. + std::list internal; // Список внутренних границ области. + std::vector nb; // Вектор со значениями, соответствующими числу границ фронта, проходящих через точку с данным индексом. + MbPointCloud pointCloud; // Облако точек. + MbGrid & grid; // Объект триангуляции, который необходимо наполнить. + +public: + /// Конструктор. + MbAdvancingFront( const MbCollection & coll, MbGrid & _grid ) ; + /// Деструктор. + virtual ~MbAdvancingFront(); + +protected: + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. + MbAdvancingFront( const MbAdvancingFront & ); + +public: + /// Построить сетку. + void BuildMesh(); + +protected: + enum ListID { + edge_Front, // Внешняя граница. + edge_Internal // Внутреннее ребро. + }; + + typedef std::pair ResultIterator; + + // Найти точки для исходного(порождающего фронт) треугольника. + virtual bool Seed( size_t & v0, size_t & v1, size_t & v2 ) = 0; + // Найти точку для построения треугольника по заданному ребру. + virtual bool Place( MbFrontEdge & e, ResultIterator & touch, size_t & v ) = 0; + // Построить новый фронт по треугольнику. + bool SeedFace(); + // Расширить фронт путем поиска, присоединения точки и построения треугольника по активному ребру фронта. + bool Advance(); + // Добавить треугольник в сетку. + void AddTriangleToMesh( size_t v0, size_t v1, size_t v2 ); + // Проверить ребро: + // 1. На правильность ориентации, т.е. ребро (v0,v1) может быть включено в другие треугольники только с обратным направлением. + // 2. Ребро существует по краней мере в единственном экземпляре. + bool CheckEdge( size_t v0, size_t v1 ); + // Добавить новое ребро фронта в конец очереди. + ListIterator addNewEdge( const MbFrontEdge & e ); + // Квалифицировать ребро как внутреннюю границу. + void MoveEdgeToInternals( ListIterator e ); + // Удалить ребро. + void EraseEdge( ListIterator e ); + // Перемесить ребро в конец очереди. + void MoveBack( ListIterator e ); + // Перемесить ребро в начало очереди. + void MoveFront( ListIterator e ); + // Проверить, может ли ребро быть сшито с одним из соседей. + bool Glue( ListIterator e ); + // Склеить вместе два ребра, если a.next = b. + bool Glue( ListIterator a, ListIterator b ); + // Разорвать фронт в точке. + void Detach( size_t v ); + +private: + void operator = ( const MbAdvancingFront & ); +}; + + +//------------------------------------------------------------------------------- +// Алгоритм поворотного шара (Ball pivoting algorithm) +// Reference: Bernardini F., Mittleman J., Rushmeier H., Silva C., Taubin G. +// "The ball-pivoting algorithm for surface reconstruction", IEEE TVCG, 1999 +// 1) Точки, использованные в алгоритме маркируются как pnt_Visited; +// 2) Граничные точки сетки маркируются как pnt_Border; +// 3) В векторе nb по индексу вершины хранится количество ребер, проходяших через нее; +// 4) Точки, обработанные в алгоритме маркируются как pnt_Processed. +// --- +class MbBallPivoting: public MbAdvancingFront +{ +public: + double radius; // Радиус поворотного шара (абсолютная величина в единицах измерения сетки). + double minEdge; // Минимальная длина ребра. + double maxEdge; // Максимальная длина ребра. + double maxAngle; // Максимальный угол между двумя гранями сетки (косинус). + +public: + /// Конструктор. + MbBallPivoting( const MbCollection & coll, // Объект с облаком точек, + MbGrid & grid, // триангуляция, которую нужно наполнить/дополнить, + double radBall = 0.0, // радиус поворотного шара, если 0 будет предпринята попытка его автоопределения, + double radMin = 0.2, // радиус кластеризации ( в % от радиуса поворотного шара ), + double angle = M_PI / 2 ); // максимальный угол между двумя элементами сетки. + /// Деструктор. + ~MbBallPivoting(); + +protected: + // \ru Объявление конструктора копирования без реализации, чтобы не было копирования по умолчанию. \en The copy constructor without implementation prevents from copying by default. + MbBallPivoting( const MbBallPivoting & ); + +private: + /// Найти точки начального треугольника по алгоритму поворотного шара. + bool Seed( size_t & v0, size_t & v1, size_t & v2 ); + /// Найти точку для построения треугольника по заданному ребру согласно алгоритму поворотного шара. + bool Place( MbFrontEdge & edge, MbAdvancingFront::ResultIterator & touch, size_t & v ); + /// Найти сферу, проходящую через три заданных точки, такую что нормаль к грани через эти три точки направлена в центр сферы. + bool FindSphere( const MbCartPoint3D & p0, const MbCartPoint3D & p1, const MbCartPoint3D & p2, MbCartPoint3D & center ); + /// Рассчитать угол между векторами, учитывая ориентацию axis. + double OrientedAngleRad( MbVector3D p, MbVector3D q, const MbVector3D & axis ); + /// Пометить точку и ее соседей. + void Mark( size_t idx ); + +private: + size_t last_seed; // Испольуется для поиска нового фронта когда текущий фронт пуст. + MbCartPoint3D baricenter; // Используется для первого поиска. + KdTree * tree; // К-мерное дерево для поиска N ближайших соседей точки. + +private: + void operator = ( const MbBallPivoting & ); +}; + + #endif // __TRI_BALL_PIVOTING_H \ No newline at end of file diff --git a/C3d/Include/tri_face.h b/C3d/Include/tri_face.h index 901bda7..6be17c4 100644 --- a/C3d/Include/tri_face.h +++ b/C3d/Include/tri_face.h @@ -42,6 +42,8 @@ class MATH_CLASS MbCube; \en Whether to keep seam edges polygons and their coincident points. \~ \param[in] quad - \ru Строить четырёхугольники (true) при триангуляции поверхностей (по возможности). \en Whether to build quadrangles (true) in triangulations of surfaces (if possible). \~ + \param[in] fair - \ru Удалить вырожденные треугольники (true). + \en Degenerate triangles removing (if surface has pole). \~ \ingroup Triangulation */ // --- @@ -49,7 +51,8 @@ MATH_FUNC (void) CalculateGrid( const MbFace & face, const MbStepData & stepData, MbGrid & grid, bool dualSeams = true, - bool quad = false ); + bool quad = false, + bool fair = false ); //------------------------------------------------------------------------------ diff --git a/C3d/Include/tri_lump.h b/C3d/Include/tri_lump.h index b04cd0f..e2934f5 100644 --- a/C3d/Include/tri_lump.h +++ b/C3d/Include/tri_lump.h @@ -51,7 +51,15 @@ public: {} const MbFace & Face() const { return *face; } -//const MbGrid & Grid() const { return *grid; } + + /** \brief \ru Получить в виде пары указателей. + \en Get as a pair of pointers. + */ + template + void GetAsPair( std::pair & tied ) const + { + tied.first = face, tied.second = grid; + } MbFaceAndGrid & operator = ( const MbFaceAndGrid & faceGrid ) { diff --git a/C3d/Include/wire_frame.h b/C3d/Include/wire_frame.h index e1285cc..17f2cf9 100644 --- a/C3d/Include/wire_frame.h +++ b/C3d/Include/wire_frame.h @@ -12,6 +12,7 @@ #include +#include #include #include #include @@ -62,13 +63,17 @@ public : /// \ru Конструктор без параметров. \en Constructor without parameters. MbWireFrame(); /// \ru Конструктор по кривой и строителю. \en Constructor by a curve and creator. - MbWireFrame( const MbCurve3D &, MbCreator * = NULL ); + MbWireFrame( const MbCurve3D &, const MbCreator * = NULL ); /// \ru Конструктор по множеству кривых и строителю. \en Constructor by a set of curves and creator. - MbWireFrame( const RPArray &, MbCreator * = NULL ); + MbWireFrame( const RPArray &, const MbCreator * = NULL ); + /// \ru Конструктор по множеству кривых и строителю. \en Constructor by a set of curves and creator. + MbWireFrame( const c3d::SpaceCurvesSPtrVector &, const MbCreator * = NULL ); /// \ru Конструктор по ребру и строителю. \en Constructor by an edge and creator. - MbWireFrame( MbEdge &, MbCreator * = NULL, bool same = true ); + MbWireFrame( const MbEdge &, const MbCreator * = NULL, bool same = true ); /// \ru Конструктор по множеству рёбер и строителю. \en Constructor by a set of edges and creator. - MbWireFrame( const RPArray &, MbCreator * = NULL, bool same = true ); + MbWireFrame( const RPArray &, const MbCreator * = NULL, bool same = true ); + /// \ru Конструктор по множеству рёбер и строителю. \en Constructor by a set of edges and creator. + MbWireFrame( const c3d::WireEdgesSPtrVector &, const MbCreator * = NULL, bool same = true ); /// \ru Деструктор. \en Destructor. virtual ~MbWireFrame(); @@ -82,7 +87,7 @@ public : virtual void Transform( const MbMatrix3D &, MbRegTransform * = NULL ); // \ru Преобразовать согласно матрице. \en Transform according to the matrix. virtual void Move ( const MbVector3D &, MbRegTransform * = NULL ); // \ru Сдвинуть вдоль вектора. \en Translate along a vector. virtual void Rotate ( const MbAxis3D &, double angle, MbRegTransform * = NULL ); // \ru Повернуть вокруг оси. \en Rotate about an axis. - virtual bool IsSame ( const MbSpaceItem &, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Are the objects equal? + virtual bool IsSame ( const MbSpaceItem &, double accuracy = LENGTH_EPSILON ) const; // \ru Являются ли объекты равными? \en Are the objects equal? virtual bool IsSimilar( const MbSpaceItem & ) const; // \ru Являются ли объекты подобными? \en Are the objects similar? virtual bool SetEqual ( const MbSpaceItem & ); // \ru Сделать объекты равными. \en Make the objects equal. virtual double DistanceToPoint ( const MbCartPoint3D & ) const; // \ru Вычислить расстояние до точки. \en Calculate distance to point. @@ -109,17 +114,47 @@ public : /// \ru Выдать количество ребер каркаса. \en Get the number of edges of the frame. size_t GetEdgesCount() const { return edges.size(); } /// \ru Выдать объект по индексу. \en Get the item by index. - const MbEdge * GetEdge( size_t index ) const; + const MbEdge * GetEdge( size_t i ) const { return (i < edges.size()) ? edges[i] : NULL; } /// \ru Выдать объект по индексу для возможного редактирования. \en Get the item by index for the possible editing. - MbEdge * SetEdge( size_t index ); + MbEdge * SetEdge( size_t i ) { return (i < edges.size()) ? edges[i] : NULL; } + + /// \ru Получить ребра. \en Get edges. + template + void GetEdges( EdgesVector & dstEdges ) const + { + if ( !edges.empty() ) { + size_t addCnt = edges.size(); + dstEdges.reserve( dstEdges.size() + addCnt ); + c3d::ConstWireEdgeSPtr edge; + for ( size_t k = 0; k < addCnt; ++k ) { + edge = edges[k]; + dstEdges.push_back( edge ); + } + } + } + /// \ru Получить ребра для возможного редактирования. \en Get edges for the possible editing. + template + void SetEdges( EdgesVector & dstEdges ) + { + if ( !edges.empty() ) { + size_t addCnt = edges.size(); + dstEdges.reserve( dstEdges.size() + addCnt ); + c3d::WireEdgeSPtr edge; + for ( size_t k = 0; k < addCnt; ++k ) { + edge = edges[k]; + dstEdges.push_back( edge ); + } + } + } /// \ru Добавить ребро по кривой и ее ориентации в ребре. \en Add an edge by a curve and its orientation in relation to an edge. - void AddEdge( MbCurve3D &, bool sense = true ); + void AddEdge( const MbCurve3D &, bool sense = true ); /// \ru Добавить ребро (оригинал, не копию). \en Add an edge (an original, not a copy). - void AddEdge( MbEdge &, bool same = true ); + void AddEdge( const MbEdge &, bool same = true ); /// \ru Добавить массив ребер (оригиналы, не копии). \en Add an array of edges (originals, not copies). - void AddEdges( const RPArray &, bool same = true ); + template + void AddEdges( const WireEdgesVector &, bool same = true ); /// \ru Вставить ребро по индексу (оригинал, не копию). \en Insert an edge by index (an original, not a copy). - void InsertEdge( size_t index, MbEdge & item, bool same = true ); + void InsertEdge( size_t index, const MbEdge & item, bool same = true ); /// \ru Отцепить ребро по индексу. \en Detach an edge by index. MbEdge * DetachEdge( size_t index ); /// \ru Удалить все рёбра. \en Delete all edges. @@ -133,6 +168,31 @@ public : void GetVerticesArray ( RPArray & ); /// \ru Выдать массив вершин ребер каркаса. \en Get an array of frame edges vertices. void GetVerticesArray ( RPArray & ) const; + /// \ru выдать индекс вершины. \en Get vertex index. + size_t GetVertexIndex( const MbVertex & find ) const; + /// \ru выдать вершину по индексу \en Get vertex by index. + MbVertex * GetVertex( size_t index ) const; + /// \ru выдать индекс ребра. \en Get edge index. + size_t GetEdgeIndex( const MbEdge & find ) const; + /// \ru Получить вершины. \en Get vertices. + template + void GetVerticesSet( VerticesSet & dstVertices ) const + { + if ( !edges.empty() ) { + size_t addCnt = edges.size(); + c3d::ConstWireEdgeSPtr edge; + c3d::ConstVertexSPtr vertex; + for ( size_t k = 0; k < addCnt; ++k ) { + edge = edges[k]; + vertex = &edge->GetBegVertex(); + dstVertices.insert( vertex ); + if ( &edge->GetEndVertex() != &edge->GetBegVertex() ) { + vertex = &edge->GetEndVertex(); + dstVertices.insert( vertex ); + } + } + } + } /// \ru Выдать вершину-начало каркаса. \en Get the start vertex of a frame. const MbVertex * GetBegVertex() const; @@ -192,7 +252,7 @@ public : /// \ru Разложен ли каркас на связные части? \en Is a frame split into connected parts? bool IsNormalizeWire() const { return normal; } /// \ru Переставить кривые и переориентировать ребра, создав связные цепочки с общими вершинами. \en Perform curves reposition and edges reorientation by creating connected chains with common vertices. - void NormalizeWire(); + bool NormalizeWire( double precision = METRIC_REGION ); /** \brief \ru Отделение частей каркаса. \en Detachment of frame parts \~ @@ -223,6 +283,7 @@ OBVIOUS_PRIVATE_COPY( MbWireFrame ) IMPL_PERSISTENT_OPS( MbWireFrame ) + //------------------------------------------------------------------------------ // \ru Положить в массив оригиналы кривых. \en Put originals of curves into an array. // --- @@ -231,7 +292,7 @@ void MbWireFrame::GetCurves( CurvesVector & curves ) const { size_t edgesCnt = edges.size(); curves.reserve( curves.size() + edgesCnt ); - SPtr curve; + c3d::SpaceCurveSPtr curve; for ( size_t k = 0; k < edgesCnt; ++k ) { const MbEdge * edge = edges[k]; if ( edge != NULL ) { @@ -243,6 +304,37 @@ void MbWireFrame::GetCurves( CurvesVector & curves ) const } +//------------------------------------------------------------------------------ +// \ru Добавить массив ребер (оригиналы, не копии). \en Add an array of edges (originals, not copies). +// --- +template +void MbWireFrame::AddEdges( const WireEdgesVector & items, bool same ) +{ + bool add = false; + MbRegDuplicate * iReg = NULL; + MbAutoRegDuplicate autoReg( iReg ); + + size_t addCnt = items.size(); + edges.reserve( edges.size() + addCnt ); + + c3d::WireEdgeSPtr edge; + for ( size_t k = 0; k < addCnt; ++k ) { + const MbEdge * item = items[k]; + + if ( item != NULL ) { + edge = same ? const_cast(item) : item->DataDuplicate( iReg ); + edge->AddRef(); + edges.push_back( edge ); + add = true; + } + } + if ( add ) { + normal = false; + AttributesChange(); + } +} + + //------------------------------------------------------------------------------ /** \brief \ru Забрать кривые и удалить каркас, если он не используется. \en Take curves and delete a frame if it is not used. \~ @@ -255,24 +347,26 @@ void MbWireFrame::GetCurves( CurvesVector & curves ) const \ingroup Curve3D_Modeling */ // --- -inline -void ExtractCurvesDeleteFrame( MbWireFrame *& wireFrame, RPArray & curves ) +template +void ExtractCurvesDeleteFrame( MbWireFrame *& wireFrame, + CurvesVector & curves ) { if ( wireFrame != NULL ) { - c3d::SpaceCurvesSPtrVector curvesVect; - wireFrame->GetCurves( curvesVect ); - if ( curvesVect.size() > 0 ) { - curves.Reserve( curvesVect.size() ); - for ( size_t k = 0, cnt = curvesVect.size(); k < cnt; k++ ) { - MbCurve3D * curve = ::DetachItem( curvesVect[k] ); + c3d::SpaceCurvesSPtrVector wireCurves; + wireFrame->GetCurves( wireCurves ); + ::DeleteItem( wireFrame ); + + size_t wireCurvesCnt = wireCurves.size(); + + if ( wireCurvesCnt > 0 ) { + curves.reserve( curves.size() + wireCurvesCnt ); + for ( size_t k = 0; k < wireCurvesCnt; ++k ) { + MbCurve3D * curve = ::DetachItem( wireCurves[k] ); if ( curve != NULL ) { - curves.Add( curve ); + curves.push_back( curve ); } } } - ::AddRefItems( curves ); - ::DeleteItem( wireFrame ); - ::DecRefItems( curves ); } } @@ -290,19 +384,19 @@ void ExtractCurvesDeleteFrame( MbWireFrame *& wireFrame, RPArray & cu */ // --- inline -void ExtractCurveDeleteFrame( MbWireFrame *& wireFrame, MbCurve3D *& curve ) +void ExtractCurveDeleteFrame( MbWireFrame *& wireFrame, + MbCurve3D *& curve ) { if ( wireFrame != NULL ) { - MbEdge * edge = wireFrame->DetachEdge( 0 ); // \ru Отцепить объект \en Detach an object + c3d::WireEdgeSPtr edge( wireFrame->DetachEdge( 0 ) ); // \ru Отцепить объект \en Detach an object + ::DeleteItem( wireFrame ); + if ( edge != NULL ) { curve = &edge->SetCurve(); ::AddRefItem( curve ); - ::DeleteItem( edge ); - ::DeleteItem( wireFrame ); + edge = NULL; ::DecRefItem( curve ); } - else - ::DeleteItem( wireFrame ); } } @@ -328,7 +422,7 @@ void ExtractCurveDeleteFrame( MbWireFrame *& wireFrame, MbCurve3D *& curve ) MATH_FUNC (bool) CreateWireFrame( MbWireFrame *& result, const RPArray & curves, const MbSNameMaker & snMaker, - MbCreator * creator = NULL ); + const MbCreator * creator = NULL ); //------------------------------------------------------------------------------ @@ -352,7 +446,7 @@ MATH_FUNC (bool) CreateWireFrame( MbWireFrame *& result, MATH_FUNC (bool) CreateWireFrame( MbWireFrame *& result, const MbCurve3D & curve, const MbSNameMaker & snMaker, - MbCreator * creator = NULL ); + const MbCreator * creator = NULL ); #endif // __WIRE_FRAME_H diff --git a/C3d/Lib/x32/Debug/c3d.lib b/C3d/Lib/x32/Debug/c3d.lib index 66d8631a27bd63dedb59ddb5bedbd15017054013..4f4bd2b20af72d2f133e8c4c251fd661adb4d11c 100644 GIT binary patch literal 8189322 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%0Ajs_R#j2 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%0D(AQ^s4A)#)wE5|ca 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 ktTJK;DA{@Kto92 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 z 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

Z241vCT0OCIfFKjk5lr5 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 Go_nR~hn9Uc3u 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∨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%0D4GxeU!tSzOYevT>(7G`koZFt&8AaE@$9 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

6zu3?wReG>Y3r0$6@){)2KS= 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

nfvsl>jb&A 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®Yw$)o{?*g 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�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

DswHAI;4T9UpUGZ`PPt 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

Lb!P)yMWFXqEf8RT7s~ZfHnOHr>kZN>=uX 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

>%4NTYMFd22~P5LOq zMkg%#F$&!_bhZ{y_Z7!Y^tl$C)HiSJCn)qVV<)_&4IRDNW6JqbcX)+($~oVGY#TCH z*Qm4PJ#ySeb2aWpolX?wKABiu_oOhx?4!i$x)+6R6E}aJ9rf1OhvnWHI_7kDR%chC z_Ic@0BRU@LMrW&IPsRAueR?pkiGZtdRGk05I{tdIX9$#yP54mvb7R|v&7XNf?)Nvb z8_OH8!OFe@&WUG_ornuQfW{9qe!{DIpoZV-wl~$QdXO7C%(#iO=!0|YwK(pq2Hs1F z-RvPQ!W-ONK9oJ<5h@D@0?Dl!^yG+J@>XLSrLSKH6i-$YJz~uv(AJG1u#h z(j^qp!Z53euv(9EAqN>VRiFQO6ZrzG&wm2NBf#3CazLI)VTV~`gm?TT3O(3bqcSs| z>}C)||EPPPrxdU^t3Lm!cHA)IC2Y*oOx$TK(%&2#^K=(-kTFx9(K9sU)hLMuAG3a@ z8!?P6Cp@EPxv_&tn#$mNwuuedpv{?1eQiCk|4mr4=k#I_L9LP2SvsV!j<9v0Fi(Bo%#zGc~*pwLOYiNAi@cA=N zM4?_=!Vm0iIIIZf2fWUW9cJ9bnf>)9Ha%#*#0gD*g9F(%WVWUgGyO&lw*ifFAtf@8 zzlAuX6EpoLH@0oqYoHgh>>L2aq@wng3v4*kPo<%^`!WV3R-87mpA#LDk28xX5A#ik(|M6Dinw zNJZB^(vw37DNw!EKAJN)9<6t5#boReQ)kDInRqjeC2#xfh>f2Ln)dMmn(YFey}AjX z@)J(HFsq8PX`ggr2hkzoCizo2a^Mb|lyCKE3u=&&I_4Lnd7mj@9)edP8!NsVOyzlf zR!3ZmI?2Z7osd4~#&3r*z-%R-Y;8FgN)RnQ&5f;l#FynLBFg9{QUl zw9-N5f9KZR_Pu$gzIciDwlv zd#sc`f6s|+6EkogzG77$Nq;k3qLmS4%L%JeL)5#Yn|C7it^uh>=BMV347-J#EBn7(vEq? zoBy9`$c?e+{6=Iia}BB6KO->%SrNyW(X0G9i62Dbj(rL%xPPIeufeJwc;BsG5{QAM z?HDia%fBMfgUH&ku1*E)Ut6&~?TF(PjCZ8JapDD%vtz7iLw@VT4k2YKr}uXna<_$D z5rg+{A@u9_1bUE>6Zg`8$guk;W99G9#J%(%9mqjOOlaAka@_SLm}!yn=sXy+cMU?; ze|90;hRl{Vb(Z-{j@*tCBjY4@2iS;IZ0oOXM5`fT%bBoYe{*98ku;T|{&xc#OZZT= zYMnWdyZk8^ID5>*YWjy2cXGj2kif;IvK{_u#kGlHH_dKD29h8lv{?QMARemaYMN@+ntTFC@bW!gM>>GNtm9FO=gzYisW}#2pb~tc|;{ z1<7iX*yE#Oq8G8C2GJGA+~JMJMGKf=ZZs%saIqY*(Qc#%P^)mc)_{s~U)+Xj6E1&7 z2|c=mj@p$O_12;CQ5`2gJndf6f@&KoTfPM5r83OJjit`!ba1?M8PhgQwrr`r`!X74 z5{<{*mPmE7PAXfzjw+a&!JJ;!jvHjWRF=f$4BWmS>IG z;;I?`_$oB3hZb?h%UAiaAn2&CMxzF?ZPYEm)oFa2(AneP;)tzW6Kv}>6#n|IrD8G% zcXqcpR`s<2=b9PLo}|7m_DGKzrABnE3~7HHubVD8dqj?1|JpF3YiBrz0(j)qK6D+0 z)ESFUKtqmp2lM8|-dpSBKg&yQ52;e|itD;LOm+h7Mbt}<*UPZ?MDZHr072t>zj>u% zLDx^wrY7Ppc<6AcjgJZW-fL4gk2g?=(9CzDvAhgL-K}D3H&=5hd7hf$O~;Ezqt2mv zv+Mg(5+%4>#GyJk#fVPjF})*CCVBwk6Pc6+Rz90mgi zDr17O=g(oY@qX#F6cb*9%ASHM50i^X-OAr2L&R4X>dmnyZE$=&aaeSWNuE(gtG1Rff!;T^OU06hQq zCGwBTlgu406^lL7f@mEkd;L(i2U`_teLWhhw+l)8-0@Mn$2JXVyjgGiu1~6-X1j)! zDe2a+Tus?Br2N<&8rs2V1?oJ-6BwqO$47LWnXwPfZS9D=ge~$IC8a61Q<%|-j=yI| zokDJ}kmB`tGHS1mo;5pe3X`js)JngDLW5jCQFeH&-V|AfVAgX{vJr{>|Bfl*9KeOy zk16fksnFma9h;KCBQ52GO1^dyp6O_ccnIn@4~M7nT{_PEOmv}?$ zxw&NP2W7#|vSAhn$ws73gZmVs=$l_2UPw*ZZ(tRN#YS@6<6!mRq=9riX~0E_kDhFC zoZqhwSW_vM9FLC`rdHX36h)0?>)4=H*>nlXccv(fKifb{S6LB|t?|^lK4?Q!V)a

atVm%)+u*#FgmLaik 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\qzPy{*3 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 z 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 p)_G%Arkq)8$t>1xf{cEq^YUIN35$~?e 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

>Wu(m2a#fLxb69LkS?uJR)$2A+)~O!KrIr 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

KJg`I!!$xbtDR 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�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�X2Tbqccq0>aVi^(+uexd}X_!0eJvGMF8?9Q_iJS$>!eDWxajc#$ilf+s* z^uXOGDs$^LJy0C0p_)lyN|Zd9wectO~{LnH!?6D9<{R1mjN#fRZ= zI;bAbwT~TaEYyh^&SL38(}oeqU|Z4#X4r7_?P24gUd!7|Jd@qE1@P}qER28;;donk zvxFl>2f#fmR4;_>CjB-xYR7`cXOnp^T>yA?CU_d-&82WruxGYqX28c+<=(9);0A@z0VrJb!NOla=eUttWquVW zS&0m+JhuepvD=ldK(iX98?)ll){Q&zYlk z=XNQwWiU#s=(>y_1X`Rk-|kRx^V=u%quC2e+Ebextew?d^q5Jy4w3z;BZa8MRl{ z1R1n6f+Ld-6fuq~`);P}{aQ*Bs+{(n>d5%Fej-G`O?o)Qf5z47ximTPh8{5xfl)8+ z+G*;kBeh$c5UzbGr8j;?&)_easEZkCxp+s};0L^@4at?R2Mv%W%ItGa z-F^qNcnl!%vC4J(Qr^q){^-MTcQb=4*Hj$)oCHhQ1hcS>x6_fZEsCCy4-?uZaOqO^ z*|~}?IBaW6nEIaESKdGB^zB0D1E@sE#_*3C#fWvC z&7{A4@TDwJ8-HXBCc>`eG@2rVIyNiA7&_t3g3skr! z3jWwDr}7r>tYZ*!GP7zgKkq3!?=BjGW7UcJY;dY`D1@}EdP6+!Wa3=TY;Mo%4Kt(e zs?k5MB>I#VzMG-_SMuHth<_$C=Q4H-*gD(P!kwd0|VV@IZN* zGf+9-3kjNV9k*yLiaM*Y8cD*PdeSL-XQ=4rqN0vnKY_e%yNUZs2uTuG)C7ckcBmYx zC51vPUL=_U7PGYLSk4=alFvZV7apI9g+tvneYAV*0TYk+H1ROk%|@YFw$KQ#S6d`j zw53u>esfDsws%mQG`DCJ97)e($DOL04cwRNl@kyaJ( z0a_dg9O4J^#hwW}H&j^AprfuEjKHGbTMu%wxkBY+o!=XYC_dG*0`l0q)Rds(PAoF} zN|#&7FnpECJ6SPF6J0issJ!+@1{eB%;?y6=!I2iZ?OGJZx_}D4PWUHvLvVE-bqlTO z*hF#5#KM6fT1#H;0-QYHqO&)|Uh~BwC^St68o`IOp}5*L`EhSqrIYz?PO>#3{O0icloe#2RI~ z9>$w;bURk$VLQ$aINbHaTqurvTR3DHAckl5?Uls`@QKX$sUF>)FD621*!$=>M(d45 z*rJ(@wS_2QhX5hIF+yp4ZV>m)eKizak>UagjhGoH!VJQD0iBE>0@99IqA547WyHXjHd$ip@2BD@UBI1vIQEFHPW`ET%TIep%&jc5MbF8-S+;cUx!QCM$>*q0U>TBplK_SgWR~eVq9fck6 z9_yytfo39tu(4qNY4GSLjKw`Jp^T$zyty_;@-dg=)U1)mEBf7qSS?_F-jSf)38BkW zY$VMjUt(*OQgupWFkBVn*zzHe8%s928HU;=nQ63hl8N1L4V2dtfP& zCzOOfEy1zgf@e#dbPZ3NI`(0ospYGVpkiE4PbjyRA(B$T2OUoK74vho>ofN8s@qdj z6KvV=SlD|(OFY9yqa|I}noBZAQz$U$k9WXfYb}a9&ouE2pI+`f%SJN*?oc@&&o=Qm ztM22siDRRmJ7Gi~4d<~+ zF(2rKSsczp6H~wC*%c~Q@uDmmBQwcFk?S2x_!VB<14SG@Pqrox4(b+uNfw7X76n+S zlytt-M_EQpP&|b_l(0Y(2<|D_?a490X(KQ5;czDpn3!_aQc~LeP zB}Uo0`IV<^y+}EKwTWNFB}XG@mNOQ#!V}=fKapfKCAUI+8nwg6maxi@3OAWtodd4E z#zX}%j7HR4QJy^Tg=4f4>bq(b&9fx zI7x5c48(nqzMTak`YFrMl>URcxI$GozutkZuNry@kEu-lQl_)K7sh2p4>Mb>{11PSrcT*`%w&P;gzFO;EYI zi;6nFMWYF>TpXjPHQ*Cha{O@~e;O5wdMk-Q-h&E)W=6Ah0aau3wgf}i9ORC_-K0B^ z9e;;L^YrZaJ4pn^?KqW-@h*h{`)i8&8j-9SyR#?i0hZigzrA{A7Jek3ruSH2=YXzx zcY*_MzGg?JP@$?n7_`>HP8L9oJDKir9bxA8l*C;E=JSZS^-?|OK_cnrXPC2 zR_qQaZnzyAH`O`h{R+2e1rAl6*nnPj22c0cYFju7|K!odRJ`c}n?wY6WG+mUw)kL* zDeA7Q#`(XV(m>O_&fcvZo-=uUkg^Xc95CTz79wy}?4gk>_Dp%%KAccn*9+gs0v@gg zqO#XNlG2Z#EjDTBw9uS5G~R@+V6$PImyky;shsGKDl}`tDzpP{7<|qpf0QrjV}9E4 z8P!R1_kddU#}mrJG8*WrKjEicz^W@l>Y$n0dORU$3@%2|b(ZREL)@BxpMIMtld$sM!BPN4hWeLMQkoe3*`I zd1x``@dQ%QyDu5YS`p^8L1MAe8A-MdUSZx5AAk5kR8>y@iVsF%)H6do8j4*lLqTSMxXS?wqDymTnP$X63o z;Y!q;48qj~m|29zIi~6+0g4)Whw?}c<$L>DieU|tIbir`b}QItoU(MjZc)^8d9tLr zQb4Ws8yaOy%_x7Z-?S(eyH=|2__q{gY$Js#6iN)GPKX^K=_leeRBhEJ=(xljrPF5yJ24Sf+4tmxUr$0a+N_Q$fuX z{a%a$<~NGImFNy{Y$VzTBH20LpPooQ_Rai$hBo#V(Pm!fr%vSE{=la0j!NcxeS4x1 z*rDNv7X5O(4OAbbA1MT5!3qum@gRU!NO>#;O8S1R5G0N%y9)Q1;q>83{%+6yj{*OG zqG%rxrY5m^Yc5vaI$V$Cgz@^HCYX)}Vtk{2O8}Jq1mtptdj0%Np} z&ghjPwS4ncJ`1b=c}lkxS1rx8gO=G^0mBDLmcW>i7%)~)E&$~-`$b@EyJL1gcfQ; z19*`-f(<5*zuLrP=?lW{nWN<{tk$h z!^INH@#|1LVB^P#49L!ncg!8ojtm0{tAf~(Rj&+YBiU>qUzEwZ6G)l zg6{;=i59{|=}U`uUju`mkq?0T{ze$!xII;?lYIS=$|tyIWQg6mwXIh(^t=5>mEM~ z=wG1^`o`k<8j+xWMTf2np36 zRqS0LJp5O7B1xzsmhoJjm(=swl7O>80I7cZq~C^KeW0h| z`0FjNkONvG0Gs#3-E=iC{iOMnPMW=Ts)GCK7VVVjX1gy8x@GqzkDWl}k6puyD&`0T zA$tL0?+_uO*G%zH^>g|)#oi^VqWxMneG#zU52YMh(J^cff-|mdHj2uS5>(UAH3R4L z+6Icl%So>Ocyl=dgChB}wB;c_JvRg9C8Dx_SXX0gj8uQW>u4BpeD$IF0%UiaBQ!_$ zjf*O1@m<{Vb*gqu6K1aLBu;EgyA1=^uB#A1-s6JIb)2Zzn#~A$0E5ku7Nr?(0xp8Y z{c0h==x@FuT#n8cL-@a4z#g6QT3Z6n)jp%C+ z&D{qI$C(xm@%$y^3O!bXRg%+|*gTRf0q`1Hmfr z$tWI4Wv0?_Wr4~+zjaLYnCvE?mYe-R?31NhAHn~2}JDJ+eN=)~>fyvLXNwFZ_Qfu6nP#l!}Y&R(uv_?vPb|{J^ z$PaZd?o@Q4$|&_Sf&6OYFoX+OA9tTa15>Y}T^g3yuK*`$1fr(_RAh*8j+oqY>|Enn zz}3c7T~wEsbkO0aEw|4zLh!>qjE`#TJKxvQ2L1 zL=o2lhzgyoE0!w_l!xW^Zu;2Tc&-W0^y0Wcy_WCbM$y432{7HscP!ZQK<&gGofv2) zaCd^b0rtl9?>w+uu9{FR^3I`dfSRIM!VN&#y(5Zl&;o~D9g-4GL3IsgiGbeE)lZ*# zagQ2Q%}HFATb(V*JBYXMn1`-(Pihw%n`)9aZs}9(S+LKfdN5jxpzl)VQdCd*$bqD(Va!G^tL`O^?i|KsriQo>%M+PsH%Xu)pIbUEzsW*%NtW zimLfJ$E0cIO=v{fde{>ezA3EpTo=tkbOqIo=1@$vi|sE&e^pU@mXxDHF?_D`L*2d; zI_(3kU?pm8Oos~#QL|VKm0_i{%oxjY;QFM%uv%|4qLXSwTma1on(bw9Qh?5`HCJ$6 ztCoW)vRxtZab~L1svgP47I5qzbSjs}5+yZ{f2lI8j)sXn1nh@hE714gcd-9+91 zjXuyoqJ9z!>Wkd#C|?tzf&*lnVFb**j^;^>0ZcjylK5rkf`&K`z3uQ zMlO0Qs7H$G{XQ;&)00Eu)FpAH_!O!eYIwLZEdK|0lcZh%ToKdRJ z-E52;{hl2hTP}*$ur3NTtGKS=|0X(6jg4RC(-l-qaxTF!8g+p@D*I+f@A3mt+8j3< z)q&&)MH_Coe3U8fAC;8Vyqxi7h%3hlx+5E+F$PvRLRk~qFL^sKR+6jE)EQ3&opCI| z0rwm1bq8pkSCgY*r-OF4(Gfi-P%NSKrhImakojn|zJUo+u{4|A(ZrZt*hO$J;fT1} zoR|y*6qJ8ooCw{yRI7o=4_9~Qk#?$r>^yy)@j6c9I<;kTwbAg9N~<+@^DVHOcTQF* zmWab_ir7x9tC*e=VJR>x#Q79Ei+eu@Vv!m91S1ozzoYLpY8bKGIk-?snunQOcs+38 zE+!YwiJ%ISzt_f+<;WQXj_XI?9GXwq84%VF1X8(%KjSG*J`m?7^|9h_*k0Y8WdQr- zIh?poOb+}UIB-{+17Q3LWMe{&Ju!8PC)#gG{}W|v z0&ye%Kr!JzMR3kDS^Ukwxx1O11GR?1K)&?YT(gb?UUBph;vOKj{D9SQru@g0pJm{} z-AyhCmmix8LlvKv&vWjF-1hGgKs_I$ZZwkj65OeHbYr(M3_S<7R^%xbpB;n*czD}Y zp*|lD_H?k4{!RUH#MRMM2fBOoi*kN)s901~6sDe_!qeNv-mf7RD8z9XnAU+s)?g4B z4d}RhsccipNT$rSVD}ah1ZakMqzw&=`1Y2ac;Gz_%_$v%20w_uNBl4hSjG%Mm-67Q zAW^!OzYphSEXoyx%V^4JzAvLiDF2)$j!lzi4+n}%3B{4<6fCd?jndZ^8XE_|%?LdJ z)L?tKbCNbWiF;sn0{#waRQLp)3^0Knem?uPUOFlGocf&RCx^A8%wE}W;?y%&_B&Wv zu&I@ufX;T#MKIQA|7a1vGpXVu8OWAJb(kz!ybb$KXsX0T6XWmqXE(@qLNKc0{{8mmz^=yZa(HKX&ring=#V+aLaI~9sJv@i~J z?~d|rRI7UW;wDi0z2+x3japZ>3{^le&3gA&@^O9og|S zQD4fGzAM754R6N!8ce{cRwu!4J-NN2J#g5&v0WK9rW{=e>$#_AJu{(ol)+~qz45NK z7)F@&wh(`mIjmwS+w!broBKp`U?unRt>k}fHz+IP-f|6V>sXi8frfJ3HPIn3ufK!J zI=xRybAO}TR)eO%Z2?WUVG4kJR%iyrCN!{n>dg}D+I>^%r>rij&dUQm2#!$}B8foy zWD{EnTncHsFZP*9w#hs*4+G5iOE8Da8{mM6A%W9Kv>+zbJ7~ z3pbPDv$)&FylnI|1vRDzKH2+ES@RFDX{MDn7Fq5NQY(F+O;vT` z%(+r3kLW=*&BSD{$o`mlX?wgdSa17aFKyMe(j?C2wYu)DmorYbeTbJfy?zynDQy1SxC6i>>c0MA-f0t%|G<;fC7)YEyC!;Yoq@IFP*UxT0a5llSHs{0I*Rvq8x5*m^YaHI}@zPm#yB|k;|J(GPcruqdf?A?U^a6snt51ZO9yTzRtuG zOXd?g{Yff`2wMlOA*++enUzYDcztrdJC!J!YkCN*=~@=6atG>wQkH^=@`6 z%U`j(g5;&J;^(F-o(V4K1~@yo;KOKnvSUe4so|{!o+HiE?LMxX*lA_pnmhe+SoiZb zy>7-XpSG1#x%AH$E02~tbF&f5TZL=M^6gngmR!wG(SCuVeWV=Pdps|w1F+*Sl(fOf z9&Oay?Ql7SY$j~uns@-?a4m!cu+`Zi%yz=>F>|Z}opy5QoMBI&yte|b7p1tmoep-o zai71&GG|iW!^Q==i4&D%M+XB~u9y*ybJB*H>pP^{qa58nnqmoUi=!W%12 zwbj%km)l8-r{&MQIpBIlf@>l?HMfqsQO+b+8NNB_CWfFz_YOe!l?h#$=G>QF&UHsn zxjnCn(cmjm)ZxMEPsHuEG4gw0bYi|GDzPxVOJfOJMk>SZ_&V0TzB)kxJc&*8-guz0 zYg}L!7pYbApr9weE>HYyk23eH{X7iVU$Z&vhI>T>GILk~RL}j_ZVI2i)!b88@zjV- z1Gio$(Le&LVVA?rk|fJx?_e2zp4wSEI8_t+!2SX7AZWtKMM!xzUhhHJieR!`2Z}Ya z*pjdEp`x_a8xmAtPK8bANrO+BF(@Fx!U(e&0h=4jbh4!T5bW0*72VNR)NYF=6F?AD zrS@`;pIPzq1>J_p&my-f`LbBtSp=s5+nW?NnPD>#)Z1M#-X>P%v*B7W3r@}$-wBzX zKc_xcc{y;&rxgg9Z}kF%JQAl2HH*jNu#Pv2b*w|BkZi(x9@ZyLEXY3yQzcv93ZK9b zD)}S%^fHzA`<4WmXwi(8d>aa0RM=n!G0W!7THt_vGfjxGegung;_oQfD$Q;?3Zb?Yl`!m7 z`VlNM2CIcpCiRPzjg871f2YC*Sw&N==={VbpY*eaWIijbgLVgwflN&g?x~@sC%lV9 zguJow02)l^&w+=!+25VufhYuyl|j>cw!R*uLv}>ut9Aq$QHq%}Ae;ETm=BiAepHv3 zB~$xBrrx8YI@$u==0kN7zC_a;q^uBBR1dHBDpVL448iO%>zi`Lm8dL}_gQqU!m6ne zy|jvbekw^|)YNq^hJARyq`VrmzAG7~;vta>&=RTZ?ux=3q;_weLUo>(2_vHhS;d7}|Fmjv_Uw|Gw49;@~? zu-6}wv>_xJEAoAf;DqqWCIJGg!53FARx0k-?gCK%a7?`g!+EjX!F!!*0eIBZLqnh) zg=rZ7HM~62KUVjWMP!&e&DjDx-zVks)-l#i$9v;O*YhH zvc%|+dFZW1v~UEHZhFJRipR+>%5ghU6%rqlnBfrgsG~^?NE<7zAU3nWE?1p%yaKWb zj=Lo!1de|^7kYdq#@O*;C-kQym`204pOhgBd{hg9c^nQ5PuJhHpp&CPoQ z&d=C5+kF5P8d-IK5wEZGK?+?{G6sky+2imUHO8gzxt1g!jX`B$ECe?zc&CTO+;VQGp- zgTb6sHu2solr?O9vCS>vZQ#u36V8aZez1hQCXS#$okZA&h9I+dD@o-vSBg15 zUrbhdH0-R-!W4x~pJY`nJOuZsxLma?9^>Q+D)zEVIKPxd|D|}{t4pvISrE-R=Vt5eaqIZ3u_jQwA5!6%ytVIpG34KGOHX;vG5fs&> zV^PH3>fh8U1{a1w%gIvy9(tlc*uU#r8JdnovMEwA#c%5rTOm}2h=;m!sa)xyDsjG} z(H&idlJ`Y$Yx+s^*Q^2>*3RAnxcpr&?IO5M^=|r}PC0^P`1KGRKt)ic_W1iI)pjQ> z{>iT)JRUZzQ2c>O7u!u1B3C;IJ@LaVO$U3~l1xa`kFpdGH-!hoL6|Sc9&K?e;O~!J zloKyQW^4Jh)nmmvL$?IlKQU-W&Gs1h%+Y;{uFMQ+F2AWW^izX!8hgtS<*(Os+hL~I z&vdHYg>bRJGen);pWAfh-B-*xQm!Oxe{Txw{e_!u0eef$YW<~~YJt3_bjPm@>T!qV zj99T{*>j4{1TO#DrdR-fsqP2A(dZovT98EH6eRY^NfKHuNkW_Ew;7s_MHWi&cN#@3 zIV)(5o;v{Rr+;rzEM$eAd_(N4@CSExNHg)X8A>IU%&spWJjEY-Up> zY>7W>RFedXQuV=KY>EY}klN3`YV-~-FSmn^-vnoKme|j~x#qXE_-(`qS2iRz$Pnj zcPXj*KbNhZ`pIV0z z+(lFO+%advo}Xz^Z$vQJt7!Vf>ThV$@3gvwub$8{H`1wF{q+ivIpRjSu}(Rj0bi)Z z5jV z&xS98HX&bU*|a0dNMiGwpnMyH@;X#=7HSU@l+QLPV{%u4XNs!f95N{z@6_x@bGe<2 z#IP(v%<`%jlnZ=!5GQI_r#TyeK|rv%&=a*yQ9d&GSKfAm_TXN5J50I*e&y{nsSog# zx67nFz*pXGgYw{Cd3y}{gMH=QR;NASSKjS(+Sz*WtRm&>7UG?AdxLr(MoOX7?_f|L z^c(Yz7WF~DG54DE2mHpY>eP#WV-l~Anob#8Nf8$**J_~R>mxed0lxD_b-Dw6=Z)!< z2l&q0r_&wiJ8!>Ec_8n+13JZlzw-_n)Cc*_8`mih_?>r-O?zPPymJl8gM8;5GAR%6 zoi|}p9^gA~(x5z;civ&0X5sHVsuR?dq#Q!MuIPTPb$keBI`}cvbjIyfusK3pbdCkF zQynObg@~(ah;0;}yM91#FDZJZ3yPA8=#vM;Rcns;m*(juu}y`S%!v01c+1}@UG;2q z7{Z9FK_Gjwrj0qA%hw9ClXZ*a7QKN2CcMNQMl#D_+ZKd(U|OysTCYM6yp>kaZjaPk zM_cuEXx=J&^l7EZd>h)6mtO(?&BT;j!E6LOhZ9_(IQhg(2b#&Pr5)zXR;_FVm2Jx9 zXYSwyfM8Z4Xw~It@-)8<-4(ja01{DDuOob4Vg{P1?bbTDsvbyK%&mV1y=~?v^M; zs7JNA0_+`fj99^pX)+YKgiV|X|1O+Itiik#i(CR%j;Ok`J0~j;15rAlo|CA zeI-CEkQ^we9G$zRDE1xRUj`4TxWwI4>Ib4|sXEeKhP)_jyoz`6TrD5>cdBc|;yz|A z%v+RQ7qPYZeECy{)}oKQe~*+tn2JHCyxrgBiAZe+6!jFvOqm#{PK*mF2JCGcw8mlX zxl<5p`ydH*@79HSJ6xO(qqeY{s$+9|w=P1Dhz{!d; z9sD<{tAiRjwzN>vx+Lgz7vdi4wRSsLTWCOwaUd#&AIKK#=W1zPeEJ?~#k{V-q(2cY z1u|*VL!7H8sd*5~8tuc&@yrjHjNX-lB25kmJSlrbax3&rZ-uAOgw@!?MM(9)Ux^Ve zw!($ra1*t86T20T))!ZyZxZ~eh?fP`U1~K(uoRwvIldxqJbjYs?S;}TZfVj(zhTfk zvbBm#0FK{aN#RKjp6yl-5ljE=1&In{#BISeI7J8Qt%W-Dmx0MYabI%iCv^<)nq1W2 zuqB+z5k6nx0WH#zU2evpdH!NNr7d9x1bOnEa^ih>yblbbA}1GuUFun4?aFOoR~j?| zy|gbbO46Zl&(tnYPR?#XverM63`^KJ2#z#q9F8)rvN#g;k0d`wJSTSt?994>LwKML zqus7mh>Lp(yDv`at-;ynmAVlxFi=2Oa?w{6%a_v59N3&ZkG|Nc9*)*Y!OFE3MPKkg zFLmne(DqPw98e1e`)m-_W2X-kE>^HHcq|jNqq`mqj)PeuCr*!4#1#c!aID}H0I`p=LOFY*Ly0Trla!v2e22kCQR9nln zQ$ZUCUDcW^Xo;()z#^{?zms#H0x*QIEDOEwDnKzm8TT`;oeJC1B$}Tb-X`ouqs#kt z&?P%^X_MjYD$s7O>^!yY~9GKfUCq5S4K`XeZB*-vn zL=-0V*N|}2ss^~ogOnV;V;t0s-!<3QUx^8B&tq*@hLo9P^6EU`)jbuj#)1=}tTce? zEi9`t!sgQ11vk*-kT80+FNp#{_8mnby_XXyrUq(YP$JRSUOLjGDX(>#jRH^at&l?N z;JzBB5oq8G&I+wbKnhYA zK;wc_(lD_jBznLE^pPy`#z;yYZc;DA`x(fg1QXlhLd(TeTk6k5bC1(?poTnGy%5ze z&>(Z~lnODg;Qkuon2AcU_X8D!QsUqz7GWNo73E4aWugu;@cv6AqLOjIU3CP{BL&2K+6QZR5&@VDPIe-E8>Q(@ax6q3 zzmalm+azK@i+ERo2Lk4uVUuqd4PXXUxxB0lyX~j|beh=fiZI zVlHa}6_u}zum+|89=#M~Wmqs5*9i>u&XoCrFr5HiVkV{CWZS@#Fr4`<TW$FubF*6;VAm9-*yh63T<2x3Wlkz{TS=SS^co z45atbu2q=#t(&bCm?ojIma7RU`FqS}G37!;&31pRhA7S@ON{^6Dv7x?G-QBa?-lh< zdYpzLPEU1dMzl!aB3%qH@FU;q@I2LW%VNY}qG}ts38e0u@=hRCk9QzdV@TrZt}cvf z1V(I8p_!ixp=TcNT>#+|bcCxx1HyEDTFrVco;MMX%}r`{@Dp{c!uik-SbQtPzW0}j zhW4r^Bd33MFg3g6Njg8kr(wM(p&!^JoI-KQIve(IbybDP^?b61Wwzd2!DL0l&KG`* zGOhvsD*P{pnOTs(J_ohYYW(v7VI3m!x^UX$(t+dT8?*5|2m$=2se&M z-lt2JcT#H@;`>arFc%FL=a&&6q{}36KI-e6)6*O$x%r>ys zK;`5s8Hcg*OGbgM>mEZ!QI= z@ckg38#ug9Q*iv;^lS(l)9BO0^ zY76IT11cN+B?(PXW38y&7U5V)Ggp46auHg*#!j>^O=#kj#OYuKg4vLV7J>V>kbS0n zP%l%oM<8&2yj^Q9jnvzaL%6m8w=Jr28Rx-qo$@CN!bD|MzFc7%3t-+Py6I$@9R~U9 z&BY)NM&_)PqWX}(LLoQ=7n;arfE-fjn~3`Xol0}nC2XrG6=YibH@e7{Ew5B`r-1uW zQ?4JkLH_x0YXx#kFBD<#@#b=r?1f5;2D!o1)A^%TR`mQ)8J9N#`tTuKs$%}_(2#g3 zH-N!7w^Qa=Z}yiNL~{Q&#JH_m(c52h&pVZg`YOe7-8+aJkN@nrB83d((FJ={h}qHw z=YlSHwZO9h-QPQ{4SOc zAbzkp-KC;QwtUcu(A8+PzJXN`u^<^7l=k&xeJz-%AIHpp;Y|f4w)yxXl?nSMxrXNA zYAb@PK?b%^f9*R3V^gtd3uGSVyeTRAZ}!pO--zl((f6Eu?6&+CNx$6**5cw2*hugc zz}bi57;C&UUw%~p+R}#DZTYP!<&lj}a2WG}s^`qkh`J=qdyxBS<(J3$* z(Si-ZYmDsEBO=nwWhCy>$#pRlo@JUK>!pg~Y5CXc;-2axkmT9`27 z$7~8WNHm|a(U@IdO4u*zW&UX!jTT!TBv(G8;SjB?h4(!TBS}-bP>E3q_LgSE>Xgsg zX#NMJ_;VJf@}-!Po6p;5N|YOFg5?(k4$L}(8#U9|3?8EAXis7GJw;88_@bl?L+hJR zzh50cI(}f^jBx+u>6}CPLcgR_jGzz*yzvwDv0xGB-^7zU#X|$a1-057KFK9IP_dlW{-!BgJF+wnZ|w8x`!CV+=ul^CGWN<#!&M0RX|4fBL(qj*CUG-^Q) zK@kp(Jq+S?pC;$!Lg%p+4+FZdrRXF)e5lAXkEU$6(v7<9BuFNVjri+XsL+M;Db`fY z<%8Kt#52Dx@tx}E2k{`_fY4&ND34|y>7gp~zoBUFTU&=P^+dhaY(^a!F+(3Ax&;k~ z7+-chuWZa9_@<&RPLr;NKnEzNT!sPM(@#YlzGWbAE+3>cIrf^T5T-$Do5ICJd4ayI zV@l4mV`Fli=L%7!_Z@|RuMLmr-qd2XU?oZKGPU^AK$5;I5JRQUCgT!D;IyN}bsX09{eISk z9+1SWMo#zG3{`v^%Y=4Z`C3dtw=8a)8~cM8duwS7oHYmHdC>7)7h)R)>6KG&*nBt1 zZe_|?Nbt4z!+ww?{_G-r$;pwl(4a^CM+O?CazuT1U?(k&@S5dyWq$jT*d%OK6OC3va`w|YWtWw(iw{9G+%`grx z7Xx*_{mzZya1&nIm<$s;AdB=Dd){r>mGSpJG>MH;0v=BU`;LI;4;egGX>?CG+`Xc_ zY=6w4X@|{jI}#Z-nd|~XWZ0*d()WL|akQf7I24@pt4g>$p)|>#Z7kh|aIsKLLUH0R zF6z=ADMylSEn+?!aF6odGagGByP5vO zg`%`mLTQeFT1XC}D_GR9_f%kP%|Xe=znnOFiYFg~=Z?Ytq5rlpV1j*Lhn0p(yqP_B-k^yZdH%p6<|<@%5Lpg=2__o_;Xl5p3q|34pk(6=3xU?xw;XN|ck0FbSPJI}^>Vwog+mzU zacWo3!@|~E)J<{;8%YE?wq7KLk-^77cnB|Ve^BsgD;&y zaWZI8>YPoT^2uGsrrwIq?@uVWx{G?tUe-dPXJqxII`}*xB=m9wn!(D(&9Q(p&_C4w1N2q7m2 zD39coZ47bTUQ6{XwAUMz3wsqe7Dobe@wXhMNv>+)@^o`A&?`pW{#PT=xQ>r;c=$Sv zunwy3?0xL9vcM~JKAsdv*gECh*vp8~+=0r9H8W`Y1ZV79_Vj8Ix-KG-2l zg(mZ!Wa7netr$sre5pEiY`-X9XonqDoi18s!{-O<%_VG0uO$O$QO0VG=@s9OE& z3Yu&6Qy>Sn%>fQ4eSjaIuE)Z-3A`Au=bgiGNy?&mp(NY??#e7RFh&uhlvF&C>B(~RDlehd>tBcV5TZkZ!9sdi`{{q z6|E55oZ$(HTQrG<{gYdlLmN`B!L1TOr{1V_m*Q!f?qp4CxFlOMP`^XFUn#EO!zo9?N*{6l_!k=X#OG$x?aIj*c+uv<8WWU8t!{gdsR19R;L^lowUni^^`L9{-;H}M^cmZAEIfTppDjiWN8oFnF$oB|v>OdOD$+g#~3>a8(& zj0(XAS50=)#P}Moisi}_<$K(j#kVmMG?&06%fix_#Ko}NBTHCgzAJ;FwTR{@n=$O2 zqa=K{162kS)TNV+L1HS1ql}-!-cD!`>{iCOE=3;*uCPIPQt0Ucglswvi3& z^;6P~NZIr?7aC|F3|+e79=ewOryVpdSE!qL#D(K9yvZTuXi&RG-9Mu)d~?kOxSTf& zkF6u9oR2XVzLMmL(&+nKINDOY+XLfN+VRr7bMU6#pGAo6hBjs5@K1m;AxvD;fa`!8 zSLOob897{plvX{MK^R4ipstEfOV_HDCw@GG4Len5>}JDl!{rU-Q$EK@zl}ZFX%K8J zKv{d|W>J`BUZvX$l_3=jH6?r&B>YeYT{mnjLD%k}17X^FW3%dkyN}eXe!|9>cv^)^ zUhl44h@XqPnk>rwX$`5lmgCje+gXbpV?igh`OgYfm<%C?ZXXEp9 zYriu{{herh1J>`<-94Q_+KAfFrA9V(lAVp&N}jFXsLIh93Smjs4%OdhHiIW>G@w@* zxKK0uiu!|oxN@bA>JUGdMF#2nNy{hhfne`qWk{uLoANKtdb3?6c|dQDeO0 zZdnv|(QlR`wDR4AhV%P1@9xAmRqrgOU4kv?>U}-15mGOO`7A;lp7cL*L);^aPAj7} zx0ZJ-*;1&kG3$oF*@N{gN_1Fl`jX{hqjGQ;GT6Ecm||t}4uwur6opFIq60w~cL_qe z<;#C%NTvRmcj!^ryCni$1{+Vr*j%Kt2!bq5S9{*J-X<6FTuh=3LH==3=D$;O9Fr9)U#j zP|S2N4@yq$N8Am^vp72Vz!}s&Qn8Fi23uy#waKP3Y!kw|TeI4lrVTG_gU zdRsTM_&Rm4>;~l_p)yFK47RYP91_;$ZwNp43{r7|^(+RA<)fz;x<2zSel|Acg0^fn zoKAN^23xDXEXSq}02%dSZe@_g3DP;PCoboxp0RBkpPWv^Hsjcs)HBQ4P3-*Gabdy) z?ly$5gHydj*nGV}=&f!BO(Vc|8THnt6A#$fy=V2LiI7zr9OUL8q!9K+gcjM?QNt~;-sJPrC zoH$CcxRg`fdXM2ufJZt}h{{$x^$ta#8K8XmP%dc;33iZtlpje3hv5j}qwu^4ZwDwI z?LkqN1xnc`k8xtrW~gR;e%j`6WtFmpADcmvRQ3(bheqADk0bC6PWx4clwg_BtMfpv z9&aN&&N(LdMsSixEP&)XjS!8R`O3(~o6kG2L zlFK8?UU(XTguWoTD5zSDr`ssTo9#}$xd@pGrN6hR8|oQZT(W`c=2H{-b_L;Idu9e7 zOsy-&&)~84aQ@-5Y!v6z8z9Kx;LaK)ThGp*iv|~c^;o8uSNfbRE~wVuq+^CHRa9K_ zxlUw*vv??<*7IE0I#?UOS;fxQV=8gOx~uoYo<843cL*wBH&wh`B&ru=P^sq2<=YR6 zS*GoZJs4i-LY2|?C0h@KR(p{PpWgVM%RKfOrSjHZOrXojL^e+927QSOXFXS=4L&|% zmtUH}Cwh`>{#n9CNBN*%mPHq>XUwwRj?^Nx$;)j_6ZO{df#|rMo_#zUw8<;7sGx&{ z2zP?FNtMV|@@2UaFUVJB=m$$7l*A6|KBuJURatE9)r^uTL5c|L=hZGu1LGED{k+DB zY;e{O)syG78Enx~0I5_Chbrv|U~4JLPxd+oDiM(qV^IdzUq&5I)7HRFaj(xH>^3?f z&Y;K~1D?Ub<|EbLO_V%VHA!*w{e~J~~f!`~kQgWyoG%Ev|Z zfB29Sms{4H+v#m#z?M5tj0G!2KI}xfza>U2uo=ik=E*t2y5o;Hk+o+w))u0H)Yqs7 zmu2%}w%tdw*cuxHyd|m06CZP7gH-Thbw8zLKkh=1j6^J3%Tl$SpYS2eD2tL$B;h;y zWCo))!jQ{6wx*>z(toOdWIXn50;NCl@@X&1!C8t_%-}ONzUeynQX6@ibF=wJ`IXKm*v!R9d9}Wf#kgLF zTV2>6n=+2L=qf`hVb1h;9E1Dwixj#6>tae4zm&z|x{&NHJJX;~tedHCfl zLNVfJaJr82y?w=jPPI`OT+D&WpZRJAVYskaw84uNDA9`eb_)b3}W{v@9g(H*ftyXW=lPBOMc&nZD6{A z@~{2Cg)Yj`VvTV-F zvPRk4KX;-FmpAq|>YHx=!$wGTF8f6WVI%64)zg~r;Qlg$V=an0gX?ZZ#m;_}!8R~G zMo9jzGpN=f!kI>>*ues}Tv2!EZ?foCgVtbsfh(u3>F>bak-v4KgSo(snNEFeePH(# zC3U~cViXx&k8`ukh*wH^{km13EO5=f@O^R=SaiZHSG`77)@a?ZzWFhqb9%R23!Xxyz3_AJX z+pHGl${J<>hV#(Yht7fbpLmjI#mE zCvAZ3D*{N4n=Fr)OTM*kQ4xE(_RhATksUS=h6ns? zv>r90l?@m#dun47;P{p~p@T1yMF)-0Hg76%IY)JA`@ak}#l_8EU`67)Xck{M65t@m z!SRoZGN0kV2kK<$e|TZzHaepTm8#kFq=U0HFcnw3v>S;C7}+>-#?^?s^D971<^LWCstJ>u(WNF2%X#MRB_i5%V8w-`Qc6l31TTOT( zT(?C>jy>MoUBIg=SopAK=mr5T51**qxhq;Y)@Ap2>*0~_j8XG}uVm9d(5kOPF*F5$ ztMjP!U)iRAC~{Qv=Ul(%%v3-Qu42|sGKtbw zxTx+lapdj>W!+xQ#3Jf#W4nbP0)kV{um z9>nY0DCEs?czHtgQjlk^NIX;|{{|Kw^dHLYszEa_$kPDcSeIIF!p;^BMs}H!={7fRFHa8}N)q zO`NT)Pv5N-DBsj>-PnyNk?v}>NF>zE&aDQL(I{#y4UYkHChAE`qmg~bYPHEBOI!5laqPrQ}x+5HR*R@)Fntq;CfTdPaeDTmiVlwf1iD3{7+@ao}IfZ zyDRB^B)?FPaH<>2%?u3tqtZ?<|eLIg#PR)RZA~- z2TW)j%y91tIjP9INh)i0JJc%lT^io^`V z-rU;2aVThlSF+QtP6rtac~Vm&+*^lwYoBE!Sm~@LY6S0MR3xBA4(6aw^*y_dL0@W5 zpq~z;Rk_VVp@}n-s@NQuC_DUYH>R0&nMs72)enSyb}5@+$cqHBhmzn)57w{|QMvmS z6A_q|7(Z`Q#(`x*<97ru9l|o?egKvk+{mmM^(-2_TqzH=Yxoi5T|gD z4;wU38&F@y#I!YYMP3DRe_I3HY!oYXnKI?Drblb@lVT!c5}bxV?cI6;1U70_h1WB= z7vS@H13K8%vtL!)9mw~P|1W;Q^i-|Ys&7=$CPh=F zx68fum>0>4do5*R!QijHFuvyZ*mam;b9-ZD$xD9UDSJ+1Y+VVRFMmuhe)F%T%66sl zO@40xsyi5{lH8E$?OD-Mc%mPS{>(*`=bwI6a0WS$&)0<>o*n?4mE*_+VE zGIdDgj!#V-C>z-~k8f`{OX;4@=ds`^pJiu}E{#gXvi2D`bi+&v5e0;6=}=iH`z;*Gh!C%2 z3ItpKP`yGANECJ!olBD+G~W{_&O<{&Jtvo8zFnhcpB;>G$}H>Tns(t7S4g6jB+Q|M zfZnueG%;s$l+x(qG17W-r4bwmItS`-A410(uNgpW>YiBDS+MHE2KCXX3uz%37=mHFvGoO7 zd0ZpTKC3%C`7t*G>QheY@uG9mrFecPFWp45S<7bgobZrL8`Q_ZM}d~F$#m1?Wm6~Q ze8x?^7)kR~_Yc8d&l+^$i4wr$)UoVb=gecn-V*4~dFZDL&qbM7`4JCg`-tbEPsR6+ z8uVv^&P4qbNF6lT$*P$HIka45hY~yU#rgo?{;?E87hif1dOi`H8imPq;Jr9cx}%%731OFf`g$ARxvQPy{f^HX>l8GOHLE99z zwk5RA-Mk1O7Ka;=kFq*&mNcL}bN4J|<$TXgcZcU(+!*N2>vZFN2EuTmP3Dq1%67Yl zPBYn6xy^ZbOnD;eI^C(Lv7x#O18Lk}T}c@)A{6@^5=Ui@Ex0fxJMTn<$}<-p%I~n4 z;1Quk;~>t(2Qg6&=5gR_CeL}eJ;>!!2FGZ3AzYLXTGR?Xz;Lw!#gia|MH@3+Fi)sl z$K?!;D4HZ1FG!<4Rtz=N>N2v!8wv1GU)fH-5DFGl^O^^tQgrrYMl2<;C07|*Q zP)CpY-mQm1c%`Wj3!Wd->k$|4iQj^M1FoCxx*@_xJ>*7xCqNzcg&HlWL{pDZ7pXiK{!QzCg_^qITHqwxbYU5*$!$K2eqUQ3h3dx$t1I zc|}P2x{XR+Gdw_SK2e!H7ubjniBzo9wr?#Lo_Ovsk4@}-BYdN68O^}S8{84ImY9G<&jC-C7u7LKSHfTvIPAk;C$ zzBO$-*xc~!#%%$`eKRQ9oit-dNk#gJ`LJgr@#G5c03`RzAi)4` z{*4D)7+O(uT)5}^IkapYO3BDWEF}0wNzbU-mh_YIPVGY-NTe&y5> zNO@2+S9TK7f`{99;{08SA+IN!@<=|yLAlRsg}o!xd3~gbqi?q;_q4Y5s%3tD0=;bzI<$!djH3fFO39}$7PYkzJWeAgpY)4!R;^u3tc{9pKhdP!Z=iy|evdqy4eNiB14GgLr*c-GY|=Nq zFqApN<^mNpdWwlc7?{fIqJzk9Wl}fAQ!NxYW!Z(oHXyiqlG4;qvoR!|9-R-l5fJaN zr`rfl>Xj2k+b-1ke1?TY^Dt4&;7caKjc1w|bdORW3Y#q%9!-g_^k?~z^iUXV6a+t> zZK9aLl)L_~QZ^FL`xv_iKF2~5+Ea9N8ObV+LZ#%nHWr8;mB$szr}sPyiD+S-Jd@Kq z)kx0Z^TLyhiajNsZy_-I(bJp4l@Dq<{R^Bpk~DSbmmceO$rK1>tJ+30@Q$K0lq;W% z%J+F;zbKXBO;F~1NfT%MMJ6WQTRmW%_hLVif^{AhVSI^!VyV`wH#Ryr+NS>_&OTvO z?!!w>Jc-}9e>mK8MtOi!SXv%bnj0A(w|sI-#aUjiA<@q{B#6ZtQ2*38{k9{7 zyYfOYEUjxOJAgXW3t29jU3_NnjYFI)$DVUbrY#KB^zdZgktW$lpj!p2z z;1MLDijvEDKO8QnhKE(kp_xtgF+lSg9Zft`NF|f`O0@N)(eY|EQ?U)3Y*uGOE}B;H zKO<(KiB=79qFS6%4_6`-zYasmH*_coS}V{?3uf@GK-WHzay2afRh$G3?{Md<{S^4} zT7xgr;=tkMX|6als1r4)La5{7_0z$bY%#q^Ps4`S@qWe$Yw!kM4A!7Ejdk$Y(oMbo zUnlvtF%m6pK!>z7(GP7w0Ga9Flq1!o5^N-+Q5zoxVadNfMggT3=f?^uIsdVi9=Gkr zJoRJLZT1ENi{tjFK~}Z~g?rX1Prw@m670Az619SsYB~vaV4PfWFlq#{BSQ`Z)LrqW z1i@Gr-Vx9%1dL|cj3|pn{YcQxkID18z`m1jc4C6gg5@G2Ziu%eh$iZ_W;246USN|f zf>)bW59ZUaa`T6Zy1Z3GGm0Hvx{z<&9}NDP_V_h1Lisig$5;q@tStsqm4t+{mfx-+ znOUu`2eWW)UUy}aV!`3n$>D>N6FR4)JAC_~1oT)>LE_n0`e#1oZL=VS#N1LZ?{Zx%zULly$zozH9r8v%6-zb}j6 za5J&>3zrLGKi;n+QAT76ARgM5pBvPEd_c#boKz_W_u*jg2h~&QgEoRPc;9;jTGPiiMhaoE!_#XK(hHj<`na=#5mbiN#c4+VRsg~G6@sOi zPOv`J=;E{`t92#@BPJ(R-)s1>u{w0*f;ilyh%p&IUF)6-8zDRz#m`jq&@P*ER21U} zhYZ*Yj=*CBi^Kpi zvwl*x#T~kHafqsI`jNml4b4TdcOAtcK#R>aD-em-+>cydRVvC@Hl^&YAZ0%mSdJVD zma%7}6quL^Y{8J}o(UEE%xRGbQN@javcLpV?IPmNxN{ zHPY-;O!@zQZqS}fWFt8|CDZ?fLH|f8^oJ`-*vxbBUf}&NCH;i}CKKZ1&Dcurm3ooQ zwMbJdAZtSWs)?XeZ;Ze%PFnt8E&v`hIPTghl8Fo1Hu|92(_e~zCGCS}BDsDZ70?Bi*jr1+3mtCgeM4@2Ki5!aWI9<;HtX;1dPZjTe8fTq!^ zi!1k)?dn=ESA9=d^&gT|(>lq}5y{Fd4k*)6lg;5c9Vz}Ru#{o_2(zWN;>m7Xg0=jy zueIPv8$CZw?q#en%}h3Zv$d<*=Ji2^x{rV0@=5{L%B&31qptB-}VOGD;xh&@a7KvRqbh> z;D1n}+th-mc{1z`MR(Qxwf8NTbRA()L;-z}{8wp~9)cmTZmUkeU2OZFxT-Gr6@5s} zS*cQ@U!+Hq#Ld&uui{S$#Y$Fw)a(ei1gSk>fuiPbeQJ4IPiJ~1SpEsG%?7uglQz!1qHyq~aRfGP2i;KpUjo4g&?24Af)RFY# zCvDqya-Su77&UrsnD+IH0hM~Lct*SbW-7(UzU|FwyF~w*oe_0`_7_Mq-?zP8t$TD@ zfSOWPFk$Uu|KCutqYTpF|Bvn4M})!DmU?4*SUSlnARVpU3U!Fh`HU0@{qLhs%upAi z8sZB5xHylVE1&8%#G`gbrcgU7J4d1&qF3w_Ot@~FtcwCQIZtMJ^8)FOS5g^z&m0A% zxvcc#hS^`g*P=I0(I|H1zUAp9aYAB*bp3|AS_;pPO;Vnw<4|+7xw%pmPqESLbcB`& zD4Rt6R~bOKSz`#?6@3*ZrvMXNP z_HnfsNwA_>q4$w$Vl5{YNXmf6_5^y>;Oc$*qVu@oy0FM}#*qcpIM=Y+?d=tm!&2QH?S=p_)!EValgD&zs6fNk;|w=%=(TP$Pm}FQFtzq`SjR$cKbu;giGCO4S1Yd z;QEn$nVHBH@=7KVDl$iknXJg0G7e}Mk4U;UBp1<-Ni-$BMa-})|LZJ~ zw9_&wy_LS>2I_Z=o!H=)%$CPxh1)yyIzJ-{*^N0x49f+00j<8@P(#P& z%am2XuJoG-+cYH#UO_~w`$g@4xTz%A+Wikh=OphrXj4zUndCb(IdRCTQaTCORONea zv~m*+I5ck?7wdYI0$q;+NajKz%ZOhD&kT>c%Uy9^I><<2kWtbW2UoS{hq=);bWRvv zYR0^T_-`KaM^`)awhId0qCS9K6(SAdl;8_CZ3oJ57z|nkAl_8Gg=DEVy1_$^bm~sI z)kHjR9UC&Z?T9SjQn75@W1H1781qjxEC)Mp`d>~(;nY;Nn$$AC6#E=+HPAlodd&L$ z;pPgh&BachTgUB-yGi>WZW?AsK5FyZ#4XRb`b~FPzZt~&_PAy97PSH`pQczA@7y4F zb9>mXu(Z_L`4npB+r}*~RK->iimNOmFPd|n99I+dKXdsdSO}f^*QJ$)DIyG*cPf zk!kM^%7}OD+m&~}Q0PSaDupzWas0JZ%Hnm=vn-ayv?;4yvv{QQ=-n3YY$ujs$x`psml|STu+aNc=G9OX7w7hDcB>7cZ*|6+ZIa)_9Y3q| z-nge8{Vz;E*<+z)Ht_efWbEC;^II1+%oyAROF zS}120lmknBmV>UdvQl5J3x$oA410s|b#9_KTx%dA$HYH&DY-mTA z_|!4}F!}6Yx-aN0;mg$!F~SF=Idh^p1Ag|W0%z2JjJL=Fv5N$Ok^WiQj+SicroU=vG756AkX#`;*F$f}0 zFHCC;<2AV)ma`X7j+@a4rra9!aty%r0IDf3(CkOYwuZ_c)y5cv-FMLqhKFn<=MV`E zH5i5M6EJIh;QIsgk2#G(Y-<*MWDo?~6P7wk+z>b4XP{H#ozAEu&MCCz;;`6& z5X%)=@)THUTZj?3;k%)vv&xY$FZ-4i7;%rq;h24Kk>ufEbsYyIKMpKPj(8pBNS{DT ztjFk529c}_$rOO&LX$(L%gyGvsLwIERBkP=x$)ipxMA3RBP=zv`b^x{e0P;Ym$)zp zKSDq+y@yH?ywa0<#zcC+JvD;fmEJ=4jP^-lhj3d4osqehLKD2vp0jmJ-7el|T-2=R zov%?kCw2*5V|UV4uvOkYIZP)G>}?pAK6Tlo3!otSAh$O4bu= z^KAvRS9(BsU#ElpElAzpuR3*8z?s!6=zyR#%K>L3hCPD7@&jm5jRP58bvbP>P$B7-m z&RAd!YbhMA8#`S(pgGtSU>54?6m&X0RZy~H07q==;)E-%bTDI@(E}eFHS@I)S4@lMAM`Te#?M0=1vktxV(g$hRuQM3t!`)TlsR& zo6!+$Z5}4p+Pk{_meYuCS5C<%qH^<4BlXF)&(fs70DhY}3a!&%?a(HZDQr)0o_*x0Cd&!m>xv^*g|Hwc<7oy4KVu4N(nQq-Wbg0|U+5Hpf&@WdQXm6`eC$$ErDYhR_ z{rL)&A$*j&xc&jguvaFt6q7eC^E0t;>QxqwbYcoSLO^eQwMIhEpozYk4kXt&d5uCs zuPWBWc;XiqC%`MORal~0ydbZjYUgzd$q8C*P2W6u#>wkdj&`*rCv8P}#IjfY&eV6_ zppi7_wpxZ$*fERNG2W=M&;uIME5+0wAYR|3arCCt1+@+ubKe{%3GOb=yY>Ki^)0&f zNy;KCbt(zdOzPSF1ysgw)k#tnrECH)KX{u?P*nW^7~ZZh1ZTt3bdEgz@QwtALZShz zAiXnzAzkNWdjeEH?@}n}w*KB}{X)C}dg;3@9L0D9?M8i%O42H3zYN-W_}(~ycnPtv zdMA1!PupE4ZW(%?Lg6{9uD5=uzO>}F3SDyr`~KdaKvYy!ie_;iP&w+gln~s0i=y;{ zafSnq*R1=4mvh*44%##QA(J3~eFcoBA6D$U4X2p#9L%IY5+{%+0!3XhaB~~K6)csn zIHmbfg@F!7R$Jml*u3j2&?`Tt68OWrQl~4nCy*}xagC!u3NW62LZjepYh&jJORI>N z^ggL@$SaZZjtNlPd@64Je0qO|mh+0~pD0IuTA^@@S|0&9>N6@qy|w8T|7Q(?Vk#;$ zBmSJu;EOk`Q>ANmhCn0M=i?j`y&EHQ?iI-X7gYPJ@*(d{wEsoLK3%?2M8yDd(wB7m z`HxdzRp`sQ^|YIC*fEG|`>*H}y&K8~HwAZeKo#s)Efj+qgv|g(%CG4R)%fL*xLdF} zz*zNlg~MI(i(5Gced8M{1-)CBcMXR!(>LSRC#&Ak0;*UPkKa=4J9If_F(V~XDZi~U z(3QG*w*We){Try3_H+ydOEe=0W zNa(fnI&G~PIIUy*0%XM>#tFnNajBil1uECs2=NE7>wLg9%EnVniO zyPm+z>$hF-LZP z1={r=4C^v!^=?r2LHh2QUjs~=b@$V#tK@GtsKw0`fDuMFuA?}>)cmTWE{;N=U zZmqkVej6y?A0RUSmq3!Hcwl=2^v}yQhKo}rY&HRCk8Kx-cTnUl>huzf=LQ5t)pclg zdQzOkcvvUj2t^v||p zjL!k)9#^n&dWq z>xexxS5ZjnOU3jSG#9+8Lg02+2QEIab2+GUuBKS;HVQi}2NnRlCBGU6nxLT#N=c$*6_+9~f2(EANq`w%I>lajm-oW6PZ>2rn z%H{xB^oANoqfyLQhJ0}&g`wV9bGwf32Y2TVD!SOdu#DI$OI}pw$0sgp|O490fippka*U8Nlil*!O z1>KeaJaI|_L%Ly>+a9R?yoExMrtsogN9}M+#d^EC93+eUZ#V?)aH_$u6>AZTiixvSaUq5AoUmBQLx>hpi3(nvwpSNPBo}QIj>;*!~ z)@?QGh1^;U`sV4T{et2F>EO3hDa2YpQ52xUJ42`N3z{2(I^y;UgXj=+E->{3fZH!n zo1B?MQb_Ma>!)`xC{iKtXF{Nl+)-ty`^#$uop47h*=MN~UbWuJf8+r3!Lv2{oQWwn zE>XVTp>nJ@a^Jg&T46}BUQ9KK*3ow+*iW@U#qJS%EDBbc=6~lTP{?N_JAtNF5RoN! zi?>{MB@hKS;})jBgO!op3XQxFCH8>g6C>z!6CDmGD6+({)7n#~~Th#!@f3P;?Pd}qbJ+b(4PKj@ox(d@eo zcXd4t1ltQJkKa|LpbLaj7QB24u+!jf3WZ0PO!cn!Oe0h9NkFUYQ%M|Wq3YGEbgyIX zv59(VNu{V)9PwaYVJ%}BVa4f)vPROROINpIjlW{!$iH2HH%3$ndat}HjuNK1+K3;e zmMTNM_%s^lNWTohX8?Po#uSEjjXWWRlR*u_9?ig*dY-~SPskTGLq#ix`*jK-bPsX^ zzb!yq9?%KWOIU0UG^UToITjqxcTVK*2M`bEoAwK;^w8Ywph8ja)Kd%}++KlNVM4Rt zE#L(*G&7!z+do{d`T6g30sAJWRO_BI;&xm9pfKe6AD#1^)(KK=b7vEPbI-D3BMx;h-qYcerm4NbeghHP;?HJ%9Zh0XtI5QTIF7SyEH`2 zxwAeAQq2;5|FPhYlhsyrRr;Ykc4Ve9nvDb~5Q}}1Mzam~dBv_O2`* z&Jy#xE%n)L$XY#St+g7puBe61&th{ps*>(~)ot4@yKLLGiIcAV;5#nfo@`zm*`ymr zoW=-wmlXI?SOd*dn$J>)S-w`>+j$gOUKEmvW}Mx!=%&VoYF(^2(E7bG#7r(pw#k5>oZq?@2+fLK~lX9G0n5(uO%5{`;Vu8RNXVAH=7UYo4fl$~! zK{4rSEaUXn+KI>`6 zPY<0F7ItdZYf-QBh4mwIhvaJOh2?$a!@DMzNFr+;JLGg^|0_{(F)n^U@~!L9vEMfR zVO<=F7r*djRv2}cyTMe2DvxpcJFA}4=}@$aH^1C$GsvNH!l6;6cZt3m&mQW~0}EaV?@R)TFf z;=h&-9CE_50I@E}&dlf(!UHAgsN>a-IrD;Xt=e3PZTFDrAjf=wbg&2MEN*NI{2&m% zS1e=YK5D-Q8zgS4qdrTMI}e&CJjCGfNEeOv@*>8W;ZIFxj@f^wyTJO3GaXm4a zEAEiJjq~*J@1ygR}jgQHInn`9Byitu-ga4@KZFB3H6bT+`WP-_or$slTK$X z@(N8Gj!fyru$+~~{#vkBm>_O+6kwei(t24xnKrg_w`L|-|IE`gDvAj@cN!!J8%{(vCC{z`0kVDv+$1*O6 z8P<#ABpVUBa`nqg3>uEq#r6xBUA@#KVM|?X9#E^l%;aH7U0fEFm0xbMaHKAF#Gu!* zUtzFpMC{^vXAhVMztSM$id}iqyh^8Gi(PCQOZFA|@vC(jzSzZ&9>DV&gQxh46vfVK z4IZxGm1k?c&ZOZAUi{Xw?9mc8OT0dThb?*Wi9r0k!6f2}UVI)fO1x3$Vai_I=;@z$ zP;1l!)LL)SSu)68TrRX0@n#EGag{teiS-tfh9P`${eno|s*|vUFD?n14Zlq%VG3Vd z7UY$;>nt%vi}r*j316`#X)X!!$~$xtrkuoOLAzw$sk5Y%lei3Mcg4GOh7HI`Tpm;v zzuVwp$w~Zi0L<#%qqA@YB|Z%pmEM~~!;qA?t%Q2f`wSMIq?9Mi`%M;>q?9Mi2TT@4 zQp!8iq1y#NXt1OeX}BIi{rN*W1y7{m_aHE*{IJ5Z5t$}W&wRwB;m9<6&wx3>M-y1s zLJgk?Yl-J^YOgODG#O+YHW|`JzicB*E!=Qh z4ONF)`x4M|eyp&F zb9jZHF9Z9teqz#WnV$301eUGUbAFaUv~_yU&l702O3(R)$+G2o&M!@-E!A^=W$5N)NN^GAbcEA^Z|88rXD z=sAD3k!^{d^Oppgto&s^|>*_iUUvlC{58%0m!NZoE_&i`W z;F<;xS8~d;wXS8-a0Ms6cf#UFJFzWJeO)_&hb=hqi9q~Z$0XtkPJA9PN?cdxVG2&% z=s~BxuBWqPkej$%s3yI>g)6n##BMQks^|tL4MS|=`UR2PP$ywYO0+l!m<%jDNoPb z)}-NxN_@|N8O-SkENoecPXzS8+a(aOge5)=7%9$3py5bM+_*t!)NXIGY(!e(`v=T7 z&or61;!>_WcQAO^@)DmX9KJRp9~HhH)LQ?Bi` zBY}r2F|qvv&ZrF~@vtQ(HW7%Roe4x-iHXev#*1?d9;U>^jvsVJZI?llL1bc+p;dt0 zHnP+*6SvjS+;ljBh9NVtJ%d=zHCR|e6PpFillK@bOsR=YgFLg>pkawkY!>91I~goY zxrt4K&Zynlph+n>u_@3QwYwM;8<3p%M4(>2t4YKXowzv$WxTr?EL_ovO#{ZMeTg&- z(TU$qfTm>9@MNc4Y03#SEa8bwgZ4C45@>Rkp4hx7GmZ@69V9-nY0#dgQIjUE{KV!! zirkpN!4sg^eII1S^I|NUqCQz}x)EHo5qrT#o4{KPQw?w_|XG+E-`r6 zau=TmXege+!xg;pY^{z-!xg;v-T}I{pTNVGy!b>Qe!3y?`&cYRx_%vWtdQcJ#LsH_l63Q(PHduI)Ql2aiF%3?lq4RuXu~D~5%kmq zBCcq|<^kiy(+nP_Xv2;lbQ!pRsrJ1Vd&$^9zZM;=;PL$}M@yggt<$`j;?~Yuwtw$98`|MMD{|xQkJG^IT&rpAL zF8D>5_E64wrG?CM+m7e2YfRBkGtdlMW)EkPpI#+t&a1a-v(9SMY5C%9JGbS#UGJPx zc~kv?a(QY;Y2&^awzSe6$oi}8)^o9at|h;T{kpH|+4ed&G0km9*}{ZYQ*U^H!6lQ*U)U zZoOuLDB;3wAvE`Un?d2Z?op?fGJ&u?f-=b44VLaweL2Sr5)SR!j^~{e8}CT89X#cd zBY{OD6??AVX%cwHoI*x3%X;1rQM@aWVn9H$qX3;udUqm0QSrFj;)~PC7w^$Y?18}d zfMp$1>>GKnZg-;IN)rg&yfAEeA7%$Jyf2AiKp3#?TYAHpWdHq1_Ve_=rQPpLwm*<$ zyH{El(H~J|^FhEepgH(4X<0!&mwfUeogtr9y@7vxMzn-658uy)MZ=GX)s8s6sBo1Zq5Xy$% z2>A}wYc;2rj(ZWb;B@@@YA}2Sp%Ya8Sy=X3YN-kG)i*9A8@UGpcalrCvGO7^X6Q4CgFz8%?i-F7dE`vE*5 zewgXI(OXpCNw7WVG|6I^PlWy8Cs}-tDw^*`3|%rzqf{?2J9A!@wA_C3r@k{G{z@iq zpi1|9A<1O5U2$vc)EL2^>Q1M(SQ+^x{3{9!e{2NW_V+`YDc5UO8}&<_8m*Jks7>Wn zY5#H{WX=RDkpw7L=9J|aHVvwMe-P44FI`NvT`%L#t$9wB@>duwLAs4Cin19{Hv3`3 z;MHkO)TxctD1T8@$UY(%M9QCz|59llZk>R7&W}QtUJI-*R2yBVRB_!#_>-W*n<*d3 z)N?GEW0*4PkCPcj-R@Gu$(O+s0jP_=NM=}|sUgh^p)1%ef)xK>h8%MfRHjn0IN*5Ax-TRO zN*KzZ&bMpXLvR}PieDw$&A}?l(!Y+Z%EVk)mPX_GZ$gG(qehb3%VK`n-^Wo@e*7&U zkqr^!D6XpbT|`mu)G09s6IezO&W=m8I{bS{Gu~`d1@7;6+$Qb2qH|GHJ(T6}>C*(V z<9nn9Q2E=cu8Ln}8P5^RAFM3FWHxse%j`t#x%p$j((2G|FSV>Fj~yA8fua@`F-e4- zH^FH7r+|azh?R9A(NM-JcREgUsX=?t=)OrgQxj9jQt4m}fgK|#XZ~3-OjJ9*Fw~=% zbZazg69@;h!~;6n{+GaRy>*o0PJZL0UvW?LBeGN)tBmcVB2Px{Le(R=!7r5##2mqG zi(yM!oIx@1SIINw9FMs{X^c#sK$icOU^&(R<#J(d|M#Bd69u$A(sKS0*p*Wq5%UG@ z`m%T6+yBs6jsF-N zMeP7YJKp}QQ?QR&vwdRO#c?N!(*Nl+tRvQJ5=(_EUgx+>C*d5g=EtigPm3Aow%L=n z?OP0F63QL2z)G=D%x4IN;Dp^~CnYe*8nd@DI9m6DC9!Nf2Q2L&b}e6S06|c1E9TiA zK%@8N6YT4&6ob+jH$IVCbh5_7J>H%t%N0}>_R)4Wi>0@SJ+oI-S;pAM$k|bYW{X!+ zX_!ZVxqdIWRxU{in=xt2;%@Xj$*HC|gMVW(NPUo5qO zsMW5c(Xfv8=9=%XtCEO=y^N!@Y@dKwxt>PCJ4Tx)$@O&-)}d843D~E81C1o@fmJpG zh}9da47|gtxyI^^G?IdMl3BC~u~+-XF_KZ5;%MXl6iD)XkASwhNsMD&Otm=D0GkBe zpm0;2WOHuR0(JGxbRzDAgYO?eb90S`J>_82pepthjfPp~=gM*mjfGjZ@mVabB$SI= zYAh)WH#Wl_%dUTMH~py^!)Dw>f@Xxb(s@|L3VS57>@O6jhHkBq%<@VEb}Jz*^fo$6 z+M$_G0cxi08U=4~X15I>IW11Y9*VdmXvgqv4U&xvMQkEV6(?>@J>4MU4n=v=+)k%q z4@GPm%S`z$MYCwfbAEcubgGDq#Xd*6ktR=Tc_X+0PNO5b;6D~i@X||Ga2D}1&shh2FX^B06Pt$ ztsDW)(P_4H1lXn1Y~cv7TW8sV5nxzn*y0i3T!UxpMu0s!%NC9RdkvPY7y<62QxrS` zpz-<6F_ukv8vrO{chOlmgAspR1Y`7F4HDKC5jG1_>F%bpY-C{M6Im1&vHHDFC*lr_ zxzdz08uq}*rvdZEvPLs@qUig<(M+JC(QKt=IHIvIO9pl;p*{Yi8cWKOfz5z4!!eCv z10@5S2h9b}(|K4W19vWfqISPV!d&3wdj*UH2XvM#)Gx<%mbAqS-y=XZJYS>W6))^K z0VD_GBwIRroiIo?GDvfMgZ55L8bsVdI!~G@orXO~vuS{SIjz$$haxTu(l2LpmXt#g zmjUUQ7w8Nd7>c+&DE}WacvwRbJKLf8(X7t0g|pW=gC*?%kZ0@6>lC~JfZsY`-E~2- z%+!~UcH8iJj^JQku&rG^rpV{9D9k0A86A%CEWo3Rb|7wB4ebcGd8&7-ED+C;fJa=y z*WcQ2nN^R_1@bfEZ;EaI+D%+T!9N+!hz$*eZc{1mMU)pNQ4SbDi>3{S50&;JqP-}I z77VThlkNx$B$f0Ck={Ll)II~;I?j6}aN39DqB)aWUffi4&je0$xGtK{-ro00;Ij`` zMRSHCi5i!O$d8LjoC!l&(S)HCsS+M0!h0tWT8B9vCVRX=J6fuJ&goXtkxzv!mcH2( z=-la&NfAHx88Ku$HwEf|WsLwF{fua`-mrX#$4m^2^$Kx0zmbvlSDG8uf+m| z%YyDOUolzC!>Bf=up<`ro7H5JBBC}-gW7pVvBzjFnPS>}lEUT`ZtI{~VBJQeZj_dl zVQxI2PhV{CSf8b^;jm){?NB(H#4}U%m)BGW1^dWtNbD8^@zXGfY^zjx(V!Q1ng&hM z!yOTcG8nME0#>J528ZQPb%c_2BtZA!xjK#QA(7l@&>dfGokq;E)MMWHaah_&?Ag8~ ziKB?@8y3r2_8lb#FNpyhwzfHj8@ovP>Lf9xFC6&!E}WEW{wa}ve4Qoj95+MtfX1h; z#)Bl}yxIz_%OBHt;M?XlAbyKkPB@jR#g6Ml=)7cpL?DJv=tSUsa~lx1f1x&8H+aDN z<~AUHn}riit(T2Zn_X%Wp%>2cV*+t>AAr~bH>jEaFPV%}pz@pY(r&LD$iC715O)5g-ZAs%v(e|f^7X*jb_05 zSC(k9%-zKeeveC{um_VS@NoMWswy6@lO#TPmmJn?4ol50Znt=X&XM%&Trvi`ccE%!sgVf_E8zdWPo0W+7UYBXZc zFI{pUrR5Z9j?V+K?$Z)@k}p}Zxrg0Wz&lUZY3$Dj+Hi99&NFl#>#k@U4%a(qHSd`Q zjrOFV4I)>cJj~2i57%OXeu7o?zuXk6)b zJV9bsFonW_kGr?KXK4pvk|=LA%*sEJNfJZc z4GEVxbM2#^Y?%{?+xTCWMAGr?5B;+H2$~taJjuGb-zU)x z?Dnu!azbx?g^eTUm_XUyzjn zcDz`6jhIQlCV?htCrbd5O{YRt!D~$p+uo5}e1bB`>r9sJQhhnkOk$a-irpfwPqrSs z-k2u?(7oP}z+mj1$)z38d%u2?00JUfS@J=bqdq!ZhS*I$Pdr3~;R)0Zw{M~H{MQFOe&X?sKW3&SCsgR3g$@;OX zUq2?Eh3&~5hh6U|JuWVHFukYJ+QY?t;s{({r->#I&@&a1XMkw<3A^OH;RuavU1 z5b9B%4M`@e?TTAlr{)R%RChWvWzVb9{xt=QGA4H2e_bbGUZBVJiY1SVdEYk-mT>7mw#c4K_C$KoH{%q+ zO>wrPvW2sJOJ~`_*7>%YEo^GG9 zXw2=e!g%qh@ClaJwCT9C$x5XEy zQ%w9uC)p$=_qV#;l*`22Xhd_O-z6~&2m`i#OK&)neE<6-`+54`((dBr!9OI~PQOIV zjzLt}{1LDWXpUSt{-krHUn1snfHm_!>l}E)BCi0{X{45zByt!R0_v@h_cYz_Kvc zHd+t(w_%&PD9nxyj^X;{jXs=)sis#`_Vel{|pAsk}%gBXeIA53kiQg zm`jDS$F{kXx9wY;>uXrdEO}=S%S`}VYZ zmM^bb#uHG!WuPOTY+4>P{bbwUZ5cVXlh3c9*teJ3+;~Gfaj&S@6w{m>?SfXml45tp zg#`=S%P79CtXLi}DCTPUDyro=9E8}G(LDO9((+=%X&qi(t9qs5UY*Va%5~21nabkf zEbG0)U=De;z^*h$_c|+vX@Pa9G*cPPWZQBUM4X_ydd#-*)ji`GzueN7&m!yB(5=tK zot|W#-K#CLlG~H@Yf9^@jz8%%<+~8n*dvW@Cv5Z?+LyBNduw)ERJ>b|z$DlXv%UzaL>6??i>&{(a+rH}Z za@VUa)0Ud5zg8;koGNeYMN0VZvrp~)Gqii}@SdSPL;XEo!7sw*+HL7Aw;@~C6W>;? z9g`cG>h4l^WpUQIL}rL1vCrdYCCeCaOR{-=X|vIAmrG5jNhZblaoWI@$(}`35^CZN zq(57$4QH&~sW;qKY5($!=Pc7>FZ5?}Z6$Q+HvV^lxVfQWH;1;g#LaE#J8u-(JLIg2 zPqTg|H|OjIt$AbJs`-&@ukN*E*a`Z|n@D?|j^p_Yb+>(^K6%ks~?4zuI% zE~lLEx7JJa@f2`TlXHEs*D~&n6WdK?Wc#Ik+Yj}&zb#BI(e5vQ)@{^l)0L|9L)M0ZoD7Gh7xxFwq*1J=7Op5llY3DX zny|Rg?r0Hv*>019CiG84n1N-lrM@{xEZZdu4Tr51!8DVclw)Z8I0tN}rDqf6=AeLL zv!VM2Zfj$U8cc>%=&|UhS$u_fPEW(r_fjrAyFsmWJ5hyD>AJGGduXRtXw8%t4{yA_ zKvvI?ULdP9uk)*I%KNmto(2LrHk3*+=X0|j(k5>&tS(n+psCY{JgBx&2L?8WC9jB` zB4+}QsKKR6;zJXc0M(v%kOWJq(n&uUwfZkZE1|a*?r3FGeG?{<=%*QY&?tVE)EPC@yYiJ=;PSc|^ug$Q1oFgP)N4yF=2fIm<`K+!dw7BC#fmO-oM_YR!I|@Xa+8hEM>5U&PVPHt$7p*XkIj*jbYd_YZSWNITnVfb!m+yn{~8WzAJ7WP7O5( z_Lx2l);V_DDC|yP+eb>laNmAhl#GWR+a9`k<=n{LtY7t=c;6Dyes~w1-;+P4N#@3t zJajI3k4DfNv+c&&-;ZW)djsokNBQ)u5w_bBVW&|qyHjMbLPbj4;hC*k_VeDFtln9* z8fuD~Np2m0){^cbEqV@xPp|a4u-q@hvaYP`wv1h;(zo7K)SaXZdvtIt(35&{+jxeG z6R3N4>)AWzj5@2HLyN+J933;qj#*Ua?-TZF&Pui0pku9`P-tlp+ON_AC0$J@ZSSDV zM~z>VGHAF+ohpfzw(*!0vt@!CiWlN+@1XU(a^RiSNyqo<%cN#&Gmk7v_OK=D#JW-? z@Ip@kk~2SDfXb3VE$WlljXKh^?D)?3%2=~a6A)DhJCaTLLSH?KZCBJK3wC}1Up>~h zJlz^;Xc=4#Ud4U&dDwQwuSV-$`-A9i_|?hki7?S*SocD<4)kr2G-NJWa9eX|XSe{R zzCY5W7#i%r&yTRCgFz_8MU^nZnYRk_j zGLXdy-C{2Bf!59^1B=J%R8GuRS96F)R6$HhYi@U`;q;aXvqwL&H!b6*+ib3v=18)t zRH+;-jb)r`St3T9?wS$y963@9ma3FRXKtTIr#LRKQN-q2q}^q*k5(cNN&6|p7jdgg z;!Vqo26d0 z5YSK_?k_sV3Ne~%Q4sZiRVr8Jdvp9l4yo0SxF@GXe`ROJmCC&qO@4|P59h(C6jt1f zfK>D&OuDfQ74^`LQ2bL{a)!KgVFE`uWuDw%GjjceR_rdy%%?OVJ3}BH@9rG7cmuL2 z(1>~uNui3xS;vxH7VY$=6fyS_G2=Np=F-Ah_JK^JTFm$w$DW-1H)${|Cq zKsDH+u)IQBCD$TFoI1mSSaRpaIMV#@9a$44nB1P(mj*M~+!anjv{m_2s1>WyrjT%Y zl9kZMGVP;5`wEwO*2tf+VIup?6;s#!#UnNt_pm~@R*m9d2G+l2!GEOZjI%uZd+-Njd?b)SkvIbT2)=hP> z4#oBfs$|#FvxVDT>wU_NN@!vA0WH z-%2Y>C(NK>f1^dA6YI6d6K(2Y%C(4M=7fludaE8zE2v~T5}%&2ZCN}jW=rex>srTV z#&*N4-VzouK#D+H6?nschTJN{(Xzd z3oN->dqB)u;^GS18d@`dVBgxHV^Ms&Ad^4HuxmCVJ8D2yeXz&|bf)fTX~b|5&}gf>m`qXvu?PwLyuZ{LBX4i|IqCrf+v)@Ej!r}S**muXPuernHJFVp1E zerWgj)5PdYn{brP#N$H#RZpVE7HIhK1N!9CrG2-i*ca7Jd?LrU2qN|wi59c1e(kbo zRX3xEeP(1YQa0iXZTxSusFLEYfM@k=(Xn|))o#e6$^t4o?^wQ2><@po)oND72;KAb z9NAB3Lvh_-qdnU-r#30VDCjSAPr&4oGim%b!wmtb&pubMw5ma`ob0Rf%yc)qmg9QVb(Z6Pc7vQn0M65V6_`R`$qV~- zr52LMccNMBi~9C*pT(lA|Kh$~Qv;=$$$rOeieJPgC;SKoqxegteKG%<^o6amx7ro5 zc2>0z?cIB6XmhdZk^UOog6Gi@1Zj2ua-U)2GqOFFEIC41@?|1R()|~`)dP7lO&?|H znvR4+5K7`^w*z|9@8v2}a7hsDAlM8pbmHh0A=m7>zM!@^m8mj7_j0^4wCdEvYP=#x z(55_PCtOT?VYdkS@>hu{b-lW{+JIt|R+iSU{WcjVi&^aoKj zyheKanB%Q9+~aXyQ}uZuQyU|7{k7st+v4RmFv;bAC#Y~=7g>~(+?w5_!8}seuwlWZFF2r zdBxHZ+JBjFi`m9cOz*MOdlky{??iOb&Y)mfBjD=)aN|U1oPAe7 ztaH)PEO|&H2EmOm^mfMwRfh12+OWCF2MS88bmmf^x%7uZidhkC{iY}LnRkUDt>VL> z-TCCEKbXPmX#f64#Q5R`SAv>@3)i#gmX(hNUnsV;)8SSp+{90kVqSar(fd^&3#`-G zS$ZgZ@NQvxF+ApQc5XrQ!;jlZ`ooo2n$O69R+2x_WAIC{V_9UfT+VVUGa9=;8CdkY zUTe;!0{B?n?RNUcW9I;j<6;?lI@$;QDNGdZnKF(&#c~_XZwEeMGl@9*aN@FLMowm3-S4!cf9a;2X zq@{gUTBd{Uot5=E?PgTP*<=BcosldvGVwO*=cIi)IlnA6U{hY`B|sWyCYR`F;A5XN4MaAzHfW1wmQ%>zr`(Dg19&13w_(?i3?%}d^X=U@c9?}w&lIYgN(D=+cJL; z_k4XRX1n(;@_-UrP)o0zOWcg~b*8P>x?ttY{?zf_qp0CDj z_7*I&uEwHF^0l75LhtedJ?iU<{r(4At`TGRD0E`w8$H|Ib~H81csdcC*7>Hiw&vAa zM;Co}afP-`53Z)-n+)0Cj^1JX)*u?SqRPf$Q7(5;AN#iCs9!p`v{Vn!aQhspufJnq zh`S)0b#(6dyODKTZxDm2SZmOZ3vs^|Mg8}5d)cjlRfg}!tWA4$T4Qu-a@f{CksaO0 zr$2~Ujb7%?>QhS;pGr~u!^mE&94C8mnT6ekV4um4q`kJ|)Rc>?0s&81u4G#UZTVxX z)vWtWkcRaWY1O!DvNCos%X|WzYx!y4VlW`l+5EUMwogHg@iSRjb=#iP>5PzKLo=36 zud*v__V9E0ed}@(8DIF5)7bcRBQXcFzZvwFUkLlYL%R?1>n&&p*e@e{z2?lQf1*`~ zUy1gl*-tySok-~P`mg1exyIRbdPFHax?V2F1u%Y=1)A$`0_&67*>xH!=?HXXG?Na5 z_JaRb`q*vIRd3<~9Ch-WVKoWW$-k4|>e20S!R5B{eI+S1srxkJAI;-S>O( z&E4KuX1z3&Z4vBA_=B`a=OtHYlMtOGSg3k+x+<>FJqdplUhaxBnn{}yV(Ew-$3sKm zz^2~aQzhU2Dfm)xRiK#FPtiU?+WR5@MTdO#TLgojC*B|!{3?EXU@{QLH z-58QV{X*l*-z8JM)2%j=FEwQM3>0Vo5N&>3bcXpumDvT7`-Gq>>x8KTF`1v8cR)q- z&t#k8fv9XYL9Y9kv{|kDLQIVnC0XBNLhDxl7A<+4svr_egOk77NESQv%k?j+8U7=` zRXkZ5sU9>_p;0V+##CRB=0_Cp^nWAka!ES0DQ->5w(kVgDgRS!cA}?4h0z&x;h+Dl z!0u&{-C$)h{8;KgN6M9KYtq9OyF)adZJQUpR}MvN;60=i~;Ge?XoCfm$DVt~DKSDL5neWFa4>e07~f90`5 z*rJn*Gt*a&+Y1KbJS>7QyozZtd+iU#{i}*r6qluJVy$ttWSfOX&eerq*EcV6t`WDl zDUowc(_*2Kb1l(|mu_z4Tszrjp^a-l%?UF~P12yUOldWctS)hU5AZF9%T5e^4R()=0*wa6cYi*b1>uyNxc;or2 lY+I!Nhm~%q~H8L|ZGcz6L&K-uiFvAQmcNiy^%$LZ>TytGB zGG)zNGjm--BQrBH*EKRTGBYzWGuK!nGb1u{?e~42GrvDR=gcto_i`@JIp<~ZcRyX( z-TnEM%ZBy6Yr@0{6UI*%|J=Dj#x!kW*2D=}cV}f}WH^(E)*PWZ-|K144_=ycZYAYs zr3mAZ5HZ6mlZEjOD>2jUCxqpd7GkC`!-V0UX~ay|2ZTOrKQUAPy+U`>F=BHsY2%Tw zHOl|`(OmZ1b`c+tOU!zFg$Q3Ynndr-6o%-9E@AyDiCD?FV>a~NfGg4e`^rRxYOWys4YYO{&p zy%&gipFJRoKUzSne9Tc%vTGSJ%{p@vG1IYR5zpI0EcVxPBJfHBG1Huh%9P<`VvlWB z)_TbVh7ZbwabFp+;EZXaq#B#o4R@2^*2aK)i;fOj1TzDb6zRWd)v}0r~hqcB@YDq~QJGq2Z!HOQHfzk~-YL zuNZ|l5Yv&v^nB&M6BHM{k;Am*O34wkdQixKE>AHY+e1v3zfTx?j^S|>9{D<9 z{9+PbKZ(3)eMI%2+DUjWP&K$C=S0n!p~Sr1J;L#G3o(=SpN%-$Ha1Sh&4PSYA9p%&@6N7~flu*RLnby2(N_ zww)m6czU`Lbdm!se@_<1_wm7C`FpJ}wx!_tDR}V)VQlphvs9*&rB4@`Eq%%Id@fm5 z;`YpZGUsHG*^1n{n=GqF!*a5$CWw*cZ%Z{K7s#^aDBw++cHx23$gN~qkxGUGD96FL zuU*6^tS8|urZ^jo(~OP9nZinNO0rvzE2eIg2@t|_SbzMTrQpB@(*s&95-35z!(LgV ztUyHq@lq5pY~(rN|M@(z_)Td_W-Z0ph+_(llQ_)1Y!mTYaH_I$6k=@9a3$JBaWH5f zw4>N;N2Rh}L?7OXQ)eeRbYn$qc_*>J=Gh{))J`n0{**9nIYq*AoJ{Ohn^K-nCa^re zo8kresO`MUh+5LIOdQ~#=|-HzA0YFTGNKpm_rhtkQ<%1zNqn%6OkloxqJksd46&={ zi@@+B#0+n)Oc;fH6dB)MB3$cJh(#x#5U$#M0*$3#Tj?TaSpm*G8ir8BM|a_lV+;x>56WleclFaLi06W;(S6v@K-) z!v^Jw!c`Y>A|VmL#2UwVI7)GtYkEv$3|Tc#x|V3 z5dTA?FyDZ)0qpDSWY;)(@wkZJ7eo0DwoGLpNA|=JbzGW=2@SWd2DDaP}_tVNKp zlfr&%II;2+k1`O=B?!D+ru>jf0k-|1Fl?HLVs#=JcT5)M^!>yPAI((Sr;~wwv{&RW z?IbZ{1LZ?N+iu1i!RuKfI_WUV^TR01j|lIJmr$5T5f7o%V__U0EOJSS z;(07wE9PFDM#@g)@Y`hNSCoPfYn&|t=TLw_aax6_`PxRHk#Ah4h&^W`X807R7dvu1 zp$vH$74+#A`7^ul&ATY}>>>u9Upc;6sL9J1H15H7lm)!27KNI zGJL!Pv>lZHKuj1u#%5#fI3U?umlGBvUrg24El`G~QZ6U76U5i{H7lWF8Ekyn*Q%&;1} z5{z48qWHQN5|bxVF?j!(30fwVJ$p{*GM1wextt7N$3%5hN37L)+em1Yhbaz*SGEZwjtA!b6txu? z{xMz{cWglcgSP&W@xpKqo)5;=*g{a*n?%jAW@3(hXwHHD&Kbh!M#~r~do2|;pHC*H zIj-43OmB?~W7TOn?g#RG?mn(iZ1qapEr(N96S z?qy%pC!n+N1)xG=CF9D3p|X~gzR1B_pU{IGD)2aXJ}p!Z&m<36E~5#~E}}UP(Zki~ z{~@XPSG(|5rIPrjle|#z#W@8ZUIA4L7YN6*=xjjwqGh7`r4X?y|2bjWyO_k`7@5F) zMMRh`EF$siLNbB*wnmK;GT*UNxp@GY*LEOh8rXq)nyt#O@;Wqmp9v682|M)fN zNPKSM0t&G?*o9&^azF!BNk+1QYIpYv#4%G7V<0#^;?pVhCGcbEoX!;`zW!B)v3Z1NFk8e!3{+7_^oB()@?4Z& z2b3e&IuJd4TA23YjDh0xCBk&L69o+ltPYQIY&i<7$AaHaM&f?pH`KnpUI2`3HYoc;9N-uWT5n=jaG%2f)uR>w9RQP?nh?$2^6{Znw zBr<1{iD}=oqa=e<(}g{G60tz{7~!p2h9VhGd4zcc;glpQf2ywtr8J;G0nd2Fy^}&> z268Av3CO&7oRx1$7=s+`|Bz=rUbvpD)RZs^`6#mdz9j)?!S7k<84nPZIr+qVyXOfL zBA+Zfr#qn@IZIKj!zo)VM~+=mw^^9FQ66+J!TFDPEi>QHCj7qX*ksrmZ}t_%=_%O9 zIJFKcdF~@PdXJF*L2O#c^B_77{#jE+`4f|g8GF#;VL$Ipn2CHQnf{AFH<2ZXU3K?=~dqYPjlZc#oONjk9> zIULLs(T_J$R72vqwNVs}E+baC?xgZ}l<`oMc}`RfijhdMQ>oyYmkI&l_&`N~tu=yo!*@wm@MB;1*f`v1;!JeaGDufP-V0hcU^;Lj1~~<*4mITR1EAVR1RVa zayY*yVHR@u5zhsm9%leltX?LJXVEBy?Dy6x`_@yo7>gX<8!bG+6cT|&0Mdjgh#A+(gF=aQ87N+mYh?#HNCQN@wBJuV%GJ*N} zKBRGCzJ8v_z0j9f-jyg;*=_5EsiqD4vW@hGSxR0t=~=;AB{xQTR*)e)t8jF)#*qo@ zki*K!%5z)D12tcDXq-^ROrwJRUID&ID~0#nnZ$fqyM_1L4J7`H9ulMZ%E@`;g^E?9 zl!nXLw}_lwk6M!rJFUEp-j>Kmo==WfF#hLwWiR&cHs#C3RKmV$6p`FGvCxwjl{!?L z>~Rz-5XmDET2x1(t{IQR;|E2~n4u*8m_a$B33(Gm@9q{g+W7rc1C?K|64gy7i8=0^ zDvIt-A?DxJS9znU z9?uf`J7$xZxQFyYk6e$y7DmRwvjlBQa6h(Cm`-cyWI7G+oKrr=Rsi!*#BtemI8E4q znabehWCqha^Og1kWCHWheC4`ncs^W<+Kvq#E=>K6n0R0$BgUFp_Vd$4Slhpp!eG~Q zqP#asFR*KSgz@5HVwUyoLU+w*5=q-g2ZqlIMad24h*{2{0|Vt_ibb$*h*;jHMZ)<0 z4r1n;dxU2CYzHZ58htjCN_KXN+~s?TW$$+?@1LV=aJRRLlJpJ4@>)_7dXQs8WMr1` z#?y$E{%}}%ay*qn6!BqZ?Z1&>{Qpi8!E4ZJVf`?90Nl}#h;G|Y%pL6%(bv$$2J=|# z7BKZXEUdp@B4(U}5mL~Pou%A*g!JG@*(M?xUSi&d7l5{a!g(7+UduxK0(iYjcuUO0 zO3%#}_IW2s{OJhUA^cvF@D_Iv3*XgSL@gNQg7C>g5h*@NESitb3kxc~qTX81t|!z7Nbs&%@+Fc0a8vQhubd+qw@l>$h0<*w>FuW zsb8_sPdG}<=)xE=SWVkRaC$MZ;+sZkoKVSaUBdR}d}8_uPGRbI2yb+VEH8HoqjM2H za1l8MjS`WJ4(!Vga@U;|jyc1KMc+Is9HWvD?ZyCArdcV{Q3M>X&QR*KUgW6jg z=y&WSWhU~O6uI-Lh~}dJfzY=~cqjrAP}KGDX3;bC!s_xRaQ7C|W?EyJ5QG7)3fT{CmC7-G@#E7#a~Bh1_q)3d8%0 zFb;sN^zQ*ehtgFHI7T`!eBL1n4$UN{)qFdfSi#48MP!tRShNJ25Q6ts3(I$C9fP&+ zQsqjFoI?4?y~28R6^X$ZO@;ECJ4NBsdScd(n?>RC8%exSOocC?{~t^`BB~o}i4`9_ zE4ZEh6sBz8Ewl zeNl@ELJl|9E5o}f&aOeMp7p~3A{%2={@6-!)@Q16)fS4gUYO7#s*qPv3C59F?1jbz zjGDjDhvs4`&s+^j?lb#^)qrj`9fu zMT=2lLdlJo;|1=086xmo53%6DEaeZF{}n_MPi`0bKOZ6%$to0%xyOlB-PkH3cjn@3 zfGnJLV0;+oHDs@wtgM|x+3ZEsZtR6tW$jM<9bVicJf#Roivx)NmmnJhH8{_~_}e+<62^AeZ}WudO~enO=%$!3k3ralO+XdR#nIr8mr(#D26;d2~zTrg9BSfU<)}A0c)8pLZ%jMDKJkIRgaA%R{q3h zQSk*@!|e0*!t~29bnD>vn9`FlFdy0rUYA{)^BYm~YGwrmyg!0`LD7ddvSpuNmAi%n6KL)({r=2wx0cYA{97AYrR^ z3*%)Jqu|(pu^}k=IzvP(DAOU-a7Gv}UPem^(ex;G4fq@P2~&6!vG_yl$b65U%qP%7 zx@rJf#-jr}LmNfre3WdBI3NrWvOHCVnM>&5-n);?QxB5m_5)P?!*EeGumg=%xGq%$ z=cA#*9`%aKr}iMsvImh5{9-5?ju0$3-|rBStvzV8_MjEDKxtcy{m)5Ts`7pk9!Mfj z2tfjYE>sADOGNb0O2i5f8(7sKY=0h2%>D2%5h^%LqVPC{SP?2KR#+%3zoHUi7xpW^ znn|-DDR|s2LIuTm1IWjV1v;4dkar@%j@*tHn}z92oS3;cTDo95(W|I~O(_1*>*hjq(|NE#H`D(`t{m>AJ!50uDfFb9Vv?|gwj1!Br9^CIC&!VMD z!#Ttb&LLiq@D}=fQ1QSqW#JOU4wj%#iMa;g-F-;;RznQ$Tlg<#vM}l^e4lMc@L)T6 zf2|f3&u>HUU>gDlh;Bed1ABX3cQj9L#0+H7{3*9&vhuReMw#`D*7b5(^N?L{IMI3-^O{Z{d!dM@a93P;ZNXJ+J zrciCdBo&C{EfJx|P;|3}`67~^Lag#+X2Lq;>nQlnc2P4Sp8)&+-Gj=Z_2`H~@^(@6 zcoMOSnQh7w3lSSwh(YF5;eB@(Vgt~buACwAGU`!Io<~gJJmLXsMDG8F6Z57mREG2> z?+~pwRXm9|25-7ex&8!#2GH7`U`1}F++PDC@2XKGZcn8=FvX7wed8n&&mvO7R-h5f zo<%2=t!Nh^J5EPt3n1*ni4CV4E43vYLw<}R_qB*{{!n7xKOt(t{5VhrA|U>H@->Z3 zSciNanf|p_IoungA}|oeJCyxxfUwQ$L(Gne4H_q|4V@yo5+m9W`nq0(e_KN=Iwc_7 zuceXLfer`Timo)f$6ZU>c#K^1>5^{fn{vO!7F#U>s zk8j74&WgOprjn?iNqOwC-AesV+=qpe6Sg4VLjKupqG;MS68BA^BFG8s73G$Nq-d9M z<1*#d7K*CJ>v7Q4lX>zkWr~i>0yW_jbVC1&nu~SMP<|dyZr0tR{JakLain*3DBokW z1VRILiQ?;KU=j)pZ5Ix0Ya2Ph_SbabUWT?BxL)WI_Wp0*n zw~L~z?UM459e=lzds~+BhK}6qtrbc$2A$dKizdL3N4PA| z%Oi|Gd5D=O&laW~=+Uwdaa2Q9-4x;Iiy1CZKKs0IpYjsRtJ^Ee4I4?AY*fyS`w}vd zgXyH=(3+^+(L^;_zBb8A4&uzlJbR!9j1-rq1b3esuCp*}h zmMF_GZUDKBD@5sI=_Kl>QYko|N)h%)F}?`7gX)B4NU0(PW3nkQ5EDqixE!4hh|Wz_ zW+YP-LWl#i@Fh$#hY03}GFS6rQSu%ZsDS;;D&crwJ+bQ9heha01g^pQA0yMK;soOQ z;AtBz%F}xj^SrlBn1-T32|)uoqTpSM>YCM}dInSB3gxc|Jg`5Z5dzt-By1|CZ1xJ8 z<6xYD@gxYpWd#kT(!Y0$@Zn5i(FfNmw@sxe6d?@HVppaL_nu;6p&>dExE52f!Q*OA zC`Mk41qf(rgBLUFS$40Z31!H!N+ts{tTiOTIkT0A4pI=TYfdW5up9%bKi?^$U-Tgs z%-tZ$UT7ocI*3gVWoxnA0$d-Zio92Ai8-e&5s~Hdh(%W+Lc+}RF#iXM`#qFeQ2k4$ zsNA%eSnvt6Fm)UtW;~Cs7Iul(Bv!czlhq)&aKA8h%)$@Cm;F(@fCS5=Vv|MoUzQLvPD@oTCZki4OhsFI ziJI#$XA4Xg^by%&kBHCPOf|!tqVoM7V!;hFg&7SJX1aib6XJi$MAL4*Flt4Z1_z!+ zeHAoBJP;~6C8D1UAXf3I>G1GYTq? zpj!vQ*OHZOizvvpo)^_km<`v2^S^rac42MbMXcbHDWY(Rl~|Ewi>R`0AZGgRm~dTn zo>-)-Qv|Iziy%JUhAlc@c%Hz(gFwae1S~{<0V*DxF3Nw`24Judn>V&ucy~FmS)CNV zdYsaCCN?WvjnzvKomQ{hw~V5qFY>+=d0?dozrTuDsDGC-D1xmBsb~YR!I*8sQZ5LW zwyheQ6<%vsULQv;sQfe{s)nUwpfH_+=awtY*nDj3JW=Dv_z{#hpzjaWtJ9TLbyN-3 zc7)!b;1I%lQ26H)qR28CTNU#^D^X;!=+w)is(u);oZU&nd|M`_=)oPcl#yf5@qkI^ zh3hIWb^}7{U6@b=LCYbLHvv^Kn5+?HF`q4{9T)Tx@!LB|H~)yn2~;SL&LEw@=*FYl z$avLuk+)+rv5MbXL|$kCi7f~?gX`#75iLbg&TNO2QXTH21$7&0JqYK{64nW8(aTy( z=3!bR5}Xq4JB4Q;rec8c+wr3M`FX_5!_c(?`~S{~$WW|!0M9_QY9NC4oS3%{ao~Lv zEjl19n7qgeo}DNne?puXg5P59Fw?%p=>avx7`KDMU&jg4*~yrI1eq9mh2SL=rI6<} z3;%3ATDW?0-E&YxGe;8({$)!7VtjweqMBalFGAskZ6fEpeFzWdjW%Cdc=XMde6Jg-%e*j7&-fv)(r9x8tl!6%6RcT2)#8x?CCAMGeBXtlm`c)Fq*RE}4`%Q5vqzX z*M}8iN-fO&3Jc2E@uN!MAk7te z{l8xeN&JE^I#_?5CCYrYBs4vhf&b}AqNL9fV%6(riK;@(`-K9uoLK3X7?gtWxXB{! zTu99HCBk`N`QWnfJQ5;6{P)qgGT%-f(S;lyK_nE4vRj4e?HQQ003V@!2i{HlM8z9j zB>LK@0=y_GS<&#zBGTAO%sIPD*auA^7M^thQ2&P?!WbZgFrv*${ykc_PXvfLUPfmF z>?zwt#EJ?B!n0z+KXV7Mg5%itpuZAPbg&%2F9pwIsB~C8s(8V-m*O*4QHh4cK4`b_ zV!trwU5k_{)5!^urJI#yM=1i%$pe%rIFBKM^IZJZiPjLLpg@AW>oGG9{10N+LBUt^ zMBy*Xhc{d=l#d=Md4 z(w-_jzo1lvy!U=k>h_h?>6+EohnxBXW4{m?&SQVXfDq;S}xKm@pAJoWXte zU9)lq{Q~ygSmEwGPU2fDxml-8IX#oy?AxQte-JZZrw=G+(Mx7syA|#GHgdD?5I|tx zCN%Gx zL}CHj^a72`1@I_}R)Hy2kHY*_B78hf$|dCR<^NlV+wfIg0?w?jE+9UzUTH=Lmc6l4 zc^!|vj`iQJ*KA75PBbiG>t*5or4K0>_xS~SmJ9DdGb#I!14cE)wfH+5P^cvDM^O$K z>tmH&PGK5Yi=w)gvPYUhLnOO(xTts>lZU|Z!5LBJ>w^*y790`Q3;T$9f5MRur5zai zh45{yBJNB@Nr!mgmqwwdVq%s9SUC+&tRoS3k0K{To_XY zC(a_9h^$^k%smiCFSz!d6qb)J5epwgml>?FZed@vjs(?_9o(ByVzSk75&hRRV)joC zDDBh94&k5d!kuyu^?%Ais!SW1un9Q~ZdQh&amubs68c*`n9JcI&*J^cpRMG9=qC*# zRE%af6g+-Zgi5@moJ0=AxDWB_kP_22qO}8Ytp8*IjODUw+-7srm4`9gi3OTP%^Adu z!1OgG)Fa2Ty-)iI`?DK~Ig95jB@4*O3il*rAxE*Tt?q|S-Vd7_1qitIl?ls73$V#i zF@3mC*cYW@Qv)Rl_dm{&cm;(ETTLRm$4JcniB&l?gY4`h>;ed%N5uv16ck#Z&H3>p ziJvag9Cm)5(!Gb~u%E2T&rLLk^~984`qCVBX}I!hBh6v|%TO+^p*f(t3JX|RpA8CD zROwi6Ol;^q9qoU*pj?3k=rHFp-h}cD+^#0`d5jO`CFG*sVEIdgXB8%-v6XG0A$cxCc^(HAl#ehgnIS4?Hzf%V zHnFl8IW*yQ>~DJ%L^(X{Z_|{f9e8{P)f_h}Uml_wsAyW0fWZx|2`$zADI(aqidg8{ zeF=Kxdh$J^6OOFWB<^S;2fMvJVJ3386ZfH}dxj`H*FvmnBqFEae}AbcNbXB4uRNDD zPOAJ>BCiHfaPU8bDfnRd?`UED2C-UjJhM>*v&RxEZ&)cR&a5NmuWk_TS9?gjjsqK_ z_bw2oE;F&JdQ46PuXh;M|9gj#Gk>&j&qK`z&b)fzet0cLHrJA~V5xA=>%!w`$5~NA zvfOdPHONCO^6PF9wC+MIU>BKkFDXyQ$ppEJ_lW{jXkvjjk_r}#q})Hx5Y_3Jp~!CN zCoCV-p|x5^rd=4_WFMjO0LRN?ggs?E+JxgV5RDlT5Pq;iM66|KBbMRzSrPnAPb|-K zO!#N*#bSlM6xVLsM*5;vO7U3Ivy!PwF(xB1+Z3f}7@jwbjD8HkgXaaOvUVYP*bCjl znG+)xdTgh%@F<4lpnkgYgq=d*$ZEm*Pb3b6Ojz)pOp&9d5({QyybeMGW(eOinE4Ln zPhow(=tka+eY#)NRBb0wxsGa>9|t0<8Yf(X5VC>DMNHv@V9q4W|H+v|raWw7_Vh(z zeE0|^mK-6&S5rj!(xt>~TPnm{+f-uFKaLcJ3r)nz|FS~ZUacZFw{(_>ju}VH_%Eyh zf%1#_!uGn>mu#>1rMY%=s~~#oE@AiqE0&=AFNhC;ZA+(^Yuk+vMu6duXd8m@Ri*q_ zJuzELZ!x!YKRy`KaK@nh2!_|N7#WP1UCGLSJtS;eO9NiefDgBd=&h6R1t#I!;lcx8 z!w?Fa>pmCh+UCaQPKnd}hdeoHc7He*H0u=NzE8 zA%IIR&BvvB7QscT_-Y+lPo1PHBi4%=aix+e$R2-M$LLpM5DN_ z5Z7@UPVwwX6ra-z_uFXh?=vY*V{jR!krdCe;SJ8>!vYldAEfvU1ScNIpm=IuTq9^7 zE?CiqORJzhzaQ^2XeGt(Iz#d4_)@fr;)Qsucn8Ic@P0+RaeFEiG_)k}ZEY%5>RLp2 z3SxHSp z?ei#pFMi}?e7SqqQ~d5NIRB?C#jidMF^Ug80mxJF#Jk}BePr-mNSKEltC$a?!h-S< zXGKlX5@NyANeKwCo`#F<%5Miy@Ekx{e^~e*I*6hjV+^M-T@SRtANrE$gH=uJ>KO^g zkVD^)f~%JVSW<>CU(ty*y`5M+hZYc+uPhU$@6knPH||j~Fm@mia>v*}bXqUrx(Bl$ z@0o%4|A;1GDqcn688o3m--Y=e;v91L4s$`kxCFr}@VuLzuoC%7TviD&A_(R$6W;R} z-xrIKBh2%|QsMZpkXZarjTEoZZ~(=L;`63ZIhN70lGo8UXWJ16U~leLwquYLE}ZK6;o z-b5gf+!-O=DqNxmj7gY|1-jE%XeyeJH<9tWZjqDRK&)sX2C=|r!HOC1jGw67zK1jq zlDyzD;l$KS7TJzf58#?JSp+7`BNiJrMd-fmOU(8;3MVkc_A7HyT!ZoD*&^rqeZ-2o zF**vqJqv{AbF`Y+QH=j0p%}Fa*S$T&qUf73*W8UF(6otItRYhv=5D|TZJ<&thh~N$ z@r2XJQ9G^}C2|m=Wksjw3*YNjVxIj?3EEiP7)#aRslqkBj#xCUL%2d0i3OhCAz}~r zC1x0E6{Y4q#0)jK2$8^)%$gHq+<@7UkaPWHQPge57BG|V-HXC=bUc1IXrJvQ50q@{ z6xOd(i4~rTi|S#UaIuk17*g3Q{P!V{2*uZCh`iZl#H?R05{0ME;B{xH`g#oIK`63N zgdaFXEb2%7!SXP416coy01||AcZpE<7-Ft_10s48qS@e@i%AI(Xc{YGPpl(mh+#`W zse7TYyn!4BJj7-#9|F=h2i1e#7aE}g*jp;^CB}K1g|07T8BA*4uoEv=G!Zl|%PPyIWe6vA#hhSn71SVkaF2wL6rt8EA3D}N} z6}or2h#4`@gXQ%12wQW2Ska?(!ly$y0G`o^u!81%Zw;}?t2=~iR*0C}kGKs)Uqy`w zfm>TeEVY`LZa4aKV8h}?rh6wQvOhXQ%&5c2D&!2>E^OQ85i4rOz6am@*$n4@q)s@0 z!@PscAb6z|pB41+)7Pty)g*!MDUt}mnn=xJlfui9eHe)Eh z2&Ug0JTFT7Z6TKZ;VuQ8muxWJzDDF+yOx-(`7GHqBtu}ZhT-j zO2|}U7~D!s*P1GF`XT;<1Z{ORuc2r@;{D*8gK3uF8I3ht;Cv5JafrN%uo<`>#5789 z`+JG#7StJF9M~oTXhE~spff_(dLAEmo@^gSgzoKCBnE47(hZK2@c_zfaDQ1Za&F8d z=AAT3n4g+V=BKukdHy)qMCManWd08V58q%E^i+h*D5%V@9Vd$&tL7bOQ9m*f5RAYQ zjqJ)c%(x~FlHlA=uMLSX_UF>{%X^D2WcpfG{qmy|ITKX{Xb63J1&tK)8?}cM-=01$_C?YsKp@X z+7waLh+_qOWo5!+#Q2{WfP4Uk?Y9W$3CwziNE->4;Q+C~Orwb1Hj-Gjal6I|*?!zF zvb~so!F;`haU!P3K+b?kqG;7_Vm=?{fP%+@;T5q6`67y~o+|ubj3wqg)hQweBE(#l zOc9uktEWM10^&ch32peWn6UkV(*g{sWeK=6Q|e(H-MEkt_Tt;iSSC1(F?J$W;tyB({udTvSn@dcK z++`)^7_?SIM&uK-{|V!Q;LvcvFo@iBSU6e``CzZD6p<2)d_&$^EHDH|%O>Id0nKlA z4ue|khX&>Qi@0XsMY4ZZ!8D3EF^7k_YDM*tMZ|*XZ6fCiwBf+=6)wL6zI2Rgg1vF9 zDCm!2R|xC+io7-WB4A&J!7FggTBkgeK@RY5Mt}?Ez8eQ@|0*APsZjRaa^Zdm^Ep}4 zG^N~5Zg8zVk$^_YS|_d&+=i{P8n9nmfMOeP+B4&H~vzk zk}M#qhSkW3vw0jLek2RM3?MQ7PnIrY+=;tV4pr)RJ>kH%;p;{>~~EiW~(#{ z``u@VS>9PE>~AI$3!h0B_QH)M3J|1Vrho|l%dQa;3ctHsSPu0fW`A>w2%kZt6iPou zn-0RGnuWax`<@l77q;LMY=I^C^%$asP{$+@F2OuMDD4<6Dz={{W(#i;_UYPT{6frR zUw2H{k6^R{B2M(>A(%cx#GU6cwsRi)8RPt5IjGpjbdvaV2iYOwI3$AC?HhaA;X6EvnB!W!DeI@qEkx`Ha_i2D z%9Id^ftY#^71~6R-GP=8JJzgxkwMv@Wp|()W?!sVj!nfA&_ImhG7PLHp&HFJFkOj$ zJ=izx7mgY%xdi8Ij|hK_2q;7*Y!ta|n3O8esclQ6ijzYVF!AAJ6V`v5^F{VoxL^!B zzA9k=a`R6zoUU39e3zezI>d%?@{)L@c{(l;T=J*&-cz zIvFQm)--s`wW9cnMq(v*)(PVUL_R@(FiZJxJL$pUIgI!(5_|4B;WX)qIdB~SX1`^e zDE+FFSWw>}s#o?9GYzg5dM{?V2+Vr%CgXa7p;t8qg1 zVbw~}Hk{Yn$g$KboH~pHfa4h)`C!j)R7Rg5J9xvzinW=%U@!iElW}`8zHFL^r0*dX z*5gcr$h4Wlb6Gn>p39h-cI_BZ`ZCt(fPEXj5QM+kA>2DNNxYd#Zis#`RYb;bAm&J! zA|f|d6D#YzPDCREi1~XKi0tM}QqVtW25l=EhhUtE>9vqE@S-SsWhXJ;+_S<{Hk+7# zDmtBP6ozQn?WdF>=xDNxX~MR(8oiuqa{i3PXAtQ?!33`SgtO>NfEKN&C6@bHzHq#- zfLI{6R>bbDB9^@k1qPJ%!6I}pf3}&-M{!k~{Rfl}W>7X5=WGx;DNBhJZK)T&+AUb( zwFNz_m~j5Op8)m$35?l*s{ox1@Pu(*gZqna5hzS27P~)1WPi|t55myA_`B76C7!st_}sMf(ql{PQurzmM*vSwk8pmwi&*3%6g1$nVg?j=Ff7Na zZk!?fnsXWX!JE5D1hSVCi)GAzQ5_D(It@po0g-9&jIB#ZMxIQ$r*{bF2aAbCURy6*vxgB2j6>K3Vna|cf$q~< zVfz$|)xk2|3HZkbkM2XjaVnV~>kYLymFq;#H8?voPKKo^*Uh6GC_1-a_*%WhJfEOB z2L1t4ltCBC&!%ltMn}lcCJt4u!)=y{U?UrARwgeXKf3{E3OIM}Li`Vj`?^9AeF*_2 zwmv4@!}b!Z96Mapd}YSvn#^R^uN2{TGtg{@-SxsXC4*Qr19b|xYA}WX#=qM{;CU3~ z5UXDz40f#lMM9;H#UlGLG(y2RrJu-2!lifFz%t>#wigLZ#q_g#nw6UulAql^N=e&H zes<#;<<@@WXOmVb*Plbgv=c~9P~+sQIdPnL%f zRNhwwXGoWdi7Q~}zyJiehhl3%=|7T0@c!BO2L!XpvLEw;z&QkSejxHBCMrPQ3oFq6 zM`GW%ScK1|5!2Ui5JrUCSF$RRKn^M5#r*%t`?owrC^e44yBF4j}$tbO4KrpGP?W zp5~LnHLN$WC^?0z4AD@q{~fJmaLgSk?0wPUgwl)n0_;+Xh}?t77le=Yh{zpl(Uw?C z;TfZZx6e6Z0c|cC0}ykdiUs`>vqkBIv)Bb^$#QEPddQNN3aiL67HtF6g1YC^gfR*2 z0SJ7OB-|aiLI!v`(YgWWZlj32Y$fJxsuF>l7ZKB9$yvm7&o>C${-MO2yN`*;%g6D; z<75saC>1`9fk!-#twvoh@I_Xqk?|T-Fp!geQUnPzUch?I(S&Kpr%}nrW0XUEspL>! zjY`94%RyOky$JlijaZNl3)kVx#G-{Om4b^D6*lCUw33Nf5fpA{7w+p$p)fl|!BO}E zP`EKg1lo@hvmV7InIY!}lq)xAob)I+Bg)A}VAPV`v_u)ckaE}vj0LewT<4pO#HCtT z#x@bY7eOenQ@4n|+eXa&Tug*9y3R_2Lq&A&c3l4viTj_3*FbdMW>I!60#Ojv8-?%l zWyFH+Vdk+OL{~8UG+m;J+f6r%(XbZ(_0J?Lyat z0}pKbcMJU;Xn26JOeX>#qG19>7cl=Haxz8(pj+ll4rN8eJv8VSF zb6taxPl%i^6XqxuBS$gCrcZ<{Uu+k_JWSOCM+8fEAu`S;90xkE3$xH?D#Lkz26!z%&u_KfthkQ^I)U@cKxG`v3K{!uKKG z987mi7iH-ONDPlt8AKlqC<_)-6m+xEt`iH8!$W9(35@kTbe_D=pAx=%rV%rK<`f1r zM8!*3==_p~Wc@kir7HZP3R}Uf>>o)!Fpa~46VN?4PI#A(#uge)u2l%tK=h7-N)|@R zz=dDOq7SAC_ZGyDA^Pu0LKi?ODlnBp3q;8H%_-qMf+z;~?!_=17+(H=xws7*P72>0 zh1eyqAI}BPam0PVbbFOBY~Di3MC9;~ox=A4s%h|?z>FC%jd$YuKS&JYM=7`C;DWMS zR*2~T`jNP_jH2Lu9*YLRHvwH}$gXcpKp?6n(6T!s;R6L1MnFeLR! zXhaUz;0_J2+iu9vhna=nC0Fg%tiY*9P0(D9kV7Cndi=WrWGI0SHM zUKXptKOzAGu5`yr^$1dfrQCz5c+222naz!4S#p8Qrfv8KB?#p{jjr0$N66yoCi8o^ zJ$XMSArze;OR*Cdghuxnj6Wcv3EBNOCyYZ5{dNiCO*md4@X1n9^VUXUzMpZLLjH!G z%1f6h9}2F((kw=8B7c4ziARo7K4{L*?8G9ir$yj%FDYk`pP`a&T+|}&r!FEb7^8eOGVof#WgQfkFa5PON!2I6_CJ8%wKH}Mtm^wd_ z!gDZc0rm{U^4N{7B5XWFN-=T_N_g>MV83&R@OGeu1CBo;h6t8VFs1;J{8hp^6cI5f zJ#rMZqZGbnr_g`jK+H4*7ZC&Ft(Zi_p34iNdDn8s_KPnxI*N4r*N7p zh=uP%g97YVqtO9#I}ofEn55bPpVum1Y@)eP9KaF z4<;$!@1h#0xVS?3Z6uc8jwF9@uc$fDN#Z-a2CBEBeE@m3dBXTC`ut$|YNsgu99=yq zyZM|5{}J>5kx=w;tlhX(nI(=%*{j0XKr35hSx9!ALbrCDI$eaami)8 z`A`Im!C>xA=!YCLV{fnt&&QaK31u7ctHAzG^amhVhDbS7Je4U-wWveb-wq4?(^w-2 zCT+lCGJ$7ng>XG`k(hlmE)D`g7yjKEm>xc*H0>u7c&r$D2RjCr#YQwb*-Q*lKzL_| za6P=1Sad9=ae*m-DZQ*QQ)t<5Boi~vS|jqd<`c8@n1%h7Dq`Ui=S0a(dx+Uz#Yh?3 zvQ~H-XA(12Pg7Q7z9(2-!tQ}=%#;+EJ(&%5+b)LqzZFAh%zRdus$1~}uo~YMj1M6e z0NHO~f&!SX2ngq^*xwL&VY9Hmi*f_PXEELb&Q~#w2O?|HmxSuU6NNuwBvyTN5@@&( z`1`d&*MR;!WIwzo0hi5r7~(sH^S%uvrjI8ln1UFJXN~Jb_G&zrp-rG*0@A8UP6Xx7%|r)2=#*fZ<|C-9man+2@Gx!l_{q%czBA+`nHN_4dDwB74%yo z%3kXr=6Yh9$X>sUl;OzX?-(NxYmvjcO`>vd2C?7^=+;5apy|T481Y{uxDenxVfV`dCgJ%uZ!gTEA!5-1+E zRQL}sB3AXZ7C{mrzkRK+-m#ll%_E1ET9kkg7+9?g$fp4K9V?a3v&ausOV5bno70E| z9~iGZi0RjCCYIwv$q3Y_;BHG7R_zeFUSOY7p*%F2>}>XI<#E({?6J5~zlQ8!3Szc2 zdv28QAIczBg{{f_rD!cd@hyFY|I^;Ys-8Y8{El?|fOP!-=PW0&n)$ehIC})wgM~m! zZ)E_&<>1Fqiuk-4zqpyI{!$@|Z^nN*gWv<$w``VKnc0e8+)5>xM})g=1%B}gvVNl2 z=b(_Aa}nqNL#Vge?4!y;1Q=O;lJYnPA;A>hq^#VAQwJY3?X<9dG7N7zjPjas{U?Y# zvqU&&6cY2^HAR%X-G`Vdl&(C7DB~DfXwy&`0-bgBuSEl|EZhcOOE;h%uA_qOiE}g%A@WQwQ}FUquI<=4#Z*2uU(Dl= zN~IC@A_WH-?V{r53KRL7AsX`M5dY1e!}1!hc_hD@61r;6>-Xs2dyirB=egJ+qec+h zH%r+smArY3fKmGi^L&&xr>k9E%=00U>`cuFA}-4JP}*Xbs_+>EL_uxMKkW4YhEVa!ycyAmiDlv!S2PNCuncCYY3t0cJ z(+HDuB3G1mFPScs^vZMmW1y6v_?Ov^Qv;+J1si&qz?FwYg)duRs;@sK<~PTsT3vU@ zyG-&%64gd{(>~)~eoR#LcqX8TUbM_4A1M=+>Buu6QS(Vl zQ};?hRM0)!1kN@Sl{#?NcrQ3D=FTSKMdH;{rnE^99{bLw*5hdSU3XCHHXmlU2M*_(1J_zr9`$v!kB!?$mcN#2?x%J+4^ByVdk%C~Q)N#1&Z?;qfM zr%jT;l?rudNRRqI?xFwZk9#EeIw3c;thw>8qGnIMMM@o^+vhp%ASQ(RXPcNhZ;7b< zZw^TQA(q-4zydX9Bm0e#SJlOYcToKZ_j_d4Q2nIKL|bNA|Fb2Nji+0fOwY-pYX14S zsax$56|5j|pju8hssHdc!`r->W9U%vBJn1LXDDrQ#ME*@Qw_`?RPJ3wMBuw_w<*bf z(N;>-&VHt#`AR9cq_5--|Nr=b;kZ9x@~U%1g?BX=_r|uOs-JFWqL;BqHIg%TnN06v zqH4a{YUWjj!ctqn@ z*$EQyE;IE9TZ{7Nu+TJICpVbXjpPSakK^Yh*i*-uoMe)vOt(;9w7}$jM+gU_F5d2F zMzm91G~IC_4=q2lkoZ zmvcqc2l7qut2|NlK_aJy%t?^>!|<3 z7UO%B^I6RsZ<3uTUPSO4UKmEb(ciI*wp`?`;YNhmG0Sd~s2?k;;)3HQIk=~&iWdSVnf(WY0#v+kz$ACI6jc{z?vJX=x|+N%_7c6; z|9RAmnoC?){e}1u^8U(f388gMP5lcT6DaPt+ayj<(t*)EnKPhz^Fou2j@cmQ!9fy* zx0bsUDhACpzFjWDWaM&g>~Esa^VlrZzjW4&+PGHC2OVUT;WPgL8y1+n4dnh&_3xc# zbpH;b{5MZljQ=MLF%gDJD!OsFNnKANI+Co2Za(4$rH-ae^!ekw5Ynjc<(N@_BphoB z*<;dPledH*6sq<%H>108Y+=-#UC8Rj>v-<4j6&XD2}Yx8FZCc8eG_wa)q@EGLZ6>7 zmAB9ZgV1`Cm#FNx&4fPVY(XUj2daKSz_GZw)T})fQi=`Fn`Op#jQT$|;`<@b@l6-;A=#I(EPTiM7|)vr`GJGtUBcMV(D}9m zi^rJ6v3;Vvug)-@H;#z%zP8h`v`oC})utx#L#-(9YuN#8Sblvu4<13FV5&(ycbZTu zxYnczQ`h#Fm!GQsVg8QXW3iX!5C$f-VZUIqIK$@|wN<3CFKhB{1ff_itb zNj=z^7tmQEX>v;_zP-@Y=cPntD{h#@g2J;TS^2tl2PUe`y*2QA;}1gzJ9YAl|9Q{PW3Q(uTBdR zQM`gzjnUULZANifGc#%eSuW&m>Eif^$_cfJv4r|K?bys%(6ncd4_ONad5_R5_6Yf( za4Qy(HUrqB(?rL$^n|Nxm{^!L?Au7hx6)CuRU(GSXT>m{5w+fU$Iuml#QrMdr_f9V zU*_(L`n$LtBe0ro>nxkPQl?UWpc@wmq)Jy9|1z!vsBhpZ zhR`KMx{;d8JP(0KdYMFBA5n$WeyN<_ad2YPk8Mr$74t=<<}NakCTW_JC{Ui>jQHR5 zW|H`BnkiUF;8|UML<(BhNSC8zRd6Db>`do-@}(uQ(wpIX9cnhN`xKE>m-a$_CVZJi`Pl$>pk!oQ9~s zV4U%`+AL;Nckv>zzJu{C=4y(HHwoV%d1#C&ZJHKUJBIlH8Un3MZgvVsEPS^QF$EM9 z7WC$GyMZB;+033Jb=DpeKE6?u`*4Y=UPZ|TqFsBL2I zoSGr3amfb9{rjX*ZRL8Rjzo!|$yyp(c-us_0Mh3Et;to8UAqIw;@0 z%0zEDE6VrOViS0%8~I;0fw7YvDKZo)dDKL|944yr1=j3C@gl;k>XmsWJb8_%$lGU4 zHMIjO_#XmNDF2oKBob4(8p6Ms{y>DT$~X1n$p5mDbcQ9D6zLXKI=rW;I6%-4{?BPj zQQLQz&;yL!%^dcaxXkef{v5tly-mpT!|!LswIM{dl5SxQ>Dp^_vD0e1y5XdUlax_EZU? zzAyK4b-Txuf51W|NTj32-?o#e(3>TWx5zso8~miJsQS6b9MA8Odew*O6(qjTHOXOI zE)nUp%T#<%x(=yk+e~pYQmlxVb~S~Ecvc+Zp3aU^N^cRlgkU%F{=G%yJ@(_glls%-zI)zIqK;W$5RI+M@`@D zczQd3zFo38PZC;F+?@=W%p5hHP>XtIxnt&F$x+kBI-a3sSj|}NcybE=&J@XOzQcr{ z+at=|FvnC6sS=f}SZ~r_Q!DcIL7xBi>{L@%&j*MBzAn|B%C6URjbYTKB~kEv8bB;Yc!7g* zcC=d~Ri+<%T)o24iNCBmbB$Nm^7l}AhOD+aJIL`1Z!M~XJ_1E}*EAh3RjL{PA9e9T zNA@~iz3RN$LBl~6;(d2GZg)u>sViEV_+QgvJ~%6J)O2tZ&!w3J)q5RrFUxTv#spXS z{=k?IvBz1Y0G?KDO*TNlP^Dg8ZoI#lEGjXR$R%oDT;o_kDYoJ31#6_?iSwoolm@ew zJ(UvOnwf@{tZspvnQM)AGu?G4$@DayR&#j)^nYiTrA>aUz2w&{0vDNgTRYaalpM9D zr{f(05o*m`$2+t5dY0r}L68x}?+^)7Yl#!6caJ*W+0LJ1?Hm^j**Q{$P|Yew!66ADKB<@S zoK1*IbYvYkq_b}?k~BPbZ!qcJZAF#6cFe>Ut`L=(#hQ%>*Bmi{O_M~W2(K&Oid81? zFo6X%C}RANED;rCQmSg-+hh`_n~2KRw4WiWuI(a6`-Ie?FzhwqTWiH}lKn|>?c_nj zY8y+lqxfP%H1Mw@cBsCmGX|lYHm2x9x+_rnX1>Wx-N&_IpOjAPtvLUewKM5gazqtY z?J~u)S+x+cUpJZH2b)EC?p$Refh}B=wn&NeF-5;C6P2J*T_v|t2Y}2Y8;yH79girw zd8u*rebaG?ABi8Tp45Wp%@47Y~$U&MO6N{b)cu1YP{Hwd0f;YXQ!Y^$%?n$&! zpnCLVlZ-}1rH^wPMa|0`sHn@%GKGI*Scdq&xkn%r=T4}K7(*a(-F%acPo{_jo^#x} zkhqrh-;jQX=R3eN*k#hU@_1&Hy`C_YTN*_9^Y)mEuXc$F$EO+}DOMGGg@;)n^K4sF zx~fc6=E;;I{?mPtNjx`4RN&KrCbe>dsN&``jO(thqAK4=IaV@egue&3Qv@z+X?%}x zHX!)pP*eEBDpAEVIp@_geU0mAQ*j;5Qss2(A=A))p{SfMCmQb;B&Co)IU@OgB>8lD zrQ;{|$fGGig%`In87@l7{TSB?RG;9ZLb7&%sV=6X0_l^S@~BzdRcf+q>L#&TF=EH2 zm_nXZpo%`4;W#r^is0_q)Ra!!!l}d2zighV-7TWxhna4nbkzxynMN6}dakbt)iU)~ zuGJ>;o7VJyu!&al37VeJlkM^31{a*4v`@Q12xtn9)aB(5=KQ57Q9X)Fz{aT1B_MJ0D z$7YLi%Ua_(GDcMVqjrw*jF=SfqKft8p+<0DHqB%n9V99q&2pQK?%=}1W7ZHZYU*&a zTrLv#&NkKmoFU3}e7WPt-r_>hpLUzzCk)e3pLg7Z>sE{MO`|6e?)zJq;D4AIDU#R9 zpKU@a4CS9qFv0bwL{-isc?9oY#v0GwGh)7cR6K|aF(U+@>~6|;_aesAONx^XCa{C{ zJ*3uzjJF5t1R?Pk7CAv=_z06twrUXN`&*T%>d)xKOkj_1w>$pB00o6l9CAEGxK2I6 zeL}V1d+MSxM~~%Fh|*07$A^??q3oqyjs-iVY{5?AKO<8hp?*t4ks92? z6cHs;iT~?n>TZdN3cTisTv#ir`tN;=H@aR_#a$j#yf7vz^mbPh{5U4TkFznU&mohE z#vzR9k@q7h71Z9*!8BY;ycRj92O94_az^khJa^uB_T$Ahmq)Rgp6q*yXYN|#dY1bK z>MqVP;TwC1Dk|DR|2G@Y%Y3G45tm@No@bSEMPx&jzDyVvo~}eOP!e*CY)&vrQ7__H z%pNW17g5a#QX#MFG!uSpgQ#TUlu7@O{!iW1EM=UIy2qB9>ar!G+*5m)+TF*wz#NzO zw;XgReDkm=`gnk-=vvl?LMV}Nl+2S5B3&n$_@DS$ls938TqJ(O3RpFW6R4rzqvzI;>p2^SIg^SS0Doa7FP(zhNpnQ2^t)f{d(2p2J8QXa~? zkiLC@Df?@KsQ8;%FB`AKZmusryY{={U(0Z_4Ad&6Cnu{02x}vey*PS}7{=1;aKJ2f4f=JuGD^ zUO6qQ@IDIMQ9N*jslA)L4;s2LKkUZ(&m+6+G~PoTnaF>kujJ2LjS({PgBgzZXf#ys zpFNKQ_I*&r;AqYs8wlI051N|15oE|wyq$5R`a3T|?cfEe&$CPrKUX|pTK$~~qS_fS zRq@Bg#EwXn>Bk;5{JDzq*adT%J!;#T@P}ohl7HN6GVSMxsv!}r>K2|g)!DJ6%i&(S z)0D1eb$Vo;+U%HjPBMtC=xIXr>%_Q^NeJ;dYfX9cE}|0M89gDrk;kl}@o!|p;3?*L z0SIRpm>}K%u*sIanGzL$d$@`9D--3Su2WUc-(iZj(MApTg^X_Ddy85QL}M(c2H*7Q z#{cLhQMDWBltki}JX4rqk!pndG3{1&Fi*H+YL+ifnPk5vqFmb<1ETJ#*`{(#D^V#T zo2vfJbq<1*^$3io0v`TjWH?ZK`7-0*I9OEi16+s@TtdYo61#bqP#ipj>>){Ay5AHn z+9)b@wa1jk_J}GRM{*M3!JJB{SW;pF%m-BJD>CLNx-Vc{-&16WWXE&H^$=g9wv^RR z(7+f&jr`yo>wmHFwPePxpW zps4s}1~Z$pY~J2(Vs}uThxFgC9E1q#UsKZ6Q=E4^2T!| z8mFHOeg>ju$7=B-wsM;BJ>67PxU92;vuuKL)};P@LX>wqzK^EFe|3{9d=q)D0jBDYZADf7 zyu$HI6;Vz6N}(F6nlyJ@xRb#ME+QPOF6JJhE+N8cxUXEYL&95UnCeH`ib_7w%edca z$O;?Dbftk5HJ_(U-RlQL6%wygMcaoNSJQk^?mPOL=(|)tATp4YIzkOi9bO965V>)s zqmt?;#Gj9uaz;Wb(IZRY7{P<|CN76)T*cxLYGlH(y-*s}_ot1g?4+plU4-FLwz|TU zUb$aXcuc^=PLd%*=EWH%w(6{?(zkb+%=9B1yhkLMJvm5%D4kB59U^b=N)fHCHSx_y zIZCmH(+IKtM@>9Nha1AhI~~M&!>HUEF#e*UqAI>VVxs%__u#&0mnr(Oy{N3eqJ=2$ z3w=!GOU*?^Z{5dP3N*wNy4AFT09!qZwHVsdbdPi+wooRSrtEk+j>x{1*F9x}H_jC*#D!It3nrVu6 z(|D}@kLx{3PHb~D&Nb$e2O{C=92vapZEsjI%X(;K^&2iBr38U&4 zj(j8swK1hL`-qCKm}bHZER=hF#8f}Ya(qZW%_SF^o4cBty~KS`x0=!~t^W#MMhYIO zlkJUn=x&+`cGF0()r1--C{?~LCUVm>G2w0!Q9qFrM$ryJt%%kIjH}O1QSN3e)r6uG zeCF^yQ)V(R@%ihf_FvTZAU%-aGs@of7*`InT9mF~4v);!6HV##`5dhDdcD`gq+e|% zD$WE&d0$v#f={PKMf>zLHAAAJil1Xh2H)x<)PJt#G@}fN)z45{12s1G&Aw+kbM$Iv7cc$N*|u?7|&loQLjP9^}mCnil1F;QkOAEL3#U0 z#x=QxsJhX+Oy=BRQR(~6nE1b#7a-mKn5m6ML}eQa#*4}wxWf3lJSwW7#Z*v^amg|% zDde|T6@=Q&Irb>u>+tW966Iat@X+I-%DX#!)Q73SJX6%1LIu=zxWen;J9oU5ok$EX&i%>7m7v$dx1hQ6YT zauzurm?TB;j2muTf36Z$-lxoD{>$(lu51P8s0u7`RQ3}W!fof6_zRTHp!`zW?vc3Z zxGDXz9q$0;IsaX3Vsj|RP;XOgW=^pO!!NUKoW#`2BOK51cae>~*;UM6c1cXVHQVv( z5{aofg^ndhB&J@EI5zQ*s|Ean>bYFUiuFuX@E3kgt(oe0FD)@Omph|+KBQRxdBYUP zyCN~<4r}fB9krb5J{pPC&=ZbfGx$D+MonIX%1DI&aoj{N>qG}sC%T^~I*0oMBIn5T zX3U71uULi|b!*!o%W6i`S^Euz>*txO8C?HOf<2yQUWZTx6|kz2E_g(GPA~ykPQsSK z#fv$n{IXh6iJRw}^v4t)qH%R|$H-HREwFu$G-h>kY@+daH;GCQJZs9<5IRRR;WDvP z1gQ`n%WsO>uZi&>{#}JBeR`mncNj|`Go!WRrP-1}(eOeNx= zy>FeU#A+fZi0J*FjU?L?n#7ZYQ;;0l+ISuwD$4iGQZssem6*?0%4np=5YI#GyGbT~ zaX=iq*`s-(sf{cGVJsRS54POoQZXD(d0heA?7}Q!A?O1Fj>;^kZy@!t4eb4B)z$ zOb6VAMpX4IuZh0bN>pgf7DvSy4#G1MxjALx3+IU{Z^cK2L@!3QNN;Ip8rM)wrA9S# z5bS7FKTJ1SkDvS=(swfgM%gu)^YE!FCgPTMqhYs7-G)4dq8g<))7r-z~>P6-IY7j;f#5mbA(Xbxwq?WH{|RMP@O(!i8SdO1Q~f0o3zTl0U`n19QJ($e|J0{+v!S*j z*EIOXh{~P7{Q|yQkC@!Ynu>Xpkv8(iG&kWaBiS&bSHw*6F9eKHJ%@g7WbUGE!n9>i zjmNi-n!3$INKtd7gQ?rRm#=w@*QZ@f?V*{R4KpSFJ--+VH^xlSKUiT8t`^LckPQWB zCQ@S;n#e6Bq5>BzF!4pC$x+^#dH^JPZ!zhuL_pB^PSlad=tlj0tm8+{CU}A?P5j-} z9NeoV#+jiix49Jc-z6+Ef{H_gR#9}amGS#ma&)hh@OW;ih^=pBO4l;xz!}U3y6w;Ly-bpr14!@Hnn3a zOoN|gv61^2S48;w&Nc-H4u}f9xykVs=~V=`HZzeHU8$7s%9C@Lwjp%^cSJLR{RBxr zPg(}`cZW>Dw@Y}eH$m&i$MO6pHU+n^2;nUZr(aB;cY-|@G&ts;kzC}B&o@nc+{Zw>3e&s)P~^t5q*PUr-gSB9Fx zO*9FjXxAzeUEh_?Rr2?w&&)$JC4@-t924N&R`FL{ru+(;P>{HlaV6594l#{uCx{uH z?I4Y#v2&{91os1YDk-->dhjw+wvK}f;oq+^?jZ-o~nnyYLsg9bzf5~+mi_K2z6ww=>syZB2vJ5X`Bi79P2TvXA2+nD&) zmE1d4O6|YLoA|K_V%80(XJEL*26s2{_y|#HVZ^LnU1+l5(kWspNDd)E-=2y$nP~h0 z0*RG8zJ~{!BHH)3@y#ast=8nItOOeP z%us%m3k(v|Ib}>M_N}D;&$LUbnPW^ce~_q9J33iV&-_588|FF2^4}=EpSvD{*R?VA z7bQe_^Y)v>`#UA^es-rs@|Kw7RZJRDdiiYQsiY?uwGT}+4IvgFM(%=B#@Bz5sDl13 zDY#>|6uiMW67{#8arC9WU){RE1fT6o%MNB!`-Aci3Z*>DCQ*OV6ca*M@e(T8QTWXk z6I#j8Os!gL0?pfrN=iPn!l>^QAZb6!hv@-w{#SIInacUaz#XlUnZ-Y z(6~oU$s#Fwlvx;yM5&myWli9)~1<@OhndFrWajtG>9j`5w3}R;& zI5v5R3(-~K?c#9r@59?AZNfZ9MwS0_jAH!9siT6kRvYgtE>XTY2@|@Snl03i=Uhj+ zk#|cylxs>y3=kE(lL`pbcOGQC{w1OkJLWlNa3CXmDFauGzE*Mm*FH-m5)Bg>yJ1u@ zYq!AX-D?UOno2?DaT(c-bS(0^Q*e&V+Fr)_+`SAamL;W9{$f*t{{URe7#7YUXp{VCf)3{_H9j>^a<3Md4;Q01{G^%|rM|`t1 zqT%@*<9T4Zn5Wx|2kARzn6kf}6IByCP5n2U(tCTEx-zN~Q2uSoB%atIs3UTB2>f-f@jXS*0f7fu+t0LRk7O4|g0&;y?G-bL7r2We zaz9ZnBrhiiglG|&5LCC^W#ZYX%OsBSX3b2Z8>b%98`c`%I_mn>n=HG9iXT`yRUMz` zI8BbvY-PVylK-4%B5#kUC71ruTUo*#{_SOs&-RKRAy(2;+0Y15rD`HY;);m4Y0I1d z^$)E#=?pm@HD;YDy>E%AK(DEen-59A3}Vmi`Ie@RUS$$eH!n1S)+|iz2#=l?BZ zP3@o)q8e^kZ${qGN0j$4olGcrmt+Jge^0JojqB`qXu4FQe$@~Y%Iig+A4bxEpmG=^ zqVh^6vnbe2D<0w=#)+ORn=-yY`X3zKYA0_$+_YAy$WC(n$e%{Z+_X;Ox`srP8qvXV zKXZGy+f_NPn;>o!{hI~N5zFakN^fDMEV%CL>=@pX@jsiKsjO_J{zQMCni6vSsgSQR zwTa`gosy#-XK^C+M1$k;EqskB2OW>GP6+avM@-d~-NangS*p~PvmI?`OSXzl)l~}| zZCgnd!p{+gftxFzN`IYaYG%=Fgt`<@KS1?w2~;9k!H^8@8#JMnctmfylL74dMTVNEhPSrp9x|D=KkK%B0_#DJoXf-ZU;?EoU`^ zVZ5Ta#C**pR8{k5@Z6Pa!qvM(rF&)b*$}5$GSSr3(2R)KjU*-1d;-hJ{F(eMTs`KS z3NE>-wDmkwc5VQzdIRX5<1|Hpp|I#r@HkFKT_%gkqww|hI@q6G_it?6V(@!MU` zo5vn&`kzM|&zh}LuwuBWx?!U@^4RB5|Jj}Jx$3svgkP8^%Kh*PQ$1plsALtxbELoH zPKKI!6HVP%ik1-g@RUg{YfVQK!?1$1qh=_rSVJYw%1O%e^C6SyD70W@h4!n?#&dU9 zTCloG`j+{o?6ov4SZUg=s9HnOP*UiKE#zjRUK(dIkLQXC+(;ohd@Gun+V=`+wPJYx z(-c!!l}BR}bI9JV^GHr+d$Tr2TQc2nefqfL?+b|6E})%$n(@C&z!-sXlwqj5_Zj~q z^bR1nn6+Y%nM_hqJ(6Rp&Qc$Y#8)v>@#suZ$*n_8@Yfmif3oqn>t%dX1EO3v5g}1LLc*%L4*>{$_whgA>Z-nYmbyI^0zt}^Rd%|i{Ju**J zGR&&@sQ#g^Nq>J*RLzS-%TV|55L38fjufuQ&XL+LTATQx?xF&ld8?6HzT0?j;J%K; z?1Tx$C^S-q+|>~2OdB@hlU5kd*%P7?om!eS&5O!2h>IA)v6M;oew6+{Hf2krCbo!+ zje7Zjsi1U16<<8v)Lg(3ZqonxCfjwkr0~1iB zSI;({fuz=vzNNQ`E#S07IJ(!k`tKH1w1K!0Tz8%|m5Uh{qP9EHQ8ZkhrReDLA(HbF zw^?{UNtygKJ$h+wv3q#ZmHH>WA8JnvlhO7|2NLKGL z>Epv_(;Fr=3&)ze31>tVX8+cMKD{0sc#H!P*t*E1R`wRo$~i!3Xj00 z2Nm@{lh2yMA9&syil15In6X%j;rfo`6KZcIN{WW|9Zb$Yck_`@DYa|6$^Rn@N>5^K zuQMc;)c;tJ0tH{LGR1scs^Y4U2{r2@p=MducKU^=70St2sQRgSjwxQAhVGSA(<&1n zkff@+k&wd3Ry?dwUCuxidDrhV1#4M~0abSwo=2|u?rjoYy4AR!B&L9BH$^E~Hpx_T z6ZqmVk76O~@gC3TLZs^%6Mv$)sPZNUOrk4wp-8XqU>aW~1f%X_#SFD&iQ_PHMRFud6b?1H)Mlmf$9;E01!MDiE zAoR=-9##Co^x|dB6}^_YNX$x2@#DiEpXgReqL<~ISvw}hG+08 zlfIR^8p>WjZfaspn9ny6|J4m9^Xy#OZRbk3wu349p85~OdTcfI^&R-A>Hi7-T+3%$ z%X$yHOz5WGqRQVVI*HKJolPRe-3-A;ml)48-9&|&4>LgqpDOguVH3HVDLjfQYfbRc z{`?%KFjzsnzC>lIayw^g`%?RdhK^j4k#lsj@$T6ps$l&>6MURN6zY5TFv%}Cdr&c- zW!Dg#mo~*M8HA!>)gcpnY^FGL1UFN}p_LYF?Qps%}zu<7Pyp;{RT6qQyi_5Mmh* z<6;DiNSE0rdjB#p5%RxLHt|PW7|*W^kCEuS&ZO7w5fv-$Z5kJ@7Bh6FG^&qcj&C~C z_luhO#xrE2sB|C32q;^|L5iB#1XFs&WEMJ^Onn*8|6n7T*-K5ht`BXqeQ2@sni#*8 za`j}|g`#6ru)<9SLlymx+Hmy|Pe_HoeLLfOk_HvH7VI@aA}lKMv&RH3qt9IRVEy+% zk7XS3TTSKj<3)M7Xed{Ea($@%Vu}gu$fK<`Pg2xztMVU-<-&U-HzFkFY&DVlW{FB( zrt1k%WAtt(_P1ZT*v!3?%GKAJdRE>)jJp=BkLi;rGlGxoBA9=tw@|^6&$z* z61zj=Z)cd=epEoB;aYCR$T`Jn3h$TGO@=6_Dxl&`RZeK;c$lgfGzLo?Uk+n=r(s}JGF-c{zZ7)eEF%XWJ}*m0<6zuF*CXqWVr8EdKo z14ShpXPCgg{-RPJGJin%|G0`G{uJ3XlwaJ+B&Zct>FftXC5^_{_Bciaq)~l3#Bt;Z zs~EDN&`6R|2&WgB%=0G*v7X>LK`l-2>~U_{$9b{Lq!FCc(=nTB3iTYfb=2P5(=^<4 zR#bN6Z&RYY-&321{8w2}>{Xt4-tnm8>NYY`br|fpdJ12o{gLx}vBx!Y93Ay%(P4+l zyP-clqQnB$)0U+^9PHRg`TfQwSt;JYby~gO`@G%kv5`MhA8?sP)zB%9dwWZj`t2si zkXory_a1QkwiSPl-|_LO|1NP1X5I+*v`0<&b><4MXZO-bI@~1dJJVcBEa2x{Q?qQk zsJh1)t0VA#+{=+#&%GR>s{Ce0>@@<@ zSvKL+IpdqQhQ$KbNbP#2%SfCFnMiOL&7vf!9^YyF9hwrPrX=s|RugE=?OXMvmlUpE ztBw1D!J>RC+nR8n31V&|#0M8?pR8``+8;8-m$srwl$xFQs8U1lRj*0+>rVG;cRF7i zOm%BgXDI#iLQ}fF2^a!Kmmmwx~3B~Iup4FGne-@{~?-65_^vIYYVY=%hHgD+7VSxMa=>^G<o8#``_KcNp)ZA&>mvmf5Fpw}bQ12Rgj7*IwyR10k+=;q z?VFgI&3$Ml?L#AJ)C5lVpdpb^>sPG#jF5Yrqdte8QHqo2^fl!d&*maDn+wo7$?7Kk z!B*4wxBfJX_7{(9g$ZZ49V6Yp!IZ6Jz=_yjiIA%0CroDEIGR-Pr+}%xV2h||j1dt6 zSCIu(xAZi=Rb#FQsKU99Je2kuwiHkcd+LF1V8h$&? zjLg2*iOyyQ2bV-l{+DCv{}jpJJ5xrU;$d{^zbDR1vsYsB3dwY%>LIUV91~9U(00c- zu80V4dDOVy>_$sxH>sY)coNA!vHpv0GF`a1pysnBrfz97dO(|ryI*Hh_|A4wL1Ac( z)W4&~d-o(!iDhF<$P=LF6KST_h}^K-#Q#EY0_DvYnnd?4G%0VPL7C^jvysM?%v#k0 z%y!f^?uhEUqsCK0NhV@{trfltf1$!fwzJnqP-`W+6pe`Xt;la8F^|VAL&H#UCAS!ke@xyER`|Z$X{BYN0<9AILFn_tY?p3 z>HAb?XPdmDjiRb1>~my2Tuc-f7d4UNLj9qg32&tm0`65@S5Q59ipeIY%ommU4d*Xv zw(mA|uaBSwa|FG9ZB6Q1ipt<^%Dbcn5zj;7jcF#7nIfiqjf4=nk<1z5^m?fB7ClU& zM?M{wd^DT9O8b9zIdTq2qxy`&rux3tc*^ob#a8i|E1Hs3=EdeF+(o~zG@0m60RGUJ`MfRC6?Ns8Ml7i1gg+hlEK#QG04=>aQ6mA$ps)Wi>S&>R+j zr^$|P-Nlb+oMICAo*rYWo2=v*SxI{(*9^EG*kc0qE>Y22Nj@WW#SByZFQTbgHnjon z_h@)vxEc8?ecte0Me8NJPq1zW65lc`N7Z6JbM>n0yj|>RGW+{E6INwxIn2V50L+PlB?8H+yxQ7Zj6IdEDZO1eNFXL*84zmI?HJx zbJI~%v*(nky0_MwU`q8OO4`p=M;$-_>;81u1a8^Pos6*i{tYJlpoc~?k5uRmg+h_|FH*(M3BQ%@#-zihnxoICX~S!kwYpF>2!fV%mjtdoDDn ztqz!m{01JyMPcim^cCNkT_?Wl`kI2~EFOI^nU9OTlIN!r9l8vYNdY1BNau&m5L@%fO0-3%kQ$sO{ zs#{Od0o()onD}=GX~8^5T_3X^1h3*ogw)T>6X0!m#xbI`c#(LA6b?e!iplizVT{W} zdYS%(T-6E(gsJmUL;A#=@* z6qBg#Tu6}YG00SZy-rm0HpcUajAS;0WE(>Bs7)+04W-*e<%Z}VgpV2_Rdd4_Q_!A= z*tBmZxujc6YN6yJFU8V)SvDDM2u0xsE)zc*73FP3{#T70YZB{f=?ko-`LM5v+)P0- z{KZU2Q1MNJslI4BZ}D^qbnIY~(n*y6F;cH6e2`{m)U`V&S>4oi*k)Xx^VuQ568UCA z1r3N5Qt>M}J^21i8!oC_Q=Ej@CnS_m{7_euxO1ME0juc`Tuo2l5Ytc|r7w{Fl6sn3 z;Jam*Dd;+r>wnh{Qt+E4k~@x^t$LXCJ=EAO=W){*>yo@QPq;(XQpSqv_2G`C>-n0n z?B3qSy=Akg>Ssy8BRPjFAu@OFGBtEr4yf0FifXaodP2kpMqG~^&2OPnr+}RKt)XRkL3W(zv z`(w1^5m|+QQ+rd~iJ%qyouiEZ*!XXV8Q&}ho`~kGHj&(^qLNppOlCW)*P-;)eWvh| z&Z25FgzC`XrF;{)53~3Xe76oUHG?^`RR26jzpYZE`k!I_|NdvB<_hdgno8Z)PR2ciKmy|5pEA+X9ioDrS-~FlP4^n_<(!@>k3b|6?+-SihdR)Zgdia| zL~hw;0!=s-5nn{M4dqvKFp2D~g_1ye>k1RATr8^bo#T$YX|&aWxt;lOE^T$lkSm5K zw9~}i&!N!>>zA1Fqa3tIJT=i|7Okc$julgW-^2Jz*3fmghWKyaeWHp^PBZT2M`$)> z(V%~iH?B+RctfO`4hBR=t}&%oZKgkbGu=`BOxe3zX}{w$tvz5|Bb$jT+Qmwz@Ght_ zl`r!E0t7M}6#2hNBN|LpbZ6#T2rJE#sf6UCCJNMVzxpU{v zy)$!v%*>rTb6E)^NwSi)vXW#aNs^VUWF<+GtgIw!C2J*Xt+iIj`h1_S*YU^WoZ4F5 zJ9Ezayx;HFk8_A@6&?)gVsFQJUZZPhL~x!2lUZe^cFRgJ@As5iwY87q{U!YU63Ki> zaROp*Fmp!J3LaX>i|v2US?PoF8OrFoyp|E`m$%3LBFDDX~o6rPd-!yE4kdE?#hcs&myXQ!I-e-gSw z?49+dX>E4AG_4&^QBcTaO8AJ-(5A0R962Q_{?C4<@!vhD_vt~QiN`eG!jcPtTP8TF zI#YDgnSztHCOjc3rgol$5xsk%3ALUksxiBoGze7O7&pm#dWlN!o@5&8dx{#fb(Di$ z%`xip9ghD7sSCn5@_*n@hD^f;>%`H8Gbuj*>|v6>UM(u~S}&80JjQ$vr8liJ=?5lI zSTup6B6_`}KFPKzqV=6j6_09F`P>=fdy@b&V(scoU7BYHs^<+eflcfcs%6_u@Wf(K zRpm_e%C}|tY50v1`94b`G*(bBq{_RRz^x}lHH_F}D&IRR=AAWCiEx)gruk9sX{5&x ztWx8)nbL>Xhzj+($paXvv;31x4*CGHXvQ&|Ri5 z(_RYGvn2Q8a>sW=Bv)PR>$uQMat*me7gmw$anwYX&k|KWojf6wCOZ)S=MXEVUJs!^ z^X3nvHqb5wjZgBxK!vO_$y;}cO8@bg8S^srKI*~6j_qS*j5^lO_(!x7Rr&*R3y{8> z;TmOcko$+sb8G4U#i7c*-vkenLIWRpyk`G6@u`>MCi)!-hX~z47KG|RH4LhsCVv2d zcX~OVzbpaNEMlsTqV3&GU2L7Gs;-Vug8mO2_(O4y zwDbsx%KdJG2^=~^OYTGB`L$0xv-hD^a{otSg1Q`VTq1o_{V(5fiGOw}%c~V{WmrYz zRn~&Yyu(Te&V^6``@?>C!v{j=$m zP3KJbZsr9Bi0|hYLF4gl-2WUxe?DhY?@SU^amEqodzPN7lS&DsHxahOm}RUB)yV!1(lW-VW7L1@!9QZTi6q%}MZ=1bCbM8T zZM26=dLT&(C|eyj;b+)7MU}^If?ts#1z-QWOwsn`eBY#QX4rU8OItbCavdq6=w>7R ze|;NQ6U8Rs6m_Flo6sGsJPl8cp95sNKeZ$+D)4ve)eU|nhoMN)PS!cHFsvi73VnP>8XxcUBrv< zpuvuFGbM}~Z$IO`xQ+J)v&4Rq7(W`v>@npXcnJZDpXhCp*`JYghVFqTSDnC)D|2lq5BJxIYv z?&TvU8~AdjD9;=!oaT(=7TPU^2T7Sl_4M;sF#Da(?DsAznNfV>kSo@6UN41T?K7#{ z_KNA>T2iR$JkL?bbPm-|9W})_(}Ed=hX%3!J2Z$n0IL{;+0Rwgmm5r|XCF~juT!Le zSnrFDo+*i`UgJ&iO?^crE)I8`^-DticZ%cON=cxML`QRBxs<7k+2I@%dvqxLfT?z&{cyU&TL>EFqOcPIIK8Wr3*!-Ox47FFZpzv0VrxQZ2t zsw5zY@Zefgd}~Nd&sr%);V10lptLb;D)+7z)zqi2soJ_m%s-|{6@s6ib6m4Tf~qaq z(kSl8avXJ+DZ|ckX!P=&SDsl8AG04-(#O=fn4GF#^7urwq|C(Hmr+DoCe=r{ThLrf zE*L6L%y#^_r&J;|fi!;Qqjm@-Uv@G@$65dBA%FK3pQa>V?e1?}PxcZO`Fgu4|AOXZ zh`l<*G%aIsfx4R)nao2($54N6w@K_}WsUf8Hmy+Ir`9yLr=}E*ug>B9zpu`bicSMf zazKNqG-)=fA#%Wsp#s*pm(Z>QbVf2~R?8SQhSf3rQPQeW+Jzo5@c)|5=P28>%M|So zh>Be^!Gvzf^60$<1Gp-Yd1bq)x|L9iD&1&;$N4@{wU^`(_2=a#Lh+FbOwBQopA!K= z-93{{WY|1Wp}qW)?F~BsQZX>GtZt}?Q&XP!e6IBfebSrcC{SrX(6PF1;$|OY9EHKdltl<#)9&0!> zt~_NbZlFacl6PM==|7Qtf-zgjlvafU9Q&5hEdytV)Bll!zmc8}Xn3Cq12V6YMumnE zEJ6{PTw>BWlUYVnsQO;gG(W>vgZf0+RP|&}PkqN;AL^RP1BdVTR6nT=gz^x(mZci% z@-_dPL(zfhrmBohAq2l6g%;IwDak(%pp5ZxmJI^PBp<1H`d`GePQhLC1eDN(nM!xwa53&DL??X~)Ay`Pf1;f7vNM zb&!t@sSYg5Q2EtbQ{0t|2lyTwVTv}9j1FJmydzm6K4ji(Z~Vz{uHn7H z2>)@G$s8ljfad>Ab6nkK`vuOt_Y62=(X@6TQ0&TQS7{ zLaoI_&nHDSuIg+mI=grcGh@KO87BQFmfdRUelv!xH&eKQ`x>7Qbev^L4u9hc#~i+M zL?$0G>74W2#?%13cS_2#9O@H?jqiyCqB1LHIbJ`&OJokPO)}I3PtceeMgJwVhU#b8 zVnJXNgO_@FnW=eYmMC9#l_~m5XHn(Xo;SXHE>6^?2XOy$@U5BccxSBm5WALVJn9NY zoATR+iVEF5$8p;}?(cmP{>fxh^%*~)+PliczUFpC-5?4QR2fMJXncKwseF%wM?`Xn z|8bC_{Mjb+2JP}uA6#JKt;mi-!{d(Phr5bnBWE7XP0Nh$J@UWQLBHb=w;~chZ*Ni^ zj&b|))jfCGl%4M?s-aJoNd}pSnO|-&O+W4>Dq4EZRP0U&))?=P zcx{UM-5JM|?Zpe2=H zb7DRrUZXyxSCjf?hN*d?L{#{`p^l$#m$1qqQ$h`2>3Fb}gw+GH9V1!3s|PPSl1C-1 zN~r}zal2WH_1}gmrtp=I-!M(9 zL=8nKDm-k2W9V)PtNZ&n?xRBLzAT3k?HoDOb*Yi80n~%+a-gx05WI5ptXD;I93&$( z!b^Bo6~4^^1nDQ)?MC=cNAt8#ES^Ypk-K=?T}#<1HCcORJLg)%CnE+f_?OR zdSj(=jVuwBzLTmp6#uo%ad4aztII)CGQ6#rYu8JO>Bt#v>rCCH-l7@{sTEOK52bg? z$8NVOUTQp_Gj*Og(zvLQQw0;Yo5=Z1qRPJ|LlUvAL{8AOp85b(=a625<~}^@5uV6U zt)k2a5WQ!HY5HCV^1nG`&Ts*t;wFNINDkU$(tEm!@-xh;$m4xXdc;XlWm{XB`f2^e zl%1A(MOQ5qeTBGz8ZpGN{h&mVdYA~iy8cpD50dIK+64a>7nNDL#S|4D6qRYA+6l$o z@=bgZ^8mC=A7Bc&fK_F5w<&mZy(ri6xu)Pzl9Lc2il)lXtv0c(+4g*N?WO4t^k_o$ z{qs!o4+$J1Jh99XnNDA~>3nSMOj9eO;>euoYAU)+6_p$`)TBSs)`N)vOJILbtecu=AA0P8>$-@M%PrYWzNv8d4_a zXPqU5n&eK?yoQnsR8AddnlE(~Reo}b@lK%=8Vd4rO!>cwexT~n*^2(J=jWQ}EcSsB z%^d5*eZcM#2|iCW;Zl7PdqtEDSUR1cuLlYr<9NK zKg4m=nZ3rh&Fn`Dn1s-$zMkS5fEG3U~b5nZW{#H`KLq(<`2NY%E)(P@pO5WH~MWabm& zMNt6-Nr(^XWLmoK7nS>;&Gdia5IA_$c%JSpo~IW}Uax@Tw%w9vyqvvsJ?w63M{O4s zSw(^|%AX;uj#z+?7)@z<1tD~R?R%u&TV=v!%mr0yhl$>PhW9_*PQJ!>M68hc*G5xu zHE*>+vUgvT-ZF%4cte;IL>;5{$r$zVTE{mbIw2DI{slWJh@^SmBYm%K|X_ zDB+V;6SHJ7PK-+QWpUi^lc@UOvPnJETTDBWhSd!`wGh0>M~uiwV!FsIWZsV=w>UBWYQrUdEG0j{An`b z5c6*|O(_~hA+&#-No^KU;ZimfR8!DIZy(E7Nd4D^qb75TM>i_Imo&*@ znK9?ME7T}<#zy(6|M_^Z<2ZHS@IO4vL{e$0V$+hoXO$`Y{Tbfbdxr0iy&Y9dPbW3u zq=_z_#$AHp2OJ+v(uIgkmr`L(9=t#l{l6U2s^v8S!7;~PaGgUCt zvF9RhPQd8}#$P=^RAd6TAJX@YHf0-kimIQ=?Wan)_0*&g`G1ob(Oy39$RSdtwz2$1 z=H(Tp$kRtuYE*wmD+&-*XNGSCzhf4Jc*{=HQh8of{$r$Dp>n_+Q+Nk!%{wkj{sE8U zi|l^M|6;$mre89(?+z9f`G)HN-}R?Rk>>_#C)JwB z$5Ta>ucJzC9sU1eQ(Kv)8C@tuWb$y;RFgVBUR1b(^}EWjcZX=7O(yw2YJ-qD%@B)< zYgd}&kM@X4e?W6Gj5*IhqjGt6s6XX8PM+rdqFH+Jlv0s|$e7V4J!Ba5f5WK#3z+(; z1G#EZa?UaF7|)DjT(FZJ_wVJIfo+paX7MCZMecbf_0US5A!yI@Lvbaq7WUGH!=-}=M440XGphLKG0MgY%S(1eq*FYEHVXiw~6@;XEi6^xPLT6 zRKdLNj%WF6vqG{3&yxFhdFBNKwu6q(Xps!>fsLl9iqR16AFVMJgyhXCx>~^f zLCO^FXeBDRxu0qJXIfOKOAnKp<`R|r?82-bB=@&n9P>z>g6mpFIux#^{sWN(C8m7L zU82%QDXl^M{5~epd{9&@=cwZZaUwKTu_8u!AJ%;E{+{#=6nz^sz8kYA#CPKfDVco9 zgx(z}Dz$8|sW>%QRG=3LiAX*xCe)5C8l)auX5!mhi7FYj*?4#IA3|w{CiG}{C}@J) z$*e`?xQ?bKdtI*7Ah@-w@qbFx(y;9Q6uasBf#7?~OeV!v3>v!hGKp_9wyU8YgvPM*Jdzs_I9TG&@^9vmddq^2dUoUg4q9z1& zADuJNOn*^9`aYYkYC_!iYD_I5HWg)C9xczWHwDkE5mmGAq={cm*#=@C_C=P% z?lZ={jhBn5KEsXcb^5^|`u7r3mB&I9#goPw_vtR;Si~70oN#>R=dSUK|HpGo;}LSn zQE>67$*r9y4q7s1YrBi<@KjT9G0wk09_SlO9jix61#)X>(P&up)b!!sZDl-hdIdDL z$0hMJ%-}p#JY`eBV)M6C9rJ153fHx)j!^jfktVWWKX=rA$(E1V#x1o?(tq!6>gN+O zLn2KU8)6SMI8Knsf~KnNrkr6|dDpNZjiPTa8ebOz4=8zTjtRZXG7G6?Y*--Bi?0sJ zXJ;$of8Y0-)D->&lsud^RsH9Sx%-k-A=Hk9aipejdm{crZaK7Ex60(s=VL|Sqqy-T z&xj{M_se8TJn>Wf^%=?i+eXLVhe@tFnx!|_(UIc1xwR>Li-jj5OOKk&+r;ls{y4iC zNS_&C>X(&>N=zm{7_q{hrm234sPeu?jdv|=nox9pq4C|cUsTByDrynR?g~i=ske@s zlF|Ld44{nKEar?hQyq5?lM*z3%DbY_@D%yI@b~FtG80dTir>)D_}h`lhU9&BnZ~{B zHKJiAg)7K*u(Koo@9a|E{53%Zr%Q`-(N3F_6qSvxqaE z+HgfT&fO&Z`d(B07<&Yd^&|gh)iKAbnC7lT0-rWvVxg5)~Ro<`b%B9W>#W*|YcvppS8@0VgEdXhSU_<(b!^fi)UJt zvtK+<9O17IGt?3{R7t zf4KW>Fa@)>ilaAY%wp?KJ-ykuf4Etc>-D&)cyphqWOR*baQj91Z|!4TU+fUq7ug*$ zb|mk1*hvcxvySsRapiU~k!?h>QT|4Elj(L$RQ--|Cb4X%sMw=}P2JV>g+SBPJ*NB~ za)9A|XSDHMHCoIqWZj@7`#AYn2wf~QsjrwCAuu>?lCMTZg}N*^scEcW5#PSal#D*a zcyUO)pY$=MnaxZ%HcJD`YZctVg^Zf(PMhG?^~^KYi~rB$6d~*XGZ&!R!v%)mmQf~? zI?P0aXjKN3wu>Zw-7^S24&Inq%)e`EIh6>qpdGF@EbJDJ=Um?y&Z zeQLtgc%GoheSwsFxY{?Eyt8!Ify>Ji5dK-yjPI(Wd<{ow&5|9)VVJZ$Ji_FioC{0^l4vBw04QyYln??;))Oacyw zhgk`uCGVigzk!u7DsN^@0nbGOTo?9AHoy0JF+XIJ0j|oFsX0Uf8p=2BHkrP}iBSJV zKa+UtfT;4HCXM%95mhqnps8-VQdH_+OH5TGH3mrim`oaYN0MDVvTs(ru7M`Fc{z&( z9W45r#DE=KP)tM@E-|I_Ycp@qc3(}U01f`<78&oZ%c4rhP+Eb8U+}Cz@M9*PYUd17 zbNvue^#ji!OV#oRr;Pt|0*h+jt}6y|9w@>0*~dZCsk0_C?kH1gq`RAjn|L50aiOCr zdzq-1T0n#hbvu%#`0;hjh}ZGUt}xXfU6kr9hoVo}I7Hd(yG&7t{aRIh(h*`aQB_AB z_jHsZ_-~kEiUv};2mcMNOv%0ZqMH82<^!r4$qB=l2%FL>*4aU(WOfXPc*J8`T(zR| zf4JOKcAp`t@D=j;5MMLR@n#2!8!DsTq)O&l7Ou#R5Sl>&H6p6!AZfQ~`b#&H8{rm( z>nkR($Zb#l2L~zmoX95J`}-UJdGa_>bafk3M)1L0+8|}74QGOtJ!(z#nz$(cE8R@} z4SrFXueTfjEqpR4UUA7}j?WfV-#Och1CP%he4NPr{(R%wF;G;&HX-uhngkJA(#UjqUZnv7` z1*FDrGw#}enAes_*3F^f-Giq1rBPy*kW!84vC$^=3wA8bdd};`y|tt9{GgvG*Narp zATNE?xb8hIsw$Z_!GX-A)ZN_T$nVqBxGE3w{IBFS$i5?{ICMl*uz9T`HB*A>p6;e5 z%iImkZ~KkA=75;jdWsufpT|+OR=fx%w>z3vND$un{5$ZoN><( zaJ|3QxOeQ}`{&@{0#xq7^GtN?bW!ei*lK~NL)sKKofYM-DRZn~(TTi$?ES0HXB!u} zT`KeLUgMudR0K7L*q}zq)M+MkiTA(gA@#ReCh${AFOgg^(*%a}7L|OhtBHR!UX=Hf z?WVNFE2`n)5)=HmBSURRsp&A!1V3O;0shZe39G$lt_X4tGS4_h{BH|Ws^-b2;U;D+ zNDy*URkUU{_jHje6bV_+h_)SW>RYEpW&YCJ)DH|XriUc%Uu{|*WNnDN&)HE!Wruaf zbDYQe*8%YyXZ`0n#!U9>-Slp_=%|?{`Kq?PaV3_}JYok2bz-UB#)r_MsoX*`tGy| zj9x<1kR>z>nP)=Xri)5FHOG|X4j1L$&*+E7)Ls+1XS}G?Okx1)X=YaNhEJH_Ya1o2 zhs-_gO#Rt@O{y3r4&>v{hAlH^Bv8CQ}iJ>q*in#`73q5hm^l3UiAx{kcV z8}XxLwyP$ROwdwBxD@%3Ii~UsGUibD|4w5Z?}ukjsFK|r@fDJ9*d^DU@+6RMO6JUrdddAC5s)gdF*PUuH#|T^jr^7<-=0OyJd$cUt979 z)ol|@$x}l`Ro_IOC{q99R!88vxsF_~WCI+Me`NBAI8QuPl1u+qc$c@if{%RpY>7O~ zrjZ&=@Cl`#P<&=iaVFh%=61)^B@%#p3(pGm=UFBd>?$hWOtK8RT-`6|Z$&m7`>i0^iqi5#Hb8<{PHWZ{2?nGwpj zhfHSBNKy4iiI5!4GK+nm0SB>X_nRiR%T)Q#ea8FVdhXZt;=6{*2-TCmekhr~)KqsN zg952blTF|kgLo1BAW3dy+XaD{!;O0ja|88fo*Rg#hOqwU&{FO<`E@RSGnZ8MTVx8m zACkiEb&@}iiwmwtmYSN*WV*vUWwMAdcon5N}aLZN(w81IK{ zFvHiOx8ruQ-cj-^5*1N>$3T^p=Icq1M8L-=hvbKhawy678~=Xh_y~To&(vIhfcg6Y zss5Lk=6Xsv%skHXB>qIow2)b&@=q|7qjFSdQ|KY5#Kk)S+~k+I*o!H2hb8|XZH((z z%lQ=8t@w;tF`^4kn!4`ncq9Ie-vn-)B&L|y0FvALnfMdu_>Z5HmL`&wkpB~6jk4` z07a}FqbceKG4&s$^Z)xdIr^QDdNfR5U`ocF5tXW9_KWC!`%J?Vmqo>2SYcY8>_z5W zFTO^Xsf_S_qvZd%fKe)4P2uC@|31b;Y03%CEu2%*ob^%lj;5{WP4Qs1;8c=gQ#9So zSPxg#3X>YxLzLIM(o{|AEhgKMWMT%cpN=<)tNV*;>ek*=PG2Ps9>LQYu-dU9iEQfM zxuVjaPcaqu&?o|K?`g-c8pN&MZEHN&Gi^fS(YdDB$NnJ_cTO<=V}r!}o;@Y?cQ!Xs zx@NUW-Oqyv`9Gl-)K6w+Ik1(YxHwe(0GHz@+oWC%AemJCglU$#i!yulKMPsF{v8sGm%LnDBa7_y=>wllNv@89LcW_nkII_RP(mM zrZ`Q_k7{0RywlkoNA;?urfPLAZ#&MF*ivpixXU^@X3rEiqO(bG#Mt^?(lnGv8G>2f zS`|c1cCyJIG(wasOk)E0pC4}m8@TEbUK%&W{d1}3V|w-IJmWnV66Jr6l`gzpcADsK zdG7}*7aTW*%LAhPD~K&3o6ju9bU!ZnaQXQUBl4#eru?0@qB4DWY@+`2lu4}ZE-J=_ zrJ9~TXvzl@`G9xR1mkN>){DB8i3v)k<(lei7-z0&NB-|AF@fQS*~vQ0F4kUC@{8G` zyt|^NwB-PcjRVp!dbEjLB3!S&3p)PC4{XSa`QIR^**e!$^d_1H@BK_>)NtmGD4Vac z{LO^9S9IdsNy>h=)p*+k#azwe6lJp*s8Hf+F_AWHMR^N0m;_-r)%00slUlS!RC3%F zQ`L^G7({Y6o8npYmd|qV9_r@!E5oBY$VyfHg+EvwWQR=swU?~36f)`-Y99w3l}%wsOocwhy8Puk66 zyG-Nm)uJ*JNy|oPA^|Ie*HR@7@9)x%U-Hqav6D>b7fErjt@{i7r;SM+V66$an?|nHs636HKNP$w)}N$Vi4vG-(q5?jfrDZZ0&0&WtswFD{F6yGUA4Pr4lU zZ4$S7>AcBw_KHd@-i_>T37zd@Qh!+`%I#q>p{BFBpzilLUY;s$V?6M=T}RfVo{@9Ia^Gve~y?yKdDs}?WH!WhYI>OA@hUfrhdyjQHe!;O!+dlS=9g* zp2|f)5{>KmYTz!|<(M{F+{jGfH$?cA$qw$QFnq)JnatxXK9KiFf8PJWA@7m(#`RNn zu@KqN*OV{1C@OO;*8=J{TsDb$lSO&gbLXJ^Clr^d?!=N2;^I`PPg$g*`ND2f(JLs* z+qlOOC;!VE=Ykq>%<&-MI7Ba#d^PZnXE6fL9qUc3;ee>xQa(MDOk~p;?)|{uHjc~<-Ao=^SxK>=e%Dk zrwuWox_~%NaXuyWU++;_dN4Ma%uk7GA~K$oT2#~fPX!N7Hqk;d7UBIa$GERwEUI{Z zj`6uE-$l`XsCGo6gdY;_oWqV+I*1#d_mbdR`txXT{}=WNkR-UuF=epi!_~6d#P92d)^5xLV^jO!Zqf{|H2-FR1z7ggSSw&VY;l5*sYCEf^Eb}%bf zMBeUZ%3nArD${nesek*tsKm45P3Slm8&bQIrnY#pm=dzy5v$*0YJHbQx$`zTrVbIe z`Z@cI>UU9-&3kU8C|8chL^jV5RlY3OWPZR>8ueQ`nZyfAM1}s%_lea0qo%fesu+Je zM!9wpYi1^d+VWP$U69Klg(tX0)v&dS@qft~lRxI17#<+`a5a;yjLZk5(;*Z+;)t=U zjl6Zeu3+e0$89`{1uQb}GWw|rXHC2lYeLjtzsh8?e7cBto`LKPssF)6Q(V_tRCpCn zH&j2i-h^MxAu)lZgb8%8LGiOI9kT*bj8M%KM=ceysQT^3D}*z?2|G@(i3$IyJ&xo1 z#GmC*KfJT4J3mv*g@ID1&ark=-)?i9+r{7SlEQxMZuMhRnx=JyKRha)1FOXI7l!e1 zgRbDAG;Sk9IbSIvZ?k!c@{hDoRJY@p+qzYEOLCvUI zO|q(5aq*q%Z@2*V5sclX{lC|20JD$AnOGbA36qbCh2I=YU6!uxR_qdj8S!M zzKQ%|x~S&OWY3{=7j*uV{u}SnoP2K4D`Emvd z{O*LwBi+Wl=#o6R9^iIEVSZOdK6JBQe$yCu>oaGM87ChaAlvd9)(Mzo(k^U*Y16QXW_;SZhKL zEfrNYhh^WKEC+TdRdD1u6B>1wsH!=g9Pj7wvY;IPPu)z_ujvD$whS<-x!uIvw~>nr zHOzX@w1EZ|Xxu&6G$h$uKw}YQp6buDssH5A@T*Ctv4D#W{vTa74Kv4*qeXK0>kL3@ z^>WAWhf7$k?CN-JvV>7RnYe+P+Wra_rc;sSWdmx`PRHj*q}q6R|L5l(Nx#Si2Jd5= z9JljvqUzdHCX&m>6Pn-4F{QiCb8+!d>LsT6VlJ~C3JRQUx>Y6Q<1^(ylL#T4F@W|4TrqR_Z>SL|$MX67x$`AIJ8P5Vo6(#{i)-8_Q{8>I zsODA+jJsr}m}lDXgW`d(m5UxR;k5&0?Kp6Qz3Dy`}aGRwG+20j5?k=%s)qt-@!JX8%3IKjhdTAnP34K z7l_}oN3s9kW~|A*IW8)|{7l7vN1_;7W~?=p4V^?azqZIEhHMlS?^$MAt`$+ap`|A9 zKJA&2TTS8`f)f^*>URR7nqTh#zW;bnO5@OS?R1kH9v~{Pl`R+Kb_qLfJSDk^Jl)Qe zH}n@(6WeBr-XKK-;dV3`K=ZdVO}sn5DO#>ho7|f^iwbP+A%QH1+?!@Ox{`o^$kR;0 zP~NcGM7Q=3Ra56RMXO1vMopLLruo%Fq7sAaOhq@+^3i;rx=}=@@c&2r7O!c!`Yb=l zS^is7ssG~;*i3I$Za&6Wag5r{<0dkT-x%eMgG_WQTdSz4Pnn`Ml;@zPE0J_0 zetP7J;hY($I#Gm<%FE<_q2m87FoEgJ|2Rmv{YcY%zJ;Hd_1~}9ZdFCSO-&s;WC*Nl zXR06BM1koh3C`MV;yw7y(9(vjcjR{Cw?kkHA2U3Zb$fDJ$=JrBGWKR}-JAoGf6X9d zd5RVHH2%{fswNXR#cv%H<$d{*N&M)fD9?Y{zpq;E2vVwRi0=o_iR-1^rt<%2TA?m4 zGr@wvT=jz``ph6xlcK8zg4s4+38JQn+Fb;%=C?xh0ru_GhbK(aJ3GW|TrN$B{`xKx zbn)Oq72mUR_wbnNah)mY?JV(s`y9^^8$!$DolHUKqNr?TXt1b4_5cdUZRearoi^q3 zaJ{(0#Q)nx%(LSqj+RM>OhG|XRHc89DTwS7<$9T51sZyif`!b#*-t`IYnEpye007P zA8n&}|Ie{brsTV>qM~bprugr)T2x=pFeTry9gpan9ZkWE(V_yg&O4qVB!eWO97DW6 ziKeo#CJImMy&4eE!pI1HXGO3@h4+R$; z{RuuGQNs!uRUK*7tFB|mTQ#&euICp(sy_ipR83;mi$v>X#@~r;cr^aPWs0ZM2LOdn z&Y}M6zSE-ec!QBRgT9NNdPtT>KxDRk(y^2|AQO21K|C^dB+XR)l?_8W>LC;*{mYB-#kZrM=c!W z_^?4r)%LL_FmZ^O$(JQyXy-PWzF?mbOG4~U7N>}B^B}8-BPNqz-HxiOyP4$Q_KP`6 zAu=kCF=`n`uw(c;{~Np=dR)Q8wZk%r^xt7hf7?-1aT)nS@coV$vRXXDG~7VmV3tGO z^r43?^L{wFqkckf_GK!X4d6FSd z#+$(5VWM(BTzmzc6Fy|pal>p={r+fC!AWFSse^4D6B+-56W2=fFHV>yN~p{wB3Wwt zBvZebI1m!!Sxg}PPugj~w~usc{)5&rO8lNjHP|3EiMpFCw69%O0`&)gy6`3;+ozaccUlBpCjReU;VxQ=8~Bu>sS z@x|*zwan1T3z_aSwwlU%+WVm3iNROU6(jouhp8o3P_s96o48gSGJ!fiRs@IaGl2#| zum}!i6^uY+pa~A*DTqY)qN(o}Cy>cHzjCr^_&(jUkT`L`#9z!2)iQm9Wc5%mt*@z! zO%_$~6wT+<^pnQ*+FDV;+b5ZtS)D~y9%I}`)1ZZ>^p#5tV3&Bu?`~6nTSrluAU_Bi z+MhCsfAE7MzIZ6_e_c!>$Wwe=D0qs*QB>BE9E5`D{4k1<%1k32Q)##ruGg3`BUG}- z)Z9qi2i{*CFp(ec6;utBi>eL` zx2SoVFdc$-L`~&iXt9N=7g#AFj=Z5FgvY` zak!rpPTMR6zwhr@%|&BYa9$zd=ZN{K`Ly1Ed;1~BzFcvm_Ny6=Bdw(tzAqUYOfBbH z$*K zCrA|TkI4B^Y+jgwqs5I{Quoc@*sh0fAA_4ZGKBu$M}~;+^W%=gY4NFTWW6I(yT-Uj zFXbo4V?7)V{Kt^HeW7u67E!s|dmGn3CWy-KN9K#VgFFy3iZl9>wgp!(OSF^i^vI_h zP3FDBqWsUaHL+if5>@y838v|pOQJITA*%jBiAnsS9WS6{hWtk&Ch)#H&D3q0BC7nS z{O@WsGia1NnKIScYqv`^Ql~eWzyp0mCD$^0LB)v%lbpFzRLwKJ98WSyMR3G4$AfLy z8EYef#ugJC)L=Zd@3Uc|ypxBR#I1~Us%MKy9_S(Fi^Y;e zQ#tJ$R2fal%ChGqwRpWL-OM9az1#f?x>ml6=b3z%VVp6)o8!6OOhoYES;t4Mr4+F{ zNxwlLyWV&^^GHW&vm;u|)Lr#mEYU27n%9q;*mcK5`M;TD0+UFogZGAoCLU#_ixzj> z4A`RFM{zoVCkS~AG-QmqszatD;Ji8j9~HIV!t4n;2=aXih$LA|xn zlrCjjrIvJde7sjm4GCu-Yh@@39vTpNF3c$-i^;BNLlrN*8Wtu19j=#62edAyxD6zr+^0dSQU^JwI8@{B`0( zW(n=N;D52sv1q;c;d`#c6nV2LDe|JIhso#rH4E5|QKV9s`Fzysx#^DIo{@aG2uiBR zmjtem*+#}3{Iffm@;?#xL1tKIQ~ysUGDwj2tzy|1x=9R8FEkkM2UA7Wy}Q7avj{Q2 zI49+Zf6dGlB{LX6P<>0#G=Fb{s6fFcliW<=0c!s+#j$RX)SAtlH%rx;ktSREVkc2$ zbC#N>ZtPs5@evjah<=X+DC$Z}jIW@TsM3|BV;DbYvf_Su+O$;n6qUbVtEn7BGNYP7 zvI^>dy429QMZo&+>>*P>;*hAqdupX{;3+cNiSMY-$lW$HCi)C}gN{A>Bu|lCs$6-8 zO|Fv>hi@a>Ns-& z9hh%?=XgNDe{7rM{qEvN;tuA3<``#|br-gpaIYPrVtW}E5dL4-)ReQ=kmXR-ai|$X zs7zH|mt&&6X)=LemCNM0H;9Aap?iS1?#eZhwFgC&|F)~iwCW?Oek~z8BxWr%p|4tr zN^PHEs$JxN=phzn;87Qro2FlG7FF`g=lwW!qi(WcI`TvU7{2`Fg!F`aCX=gBdF zGbvH^cTO@TH*OH+Sv^fWzb6DAXy>Sykd?d&p49`Un#j78sPg9sJ|WY3f~kLtC>avJ zCT|0wzwI=skA0%5J+nu&+ zO1h?v=Pj3b-eT3eorW<8JhIa??pYwJVa7Dm7-W%w%p~RwD0)Clc-eVTo`c;8%`Fzs zw=2YRz6a+K;@L@k$WGF8K3*kx*@jV)2iG8q2@(0j1e0kqTvYuZ2v8#No3^I>dB3P? z??@9mHcwP)7kPoG_^yu$+}&GLaw(-7h{d@0)Ft9RXnN#`$(F2KC@M~lj%pdW-{dvy z$tb55$!e4W<>I=jRa@pU1& zZLEpCx>ZzNxBjMS*&I^AVZutZGsJ@iLQOEvI4=16PnfJCUwfSCbEh`aiTXi8+%*#iDbJ`@!>K zCZxrU+z~lfY~;LA3Qn`WKyCZ>y#KvDi-Pt9A(44&h)Ex!eJ1MX@X;X=A8SGz>qMnq ziksNa7KtiPkl&Arm2FM&>pes@jSQGzM?!mOPR%#rtNV*eJ(%spfjHlDR5c^_fh|{z z;tT@Hjy{~JYCp|N3AOFcIKDqtYLQvH)TIAD;J5%y?>eUxG1eJI4lLLI-Ah(#VnmwdfsId8!|&wZZXR%>;ZQF2}`y#NqqtIFm_lpjLc?xJMCZR@9xUg6wiG9AM%KDKTM!mP3~lJA8sS&p(T=wZ0;!T zO%$AAXA-q%ra8`@ms*p)V1MU)Js>xXEZbOIpfYYj(*dmK;6Nf zt>J6B0}L+au1Qh=*Lc$Or6!OL-m31AGI%K@e+0oEy zo=JRJCMx!asiv;iCQ(hRnVKW&>0*lS=Z;ZBNJ>M&Kt?&kHr_xCSnIf(@dPd&a?0K9 ztjRV%F+o)96GlDMajU7O_1s!;y+aHTnFzBPG;|zn68}nyD!c<*JmuNQE-_t8|klTyM+2@51B;Mcu}r( zNypoL7|;3i!YSjLzmBhBoiu)O-nfRcwonsSnf!t|V)8FZet!0nxawAzx;NP4L+n5& z6TO*%2EmsCriyi&3ci+O!t-c6gkU={)h|sERq-xa&Twz$xq(FPT2njsqBxj}&E-?i z-@}uUL-I_2<1RWyr3z-&ImV0!jvHHx7m***b`NDUw;1oI{X~^L*Uqts zb___g>u=J}GW&slm)At+E)-S#8u4D#7ws_VA8_x(z5fLHKl|Bi+|QOe#&+M}sOltR zm2a)1U3(d;3Jy4~cga{)dC752t&CM|wmW+GWvpV-VZPr*#;W!s9C!4Vv8r!&ItN_M zKU6F@%~iW)tQpLi9)L{2Rm-9V#|x|vFlKmz@%?7FC~y99Q`E;JD!0Q9yP`Yi?$YefF%`GcI2bh_%rx=uSivIEak``JaYhBoUL#hy5O1lPBUF>SSp(%visGNC`x80y{RWDPvc)B1frJJT3 zZ*@0Og{w9h|8l=Lj&tU>yee(tY}2SjhodIiIw~sp%Z(=5v{O`*cfU#eaI+}4uf;J_ z?=XzniR?}WfuyN>qZRjmD=GQqTvOQTfOzg3C7xj(DZC+{TrE1Dn!cPdX|-`N6H>Kr zoHU^!3&i}KjS5t~7BCGpy?C{IFRA*ljiY6WRAo81JMwH+4^4JVyeMuJD|7sef2Lk! zP*KlzGkG=h#LuypoP+Qo+B!2hqHzF{50N^7 z;x{NhgzNk=;~vQmrY7)1DK??a5R%f=q63b(G_XMaSG^oZS?jC6k!qv9TIM+7m3;NL zP1OJWjSRS>e7tbYC^3<5NV7uu!NVpqx|Sm3S_+T{nZ&-7sMyLnQ}_RNifUperOF>@ zG2U(SM8%IEH@;pwMV0)jwW;o#T|pmTmQ2^TF@erRKa`K$FC;%~XW~29-Gu+#3R9A| zLR8Uy`h=izHT^u*V#@K<@A{geujh+u_AW7HFR!LI&}ymvl7$vH`qBuSDaNs=T*ulM`o(c}63B7b*2f8U??=ksCL z^B@U7I?WV!q_a^u`%QTAY*EG6E_d>iQjF5=tSg~9#7+oQ-GAI9nv{w1Gec5o|GOLn zruLn3-XwahHf(d&^c253!9Gs)AKG@co^AE&L`!GYI`OOZV#5F7s6Esbv#Saf486=3 zF}CBzI0I^rV*T$A#QhbE!z#~1b{i^(az+#46KYJddX=cmZ^}&3ppBw(GC7JNgQTL& zF}z#yrh3gI9~^;)dYhsp9mV`}iWFg3+j-}tyYr3iTs+7EBI-A_HvVZdh~Lj(C%Y3r z%sMWrew<(mrm%*GKp}BK)NJ8QXGCX_s;`z#ac24@sz_Q?;U?Rha8kkurY~qAK~z<2 zGQnrq4W*_Ibr$oJ)I74J5xHWNNh}#7X6Xt^AQo-mB=<{9G1^h7_03H3|AvZ6T`nfM zp3V&ERJYcmlD}{Wf%Qfl9EXFqrSsQczRX^!VtPn~ui=G*GA zzRt7!xVpc$@y9{}&VQe`&Lr}8hzT>saGMEPl$VPIBNR2tujK z08uYQKIG$2O^%z=8JXOcm?_2a8BWDUDMrz063q};Fx?^I7J%pfNMK3pV|8B>4Kv0^ z>;RviI>_8kZJBR8hlYsxmZdbrzNvG5ohLC=eZXcUHF~BgY(*dv-fpK&V(eN`(E*$z zqC%YitdFYS`#B9`r3xhjW}2Fd_KK>ymja5=>E0%sw?kBA2ZGb;HlK4XTWnCWncqWw z$cPzX@j1g!Nf@Eucbo9=QE7f)l$4w#g#w|ENfbr6`+O7nxEbGX#<#mTm#mhss@UyZ zGLY}1f${tZtppgwc_$ZR#EfkIMK1< zRp~;`YgEkD%%E)Xs=h4CVZ=#N>rvSHkdw_C4+^RY$E)uTo2qI)A{3mR;+!J7jp(~e zOz|7!3LtMc3l#ADkwq<}J=rXSq3%K^4aUct1%fy0O!&kQQK7@LO?WWJ=fRV)$cZ%( z4?ncO)CM0p}(Ot8aBF}+x*K+(8Krs}R`f;8LdDB&uDG%93tVIfIf&yFV5ZT2~h zd86xC=epgJq5jIxQP-WrA%z+0dN#GH&LlRfYp0p4YwJXn|7#@DBc*T}iw=kc4w>2n zfh0suWtzlUZm&S)znHzCdUB>I?6yf%{Bu%l5!}t@Bh;-QWQL#WA}T=jQ#Jo2`vIXJ zj+*d24C&K6LZ5H~F2XmSZ=#RQ6flRChl;arbB~&=w_3e!~W0IoNbcGplMWv zU5sy&Uks-m_)u9Jby7#95}~&{nQ;65qCy>7nLz1DPDwc_BgS0L5mh6d z!sg3GdH*`j)bt%ED%yuI57JRT-$2#bv!>)0hEAwyG{aP7F?dAi7tU@*ICqt)ZI~!3 ze2l>gs8x`XD6$2rK*kWG1f1?_*vRPm~!fsWTFUe&9K8F74>sDki*Xa8g=K^I^6G0sQ%{NAQ8eT-GDV-n%xQMDE8P2@-7!btqcJwT{@ zfpFdnd)fazHfVyIIV1vgudyVD;m78is)soD8{RL^7+>yUQQqTxuJDx;&sW`u`K!j_ z{H2HZ)EzWVY7|p1#5~;lsyJaWHj?;H>V=jj$%H|rniiYnaz?{QwQFIL2N#NYo9t(` zgN;e*H0fAKUEjrtPLY(Fl4<<6b0iBA_i)vVDp+rP?`-7$Uw!Ogq>IWVjSIXai>TNU znoPy#r@VfTDPm8D^33P+RNnOTV5EW;FCVI;#Q6;_0OlU9rfAkT) zl}-c|TW1;o#vx4=19u%Clrtrbeq9R!_Qybw94@8d96(hK%m5Kc@T2yhwW4zU)M3wG4 zU($hc2hU+=)sOgLwR4meab%@0Un5y4>q)Mb z>P0@Ux~bba%Xp*L4(H~Wl%f2^Bc_lRPDS$io7z}YQHkT5O>i5-F4V0eKLnv8jZOG( z3q++uA2%?>ZjkT|wC#vKy15*!B*d&)9qIw>p zYBB*u@>~AD+91v+9OXk14yHaYGm^U?Q_!^>h>G%RQ2U z{|@rMkebugB<^9ppuGK!Z{tugp9IB+%9_JYieLspZ;>g3aJwwyy?ih2zdo)WYGT_u zinEG0jxs8S_nzL4|1k9*1eeg}HT(oASqNM= z#njAU_=V7su_l}zLev7G9Yieg2|~hCb=c&s>@UjudBDW-SBQ$=vf3m&Oc0gPxTgtx zHCt511#HPSLwOISz;!d_i@Q?Bx>CM|FjC?X@isQK)tMABdb$&QmPq`v$W*@4j6y~d z$;0IOAh=_)sawNH2_bqulfOtpsOZ?plr(G+RW*t54u+q%#ng0QB8tSDjky1tM{40g zXV7X%srP0$-wu)#{C8!W@|vxp5Qd&_nY7o9Au&pyeFp{Uu2+|?#;xfE?Vle-AlE_on4&K9M6oztBp*o zfWQ_~i|EXdoS$V<&G^`nPOdm#RO*UVCV7aTpx$Zk>?BaESkF_b?)+xzt5HsD1@#z@ zhm3#FLQx5(*v8jO67aq2adyVUhtT@&ium6Z1eo9*TIK}Wh!>t0H<{2rMl1;5*3lGg zh>3Z(gA}RFY(Q4q&N$ySl_G>Eu%jBGe(Rjum@%n47C8NTN~nLD$G}O>ovkILs`*S+ z0dstm-E!9C&Ex=bcm{MZdH>~t0(ks!CrbG*pkxN;0KxnDaTD(|NL2Ew<|gB! zjpYCG2<#tfvY+9szL_f||WE-U& z-Df*Z`Vn}-<#Fdu`Uf@oh>3ZFqEaunH_7?CDdoE<;Ve)fxninGwWmQt@(}-Dy^~(T z13Qm8XPDz5bpxeYRrNRiyN`-WWDIhAODWYld5w6&onj8z|6BT2+93 zMDQ#On9#n76l$U;>D%U0s^`-~HJJQP2k4~^@HvM}-b@x5;Tf>X@uJGszfA;ycnA8T49eC?QjI4?US7NB^Ft zlGTm*8p`fj>kQ@CJ=2djGAc~@YgWFaNCZo*E#^7p0R@ zH>`FpriE1PrXtN_AfKTc)7-@T6UEexmzWyW)1;PEh)TY2%B23VgrFs5=ig51O3Dk8 z-<@_Cyd>2w>bv@Nqe=A`FQ$4gD`xr_O-WT#$2kv{NlHD!AE;(_GXBi=qEgR|HHl0n z9;%RW0(_eeIG>&oA1Z5)IwKQOiO|~%O}PC!f|lzDNbZ%iKD?cnNFcU-uJdspiK%1F zj5o8Zm?D0z@}72jZ6!#>c;IpJh2iZ%AWIEAY6{BciTQen6rkut6BBrl(oAH>4FhDyVrz$V?>3{`b^l%!V*f3Pc@;3*nWY$_u2KP z&e9|!@|Dk&J~4r4>jbI&J>NuK+aq20Y+H{RZV5q zoO6mdhLItU@)c|}L*dh8Mj%qkAOf}b(f*_UyosjrHC9PcJylGc-`jk?gT;y+R6(tZ z2WkJ`8}t~_RvhnbKZPV;y-;Ov@9J;m7qle&?(pkg3t z9*9axJ<;5G@Ho{JkDPR7E~lC#NA2Ptia>}6TAD}X!+@#% zeKLiSLbqtSDgLOtsNh4a-M}+)w3A}_4b`1iI-NOg2jzE0oq=bi9G?Aqov#_9t1U5; z_f{uSp4&M8Qy=+Pkn<1E&qqv21$%f9VvmO^zIdd`zip_f+7r|k#6Rd_Ja6y^sowmi z7=D0>2J&7T;5;@?^3;sYCVtmOY74Dlw>hl;bekg?!#pPNQ#(=F|H9I_l2OgPUByqI zQ_LG2AnO$HCTFPEBvZeZY)I9Zj9OH#>SU^)TEOn91yXqZ5i|VP4eXNIzz(T2TPAN@ z%@(PpCen@IBT7z?y@uR5oY#u-t2x&L@uA~P@+L;a$f!PJ0{;Y4M(Ku0+tJaz783RSd z_hg&kw_QZlZDpGthMz4n;VW67Le-N!jQ2oPl#dhfRNDIslMeV|Eu0?IP1SU)bH!4s zAMQHs{Jp#Qka~5ZNxs-pRI2$VlU&K>B&0gfNg???Euwn&i1S%@Nvg9475N`G&Nu#H zBSj_r1fAjgtg(ra2V#!Rq%)c+-eT5+5FO4G3!X*H6;M#lar^23DSQau-o=!j=Jpdr zdq+)KE@`+(Trmm6|Eqo_T|%AN=bRnLO4va0+?j3iRt*&8DH~(*PIVOJuj_8||G?Q1 zW+89NNCsPUk-X`!$*3z66*x22WUrx&{FgG4WTchWM?Hl|r5LZN`nbvBZ>-8!O*4hh zG1En)lJPxiM{^hfBEKa~eUlEND*qEO)icPbNAM7dh^YH;w;BFhBT<2#djAWLnimI~ zswdY|NC*}kpnr$Y%b)_@Uyd1HjE_a#NGql;q0FfE1DrvV#i#zh&cue3xPsJD=8Z_c zIM1a1SRg9-x{vz*x=&I}iB$6Y1(OY+#l0GLH;IKyg6gh?nr6>qeyH}_ox2H8s=OnP>L*g@ z;AIullq}@4T`1AMHKyd&*`mt4GfepfWXz-L=Q@)+Tf~{p8$R}ft3v-d$0Rph$b%AV z{F5XX@k|;KByVQ94H*y6;~{W%n#o?z&DQJr4FBH58B-$}$m+vJ24p<2&*W?vDysay zM9EM%ue*s69= z$%Si7szpLnauw|XQXN?vLh{IZ=e;?6*4Vw*X_(1pJyTLQQEuSBceF_iTS0q);psU% z=7?|eNN0CT@gcN<4QB{<=xe-J?4x}^Cj!o>_?;wkg!WH0;r`u3d0rl3VxKIg3}GkH zb9jdl15&~Mrl4Xg?E})^a8V3G4-YcY+Zu~0qH#e<-~Oha>;_f(KTfYi$-?F)FrI@d zkyns;&Q9K>=*Kxt1$Ei$Oqq|pWQY&zZj!y}wvo>GJ4xgS{MO%OzkN)y-zGjgmH>gF zLmJygvTp5QayD$IP;I9`Z8U}Rwo<6JN+dqc)Q(w5!CEMh-#eQ6OJ<9zT(jC#&l(^q z`28xy{U09>FjY^D66HNeHG{9PzRkHRxgeDQ4Orncf!%{Dk!k{_xD_jl4smerG# zn=K~!(oj*UOT{GrL;Xjp;~LQZCx6IwHdBz)=QYmnoh6CXP5X^MyR)c7R!=9;Rub@S zCg7lU&oZ%{hxyELoEbE{S(BaMPComce9W}_>K#_V(mcW)NU1^SYf>x`?$0U*@+p+6 z%*WO_#D_7_M6x#}92w~>6Zq{YAKg*O-oQtuS2%T_F8JT%CZuh}E2 zaDFpM>myRlf&*&lw^dXInEGbBMOD5*>Mg3DWkQMI4@pzE z06nP6-c&5v@9ve1Ssk5c80e~*+nlGnOUBd0e=}xGbDr8N8R}_5Bx_ zBT)hVoN*>Kw~MI6y*->F`u_wE-UbB@_O$PKXO0D{=*g26aF?d5w zjOP^s(P~wuNlcw9s%kw8(kPA%Fa?BR&9}3p07WO4bN}bb z^8U%bAb3Xk&G6onMdiJCzA10JPn0Kp*DmoOZxP`V^(v_p>SgM_dX>nfdS#tMMogY* z;0^YHn5FY251yO&Lsc)*v(?Q}6VGMeAdz9fue3^uFL{dRh`(Ptc(er48Q2%F+@ImD}1TIkhJpBlQ zKMzs#|9kqI;lGa-75b6S3E>R#LJ<12*o1qI78RXBcC>naw+T0*Z9wozmRZ##B4&vE zd7`O$h6xC~haBJVMxwmGW+Tm@qDGuvo2k!ePJLHbEpq;LocfFhyO{j&5p`(mVoNGMaP-D_O%5P^VWjgc5;C9a4%=?j$?WXel zMxx5QFhzs+t^Mav72m=Z2B=2A&577=F^^DRa6A|C-tOm&Wpk?Pww3dLdE{N6<@BI1 zsq1$*H`GX;x>20&0m(CD%yq{N%#78I6HWf00#Wh2I+N^IEGlE^08?~fuBhx!xb*rH zvLasVL;L?)AIVrg&t%@d=?%_n2_c(-gkb z5`Db2Gv6;!1fL|kNlhYuKrJBLqGq$ck4Wn=rs|nP6v9Ik!UM)PdGIr z3h^NCi`C9v&H~>%o%8>`-sXH+CwXdr3+KR6$y58Xov(UGp4xxR`DUu*sb~5)vsy@= zdM?YEb(HVpAN(9Ox100qR>@PdmxA+O^5)by|7=E^-Ap`%Q%q=Mdr{%5RvPcU#EDdh zRSbAuVZ9%r15S7VxqTS%2^-E3&nGzzNzD;Q#%l*n(M1KKvOnu4+38&iBx6OxISJkg zYQQ>^^Dehpqx@|;78KIOs>prKP3`z@qM{c}GWD%DQTjJY<-204U+66={sn&-1b<=l zgSsyoOhDEX|KkzpMHC!0FA)Vt=vd5zGn9tNb<~n-n1E?w0CyVpr}vQI-l`XQlHLub{30Q zeJakbf&6^z?qt&bn|z|;`O{6Z|9DXu|DJD(ny{-4*}HpEVpmAUDheyI{>G^%DBqB4 z3SXH=;haaoWG;!?@vBU<5iKU_FRL(>?-C0@^$Xh+=RfsqFu`ADQ#xl$-CmB@N8sjc zQ?qE0sQ6xbG=z@PL?fI@6OGU=nrMV?qKQUyYCGqJphQvi>;mIG%-R8bMT3p^w^`!- zEj>$obwiw6T8dA#V2rP>rq5SoIhcnM;zMdVO)Qd&+nCg!wo+!cQeK#(B6STzb0m)u zKTsbua`q0EBvR)!IK5Xh{^#NMG&KngE~ujE#`gh#8MUXY3B6092^m_3A#`oc4%x`+rBdQj?oU6hT( zCO5CQDF6Fu_GY|)zQivlmlK8Alw#yJC;WoYA|@RudF+^p7qk(TyrZef_|GI!MNQV2 zj6*eI4wH1Oz9YJ(4xKhx_Y#gl&gKSFmL54@%G7;3&*{aRF#M*|rtq}_QIT=11EThk zQKr6qQdH#!hfMWi)&mi2*ktOySt}~gDCCe&6hO_Z1t#=UKvX)Md4_6qMna!?Ot@E_ zsOVIN*y;s_*$7VG<~+KZrW!9Ea{fVXHzJpfGgbd!^$gzcI~ZTlHmch;@ix%lz*k3; ztZtdBi2q!g<#ZY=J~ecc^H2lTtwCZX?Bhae1x+)Oi|3nEE7pyWTvKLJooULEJhsgF zfGVi=Za1m(CyTj-DyT;EK)Q$c^N*NBHnUb$+|>9!m_hZ!9wJt#96<~Sp?Bw)aK}|t z!&Nj&n@w!Dh{HnVZcrta=k0l>h*eOtqo)+1_@g!4|H7l_TQ&`&_GbEX!~-N3BY8Km zK4cuG*+tQ%3>;8h$ay~SoapDg*Ht_yIKok$C~v>p4DWfqsH)>z&*{dS$xBC$<3VV9 zg7^=Q@byfGk$(*xk!nw8gsRUM8Q;GTi%JY4X&C;x119?jJKT;mlFX*9oQs2!shV^@ zXB}@`G~Z<9Pvxc^l2|^NYwFkKriF(Tu3Bay6G^W}?G#ow5xtmL4(hKCno1TQRQ10Y z*&zNUE8r--V1ubU%yIq*TsX_Qof?3eWy?+IryZ2~9TFbKKN_LW(!}sT+d_#yWTH=y z!>blBnnC!&mVq#mA}Bu9hc;eay4$&iR!(JjoC%Y~ht$fWCix11YNT2boJMlZQIqP-00haO`Z`U zt|SMb{KCe@cW|$$cq#Q7$)USUX493F@|99_*-4XqYNKSI8Y-Drad?gDz|l+U%F`w* z&{|aawk4+iEmllXxTcqhJi3DXZyvQ%Y1a{Ldfe1sn-EpGYlf-*H*GuOUvdZuN}IGd zbw9Qe6==*MBY4wbOE2Edgh#0Y4_FB z458HG1txsqL23g^8G0eKi6AnP&;8UBO$t$Q8Sh(MrL;rzDn~|n!F2i|vkMt)0 z>qu{^#6jczy}u~mh@(!QRn!W!WaOf*VYsF;m@%t|+n89%Df$Q^p0AHF$yZiUI~bn- zxyB^l7%eJwEp0xMKW%ija{fC z@WpZyI>-^_#6ai14dPKF2r?pXbeD6On2g>pF=D-@a*4#$*mX{dvjC8I zaDwxAD{@9#asSID{0otIWWI?vWhjQ&Bj+3MBmKlo@`zVGLRel++GqSXk(!NyCpVeI zn1SNZS&iXtfwC&A-zwsf8UETiGAMNc%I;2ip-%==bk~5X}0jj z5Dpb_jGv41fQ=K=9FEQG=b&&W}%acy$PK^ILGbkk6)jYg_`n*75!EsKatHjjkm`VMI zF(8uc<58(UpEk+0OtFxneOJlj+0KW(C8_p}GO0$?f7R!RlSmJcR03nhJJVLOWPcFx{j;)A#S0jD$TgYdk1%vp7um6GG+ ze}>y|N)eljJ{e<*e%?;Ext-OJEvBSjH&M}&*(UTT4U9TC&x8ju7et**G2x%KiHd#B zvW+@*)Kt7bR19ZCR3N_}NBALKn=w^XMg9hpef#|z({)m%nz4$eF5ciYWnQ2zVKbU)wp@|_w~D|cJWsF^qNW@=haK)y@ML3W zIxU!*O22OSOHReKxbyfp@u(-~IFGFn590AwCYj$|ROZklrsx_5DA%M1O3tOs$6D}% zrOlj@S(2%W&N{_oBvX|%b%LA+uZpIatoTMzWzVyRRJ}0XnNNG5o-fXv8mQB=vha2^ZzNW|_i`(?!+a$TB@5GX|O3namMU z+G?z+J44gg)=X_@76(HzQp{r;T@B+(-klQL0{;Y`i~Edf_WM>xj?& zSKmZ@>elVfpBOi)&h&;Vlb^39_B63jYcYwHRBMbr#s2Tn^xOZTIw84)FbYzwCz#|~ z=5I)KVZMgsaiZ7i!wt??jU|axqqWYh^zkaO+4*~ONvS7kE7U{HoJaXF^$g?x)H7Se zU);i^{y9oiB9~FHDq+tLd>_tp_O7B{uVNl}${EGV7DDfl8IN$M1*Z5Un@-Jf-p9pz zWytAFi-<@g`UJ$%dnzPmPVvSsBaJs_l9;lXc$II4soLCARN<|}c@VqCW4t#qMGYKC;E!7nOYNcaq)-}ahtAzLg^a$;`?N1=d!31VP4D-07vjG`*6iT@!&p3pGfdI7 zXGKMS+-Isr(1?vlpOKu)I6UyO_L7-mV4&(b-(4jWPCe}avPN_<^&2?9UG-jND!=U~ zs`{PnCOYjDwFnFQni2EZbgULOGbO@C7sTraOFYdZ8K^OtsZsm|xX-CO$7CU=6KD2z zic8hoT*ZUfo#Tz~6jf1iVw#F|Zf>gH+Ak{By_xYHqU%NMdcW}<9w_E}Iw{1uuaUGq zVm$-SEi)tr-_gD%)`zhid_OifvA#V-`S=o=pMv5;tUnzNd;}L%><+qbHGr-g!GG^C z`HMD+@||d8Vgq(k{|D^kJG-2p87LrgaZl&vIg)AkSYF1Wk zJ-{FLH<5Wv!BIPJiHZJsh^YEI_L<6`$O%F9)--$fw=R^@4x7!0i#m%6v}o+y%h5}y zSgdxEpdQrIsYT{Es-dM@9J$l|7t5L_Qf=3F9k~dZ;bKgl3c3FPMhfM%!E*K zd(=dO46jiT3p(F5kpdLQb~xpn|A?B#8va%d51#!fWBo(DvQdY&>RL)wWdTfN7WH(4g-X?a(2tUY5(Sc}eG=XJr_&UR!GJ?RH93&aIrJd~1Kn z{CDv=`*~x<4wE%*i=_2Y&Z?9u+)0u!A}_Fs7_~2Cn`rBUqUr}`naUG8MO71?QKeTm zHX|+$i3$+JGCAEPfSL^*P5j%FqQVyiOx2=fT6l=}XwdjdTZlQYPJAla!RdRL>VsBH zwbiw>Y>I=)%p*g^hgjJLr=C_%jcIC9Z_K9l%%*SJba~dob%37>XD>hSGSFH{@g`;stX;6%9`&yvWrsB zIPVpjVzrdDL&VA%E2uFuO=>N9e@HHkn^YSLCX(xVaQ*|2)O80<^5hsXA4Mgp_7fRH zs&UAZa;p@nwQ4%09scrTCXq)7MwN2v8GIjgcJ>kCL*@Oax&PyS2A!K} zA`$M~(s;YjIUsV87|${$qloRJKAZWx=ZiOwaiglBup)G5wh0gFBP#Dx@;u;a#?%`H zRok7zqon|$$H@N+J+?%GbJ-$=NLzNhpyK0zsq=OcRq|>hX9>rrA-`206K4RTk`caI z$RsOX74=(ca_%5o?hbzZOIAtL-jMUwAFpzw*P!O(CNf6?v8Ibf|rqMJUwYGs~1- zw_enU7VS;wWH(XadpDcV9#*vxzJ)0^qE8-iUg{xHgqtwSR-x0TYH>%YO7rmkILi1! z)NytGWQT#8PxWKCtp2>x=|Z$zk;iK$RfrFBxultx(<72b~xNV6uaFtYo6(B#jhGpW-%51Q@=l$N?52 z5e%|ikEoA$01}mrOgcXCtf*ui6H#QI=xBm~ElQse{C zHd&7!5mmS^X=)b}4?y&a!zQwHim3Xb1glVa=8WR}&s|KNQJ!W~5vqrsZxS>j%J=3D zQy*tfH)4OGSA(ZjhjW(k#-*%msFr2t5MpgfqH%jp%YtVeABB3Kjq0c!B1ntW`AG~# z=_anyMgDF=qv~^xVlf;R_IW_c&*y$ec*<9q{MPgV@Qfw^sUG0}A@4OZVo`qa3KP5h zjHvje<|aA9Co1!oRkZ&+ited1m3MR#<$cX5zdR%=|5pAW)pXR1HrC{~nqJ!D2)%6)JM zv|i;Du+a!L@9j5Luk;b+J=VzhLMv#%a6uaKzmvru3OZ@-dwX|L* zO==y(a3udV!KD7uS5)$?Atu$efd;C9#%YGLou8+^-e^)6jTO_6Uk86BiL6L?_Bo+U zNx-+gyYp3gu=u_j%*6sMx2n;nO^BlsRQTG7#(PzPsK`ZBCwP{T*MZpA9h_g=a0GuF z@p|f<2<-_9;**{4dvZ*BPudi^Yn0rA~|OH-kP6qIe4W#W&r=^V*Kfyw-hMJp5yU2Sr_%_OH_nhZOSt2dP}cSJh#_qF0s zuzp9wYLoTEGErskaEXkW%o`iKoAPGl{iz?=@qogw=9tK`mZEB3Ib=%zN+22|E@eX^ zLMIoRaP}Yp^Zz|_P52gOFo;fPpT2r&i8G7UDz&tQ^Yk7TrSVD!6Z&9+sBo8gCODHt zXZ1JoQUlT2p% zZK4$8zE;Lx$wD*|`F@YRzbp3+UHX ziY8X|V-o`M-a6{M8IwFzU&FXTUCU}YJYS!4fZs!HIb(8p#Yiojcw3f6!vQB22Vb^XHX9#cFgt;6Z zj(iG=D%�M7FmORri;{rnrHvcc{LYAEjg3-_~OSg+2`O><-zvYwq~>fd9Z4+8B5IDxJbK+T8kP3UJ9 zpAgPw@d=?X+L&;kUZSGY$+l99(p`9v?Qrp6Q+~x>QTbO9hDCfDyP1%@e~B4(Gt~%1 zo=#@iup?r!qB6`h@MeNJub;`;?-y0Jt#x+Ut>2b z>hrgm*o9L>RqxL=rM-KI8qsdD39vw|YCd8wF+!(Wm~alMSqSZA8wbL-GC@W3DSA+( zqc78qBJ?2v0ff5}4?xwwSht7wI3pkU2$`D;>15O>norgLu+xU2y87!@CzmmxdMwNM zt5=AMzvD5U&Me(`UdsJnv4y5&(nvAqamX~nwR|KPeqfNvdwT=B*GN1n-EZPgH;786 z%gnHQxqui&!`X_5oJ0%B8NrBW@J#1#?PQp`o5H6Cb7jxqG>^Nv`5IZz@wuVwLzdl9 ze*BOr{5~owvYH+XwaYnf4fUQGrnJ{+Q6t)N>;ghR)7>JR!-57vEM%x~pCu;x6tg0= zXaV=X@(5qN-U%O;FslAV+625mu~-aW*=VQH0R|?xZ-&#qm@hrrv~d19fG<6ACOebY zF)&%jyR+P@1QfL=zrZs1!i)G52ANgn36lYiYl(|Xlj~}T943FhFhp>oNa2~ zJS8f8f{z%%#YY_eu|Y)A?Gq9~==)YCJa{?RE8)BXCvZ~2N_IE_swhf+?rtK7Xo^w# zG{GqZOJ|spKerGQJuM|FLTC?_lUkc_895+G=ikZX63Ka;O`>uWfq_kuM_^gSXK?)< zl7DBQieZ0SV2TPyipr@WK2W`0?k#NPJULbFRm_OhuYh{ay{KY0|4X|1Il9 zlEP6=P(gXeGj&3}e~yV=+(T6Lp{AyA&?Zs2?3Ba^43y_ z(tbWOqT_N=@!5-zUL?s0?aVOFYg0v~ToQ=PjJU~)_TheDHlThw$vK|{1=Q@}&Og)! zTARoxtwlv^wwcJT`JyUr=Eif>4I5=@&k$Qwzc%5(0v^>@GMYhEa+wK42&baB&k&PX zkS!`uNr)B2ed|o=rCi&By3_MaaWY#>J$HW~HgSTfIl&SCh{tD}Wc~^=p;qvZJI4Hv zN6|G$P39#_L}eA6Z_2lJ5|uxQNds#C=Rb!?*8kXqKz5s|{VRvn!E=J}3d)=IG|4&r zM5W58;z-U}XL7G67L4+17c&04cA@0nFvs|2Fr9*TVQb@?Nv8wvBBs?S?!3_W_jeUl z6>4J2M;;axT}gBcp3esv|MBr+R(0oc%I;Em<$-f@dE=_|bRKAT+~j>!EUI`$bCdtv z6j7y(2&IOC>G)U#gTTS)9*v*E@j|Z8GKlKt74`rKVMzN^OOM084HuFT~ zq{qZ0XUsluDBz_m7PJgX#!u|`Fzk%}3CA{>tQ-m=YDp+EKa=N$;!g$|?|QyUxVP~_ zGi=~eQCZKmH}RdE8K$0!ndF#FtpD&Bb{E|uihPV}Fl-Q)h^xEkI+4X!K!v}g5kmE! zTATP&hxv^T(=HI6!LUKoO_6UizYlx7e>g5VRh$`6MIuXl3+==Z-T}!d>gp6PVE=b9 zJ77yVgiIClKUC2YlhuQhYf!%OtnrSUBC7tvcBXP=LR9sWM@`}NAyK)nPc(VIvB?;9 zw4kcGgaIic73`pe=kj!W9$3c`DMkz&Z)!eYA*yzGQ#0amB4O$=ZbL?Bu;2L`gG)p! z=bMUNQ^a&)(<=og>GSm%+&hhZ}aK#?!IVfL7HZ;6sTB*7K ze-^4s*vM2;!2G|6cnmymt~RCLcNA6Gxw(lBUM4ECtDA|I?O+**XjR5FZ4Af%QuUvPqF>NP{&iaud}IQDaK6A zmMX(gZR%n1KTKH0a6I$FxL|^*KfO^@{@vWBtOids#Xp8b`6sm!f0{={_DqxiJl7?l zgic6R6p}877T{=V z{cg_x>wqFQ=7+_YRg_RO*z8_a7rFdF@b>aU*30 zfx}rQd*uqre!YuiugsL}1vNMbk&H*$QU4!lCm9ndK-3nQ^ap(xj->5L& z2iuCO|HEigxpu0k>e<|KjKUjOzDMqVI-0!SS%yLAstsnuIF4&UL^hdVtMq6Iwqoo) z@Qf*9Jx>LDH8G)^`-v(_b4m)T?&@S>0R|&fu$v&)%AmfplCh&WMsO(L9Bzto|$$oDGvcfJ zd~L$y|KqHv!WS}4?k${TiSp}Mri1651ID{JAPzI8#T43^OjJz+?*{R{!j~GpX9&5% ze={)vRQ6e8BE5#v{`X=?b!Tf+-+Y;W>NtmVwZ#>OfKVpGpP7kKwD&2!c1Ngpo zVpks%B>?Epg=Mv}2!%~OJ8yWc;A8((G z@a#3|x_)FZVnjY$ePo$LH`MEvUYST3#q(3+KAFZBlqCL&5Muqrspx*e+jG~ak1Ttug{ zNJe~3KcV)sl7;#Mb4@&(h!&E!vMC7}Q>cz8IxpActzcW8O0!K5#a)A@;tzYIBF!W6 z8JRAqc#6gYd8@Nb#^W5rfvnp(niut7Fe_I3icQf{CJ`v^G|p5#y_7<>l*8sYcoL=a zyPLw6oE>BO^2VR)oJ$vS{v(grjZ;l^PtJG5|I>8;fijlq6q|cHbGhM}&N59XAJxu; zTk(`cr2L1ByERE^bp^Eqs`Ucn@x>Td#-zR_S4b5cMEzfIkko%bcNn>5^Ns(nT)R=3 z@tI_+L!zpdw>R}KY!a3Hkos;^_ZetX*Rumi*UdAb!>2_BR~71O4jxXpCh>EanR(p3JXi>Tyv)CM8dz0Oqk*~AVol|Qq?Bro@gs#-qN zB=>h0Rn&5>@t&I_-gD>p{tq0NdW>G)-}tt#6IGF4V&Y-G5@g?}V~Vo#L^x0}eXnVF zoMku=En=p;IYCRD=l8u#RTnN+@O9Z`syc5F^Rtsug?!buIZ>fSy-oEaBnQ;Y5vIHu z@gbD_v7cipV|SF_OlK8ie>h>P3J!?!{k+Nq_N)+9vxedmME*6;B(B~hDq6Bi@%)bx zPCaC%68BT>nTU>fZvqSmT-(X`dX5xxBTqY2cWpEkC+Tb;b3#UZS2Mg@)?y z=&B=iFGs#A7;UPLyeTS`B}0PhBZEvT!%Yd*pPV+S)L=<*{gnbgLnc+h#RJu!w==16 zK~dG8)R|OrwW#Wk_QnqB;E(mJ8r?z#J?U4oqma}j?8Upnu`);F{FBeJ{nTJ-IikXapkePPc zR6OhvHR`q=j$0YVBQuTrKN~6FoOQ%#xk&tHk8xl7mZ+juIa9p6qbT>aWv2XJ+@RnI z^fTp;^MDHV*l6Ru*i52F2Ajmq)cm95^ian)Go|F4Jn7c1l+dE_*|!{vnP`C6e;7ug z{Lk(t$7%5cM?d`MDX&rA9W{+V3=s2u4{1bcPnK`QsFBo{p`^!FGm4Cx$~-(pGI=%? zQ#qHB$xkq4Z$`zuy@7Q-Hc0*7dzw+pj);nWO0WTm0ga~Q6d6urx^^;+&r&6;o;hJ+ zq;FKF3$+7=Go(ukDZhAz_%9p(d4dHf>CwlGO_VXUs!TFd=(%FlUO(wlVRK@*nQ!;}5U&)A` zQa)*fso%OxR5r)7Y?L?f-J@dK5JxjE%V?O*)n2)-;GqRCYCy4`eP~;AlQWGN}I0 zXHtU*2mc&rey27Ntq3&Ou zn(QESn^s@HWx`_^;;Rs!gxOBPV~9;H*$-9MstJk5x?_^*u*UII&Rg}!1QUJBE2^}7 zfhnJKhJzRPuQCuKQ>)T#oDD;SotH zayRJ}xc_#tNrsl3e*372{He33+Mf|SQr8k4MS2p040ZnnQ!!vAQ4bzcFfC}@2m0rQ zjkrIeV`*HpE~(qn%ker-#75E8^w$u1;e>I$bU_?3TCCyG+o-$V?dV3u13brDn8=7Z zq6&_3msOu_Z<6=%3m;2xwReo8!U07;8ek&%Bjo6gi0f0H>#Yt?a(r@8T&Vli)F#bn z$(r!ptB7}z@pL3|4tIAdYt{8BN4N7VLUEq?-w(6bW<2e9DY~5aJt8v)8&8nC8{F?v zerx*DVkfB@)V)SA0g8Tl#za;yQ4Ova*Ei`&i&YDnbf)br>5rzEeCf?wM5Vu4?Krtn z(uS2RPvYyLj*D|8jfO`U|EXDoP}HMCONrJI-9HAH$jgI7x&D0KvErDx z)Sr3mI_iGD*Lc2Y%?`Gf$Z+PCP=6=|pAvXfhr2XcKucNWxUQzC`36yu$%l-49eEt} zGV?ji6k2xV|0Xs$M|)0++8s8Lr58kbrudBe112k&h1CCkz-I4!Q@5FNOT#(TbvSBg}B(z+BeS6g|0El;?pV#=Uu?I7l{a#%s$=WI0zxcphYpQMgtw z<@{&kTHV`Zj;$8eu!uVw;^PjR$X)t<^1WW!(YU6!5cAMHaUpYfyNTRRl>`dwY8_)5 zrO+IsJtjr06{zaoXVnx$UY|((=k1fQ}FNUqUv%pO=nu#i%zQHl(sFcF>?-`~veMD5npXqEM^&m+}b_4?aZngLpt~cpVI*R$2Y8R9a>gOQx zQHu1xx!$9^YN8o+5B&i64%9h5qVI^z4NUjB;SBx%o(GL@cXu)W?uBV(>ceJUBrhpr?o2keLoYgB>9BIT|A2Yt~_;K_1jH! z91(3)tlR83u}CV^=LDEgy{Wt7|7J_I+PJMrKUykVe^z5cdrCx=jdh#SCvsv&x0O;< zpZfoHN_C!ods#J;2avgbxud&BGN^cRi)pxaqL|yb+^WHg9Jd~n2DrB#Z&F8F$1Ryt z4W$t(rc`vIgK3?mD$>ev&lJ}GW>Y$z?_W*ehfIH3)Vm!G97HP4F#*rC-6nmYr8rK} zo|4i5{5;ZU*O~ZLWI<5PU`3_UCrtI;5>cr->Oa^p_+*re)Y#>w`T&Dcq{cGJMfF~) zSC9%5tVHz(j9`%p^KVf3G^-}4Kj%#KQI_mPD#vvK)t{A_RCX~#a-Nd$FNWwy)#VwZ z*YSNE7;I9FyeO&d#J^RBj;u0YmO%jPa2g&ln$L>sS#- zjbZ&iG+egA(K00s7~7n*EE@A)oRda0M1m&ExvS#e4>jd|8HAYCv>cq1f+o6yypDR8 z*bn?A-Avhq7UGyi%c*wjX;U#{HVH>u#y|#{nT&*xA2puxYh>#BnCJ&SF?)GoGz4du z*n3nnpz-()Q~l9uf)E5G8qS&OfAa|;73UK|#bqZPEw@Sq8lt3gF=i{7FqFp_S;gFp z|5&kJ)eiJJZk{Z)2t7m14oWVN%Tsi>RK>B5jwyT^h}Tay6@B+HxWsR`prPS%k_@U9 zAG!K*Pg5~@rl>eKsd!z!Rx0}RFxdx55}@MOElfj;5;2tAHK26J635WNQmSsBU^1;& ziD|`@Ds?$!`Kl#r#Hm*29Sp}aXqdU##2Z>mJg=K5Rm`Y(?wDigL8(wn$ciD*e~~F$ zxJ1<0A0yP_z@a$(bT-vM^tLf zNE0~KLR7H(QImLTzo^RPRJx+-rKu))c$cV%>rKbW6%s)*+{5_($n<@r-*Jq3ftqsU zW4)OyfL!}?#`VT7-q|h*?%r<7PqX$Hy!ZD2Yl7d+h{+tG#faHWn$z;mdouz;_~qdy zE5s7eu=bFNKT5ei{PUKpyo3sen3{vkT1M(kw+VcALR9eA=S^ZM6VDK-+3NVNi$sv5 zYo&ZEPMO%SjiPeDoNWq5&Jq>;nrZm(jazEGt5%8kMdCm17mhZmp{-#-843>&{a~0*9z?0-p!HHXsDtAcMnmu{JZ{3@5L|%&iv=cI zvPD$mrM*o2-zPe|wCdzl{gei`8m*VIWDXi$?@KZ+y&(et|e73VF|JTHS*hqGCnQ3e(qT)yPn@}bw z<`xziLHasAW(0|1nws4bMB;tcazW)P=7pnbLsyghVt}Z~Yokr{h8Cid>nYcO@9jMA z?(M;n-g(fJk1Y`uThPJe9$<1I3In}OWz_&tzH>RpfBTCM#p5P`SuJH79f4UAMq*fB zzt^@KkwCIR8&3pu_)Kg^A4E$hd!_Bp;)s64{;vBhm2TMiXBj6BVkY=v@7{ zze%0yB`P>{wn@A^Rg`Z%2ODCCxP2pdS-r_TnGluF%;jc*Sa_$&d00jRZu;k{@>(jO z;l6Eu6GAk%F;okWH{m^AQU1RzG!ZJIRC4YzlkHt6s^McU>xggUW`|Jq7RCABhTkCd zZ9fydlTvFW)}JuGR~Lzj9p)V%_~T(F)({hwE17ECL*|I8{5jQ5@D*=yB({nV#g}#k zC6|};RE^e?q~PI&$g`Q*=y~?ux+BkjWD`5E!(`^sK~Rs= z9Yd^WyU7itL>~o@bT^g4Q4U{Mm+@|2E#7zdiUxC#sauIBz!Me|=FCw3Ef-8=b`4)q z4PTEw|C3Gjm)lIk0eU8g|851Q ziJjn@j1qS%6PtHhRBkf)eH6M%Ol1XEJox^@_lV*oYse&5NWo|-BjM@W-h^K{F3P{) zgvnk?X%8A+A!>$rehz(o)O`IAn6a z<`_XifPfZ)-|{4N_^ytb!j_cznxV9$@8@t4LO%S}9Om!N5&vNd1CZr*tQx)_Y2y3m zhzgBu=jc68LP)n`%!gn{o??&4OC3#e)hSWY>k`KIcb}-(Os=TNWou30kGXmMnDsv@ z-7GW!--p8-`}&Fx#iLJ3u|F!Vf3|k)nkp`M7}=`ubE}O%&l3_*vps53Yp03|-95`h z+`C03>jxR%vMHisD{{!Q`4#0<$aP@)EnNTHW`cXVi7Nk&Km)w__8^EnhmY87rrj)_ zAu~*P^Es-g&PjIjcH>`0B?Yoq(gQ-n7LSQPy^o@%eH1XQBeu*YIr)Hz$}Um9)f|ur zTx8}GYQA1$QXeqbM5vr~9uO<8Fu6gb-%#+lqq5mDQNCY|Fy5Up@$Mu*HP`ES{7wGt zo9y&?*8gG?ex3dr{Li;D*(;B-$4A*)Mm~ryN|~Bd9HdBnz(I;o`C1c65qd?EsaVSQ zm$@dk;vye0in%5rH)w|`c!C{4<)t3J|4Yf|^`e`te#tQa?|U`aF9obtU{>;6NO*=( z503CwCMLkYVxq~mKOm~%jUFccoKIBEne`^*8!9T2+iQ|jsGNsyP2SDM&7<*HWCpog z_nLxL#Dh`UoR1Q|Uz4PScMngt+_OUhy%spGo*@B@p|V+p!(3`n+j)rN+GAuwj!FGX z`y7||kb0FT&xq1|KOQ=Tik~(&RUN4PHm7MB72QX03RRC${i5dZ_o}_ygs6a0wRchB zphoO6^$RLQ1<3)bssa5?O~VvXm2dO$BVV~=v8dYa%w9tB=W89;^IanS0KFqsJHtdr z^rIY`bpk%-hNBKFGoFVj)kXHr>5jYKk}Mj|EjICganvLFGg7jMJy@rB{(Ee-aesV- z7d#@BEUl%Af7b&`q})x)U3qsIUxu%4g5ykU ziK-*J75U$<`G@M{TaM2z@B=tC&6K-*;y6irQYx;PW}@G;7YA!be6vx?3kWzXcRy3Y zg-VV67nRN^o6+A?Jh51mYkhy@*}SsMc-qpVMfllv#(y7ILqtY)a-5_^a?EU#IX+%g z!!ukZ5x-}#sd)VNseLpJ!HPvYZ{*6Hj4N? z%}mWURy9Lv)o~LVzFw4XDR(}^ZtP)lS8-W~>*cphaOXC5beoiao$tb?3wt?(kDIL4 z$}>$GX&cF0_BG*Wa(vY6 z5n@)A^5vJ1#X_uSCzET-v>Uiy;0Q(V-KZ%)SS8Avo5#o6R$Pa;0GWQY{YWx%Tp+xR zYX|%fHZu`#TN2SYwcjM8XGCSbnQih7&oi(@e2Ul9yvu?VNWDT*389gUXyALEkuqX^ z8FnFeO>g7+WFndHi4xq`$CUr?uqf{|JX9+MU(!o5{b3Z%^!2(hG!B9XoNqZDjxpbe58mxJKRLu_ZF3W zcAm)+A65;Vx+?x|!i27)LI&v{T{NLOCZfW}0LGVR6Pw6Q0l6_umxOy6xqVdLOaU?C zRgU5TJEiz$uee_)$7i_xzTSsi?oksy+DDXsUmKGh&Hxn+-0f6+_W%=$tPs=5@p-m&RS+gUs_rkU`ueWLsytv8XC^xqKewB95s zI#JoXnM#3%bInZrU(|Rbbi;I$zT&W`P{T3f`x^%{VpH>FY#7^(SLOS^o6 z^XuLw{0fVe!T;1+lf85VBi#|wux^Ek&*{!UmwV0O&L;IX1*&f||1(5iO+_dWFy|?? zKym`}GvHfUZ(?`t5tZw+(iHgSiwgc{tMPT<<^=EBMljl$Qsv0+mI8QqBPzU!uL=I= zsDghkPmi@V6^dwhjR+Ou3l5r^6D-Mx)cbo)s5B-j66bQNeqj8D8mEZ9#}?yml(;Pcb$=xM2J%iqH{sCAQLw8zBzB_rf7XNkKD)iH+1dKWHEf#oK= ztEVXc|1C0+hx=0h@o-;B&aN`q9{WT!>=P4T$2&wQNYq+gHrJ$1&JY!BK|E019y5ux zmeO-PMn@KzfXrS>GaXP_E4WRcT(d-o0Xp}c2 zNd@=q%bK*O<(3;hX~KIB5CuIT{;fn&5P6J#0g_MjGudCY5Y_N8xnRUMauY)+U&Ad{ zwc+lC)VFjH5d86B$M884MB>$H#&ZEf5`hl{H0HpBRm8$}hj zChB>`WN~wBnWCxUM&8pfPgHncTjPI&Qv=z)eN4kATv8F=>iD4p12&wP-nqX_f z^6G9DqCsLK3(+7lw~vXoA*X=klf;MM`)e1)^Pl2fOwPxB8g4J&3o3goG`@*-V(y(G zJ{13Cixju#F8PA_E zMd;_pO}ga>QK9-4CfJrCwDPtxiLDn!`8Mq})rTjFN~K6%AvTF%HF9CnI&iNUYAOeD z*1$Kf#1s#1FU9%6v!$@bZbvhg$bg5-qzZSUFNfr+oQZZJtcI+coB$dwtTXWs&$Ddf zd5Nr9WJbNol3YmFlaNwVy~ek7v#9LhMP?K^#=LG~FAX=jCy31<)Vrlgw`2T|;MK(W z)aVW-@lGpIg+pdIZaXi9@GKf^!ryQp*O7oypy|nFQ5lDZIU( zsr2n6LA8@}|H<>BvZJUWKqF}u6=$V16&jc^=`K4&g(yN-!F-oa5>z#VP2znLMyPyYmZ^HR zkErA^w~1`pDJt4yqDj6oN0jfa$tGPeOH}MB)_+58$^}#CCufMtO0EmYzQhDJJ96YA{2m7){2K|sBQm3>iMDDKm3*|!WPh8=g&4gNT zy+G=#j0pv6xRi5==CTgX|1n&Xk#h%)yAxBdPzvHXCz z;JIRp2`@S&%0Gc8;~;V;!!z|I=RcAmrg|dt`2^Fj@S-#<%wLpvZk-9;*@`h)E5>PE zP3i@%ZU~&*X@b{tFd{LZgAu+Z?M&l6C5sN%3JQ`D+_A-!AKxv?n_b56jcK=s zmN)4{$K+5a?)4o^_#aVG{+DK%$YipXh_+ziIV2xCVzQk%|Ix6cr-`p%RD_!E*>R+} z`l(PwFB5D=FG&qtZ4xgm73F)0=fCSFMs`!>hC2#ASuCpZ$_nEfa6y#!i#}K>1xMOC zKAlPBEWrqmBm57(0{GW*k4Gd=#{|(9+e~tLf{saoPRuaVu!H;`8+tJvP0ja2x{*4v z+=MDN@(woAThU$%ZW(40s|L_vp|i4*>J@cls(|EPzk(60zIAl<&53B~$} zimdHzqSu`z`*Bv1FVY8qZ(CavdxVq?aydpuD9pF%CWWXhj2Yho_Ei00n<=h1B*lTC zxL>07*N}dF$;F+HZWY2u78(D)IO-8uO2-4y4otH^a_M%Hy<;7BJ?ReA{-I*drt;AIY!ad!YM-!nWLgQR(Su zeEZ4qqd2fnii0KMKGWavpQWsj%{zEBW5Qof5as{(ZWDR&BJ0O9QogprB>%jNb>w%k z>NVw8XgD7<@edY}|6>#CRbtX~sa2?vOD2LHxZSF9`h!TkwbA(g#_5RI@28qvY6nYq zGm8GUyQv(xnWekA*YE0Vic9JLl?|4{T5*I}`xl;NL+Jmp37=hR{3H@oWHXmcL~odF zlIxdHEg8Ip4%TBQJ!|pfx7_bJh$&C`Zo;zPHbs*g}qaE?{M5gL={U>UQOp6GrPn~)Hd3oSA3XZEop6cpS2g^9epkx2;~Ie@2|Y!SgMH2l!o#PbKpR3X%d zQxEB%vZf!Q zo)x0PYi1ZfpOngcPw*KHYoaDT^Mt6Hj}9v8fB#-@LN2=B2%Oz+g1_P-gv65RCQ?P{ zO`Sbxl3Aaq@K0Gc4t2+arsgWvdq;Z5HWM2{zYw__I7?7)Q_cjBpJj2HJV$6NMz5-M z2jhL7FzfS7NF6}on7Zk_@$}roJLCX-iF*Y6kIy%m@AvZ#>GizC_zv+$W|^7;IZ>&< zUofG9DWU@ZjhSGd-t_<2Bq+gDk*Yb4|I*DuGJDkc{*)3G{^@2@cbthssA<>Dl+^k} z#crQzay>_jD!7?0R zy@m)08s4ro@#je*pyoUB!AMb9q(Y+@J0N&TjpL@n5=3IfUK6>OyCb5_mzd-<>VMhL z6KQ3_R}=9--HAP>=BJd1M59us~kzl`saTj$(?CUbGJs0RK_#b@<4H6PP2L~7ey6LQnNLf~8* z6YRq!8j0t)L?cq&#qnS61xV)T2O!+RYwEsaei3T&?KbiqZj{(i$|I2LdBhalS|KWU z!r{A$>$z&P#qpCa;!|x08tG8b0hI2gxB^p{<(X3qhwQl=wk8> zYv~XmKI<)0^YLQdDP{6+^e`beLox);tv115GsZ_^X^Dwc^E5PdZk9=oBW#awheM|B zM1Q_Y790Bs(KN({E;hLvxys&{VEw1tc&7+{#qb)w_Opz4MV)w89Fu}O+B=3$k^*%* z5i@vttufix2vnf*Peja7^)k!%AxY0j`BwBZ>32OuNSTDbgbXTTcdSN!wd8tDF$Kdq zvs`j#37#b70`E$$11k@Rd&f-2JKT)mnZDMz-ysW*@aMyf|3jCk>|GwyaCVTW`1^FV z5c&rdedYdVPXz0SxMu_=R0j=a5czdOiO-cFPRzEcdP z)YqKjXt;yZ6vcOrz!oY0Q+LPnbEO;&g&mq~mwZFvc8OouX<{8FiK7`Uu5MvQwOJPHh&6XxZXwh-I z@ywems_q?TIiji)tKFfdibR8|ex`vsQsC%cgad(@>bW3syK}vN{ zPySF<&do>J@Zi9^x|;ay#C{RGFwK;FFhx{V$1^7M{#&BzKRIQp>pWtHFO+Ik!BPvT z?KHvhvv$mgZ6_rQwwMeNGZk+^=M_cyR%fLM#mjjJDHmB}H z;z#U?R*u$fBnE%YKu7sb@vDl#j?oLmuj+K8 zQCDKToA^D7D;#;xBxyk7oOO=JdEA!SMT@yh9Z$rhQOz6dc(S=P8vg!C%pc@((`HuK9s)|k+&#n7e(vR5E732x7n3ZQ? zF-|dfe0xpB^x>>*14cK>{pe`pnJ`?Gd)5|{+`3bg>odx5;JJno4NC6Jn0V_0Vs2e3 zag?28(K7Y*TvPG|tLc4_XX85LU_@G?<}NVt>wB{N8q=>k?{j!HCD_kh5^h9)x9p5&`{z9i1Z?%jr0xqPHb@V5mP#aS&VSCr!EMEAFVfm zUDWv@bhNdpzJcpJYW{DKiS+8g3wDsI$_kTOO0O8{o9j*KlmWahqoChScia_|29>{? z1hE=<(lKI!G@$h3LB`vIw3_Ne_zevsxp1iAjBZhSV1x1YAepH8P&0&vJ5L$c^Yvnu z?GcyZmc0zmbGt^~a|H(lD$`6VLe=y0O>JB5TnOwwYRaE&FDkXNdlS-XE0?lhDV1-i zdVPr*Gl%*w6sG1iVRii!lFXt|=`N;rGwVfcX8q6TMhd}DQ{2pCll?_`oQuULi#&g+ZQH2AE-30M0u9cVMTHjU2~NDtIpKlNBy5}N>)?tu2wB|yug4`t>glv{>;=%^+FTEwI%9B ziVaW{Vf8c=4r7*#x?`Cs>by%-b)nk?{&7-Nb-2PYW@BEe$FT12`O&8Q*DNZgqMJ-(d2UR{%eve<`|5qW({WV=`(~P#6cxKHql{uziq4ug~rusK2F%y_IjrtW-LL<3vqT>Ev zLiZSPV)d$I9}7{TCO_KI*iLFx(E(HaAd|<{)ZvZ^`*~qH6i1IWsi&>50k`EzoN<{Ke)(8%n@Ha(LqrHHO5kTf*7$nI>hn8 z1*t^!R1P%tz&gi0dwIRRQg9cADu$lXU9FiN+1AttSl&6$CfAH{Ka#h!GVZI|h$?@H z)htkSh}Cb*R9cFV-{E$O`XBR*IJK|4Ne-MLDt8IVXSm0oG@eW=Q8lZkn#$}5QU20H ztpCWy-FAZGN-7-SVRc(I=E>ov=ITwNQopB+0-;UwjW^58xBJV)`@$~PcU~yocezQ` zv(|fLtrXwaQ@nTY7jKxkqxG!$Jc_>`%|NG^A~u#@EiPn2qdxz~yK1O-zpE4XBOFv} z=62&@@0EY&XcIfnOcxaSnmJ~(I*Pi7a~-ad`Au5TlA`^VegRTl4w&kz){7eF=OHJk zzN$C!oR|COnpBq({$UAyL9Toly||AVwYHC_qTjI)k($aHAZlzYGv@IN;^;=(jq>X* zX3QQQ--PUiI#W6)A?C3ytTn<~!j(+X&$Dq~NnHjS?o60*A9WT}(N@Nx_}4!17Sp>W zv8;T3x|=aibQCj>XN90*-kT=!@qAIGgXtfr+nB75lD71Y;ojKO@z)hR|A9@>PmVOH zrA5ourg+FnE>0tjKUO9xb{%7RZB{0Enr*mQPmdY(N?ZZ+;xB%V=uH!%UYKO^=7KVv}^ zzmkkLV$)8TTw;JI_vh4(qVldr;~7my1<9L9IwL)zgDJj?v*oV!;`)NVB|M$>lEPzC z{4F7O_%0;~jmpWx%;>pYMY;bv(0HbH6%`-pHYJZo8L&j9{$2tSNPcq2R8A(okE&Ih zP3X1WV*c-pgpl;jG^rcUvi>8RYBwufAUo{13H@`OsKmH>6L_0`AZiBE=C%i#Y{M-zLR`fuc>aYaO7ng>*%vbLWo zxHC^*?#{W)MCXN3vi`iu{J;`6sko%QsD|(`ll_d8JmS5m`9OLh z<3NmliU*n?Hf(^&{jw#K(&;k}C)S8y{%iIae$rzqHnQI2e#4QBg8DWl_}@;V3LYg? z5C0$co0xa6sN5~wM^Nw>{~nc>oHwoqH*&gd6n{2oV%^SjI-ciLTxRmFhq$jJ_$CE` zD7=~EApFP9nb_l_ne$CTVIrw@6mpYNmE|1qD6HFW{HJG#DjLDfN=;@}T{MJ9I%8}- zD`2ZItE0gEA~(4g+i?EB$koprr#;Ro!QBesPpQ>}e-Ec0BF~W(LGrmRruLaBqS6aa zn%JZ+lwFaK4pT=0_v#gLvxYfU;ydJXl5+M4Qn zgLJQhbhRnRN98y!U`Sq0)B{z2VwDXf_mf9PY|eg@o7j)}UlXa(qPRp=mhCd`1R-~o zq;qMwYbCh@Cij@S7j}vYf3(8*xAztmc`9a-e^@RmJAm47G#n+JgZLYSUJ<%_yGdQl zljXSBLxL_Q1S9bp^MNtuYwq2MPGtQCb$@rqz1#V!woA=a0?#VE)^YzqsZj;9OzNpO zMP){^IttRc`KCN@fz{Qy9;7#!T)yJ4ci zVphQjx!sQ$)1;ev@2VA}U+T@}9^yHm5ER@lU!N z|8U~BDA`_XVkdetBf2-w>T7L$kCJde=(mJeknZF*!Ac)fsyR@1F=qhjIlCN>*Gd}o zt-F}fE6$2}j0I>OYsUD0%_LJlm?%IT}|=l>qS-lcatgo zXqBk4w#`l5djmwJIet|6Am;lZIfP?Fea~q4d!GNAy85v3eX~SV_A}OgMI&YQYSi}q zCN`J*K5~;rn!-DDq)RG()MQipF+G-AyOjP7+?Q z+xRnm=tTCB$dLUeS-DzN=5R05Fuz7rJbBUhuRkxUxx#)n&a>Gy!U;0dZdmmB$A)QUhwlkv2 zx^bC??_sJV5$a4b5$Q{Nn!1+`iwZ9An8b+<>^Q5Yygt(;&MX#{uldaTCnaPR`ULeDvR*5M+%J>eMyLTE_5eGI3&k!+3)k2nP!k9)euF7rV%Il_* zxF0Iow(pRu!Q(8o2ms~*s5Zt@qEnD z4sZD$@s^#TrNE?gF~4pDrr+Lw%rQA9V^Q(lPLt?(I4^7@^J)td_*Ipt!f)FdR}Ve` z6n;v4A0?f*DWT%VPNw9`?V`%dR~uJxdokWw;xZj+`P4ovF-0V{RN)x{l&E?tKZFe{ zUT}+4CExcoo@={^a((MEo^J@#!gYr6n;A-rZ<$aE&$Y8n$38bAC3J2KT&d`B`1 ziH@U9X8j~lf!?gBpJ!7#tE(f&0i+tp>tNI#mgd8_ip6I9%`Id+i_?v--!0>RI$y^B zmS@FZ+#%!pZR>=4}=g9a1TK7d6-;ZDS>mcLf3uOF{yUO@m2Fdu*BiT8g z1N>2i$&cCJgM$eBPB}iDDPs@__j26XUjnLVi6cYgQ+WqCLT(AD(Qi2d)QPBMD@S!t z38-RTLshSJRIZmm<@!9E!Hz~g5>>O+5!@yLGmsWZF3ZZ-Xt1?x6l7z$Wm!R*n(vC7^ER_ljVmxurFq2uMDdTiI#T zjut~lHsPZuy%xBg*Ho^Sj=?P?paO#&ezye7Ccc0FCJ9vZX~Kb1fx8wuM)2{fyZJ3ucsBT;37_yn)<2K&4x{L2x6}5GgvU93zuVYl6zZeBh3p4hl1XKf`f{OAV zss?^Y)h}|4n#Io{D~{AOzCx6_9OXg28WgZ&YVb_Q&HNiRY?C8DY&gHboogIp*;{qT zKu72xrx)t@k5!CMMtL}r;VRti@VLcg2-$gf0}o7e+&@SHY5}jW=GQx(+a>|z1Apiz zW-foJ9_#Ly%ez;T&pRHtzzbp$JE5NI;dqvJtQN7;W&9-74u;?YTtDTgJXX18?wVIyB9 z9-io!*_uxllY@@?H*qXr&QZsb34H%-0uK%}g-;9-LOcLt8R^0#ZV=~!&bhDAIo@&Mx z@?asBUAwnQCt8qDG=mmOiMn#XN!~^p4rN;}INlh>lkxD6X{P+bUQvOL zoJFW#GTQ`x(N0t>|9x|bA>N$nmFkjnj?0co9NFE>*F$9QY7_liUs0n!J!2|vTu4u7 zAx}IaV2Sct{zFupqlbj*l-GI4_5YsbICzf7WSo;R zZ?ra!2=~0& zO;P{-$Z*G?S(I7LlIVfGCU;36vRHj&%=^1dtYafZgbYspObh^{CQULmzZfX0dJ_2^ zlsve^#Ll)9<=HgM6h6v-h?4z0gS>6Ijk4D5@P)WAR86rxU8upS|fg$|41bf3wPQdb-3BSjo>Jad?&~ zKiyGO)l9PBDEx>nH;U&ln`REvJZ4Oh;@=Y;nM=J~@m!)@kMD<10;^}5#-hEV#(q~~ zeD6*W71+aRgUZG&rs_(H(#$~Gfl|?PqNyLSLR4TrS5H*m(9#5Ec*Svq7J0`10X1c? zV^*yM%vsvAlKq~AB~U)4w<-R-GeIYk6~CcE4pmQ6`GQKKpDOSOS5j5#b|g54RLNdb z_`_aN9wKfg-+ULpz@;r5;u1!K;<rtIO(qP%w=5!V2AMcou@a*+0* z6dw1Q`s#zCT!W4}ZjFiyg%O0bqWRIy&kKn5qA?4Y9rsNumP>`=)xQt4dWmGWIBSFbc6CAfS zml8FfjJSHjV?sxA;y6otR?^{Rrt-T@qS8+uHL-JbqDlr&G3Dbpi_|@wJt|kD`2G)X zGv!^6iYoixBvalvLzJr@K?e03q9Cg8IaB!g20mI!aPMKkAvKOT0BTyqOltBOQAI~* zJ3enGMW`IN(YX4x<8|}x#Phzx)$byIa8U}65!Xd}9GOi->zQnh5zD-I?X{y^c{-3Co82)J$ueHbff$du3MIw!V~9t-TZkef7ELdm-i9n z`t5O3_ys95l+WyH5-pF5a{ZQ5428!QnevAhigMk2))aoZf=_G(pVoSl`0)4ouKWQ;uH!HFh-)G{fb{DeV2J*eWTCo$qp5vhp{VquZ<%;t zhp5bwm8Q|VN0jH%Q^u8EA*$-liza!BBqmCR)th{D`(aU4o7*;7KuhX%?q1_hk`F?- z2UD})nz+cMI#Ugg>XtoB>b~8gsxKTc$#+Q8qUxcACOxMk`Jj$cJBPXfB;udYm(h5&q8JmU3Jrw>;D=$iBESm;U|Kk{QqMqR=83`F;RH&j0r!! zRaEws?T+(q$s+I)ofTwW<{1zux%G%+43~1Gen!P4sxPA#gw(Go<3e?s`Y+w2Unfn5 z=!SzP{Q=(Mg7uRw#zFu4iGb2J} z|2NtMvrEK`8!185tXt-Ib%WF(bzgoYn~@x`)czs8Z?^IOK)MC4B&Vd|AWF95<7d7X zvaAQJf|+TK9H|x5yqt2ZXPH$rvbv&5?`4q__%AS{2CjR$JM#DJjVsM3qV8R8!cX<*bft9r z)pN!*frA&}`CU!+)kglDrMYUk@>LC>T`Ar%ov8l{jph$lh_{lqdJZm%H+V$6H5q>0 zi9cuEh7f9wN@*pL@hZx*9z18_KeQFcF4|o(=7W@DUtfaRjFjK0a_pdZ0l7bHb}XDE zIk5i095%Sb>uATTSBO zmZV=>O1!AT1eP`9V{ay|8yH}yp4@s-cx0jRmvL`ECU&t2d88N``MnxD#gX8#JSslf z1Yax>m7AR>nlYR8-Jb1b$}1;`8vW<FL_-vJ_PaNVM9g@sn2Ai^n zhcbG?OkzMt+;qxB_jVDLe)T|;(X^a#`Tvr9LMFkD0TuJQnc#~D*uw)7+d#=BLVxLH zymzLg@Dt_%puR=Mk=icxNVXnnDkn}ARsP9LQW>o_CI*se4 zJp$ZHSTF(SsY?P%Pl2NaiX!p_IJ+T|17%NsC!iGbn@3S(lmKTp)GRz+bO0j1}Lb3BSN@ckeD;`~_cfuw+Q z0!6pqBfxq36;8m*NC!%vz(WC7{22kImv(bJie`N%!1-k}C*bY7I44m0KJEcrL-hhm zFJrv`ifUdG;QaC|{6D@t3t9ag0Y&L}6TtZq%&9zHLU#bA8zGzkE;t(VC_Oq}K#>pf z1J3=Y1-NtH;@o-gn0gRiaM?widtwLY&U}G$yYe}ADTfWGjVoKelyjHk|A9OTXN8F^ z_@YtC3J}%E=-@_Og|rG3MZ6-wwc~w`hg)tDP!#zX`WlPR21_4S;1>eszEJ|)KASim zMYWs`T=ltmJ4u8)3L6nb49u62Eg^=TLRpQ^&F4V zJ?J~Y^*)Z>0B*w;0i~~_MF7`(p9ydq?4aAZqRgQJTrcLLoLrQF7XgZH|5HGVYv)eX zXeVletrsYL;Y6jBjT%!~9%XB0!HXeCK-bv){DBQSL@&iw%8=WY`)>d9V?$LMjD0>*VqIUco# z77LjCI5;6-)(0g5=1%&O6X1S_n+r@{+t`OQ_&vi_eSe35@xSXi9xXK`IIRrKnQs>` z>z6eoYWiu7M{EP;1T?(D)*@P#!PbC1BQPU|4`Aaj$?`Uty;M#(z^Opw|C1 zz9Dd$8};RP0;YV9r5I>>xQRy#S2P35ATaM0Y>fiID!(#`tKHS@!EU%(@p!e!gV*3x z#$$Jr2c7S8wLG?Td9ZaSSIcAjqaHkG;%Wsvj9*|E;yiY|1GM0>30w*fhBxG~b+`ww zA`Pg%bEkmX*JpDA_H=T!0@U~gwtelv^IEPJC?|a%@b5bbmFk4EI*)C34_<>CFOTQn zg8c`VA`2E9pq70oV9vCq9FNgccy1TX=4cntSc@hH%G)vM0QZCwP}9OHM$nM(cob@O zU`Ee*0cBtR!0~W)LMR1FQ86B_fpG$gV)tPE$E9q0k$}=~;A#UD#bT}l&JP0wl&<&1 zPUg#%eN!jE`N9E?N9j6DTfh}37EroxIme?6W$+=W>q6sEZ>6%nrxr6oF|Uek`DJ8~(2c(5MX(Fs7lE6EM%j zjRDH;2Zuaz7FUMDF2dKu}nqh1<% zey^7gxw@hyY*(Cjs27V!^d2hm%Cgg{6mLK3+B;m$DpR~2Ql=m7T_B?0cBO`}pVGX8 zV?^0h_H%leD4WI2@ZK}n6AckN9A()|z0O-SfsMJ~oi1XZ{O_T zo+bm~!AstuwCwG4BTarxCZm>1-YUwz zda$YWJn5Qp3Ji5Z1%&}R#M#TJ|6%Q2A!sqZlYn~HF($Cvc zLMbseUrYAfeACY7#X3JWX0JHSOzyv?i|N^iRBD=Z2sP_GCYA{_w)dF$Q(tmsNzuja z={|hOm$tou{#Rnrb z^xz-jVD#eVKg6d(`zbnF94t|TydzjLDuUk6mS}0u?KxVqIwTU5ceOSpf^^vuCGGtq z&yO4qiGnpaBu{(MPc;q|DTWmUBA2apP#Bw>BiZLgn)ad;wkuy!=1p1?ZrgBLTSmVP zOb($dyOLGZrgF-t*CCgadu6VeGTy8mQ5(9Pqf;;rO+a!EQM(;Z% z?X>e@t(LC-bCTqS0``8BBd1p%)?$?PCkUmy+m@uJ$D9%qTk4YRm&pn$oc8SOv9zpQ z;-I#1l394?tZmV0)M0bZNBF^Kh8IxpcuB3OfPron;dfEFB%J?UKT$G3B=0LBg{^vK zcofJqX?t`CRaQ#;XkE2rLHkrm6ziHIS*Ig+QHF|I>Lj_;x;{)oubIdAwkKjB%IhUo zJ~5Tn*Gr^ydC?dR^;&!-G~}L`q0~7~BBO&1lEKW@AW1clW27O7>hF}~kZ#Ukf2ymT zH=Nv^Y9)Q^E;M+eU6Z`U%y_rt%{Y?0CNX-~)HRbWyU<8u9xn`_NiRqSdpB0kr0%Pw zMwa}ViH!`PQh>EHF}SD%UJh^ zl22FOFhbW&l`1hp4^5Sph*1}c<`zSV&97`1bbh2fvkshBbolGAzC;8FVcXJZx zbv{%yG$E9_-p!G*D|OPhL=+{{`%pupR7RbD48jULqcD_~%qf)JOw&`>g5er6Z%xHm zEgC1K-VaODsCS+etYmr_;#s|4&N&c zydGthw@deglh626EVT6_71VZCtswLGR6mx!NjgQ$ramiODxsPCWPX&fP3l9pZ-opL0gw++sL>>T1}!Cq!!w{U8W#CrOTM|1?eSky64HXU^>90Dz=G9<5gsQ3lF!x ziM2v^CC|4%YNHRn8X8U&O>!C4y(v}k(E(z9VWyKaij(>=pxkdNN5z>*f}l$yO+<>JJsuPX#hP*)wySC@M3zk=88zGaBwXV2=*4|aDr2%&p-^P9_lKoRe(gTABk!_kx z$K1Eb`~#`t4w>?iA}KALD~lA3XB+3r-ZQbc?viy|Xu?WwVKvoJ=aVv(H`<7neU)sY zLz`sp)9-KI>O=OQi+$&^TklIPD|Ii?Z9U0a za{eO6OnO%4$4Y;ZFA~w>@ngfO`)4`!+l%rb${Gw7V&EV00rbV-93_kVLw;ONGnOWq z>GB6TI;#I5$DeKYQp_Dmd#9wRX=0JWMz$aF41DNKl60g-Qb#2Qee0-^Qo2&C_?lXV zWJsuEa7Gq6OEG^PnTYmgWTuDCHEFiZR~k|D zC(TKz{Q8hZR(v^vw!8EudSHTHOK!J5Ni>oiUd;okb4r$i-1~DvsBo#`Fohp$PN5YO z^l5b09d;AxmMX%SyG2owNF%-Ee)Q;_Sj;Zh#n@=iooO0s@RI8((@Xv>d5PtCwMWpa zCF{~d=sPi3l8e6%*ReLSJS~Fq&X@VL$H-N5G$!51wnfW7715J(V}m_2KH$0ZKsF^# z&LZ^Ljn$(UG|s4+Ry{v&ZbQ>T3N4d||68h=^jUI@sY?@rsdE?l@LZ!wP9(7isXPWg*n@Xqu9)RHFQ!{)|yj^oucpV9Ls3$o4^6 zxDaCYS@Mhk%0CbtMCV>LD#*TnxQcrAqb)w)rufZ^Z1c=wdf;7o2aS9Wm95udH%x!Q zC}*d3C^9nWw-1bZ>ioJWnC5;mL`^F~2Sw7lZ$|pDwsVTvK6Llg!FqPCLG2LH-SgGE zX=UKd;gn#@Q!|H1`Iaw*y&LOC9k(Y)X!5FzFmkQRu(0R$OD=mcv7d7H#2bpQ_;`qh z{aLT{)lu{ZV2?NWr$&=|hQ^onmuNIpd8blJ8KyZVs*Da3(^yfGmFZiRANLK}HTtwL zTB}b}&_!7c9==?uGE&1bu^&}#R7O!p3pnxBXBGz0#V<>ARM0WZ!1irat`*bS7Nwdx zL|FA^=jRyc{d`QaefO!bEd?jWBf)58m7A4i@*8T}@l}zGY|#s3A}5t;lgg?0C(QtI zjma5I=gvn-*t4C=59PE~CehHu?IT0k^ShL%qp5g}Qc0PyY0~x&G*~!BxqNBvdThgK zelghj%3K3z`wuo5wOP{w$nlLbklDXhzU)O0uCl1;>f9^?b)Qxmsp}I>0Cjz*jHI3k zN_G2p%3*|jQo6b%9_zq&$|1B>g~~U4uPh|JTBW0dpJ-J44a2DNdu0jf4HzaX+Evo_ z@0Gt&-7XBDLuJ@U8-76HTf+2e%6%#&%~O?zVfkk2`av1!c|_||DQ1S~UkwUkC!9`v)c!%?o*TZrDT7~T$E>|2M4JX zl<_OL(>=c>Yj~QveV|ssP7YGtq4z97`)0)Wu!r(h*<}*2{Kyrb$k%0ry5jolr=?bp$ET?M>Yn6+Y1RBtF#8no3FB7 z3*R$h63foj1wr;xS^C24^W~~1L|z32w8K;|fd8F0C&5G~=OpOKvB04KL+P-w z-&UyZ)!~jqYYQXsv%Yum0J?miLq=)ywEnbry-LM?U9YlA`-h}W>2QqUtLb!8a-7O zImT1=A2}&rHS=lJ{pw^{AC*GK7pF*s2z?*})3kU~s*Y?AsOu=~LG?JQz0KUm?DhOO zPtG%Yta7<}pp?ujQ~hXS*2p;8a7>}+L!oTuYV}tEp27Efl0?H6ZBefax&E@07PCOh z?^yw~By^CGwY;yMqo?3i*w@;E<7MQSJXTNGj?ksQB&(SHFg7MSbyQs`>}!O54SPf4 zLTDxi=pgM~0{e6mZ92c{8Tc&NQj;MNK0Z+QBu#6t_4nMSFCB^D$QOW+nrh_OYjlwr zk^DtU=OQvRG-8>~m)0MQ8%nkue={pTu6BCU^Er-U>UL?3WP8s7?ZsVIF|B+%b1i>q zq`jx&FgbWy+u9s4^_)U|D+v{Bt;KJ=r&Z9}RBqr{MwAk)*M(k|fQeT2Bg7yT+2c#UI*}CZ&e- z4ov~u<+*a29y6MipN(To74o+>6{M-mcRE`V!)I(euMt>u|5(N(MxjikF zhMLBzTJo`n1e09_?U2D90&YA-C!tH;U~6OE(1g%ldpTB~^Ze z9;y2n7yBb25*q3k6G9=SE}LgixGc~T3t3u1Aa<;L4BDex?-*sGm1n?UsQi6Yq+MDd zqnb}N_X!2J8XZz{h*VG(AJKsCdL%?lS)CRsJ#c$`22BY`leN#w3M2Os%-`3JXjXaC zq7#~7&<41qLMmN7))!^#j-j^H_6j(>iaY$})H@_Ul)}DF89<4x`D%9fq-IV!y?JR| zD7B2p#CRBy8OTnVwQuz8d*iC1HtMK{+Gy`kZ2-CLV#rrEY?7rjV=xh_W8$dy3?zxR z+tMPb_a%dYUayWxr_Se~PdYh7J1Y#FOVTlojT&@?5}I4CjruzQ@3=6=msZ{eCb(fm zS}?m*t~K(Sx}*>=&^4iX81YZ*PyNbKw4WL$y%^!9x2g1pGqMWiO3%C3{ zz%iOiCu>#6IPYQ#6kU_Ge3h;-g*;n{a6Gk7(e6X|+9MKf2>TLPJrf8hJ&!CETA4S` z2@22RQ>bm0B!SuzXAi_q3s(7hbj6+&{=RFBX2A8E1rqqpKss2Z<#|_BG?R|5$u}d6 ziVkCQrfP51A_DHuLRY^jYu0XIC^`UZp*VEMNqo+%o$hbX*xZgSq`=|jmre}s~CKd*I*Xh-nS2x{A^ zjibDn_z0G;P5X?JLUw8OG(|L44fYvIs?H{en(@0ZrUbR>m}6Q!o4ZTfYGK)*X@7!= zh8I1-F2=FiquOPCGjZgzZa*sB3?b>k6%ck$JmXgLqX#n1o^b4i=T#DNeSyccd1)l< zzAv;&1vT-!Xh<=(=S)#ye1Aw!Pk2FsY4}bHDd}6SN4Q{Xzts-v+ZC^Is(Pjq>F$J# zv<{oW#gP#L7pSJTBpn7WXflDF%1WX#A8^WTM@NaNa#T7NYB$)23Lk9D0&VLVt(-y> zx|KYyh+UEA5N~1_jTBa<_HUmwRl%%EU3>`LvuOzQa07K?>CS^g!&qgs?(XYMe=v0? z>WrG3SUIXv==`X#Q>&9-KlW|{?Ecx zs*HjbwDgx@QPh@I>O=b5q0~kySnMji?)~dx{HSi63wukBQ9~WCfIsf_vH6qxGrJ1! zl0t2PIa8^_4|>3dlELdNw?l$;S7rFphGbn|*AGb1furHK#Lg6*57VdUJ`quVX1*Uy znVqkaj28r((CXO(XhT}QK_HU58!{;NJsq`dPLZ-XgLHQ2-gy1|W(BI$Jx!-(JyUh< zD%v46`IGZEKdh`(dMw#>IumrR*$?ad>CjAnB~4MpgJY`Kh3~A_&9zbGejf#0y$~m- zu3;0zRC^&VoZSCZq5q!(du1Jtwe(s-B-_%a8-vmQFHcaAMVY085*?3OcVnjs%7eYT zGBF1>P7p)<>D&p<#m2z1_ryUq?%kOz<)coHQ~N3pTg zu`VN-Rvy+#*bn=4Ei$qvnEhydR2CSKk91;Mx&jP~|Hry8>PVC58A2)Etm9K%rWbv1 z6zoOYuOpSR`r4Z5j?$?-B}Cb$Buqg;qEU!;B{7D=dn0$Am?NjZ~fQ0c9{*GXN*q~XfK`8f-kM$|8;n@&lkw;H8b zM=G3dn)i&-pE@s#W&b5ZfFY-S~&*b8BYIsBWyTnNxl?lgLw@^{2LFL64o8Y3J9~mX`GzNrt)A9Y z-}oPI-0^apAHAigUL`HAg0bo^JVh?A(sSNF=3T$yk1TK zx^YZDSy&`;tD34D4)RG*8u}lX^q;bM^lyEoV#V;-m`ZBu{=Tq%Y|Z!eawZGKs{f~q zCTB8VFu!Q`6+Nn|!LvJ<=)`$!2*R3mvH>13vqOTFl7E|gc(J$4++RZ8ZB32UbE`+x z)KxVu99>=2IBk}&h}*_zkNCGO0Kbhf$&LyqotkMeQX=VwQgDJ&Vfeo+$aPSitjw8J zJ#EenmFBw?EL8uK{%Z!l1Orye>HY5}l+;M}(pydZ=#ZHI+Z$bf3N>sth;{!rrSMf@ zd(P`tz}CgzqZg%GYoRt69`G=A<>j*>a4>?FNET!tCaMG3$8oe?`Fac zqf9@HdiKGMfR8dn&&X)>`gsx5aOYsygl~r$KwGYFjHAk5i)2(}hh-^ieuj}(uS1t; z*ULQ7rSo|msAaH`IxK@7)S%MC7tJy_n`zthgJo>lJ^Dg9%OU*)FRH3e7)1wb9CFt6 zn0}twHygXznEBO>`3vf%IUG(WhFse_`X~by*uN*RrA%KNMY(g5LaFUDsLvDs)XS;( zB$zWq4K(|uWFAKc zGbQ->=_u8INC2($8AM@QlMls6Y1^&P6I339ilFDIG#_?$x9`0w50{5CBH&>0-BM|{puTgb1FpG5QzA;5D0SDvgp{lneG|>a{zBw)LUj3Zs-<$#~fcyopNddJj3-y3CxbuTZ!?$?KG{6T#FwNZUD!2(KYbEzm`dK(@+s62Y*5p;8CetB z%~*t;zC)%{n;NT5oVsW_^>p})X=;Q4o<@ChM@q9I41qLwhXkVW+vz5_cfjKpA=o`v z!k48!(vZ#j>IPF#Lavs56KPP31pi&f07LMAGBBsYO6h5lajAI_s|2$hScpEMiFfwp zGl%}y%vpT4&=CdxmcL~yEWWuYK2Pw!%fpv2uBH+5eWriNjkPMgseo%)G&g0T%L5Hz z0e>qNeVyz`D#VtM9c>tB_&2F^aiC#7)_gyxcyqFo_)9Q5>*qI1 zN7_6;1-0Zm;jH@F@EraUL8d*?{&Y6gPfm{4hKDeO0$^Cn2T$VH=$Aw?opS_jYV^B< zyoR|=WNr^fRp|+jVucVJw#=_~k2JTssS14Ty!i?K^l6~cLe>zY46d=b zgj@($(&G6EYCgdVAFUxF6!$&M^S6IK#E(=PO>jGR7*nbDmckH*&?7P`WAH_{9)^~Q z56J~jd^;3Ehy{|Xz!=;g(o3?k`cs@C*f%XEI1mUKh3M4+qlA2d%iuk_H9CbV3yp^B z(e{gKh4|Y2<n~I9>seT zQt8$c#$N2wCP{lwGQ5*F&lFR40DOfLN9ReXVXR(4drlht|CZy~cd-9o**6r7^7}(1 z^w}@Pwwt3G_H&OB-qlsljT}NtzBj_yXNns@S>J)dwtSZX^ROunT4WP^2SAJGqcK7% zJ9NhQvWQxA{KV+El4hV*qe(_h2B`ECPQ;8M{S#J<*}haQPaDTpnoM_l(dfdu zXli-Qrl4cLjvPR`&_QxKXB%Ym&`LJR8c2EGsAQHv(^56rqfFtvAEts{c`!YcjgB%c z@uuT>CO?|c0T=GdwQvp&c@)q8$1+$KtgY^NX3RIyRNC>3DTTj;GUcHqD1~Ls(i4e+hb7@9zhbuQ6UsnLYk8+ATGyZ!m9c*a#2Q{!}(^ znE&)xicA`8r>lO2@V@XV9KZ@@_^$$^$cvu&-f*FHf9aA_k9S%mD{k;VC8b9y6MSh( zMM4Z&ra6MBf)B;E2Kus~+x*Xa5rrCkNy4V-C53R$si3k1r627Ng>xxSlnX?jk=&41 z8N&wO?jPk5G08v7rC}?U`Y%iLJbTCo{_t8-$7IpMi&)D#jc8s(;R93})93Ti_957w zxgYprNh*yArSy+reVMY|{}c9Ur++crHF(2CXJnR`oRJ>ug@pXX0sgdoPG%N=v9XU2 z_}^k`AAm(|;(&}0A&@u#b3)i3*;oFLM0qM>(!m&g?me8->QV+ssoOs-M2Ip(U-PFW zSNwfhVno34K9POoQWxZc_uvQ6DuEExlLCL+>(O2b+$`G$#=z9VJBR2~;11C-FlG>+ zMM{-%0im=d1%`8bC!8f3HbTUhGB75Hb;JeaCV8s!c50fDS*HYS?=xyiv(Y&E4K8@B zz5vh1Jr4!=GyC!YI3oz%IPOUodVLA%e5E}=$6jj>$bub??_k-k05~2m4mMCvP@t0b z91FlLIceDVuhH0=R{}nS9|WI!L#oY)@h2#7wVr#w0WArNAS74Yc~KD~{kB4<@h@Z`g6xg_BQBSe#Ox z8s$gsc$kq|r3x{{R;BA{^44G(+Z`Gt9pfqS=~);m58WDs!L3V&!Qu<6hTdI*k#i-@ zolIA1@UH9U;f%?aTZ6*Al8b5k`k>J36j3tmxDAADi_^0Bi;hNFQ$sou*a@TkN2UgdxKi2^+3>Z8r>ZfLAKZe4Ryy9 z#PHDsa^4x2i!%tB5J(ONWsK}yzkCD6l2k5$+8}iMmaher(AnK@W+j z;et-VUOyRBqk>{3D1ff~5H!$-XTEb9tD(akRox(1klOYJ1^Ix*hAD$LEAsXQJB%e& zPhv-~e`SOg;H*h1``_el+on*G=uD8Cy1ork(N?&<(b=gbS@i6WLB907INOKXz6z31 z_nDyR{w$^B_uk+bs(dcgKtGl@S_Jbr>-{xoJqFNkL0QxzEi~=C5OgGpw8oJmS$#;b zqR%sG<3_k}o$E-GvaZHVrY*$;IZU=&oJKI8iuarj}wK zgB4T=g*KTDwC!#` z-j1=L76Zp)hf0Ep7LI;(RCWQjfSyelln>YG4ch;d8Z3;wo6AW4*D|(B;jOy1POs=I z=wGvVCnqP6pFEelV3dpUlt!}Dz*4IIwIi0U1 z1aXLxoU@)tvy1G~R zrj!2LU=7Xv9Q|)wTZ+RKX|SZ!er|@Cy4M^^Cm%LTn0vQ*2@EZd2Z`Aq2h6v5v4i`~ zIGbyGI3bYSn;ei4A9pyZ;jmf8qPxu}eCgJ2%;RYd4IW2br-ID6jyiS|rs54WiXR6cYqaq4RsWQ^> zvGzo2cwj^TtNhOVsSkyA4~e8bCyIP2u6u}u-ic^VqOQ9FaB%D|b2RJt%X~7qZ$YZNM4cRFpS=n%3 z+S?NnM6R9?bAQyp?mHDSHGnSv5t2{SauWS0t7A$ewN_-Kwbc<~I{atI0jmEi1p3LR zamr5cX0dx^8F?D|=P$Uf{?_7vp7m0QQ9K@c6WY6SQV6xy6hq}S1P4U;1hQWa!TRti z+~m$)!fkEKC&TkPsaQtaUPhL#?;LPn0*!rjDMSTE{3G15{K;$+j^mlHgtYq8owp9M zuv(KvC!&3YLnCPaRGjIHFs^7DM%tL_hmNGj;6cp*}J`3Ka_J29!rp z<>e4r`%XUzyW($oMWlp%UGOcaYNWD#i9xhW2?ba}P?qICruf4N;BPlQl98jQlea)5 z@AzD%k=<0_4QbC|#&vv@D5f9xp>;chV4JG^utL@TM!c39f-D0l<6Jg;oo*ntQP4T~ zlulUz|A?$7P0+drS==7l*TI%KI9OzZ#BvX$^hT{Nj69&MUE&=Ma>7F(S^Jy>cPC4|_YrIthyi*;L|%e#MuRZsP^ zEhgH29)1CiCm^1mtF_`(?QB#Zv|v^-+cGcnod7r zDPl=$EcoI<*AZw-w!amkp##MU6Ug=r#K^AXVKRzO9)?rYWGU3paMQ>vFZ5Q+(ei3$ zf6DT>grtwf&*4>*a`xEsmVH|K>XB3&FDn7Ff8{w#G}(`pEQb8A5VL>0W+~UP*aH@L z+I)Mv1)|0>utm1tl5iq%5yqHM^U7sLF;9!4bLEg_5rTXBre^4w7V&3JuSHqMG0S&K za^I4rqWg0lII5wWWNM!pFJ>Q|wk-ChOAo{;NcKi%70KVobg?_mSat)3-8MlsB`34bt@(y{UgwCIh4km2_IQ;J3;bL0y>u~HTtvDli>1=c&ZT$!C zX}t(BulpA)nP?w7;o>*vT=E7|Tra?9v+c*&0jcZKTUY{)7(PO8}O;KBESN#$t_v^EL7{V2}; z?RCSjAiTlKpRrg7HC*&lbo>o9>r`A^Y<*_=n^-KcwbEVOt0^Ymrl!neBd zL5iNq*z+HJ0?!lLC9Mg}<#n2^DrUtErMAsady;=!38hJ`3fA_7yxJhr6B^{&Dt1Af4Yf2f57|t-9Bo5cg;%m*y;G< z4ehf$=O9)6_wXcshmp#zlc=pO-~Z+)v{6rC*E{}0s)7bBnH$LK4{!jk8uj!OUtnu;&O=x7+WtPWul@4I+3>tqvWQ1``_eL%9!F6FfSVdY3~V;|M6IV9S?VXF+#%1WgdI!n&Ez7z*~q3hQ){X8~9_HaDGciKLSgheE7e zWi1zxsXh@4#uNC+NbJgNC8f7pLs;t?YxfWlPQ;2!(oow@t!OZ9Oh`zfLc8sIPdJUWCfUBJrv6cVXQR#8 zm&A~y!19=DgK}wVplpp}sF@r_7^9DGwuO;>F&=r&3#S?PKV?&_(KsehuUCTfS}cd` zkK*o>jg7IS`?zp0oA#`2tAUz^C1FK=6B^m>IM|&J-USolx?xE%*J(_%*Oo70Z+F?C ztpBA^iqCuG#=#}3}9=5yUcnj%2vFU012Q~?(hbk;yvR+l|y~At6;sCyV0!<-FCB zm+Hu8OQM06eqby3S5hcl$OQ|1?J+v)4$e%wA?nD3TVBOSHVt)sXuJMEAs?P~GwoXD zU>W~t-v34vQx6sE)8qW9B^V;CCO1y|Z_+wHv|$(i*rxAK`}x&sKmNt~o>Tv^&F9~w zwSE?l#y)I=kHyEf!>~;r{={abt)JLJJ>etxsVYK4gQSPAahB0T7|7>W(A-aL!}vZ3 z`Wv2^%TTZU*X(|l%I>W!SskJ;vUehMl3xNRtXasvJZ4fo-Qedme8Z8FpT@7?7LI8En;2K zg85A~GZwn*YZul+VxjD>Z2qciG1yos>7_)dWpIBm9J2b2ivRoDcn+#;VPZNvaN36N z)pWTE2T)HrPEi;9Xw%X;8Qd!bTJOCoF$FG6(US5JI%#dkcr1+BU7n_Za{b(C-J<3wg*ha&De9=Vc$lt^iXniCelW&sEmqv<0 zL;s%OZstdVE>z57y+RMD;1C*`N-Z`k)>9{n{YVoEmzW!AIB!#LlrY?KS;42^lucl0 zIc3=~sg8M-!Z!l?3mUXK-uiD-=jcMAxed z-q+Wb3|p`j?iSNRTRivamtaL4xL_h&fp1Q2TM}ww`stx1k@V~aseuL#vIS7yYP3XK zA&%*9-XJyJK-2G+%UR_Eq3_D6wbklue>!U*&3z^-ONjBU*wa>?xmFBf@SIx-gZtn@ftw zni$sCg+f=T&u4}3{di-l(DiZlu+3pNJV5{I#F00=XxA#6lp+HirvH?s@AVEfWMBt| zCV}qX7t==WhEO%rj0x+w{-U$~UaGq)%-Ub-_0+xs^;5%M^RPRIr-nV!ABrM;vjPX@ zc9rSde}*A|*S3PUnh@+rCC5EsaW~M; zh9oTWMBB}d6w=%u*4!VBV>|B;)9s@9UNKR$Rsk7gWls1yEKO~>;mK5y8*cZ6EAjJl zZg?JkhUA53;pcE(csO-Qpgy-6ar%2pUO4}$I#3_x9bb?)fEJAmkJIrRSOL_YqlJSR z4mvqH`~m7T$HgF5$hnL$o}UPeoVZwouRRwc;W>N|#(gdW#q!i)G^Z$hAi^lxLgkVo zK76bwypF_^;*)6nDCGEAG%Vhe1CDFO;e7b@;_#1YL1}n9!gyRP4RMAic)}6v0cZFw zFDg`~7L(;U9N;-OD*QZcy(OHli0}p-RveGsbxXL_^tadG}rK1h*=8 zVR*3jzmWWBTVZ&D&)@Dw_OeT{fn?jCk^1k`f+&1KxIby8WroogW5aQ#;O;mj#pOa} z*?kJ?y5;;OJ$C{O-+y`%@MVyk2?{!Q#s}8aPka@8mO%P~A@$PTaRIb-T4p>wI3C@C zPzWX76Ni&qGnB5NbU80;Cq3%IcQF=+M^UR^1h|&FagI&< zL$r;CJnF&+=xz)2$5OkwrOfi-EdA6Ol8v zW_9~@6V5+Hy@7}OFd7g zNXW^cXe!tdp{7=fFq8ech|K;}Df?}01ep2h>mo|1J~k_yb*+!UY2ST|@`K2|HUfwH z?$58fIapc1Pe$ zy*v_Me3VDNJe(@SE4367nGR;!Z$z{ZWmP$mU#aPr)RLj}duqu57FZVPlM1OEu6Dyq zu&{*N;6mjJjfDcWYLtTY+!uMsiw@itg-<&TiBywhVU$TQU~ITMvVm+_c}mK-Kc|-I z?ulIDMQb)hfla2UQu=Fkl$ydHiSqM?=La?9rw6sSM~bQL9q1;^4@EASPWNtv0m|}A zzsZvJ7s44=%$Or$95Z2d+l`;zjkL zP>L?|ipuT}1yYY!RCs?hoU}1{Qn)we1@_Yd*(~pFhZxuZ zT@*eif_D+>kmU&pDHVQEV{fe8#L{HH8_M4BN^V~ib?+>UoUZEf z70f*#N`;Rk-I)TP#g>#vKIf0rxgqf(p$uw$2QqBj+u22|J}*ip*TVN^!~*^p&Zz3D z>B2`i4SqKe8inu4@ZY}NqRI$jQyz)R_9nxd(FrW}(WoJCW?ElS1dkA%oFzON)h42c zqh_{Kq%&$Q?b#F+Mr9eJg6Tv~x{^&^7^V ztWXY*wZgfssk3R-(L#gGojuk)zk0#+dQcoUFylAn()}$L#h;D(fPNVW*Fu|DY(90y zro({rN0gNkddo6c;U7`&s)G`3H~%FK8j_Tplw?n`Q?J2kYDrE_PDvh=icFQturiJ- z`KgebajuYiw6~D+O&!bC9dU9)pDpD^F(=oP=;B^_wu}pkadDYxrCe>Wi+ew{gnLWl z;*N$FarR3Kxb=Yg@&az>UkkWf^v&EOCq{7tCH34EaXokD;yg}yaujzoVInsR|KaY- zr)yid(<_^}%BO3&<4@tgQ6aoV=H^;WZjLK1;%@o1lv}%X3?~kq$Tg|va!1aTaZ`aw zljd<3Gv;y!*396R9xmrTi>cEfpDFXg7b6Pvl@Ei*Xtk2APu@GJIj=I%2!bARG@DKO==dEB<*8mwGdEj@-p&T-hu;k))+{%I$-Z5%!x|9dHyj^CZfoZN{q zMO@v5QmziaZya@U?~Ed#2Y{XZNyX(0F8_T$t zo-5@xA${l*Wn9m@PR{XA1y?z@g3~@(#udHi=DNAm(Qa<+C^y#y41BDNdl=vzFXNhk73<2li@;~=Q3$YmZ5ekO^ilZr zd!&rZJW$GYV^D2MLi5f*{|-d=VayChmz_WtpU0Td*CC9q*n)AeD96QhOmuPUTb^}s zL65k&6_YVyBbvEiew)LYFsK%VHFHNXSl+=PnRI0V_wayb?szrEiJ_T0Gq0KJ#wgx| zY4s}lF|MeB`wkxoFW6GcOzeZ8rc`+A#;J2BnGP1^3_##Xzyu7BVq$2D^^FijVQ zqKm?cxU=|e`5FN)1YSj-x_7v^clNuuWK2ORrr=^(6L-W?%RRcQl#2qEzwYFGH&t;x zL+5d07gcd}>t^6zvNUtMQ(TYQ}PUIS*CUU2#k-KBSMD9!6cmZSJj@rqbHeeKY2huMh z{WkPty8=xc;^wA2F@-y^aSGRj*%5~6myapti&i{=SrAoH!;SxO6xWGa(2LRje<(Wl zfTqj#jbD+O5s?}hnHiCh84;P8@sgRDAq*Jj(->onF}5+rIK)fljLgi4%zVj+%*@Qp z*T|5F%*@P)jLgW)h|J84%*^-^nJ@i5{c-c`^gTU?`*7XY{V=_j&*;HsmTVBcyn!Vf zKyS`W_cOblHLBr5Gc)|~Z*HxLD^TzJG6nu(S8kqHkz35FyQEmB#usaBRvG`1%A&A8 zc#{2znuhd5*J#!!iQ32<=%VfENmM#=`teL1y(?4uQ@G*O8Xee%lA}#eV~+5d*I1(? ze*R28WR{JS9~@UK>&tG1{OVRuJDQ7`J*y*0-II)(_i3W$>`T;c+T)Fx+Aud$Mt(Dc zllHPI4zMalSQUp^6{|Sk0O#4vc@BJ@sQHM!7j^vwH$^*gUY4fK%hc)e8u_cdXtz~W zYGif>@WEN$S24lrcs=_}wK|bm=<+>(Rx7TFNp{LD$Any&zN=JtREbPaqsKXM{{hr0 zGB`R*Cph`s|5Rx|OYt~MaUaL+<+u|ZcZTCOa9qb{iK@Dr|7tp)7o9xW$lHlpjx4&| zr;v$0?Z2vA-mr4*%udt%eMM?|t3XR`&QoN)RrS++>VC68F{V__ekw&9k$RTlATpI@ zYIq*KfDErim!KmWXv2_MH~2KAD^VdIC90XWWw$>`V=iTF{;OO|BMSIO0eM<_ZJzo& zD-`ghPiv;1iwPz`b%X?Q(fZI7g@p*3GYysq7fvAQf* zd&=UKiI|Tj$#A7t$DXQ^HMmqO#^q|E$*B2LST%lUhUPy@SG=C2w##!heO9i*|7+K& z|5&vWRa^BPsxjH<|JYh|*x;R(U{B{LJ#B1k~L~Xsb zM!lsq+KSok#%ynDm5plKyz<3Xiia*8Uso- z>#It2Tv(u*RD(`CleFaAQcdS~XHpER;r%GyxAL3D+e*~%tXY9AX05q2MN^Q0_Y&np z2Hs0j=#Mt7IcAf!us~~WO;*FZiJE|n;k(U8ZJKe^=GVp<$?DvhsJTcjUmW?tCI=GE z0h@L(Mmv*qvdOCX=b}(%rs?#hxf+$9p+zez6h0?IBe$k$DWA>iw@^tGMUU{^iu5Hgw9R-BKzQf>mNxBF2v_C(b=5|#ahP4jwf+Bt&ZIFlk@Z-w@~ z?@)--uH)Y`Bwsr?fkTUyIFE5FN}AXQ74Zi%kq7)c3foEOuxmW9R(iEL%PhgR^AtSG!xm(cTs0k zaHUcdi|pj4V!65Y|D`AjS?&lIbtT__t&orX72lr{(n`+_BTh;S0@L z=k{pj?d1x(*P})AdB6o8za}s6pmIH$gskR^dB3G-$Eg&}L^l7LqUlK6$rQ~%8iG8U zfDELR>R>{NLY{SKyc?snHeQj>$7}N%3>j?6lL^pDESG z$})|*FGs1@`E%5Fb&hHib9BUzquFgP?ek^GRFt9EIIq@S$Q>_sDF_)}&n!NSlbP(5 zW2r}>$CI>pZi!;!lht02UU1=Lx)U`UInLzDbtdW{vd@J*K=v}fj0bEw#=II~zBKTk z(vSO@G`2p6Rx+`Mm{@z6SndF$jvzx!BKMhOoj}@8;%=R1RB)70(;0##OsWG+s;R#w zs~KrzVr?&RDR#0^T~{Y7E<9QLm}GM>vonLunl|Xx3MR7cs9jZTh|K2sT5X1o2xQk`OwS6$+idAw5t$k01}X8gi34Lne$Im^*}kCw_5ggO42 zyF@lK(<47g*6J&AwUpWJyv!&EPZh+|+0G_w%)V`@k9gRll;z_sS zVDjF<`{Vq!f!{9vl{s?~4T>%qb&*H&{RtlJjQ41_!J{~&oA)z$AOC->?ZFhyLw5Y0 zq8Mb?=@iXJx>zBZh7zq{_K)FYYd=iT`aKDXM5gk3e3?zl+%`?#Pq@`wq}9t!T2WJ^ z*=Zgb(>*$f?BR9SA1U(B=FgG(EbU5V=^?vQm=K7I-CClJHI4@VmNVAsqVTSwj z6m=v|9UBX@x-4J0FBfWbgISv&H!J*EoQ$+`nUFe;X^&3T%85>G@bf_R9@RYJku%M! z{n=gxU+L7!NlsaUJv!CvFV!SRrKVn6DeKS4+Io#qffvxeldREK7*&gm8BW*sb2GI5 z#te1wlrypo@?{vbHp`%@bc4cZmo?iorOBps$gI^iZF$V8d>L(4e@XJy;2j96@^X(A;-Q?RgCOAI9{!YSaJ(7Fe^{`bm;yvWshU2xwzlTA{O*hDZSaHA78*EzgV38&rOV+7(xslHR8$ylRdO6isoe(U0WgHCJfkMFcmurfT5AGFgMl zr=9yog|_-xM!f;aI{SC3ViqK6exyMMZZK%;_yXC(yjpahT^lmWwS)^ew`K5u*zCCA z`8rZ-)`5THdVN@~zFe??+sZXQr9#<9%5@|cXEU=x$I2=+wZB9GZFoK>>`MBnM7_lh zHCfFlZif@K&#^ zJe~DNkJg2jt8Hn%Vr#uRJyam)zfndHCaHI0ism-Zi&qpXIG{rFJ1y#b!K=}scFnmm zUJ(WHTK_?!rrgQ+H#8+G^2c;7`zTRkK|r#{fmn=xio|~%OQ;Ow0KUEtpP1W&hi)DB`Tdkj^YU4B6%6&dt2Lj7f*M#HI zldBj2tI3-)l$u_u_2US3{a+<((pP5gszyx(gj^t3s{(6On}iR4agCOnS!QEuB&7Z- zfP~5T^%sH#1o0090jG9cU!&O3U_6(!U>=u!x zY#z+=F`<5Ny!Ky2{D1f$0YidIQAl@gg;rdZr?5Rm8u|+pJFZMCE-2H!VDO*E49cG2 zReyDXc3kCDI>XS%d;5L=KracJxi&#_USL%`pP)`&@8b2me4F+nMl)J5*`p1Z?w0pU zweV4sPF50{|CQud)!8J~{*|OfI0#kOjU?&x-$`=+ zBUr)_9F?%EjGFi@zeh5O9Y(5(WWy!TtRmvA0ETH!mXqVwY$#CJcX|9*^AxWJa3)9o z(MlC1+SBAxM0v8--CHBWzwq@A6F{7Y{f|yj#9VqL6KP73HxXy#QBW+vt37uZ)e}pv z-ISuqzxvdN$_=^PDJzbR9mi%DPR&Fdl-kKSyQtlv*U>8|u;pC|@`vyzkT>H!I1NYx z&ckXPhov|UQy(*F;(bNx&GM)}+aqg^N7=N!f2OE=I7LQU^Fvw6{pb5kk1WVx{x_c) zG4g%Q08S`=!wK3Jdd7^Rn}da2g#Tc9J6QprCi@j#S0Ya+iUgnmA;?zIR!`pTp!hvYOfe6Q(?%!$G3`}VtrgRLZv>pG!_b1UU{zEJN!(uFI&*)t3 z#(xOJf=mROf|(yh-khp2S2%U_i&Cw@AJ|on>6~IzJ-$H1 z4kjOdKrS%MBCOyHtl(s<;ARsZ4!*#SJUpHkY&yP%e>S?QKx1)KCJ`s}^11;NJ?lY_ zGM9KXKGUo4Bc)o0w7*-X%$qCKq@(iHk(+}ZNmTW=Z`7hqeDpBonD1zmMin$bgK(51&M4a(K=-9`z*0uR>L@A zyXUMLU26iAFzfh+LM>Wdq5AI#>1Y^CGO1S)+e3>tvu=u@7cwUVVmk(st6$T9TnHNLQRoTW9Ae=FS}L zcBg4cO0hcw+M1R?x}01!{RMUngsm6$*qNeMy3P#-ZKDCulY@t(wV) zS>BoG(QITbPjr|k+V^RNw(qIXS)SC)>ls^%6#r(CR=r&WZdjyc+ln-W*JomFa^GQ7 ztABB-YyocBeos;yPq4a!wNJniLkQJ6ra-6Tie*1a7cS1w#>8Tol8dz`saWC25ni_x z@O^KkMxN&6vTD=McslfE9lcpcZ${jZqhpumXaGRU#1n^XDOL9wpXx3m_{c4h|3Hvg zN5E(H-i5RI-+XOsEFx4cQqZ-mj8wB4S{UaQCar2p*Q5tAwe|TL|7d|`K48}3OVZ^W zPqd2+jZ2heWs*XwD-=Ap0#DhaIBTD8I7#zI`ErpfQBb=PO$AK(DAvO(DSqn==9XL8G;YM zfqKie_8zhtHv(j&o4=@=*4dBE3_#WsUm4H=W)9Z1|-OebcXrq_623? zxwlM5)&QTZNLA-oK8^mmLL1V{)b+eo%XF_++oqclU^-+rb363L9n^d>LH&-aiQ)dWtz{0hJVBSa-oJN z5)?9<$Qe5luq8(aF0TY7AxK?L5L;U#%S5s)AC+p}hB9;sIKptU!v78ayAZ86kOK;e zs$KGkO-Ir3V?VD@=RFQZRXfzT9hJH?UEO2Kb?}06{v~d?Cf-${kU50c$Z5i=kfScG z`rf4jgj7c_A&3p}==jz`&2KBzK$%_XKd?G}WbGfXknKRJg5SWy-dd*9ub1&(jfqa$ zpY>_`K0M&hvy|G9tt}}z+K}qhAwsg#=N0Ps39sT(%VdhK(L!!^ApzwXq;ZK&bGXs5 z#E+XvPFc9wsPoG;kDKkFt)GoUb7zIdAgk~8s`;aGonYpBxyej!GWJsJ|1dC!kGtLX zMxmm%70QbZm_+cn@)UR2TBh39%hc_v)Rd*gn#7GRZuZH*&F;oGJp5R;W^uE@PnK$g zsAgilQ-M3v6n-Q4F4*5RY{+S>ot=j;@eun{%C!6D40%(rDGW`3e-R+iO(X{v#>;`s zeSoM0X+IB$v?D`pe*rjpG8DWeLnjXwYG;0h_M0jcPdoEQv*xEW^D$mc69F6dF(l(` zivAIg>KUIR`0UjG%*qY*Y6_q4quqn2yf>!;FcM_#KUwm}{@bfnKb9#NnUGtls6Tn4 zli)T0NJ(eQWVC0k}SD~hclVE&;rHSkcsMOZsY;`@5rWM~=6#9`@GyfpJS4TFa zsz$yVGM`Txv}c7uvuK@f*i^sOrXghWBgNYCQ-x;zRN-fGU>yF@a09GYWMWvZHe8h} zPc6A!Hz6|7%OK+3W>@$^+~>Y>twaL9DOW10Iu=#E@&>O?OhM7SUZ}}K7@lC@+Mi3+ z$y%RiDOTIFC5pm;br3g>!hoH9G)uux8?^Xozd_zVT-tPPf?9T@sO2Gtl3paau+FEd z9H&lYJ7xN|S_k{9)#I*~0kQ2ZP+hxAgYUXzxu`_Teg}I0&8$^O_Mc`=`h#KlGgWmZ zRvG+O&3xRYhS7PN&pADuFT6KPk>3y>MiJKeEj8Nt4bJ1oDe~NJQ_TZ5nHSmsrEEIP z`yB~6n)?o#?^96DO?I7l6MH`fyzrD=BlvSWnt1X!i()P@Yj}RS7Tt^aM)gkpAW!bY zMQVMyK<(o()2Uun@qNcUm(KQQY5%zfjqC;gX?)P8G4(7Zq?v{@o4MRQ^- z+T2Lgv%;&fvn=Z3{fsBPvY?cvjQ1-2iVV#J$E*D|ODq1!*6de-*E_hm^NI1^B>nQF zSu?OH%i_xM8E{Bmw9ET(fkO7>Y04ku1~S3_4(u*e_Pd4Zx)+r@2PY&JjF<6T^kBL| z>eDsA@C-g>&=O=cvUH_EA;|7+`0>cTHry`c5EE=Nko?9IO^ghFq23BQKW^)N@lcioLzg6Lu2hSUTi0hv#Xm~-pgwXo=qLI z+BgLWmPs7JBp#rfM>7ErEV63PaH_^KN!$AJwd2Neg)rxmm~*)@$X~}K@XWL*;&zKx zKJL{H#`g^GxAJ~6V;|4hI~dDshH3wfLUqi?U}a`#Dnm7kp=$ghMI*Tu1(+;4mSd3# zX?~VDGmZz_Q>e3_k|pmc*I-qu)-A(lkePozU8I_40VLmN{qOyp=yxB^#_#3o8IOB3 zp;ViY)=M!I6H66%d8uq5BHhbvN`1_xr5L9ZxuBQ?9`-zsjv#j4dweJy6weV9Ppq4k z_mcpqG6ASsdEWxhK;I?fr~=<|(;NR|*1}xBMT_z*+Lg`BMuNC=6L)Uq&KGg#dp@DN zxa&af`qZmTm{*G61QaQ07uJj$pS$0zy`QqYPNR&t`6ItA(3#&CsP^;%?HXLb{{>&5 zmj7Fzs-XoM^Tz^BKD$6oe=bni{}yP?nFVU|pBJwUqvOeLL8}-QuVKaO=sEFf8d;#_ ze=krE2Q?2bQ1J8=EkeewGpgkUK;=OaNxvHv!Rywwq-JS*Oh(D~wrd`hw8WX+DrtRQ;Qh%lPe^aStEe>*fq*#E^ zns=0G&&3`Ykoc!cG<60Z@-4uwHx+0evJZf9E-AORQ`uTK#i_=R!2E-=lwJ&QMTRW9 zGDCCjb!qBPDRP4`ZhWdhkqOxU)tJ?F0}d_qxis_|jwh>N(H#T_CkUf4rxWdVP0h4u zUTu-;Gb~zrUy&O5AMYo4zctmOsC0{LmtYzH^lCdk!rAm>)jW>2T%OMI%hHrZS(^J; zI+(9dL04kF(X{)v145!vr@f5-AB}qKOJSDZQ!XoN@+7U{uQDC7WoS3aiLLN|nsW?_ z%rz*MSg(^dhS+Z+pB-~0$?`m&(^{JbX{*uznuzxf67S6ccs7%CYNMx2(FF=8<_qzs zX)*)18($+s{bw;*ZIik=xRrzFzky%hnWX)EzLd{XIl)X$aF7!&hvYxPTb#oqR+kv-hha0!`mq>E(rN!D~jPNf2m;Zh^}&Jchwd3#xQ zJuEjTy*B0ox3-LSYwr1O9YQ=~+^}fe3JY}0fBYP%c7H;PBYQRX7pQYr0jYS6sUX7u8gJ$KwVM}%Z|13?gAd=zQ!cMFOFTM>)VVxb=3ohx zqI8@D7sw=DPk1O@E%*i}kK@ZD0nPDh3`|nwv3Rv0gO?|1X>giim*i+;N{OaNqvUR~ zX+;@cbdNsO5RT}eSyq0~%?5|2xOm(8-UWU1LMVgKc=4jEb5`}$5 ztbP!8j1Y3@jzUZ=tERe8n_AooUs|ON95c<9ubRsebqtAEm4w@!riI5yGH)wUOSest zNNhO@D7sL66DoAZgEjR(?$((5s+1aymz`31_#6%m!V~RH^6t{SNOEVOOM@Mt~oB?{0u^J6588| z7p4t@{qPWbuBkx(^AwRh#kz;N37&pQQNHHC0+3jfsFg??J&`(zC;7h|>3ve}hiqC) zZ&=>Q)1ujhI!I4M*AN>##}cL|M%@Pkik>igjWRBS^?0o#l(gbbriokm-qNw+qrVEwx157m+j?=Tc-0>GvtP=r_6)cd1L}$u4zW?^62= zmpUf8RP(7tBaG)x#&hXg7A*l48@k!ecU9WTIU^65bmHB3V4Eb_E=rTT)uhD_fHTLJ z=)_b%Ug(Dwb-d=4XMUm9yNcBOS_N3ITfPv+b!R+jRFkSW!N8{p8jep=`z5T~t!Tv7 z9K|gJX!PQ?|7DT;h*yC*0E=Zs3VFRk>vQOlxm8-XFF~e@^5x_BnLP2Zk_FcU(%lBVIj0 zX*!P4IlM4OtqV#>x7!p%k4Dg=efI!l(4#|Jn0(`supe#>T*AX}ocA-6n%{}{tLD;p z9p0Oy`fq?yu1ZtHOgm|KquP8*;!yK|lLE~T!on;O=rn$7?R=-QBSGc5O7A^b4 z3-C}V_X0Ob$SUpRM4n+2Oh$0ux1d`jCMYf`L47wuIH2nnH|5A`Em3`vO=FQQfETrg zEm9XDb|R*Go?BCHtWrD2bpK{jgFQh3C4f%T>4~vvP6-4T> z5l>m6S{vM`-YU&wxID@EOm6ZCtt7d5s3nMhS3a~jx^V(ATPkkRp<$Q8M~bx?844i4 z_^Vhu&K4{6yA-9@6)5#Kuhw2tpv1-AfP16DUY&7^)WMeFwa^NIb-)%tmfW_wL?+=Z`SX3~t=W;i}Yn*KEg zD3^Hc9TL)nQcLVs?P>t$UCz=hHz{ro@X9s-%o1F&+srbSnlx}bH~nskg5LrQ7%11q zY%?4IqMl@eoc94N-z!(Z{uFKD`|W;y(DOyUS|iF8nFBn^4;#3Fc5Yy1pGOUO<(f*| zcB~!R1Q+b(f``@>X!ZdvOjpjG1V}|ZH;Sho)sv#*)6DAl!mRxTUhTf3Om0X^t$Ec7 zc*?95NHD!xmjyC5Dp$dX|L6wJF>37W#|^|Z0PA6)^rqtd3uKFsU6q~|x8HLh1f**DRFOZl~miS4R!kxJg3M|_NL=`raY7e^mi5O|WDu|B%RtZyN;Q>4^fXxURk;9JmjOg{XQ}fz znBtFxQa}0Mv%ivwwkBv*VS*+hwwoc18{vDNL)?EWq?6$!&FC?x{S$+FZ3Q}&Zk8b# zbR@VyQP&aseVebP24L0IKKU+9)?&Ls!AKMgO7pXD2b=BMzR00kjwj&dmN{;*| zt4rYb+vJONQFP$a(VH-_m$@`M4D*YH>ia*7_V zbMR|89@%beTM+L5EZkHPl>131LZtze=xO$Ua* z1q5uAU3Gte1-3yxHUqsCq-rL7fM(`HUm(E6Z}gzKP>1YpolLHRd<2d(IbT&T}nbdOFs0rV}dO8nvj5upjbQC#TWK^Bas59H4+`NN{-EPo~cMKZ!5DA9~X{z}$ zM-4Awgx8Wa_=C2wcs7v#-!w5z9lz(G z+ev3RIWRfXF8EN(Uq>Jkp>x8`fi7x?%{&!tHk#;0psOqMGV zS1i(|V5H+%x|V-z(fT)-A8`aCQOvK)s#M1@3%`JfGKW$QWX}VPe=OeUs0VpMWHIw# z-GynIz)jU&o~CZ*fqe%Y&(Cd&VW`er@=p@dCGS+1LZ{PSjw2fGf~4lsqFD?ZL$|%t zqU_6Yy@&&=)&sRqB*7U;ZU{cuY6G?a{a?ScTwyV+CVyCkYAQ(%yhteZj9U#%qyRHa z_=0?`e-I`^Op-i-X|l3j11{!bcr&g4a>4(EC%{tOJi(nJUHGm9u`4J8QU+Y~+5GGBwT26XEHP*=vgcugnz+%0my#pi zo1?~ONV1!?>)wA-XZeAmx+9)CGR5@ab6?3!oeH31oQAuM=8E`!Q+@In%W$h6P$G-iLE z+RF1)A8gguzp4A+`;lH0P&UD2R)tK+vT!hf4cR)%1T;jHY5c})9o*;E@WE7hu1wPq zGXB~$?YTNl4cFlRFPWUC7~YJ8r@>%L)7G#wjk*K87R0mbJ!bIJMSut81SI)t-%VWb zUkb}=E41=rsP+cZYhRV! z*yl6l3xdsLBwvUGMz~>5RB25M)em%S=Pd@sTa&cu6Sqc&a}>b?jfIA1ORH8$Re|=r z=vMdRRdQcgDC=oIiNy*i(};I8)cUU(*9-~`y;_)0Zp5{ z?*nAY7ZR?2U#Re6vZ1dMh}@E>)|(SGX{uEtWV}c2hAW1sqeN|OlUnO>P~`7WaEnT1y^erlI@J*EiHb(1Y)jNy zWcw#2nsWdKY&!|CU!%PexIb) z10*HxfVA{740q%RC(q_VS~*``zfI1YlC@=XB1Mao_)vh-{&9&`9|k*!hE)?yZ{3vW z*DAU-=zmP4vvB&j)4eyBsHq-T4C!0Voxcn(bOYQ!J|7*Js3s)y!bFXF5*ii8XaA8L zt$DpfoA$sHLLs$X=Th#?F6}@eO+YoDAyf~)5jg*n3axP!YUY2L*e@|`58|Fx_)VJE z4-vS+qyx08m%DYW67oP0-D}8~tvE$1KsHB^CL=1d0rVo7^tlQ8)#E8TfDGDDsUJc- znU$i$IVrN_Qknq)UHFxoFT%1S+k+D1y#{Xx{jl-vO6~i<5_N9K)LH*al-Qn=r7mS@ z@xn|UygyUsFG;2BfLiwz;W9Ebn5VuQOxm{w&ceJhWZ8W_GOII4M|FW8sl`nRjo)HAy-WgQP7`OSpwjO5q_<_zDSmgB8}u1 zX~$G%I?}^2J3b+&fCL3NWqAgK-DuI4JIgidd|dawYVGW+_N(rzYHj+uTDk6AE&2ur zv=0;8k9kD`dn&d47b>34I5dS!`0g^I(sSY z&AFab%tPdeIxy=zeeFtm;VP>F@6Xp-KOB(><+Af#Tm(t$cd`G01NjOKf@pLV%4C1O z!ZWH-kDA^DU_D&{7$1~-~@dLBWGzY~LVA6Q%@o{k$%MiXq8 zH^F!2gWbOl-*&#`Di83ZM6a`v-)-VCCJjGAXrBeKqe4p|Mw<^m$+$_m^y zND8xv`xn74zr?Mj#FJHPDO5sc_*02~Q~j#HGgVa^tn$%ao1QbPcX1{4TvV_UfbJM$ zl{isDP?q}Muxj?hg<7$mEGH87VuIEpo7VAqcd52MU#2;iQ1eB#LoKvR_x~tTMuy;* z4!uRr|01IfUj{e45(NckwBs3gq@RKRtbEj|=zNzNBXYH@7oYBNr`DQr!*5Ad@BK`` zx=MgZw=&0430vUOjIUf8Xmo1AwYiGE8@T-*7!mWRA)!cpQWu=?{i$*+Fl*|)X2r!Z z3=f%A^C0t}-mGSNFXCxp<;RiCHo3>WU;!UwsqaS31s7meZuHkDJ< zz|b6fQe+-BG4ces1TZzFeSy|YYXdQ;WUkJhj9lJf;2xvzo~mN4ds?_E?kV))Fc{YZFRqBbMm zHGUk0kKL*rlcmA)v$TCO;WVMqfj|f%7i4KBZNT&RhF9Vbz5tV9UA~&u=WE7G`HFvo z63nnv99CvDGV7^q1+UH0*cY-CvNm7KImSZGK&acJn1nQCzL>8wNEp`$ytPpFD`3<3 zxk%lGS=zX-kb{6=;$f+Ok)#d}h3Ti_wfMJqMNI+)*+3p|DKKqXi5$P-F+0&>zx#Az zLn&1fWlD`N)RfOy+Z|rb`7KW|*O?U6Os1qKQ%i1fD{fMi45v9)pGirPnTq)$Q>*`% zA^)r)9F1K-WtXsiF9P$s1|a$B5*@p$L=zi~T8o5z26%ut6Y$&j6azZvXg|pJmh&9S zei+ip6)d}tOJ#YvOshe_N1wImENJ-fIVJ_%X423r5K;oNG&R?)h`XzlzCVH7Fa-#Q z!T&?nL8S%vTLR{{fcRNS+gLRAYnGF*nZNp@8&4|@e+*+A|w7J9FA#Z$GF|8Xqo3(6gQd5r{@y2;lHHp z7R1XIAFo-RR#~ZbJxUCD5S*vB0{`+HN-$?LL7%4p=?i=d+MomF)c?6~ATFX<4By|R^mrc(( zboNCE$h#9Y@qL~gS+@(PWfv7OKiV{LX0n#tP@p-B%qS_l=I?gMpLq>gtB>&aJ}jq3 z3oXY<;@^eU^bmW#Abs`VG@FR~`)ul&0s*0!6C%f-!3F0g7xVeL>#-YCp=y3+Rq%~4 zsCHV_a|QJspW@ozfN!`C3g$)<5-(6!@G>z)5OhD}pf}%7Z6+brlf>l@I#hpgi58ut z`i&<(-eFQElD^m?XPrfxoEEk2B(U7X3E3NDMiE?o$i&ATkO|gV2|Ni%xu}_ojOHQ{ zzX0tctB&TA&M8;JiF{>m%T@Qud?o#g8vY?)2dDeX70m}n`66nQNn4ScjVAS zuzc7Jy|0=`KA0#lG0FWatS8>rwdSfJ#iC)n+Bs+N6K=_ng&PU`8pSiMT+4r@4&Vg% z?uP~1e4khA?}r6TNZ0=xoUb0-x10)X`U0-0zZ5JN-ERZI9w=e60HppqW-ABx9tY=t zho|HOtvKYpqQ1a9U$f_#+RuRN7q005PT18c|1QHoekA~QKG`S4T}2^MX`rU$B$4HUF7`pS9(mPItRdpKPppn5k%B0P}ApdW5FgxBvMJU)1yArblf)l z!DFnpQP>B6ja$JM0H)<^zi%<7^e@0!Gq%GSkwd8Twjfe1Z2VB~{1s(qv?1$|Rt-IZ>wfE*|=ikt5hwR$5sapN_ zR%^%JYB>g~6&;WBI+K{8Et?%uveX(%rQ8+R`VeYIN*&s6wX0)+Lvc~$fKH{UCxCqh za_}#IrP%#7r(d>Kr|eH~z-urRKLkpB0FU@h0+IK~??=M_x`B$;pR!fEgSBlc(6RtR zubDWL&+(*9{`-<4H;VVGW8@&iggx> zX6sE9Apc&dlRvTlld*vuUl&D(ms%BD2eMV^BkJ*K+WagzkC5y6tU&ws6==!=m`%u* z#Z0sZt%_>Tm+7HQXcu;cjV*_W=FsLe2lat&GAUqvbnozSlg#4?%f^y=K~kqA$;T!) znXweI!!4}$KLUx6eg(6L{=oMt|vrs&GY}$&< z-eB^}^C(~wcbSRG9es0!79(4>xO6NePh0M$l3@oRb1;~Y!>*%O&`VF?-y=ate0Q2= zN9Jh7Eoo|N0MCs}*ZRMK=QjG}Ed_x7z#;pABF!15#2Y?opCGcwJJoTPO${C}X#AI~ z|HB{RRBTOA5JM3xBG->nv~*FCJPS!I7sC7grdsQh8S_M|OqOc({f|uMJFr(CpvdU{ zB6ZXiv0V?;x+JSY4OX=zK)Jny;G`FT3V)&TZF0Sr*mdM$3mY9kQh$v2AK#(-JqS;J zJkc%NsLc+{Amjp2daOc6AH^E}O>ZQSWPIAIm0uE4{S2W1?HIsB-Ebjk_3Kl#YASOD znf{-6C9Pz$nJ43Q*2@aSy>QQ8gXv;to12aFstHK%ljF;?%){za(=Xb zOp*>gQ>^By9NFejmv1fy-}8|sFI8kknKoXVqoeo0|9Ks9(Qf$u|Fx^>@hq)90GjoK zOTBMXl!l&YG+JeLSheFyc1{{$n=Yd3!)J0ebUDT89X>Uuk$hTLt+97i>&OSyYW^Pl zC)iO<$~;#)BYirviIQ1IvHBCq`Cp%|{rzT=&{>-N3pt{ZY_-1uA84vm?HhcW<$&Aq z&se8IPRJ>iyBVI(Gr)Lx1sZHAQru&OR8l*1>M`JbHyojls0*;5V_qP1Z>m(y33!Bi zxWk(~%FCnE}4m7)kG5g)7IhWZq^9mdL_}Fyt?;@=ovMK&2 zo2E}hLTzf}yH?uilV~sJJ)iHr%BCn@cTQlfBU?E~8{fxF<}=Q->I^Ac|Jfo9{tx1? zSW*F$&X3}t(Qtvz=K`FdgCCQO(SDLm>9+tHjKhk(4uj==1a|bQmJF@xgSuma73Ign zr9^A|GU`8rbMYVXm|pT~$<-httJvn_1;}pe-0J*C9;;GJLzVp7`7?ToTdhG=+Ro$% z?=opxWu{s;`+&zEXnZJFZX3*}x%{`Xh}thVbYJ~(Srd#P2&QmpLp zVcy;tp9^X0zZB6}=;;==`) zS(fy>ZUqeE#)Xz@LkOk!q0|o*6RzI`;}(hRrM{rYpRTdzQ@g(d{r(HfFy5-651`#` z%ThmcBC0V<6SlC+*~>m1u%&A6>J07qn=;M!F+8{7OfsROm8tBzVL^TC(e}ON+Wjz- zYkrP4W4OYmrK{ubY#o~iY2l)BH9nNC-KVjHH&SzY16w`$BXiYySGi)4j%lRRp+~Kz zi@W!kw4=_bIHdpI>=DoxuUsVJGPc>-27~#tVP3y(O>sye*>uTrAhWB?27V`(XX~)b|(5Go(u-b7KR4D6UcJvz%%19+tVw73*qG)Uc1gjqMLZ6b z6K|-hoaMLQ!Y&#>?o**^Kg)h0^X;k%CY$y~z6NL7D8Z(31_kEtK)W4#jB2(gQe~p; z2uxM>d2F3GmGRpHqwRw%9e6QYz3Z|SAIFXr|6*UD*+6l3g6Ys2A4t=buh?GY#xgm< z_1x#OB0k54I84gD9Y}6H^W-h7oF7pO5?`UQbE;MUTaqT;>ek^assD{({Acx>G_sy; z(U7ez>6(UYZ=rVk>2$@PU#6+off1y!&(}+^mNu}p+H>hRBI&AsDN9Y^xoWazYWtW3 zAe97dM4|$5w|L(eo}^u`0&02Sll{qtB%4bqJ}c9vYzi0}a&+n-{9ofwBo(%?WIjq` zYfOhc9xIhecs$`0kv&Q>DS+K8u4b&>^{HtiyWrhZt?=$@?fGN#~(Gu^V+` z*ud;E3fN}3wd!gZHT1;L5z2R0voQg(`Dr#I3ro=9NeMc775;xZe>P7}U`s_32*{>u z2{`s-u)|4)vXPBl8Jhbc;W#pXK341=IA6%dyD?DnY>F6Si={2a+Eh^v_pd|)fBA6b zF-(sV<2?cU*-KAm<2^M{CpIciYpwz6ea|O*9OXxm@V~Vc0>KO)g@NeyolJ@|6b@Ig z6JkrHjQ^!8H-n0IWNG>nY{OA#*Xpk4mrLvT4AHqkt?crJ%%N(=al@ zZBzCK#p-&95}Z(apsQ48*W;4avX{$dcs}$@7dPKr}Oa6!Yv2Bpe8%gCZv}n>v zoZE{tG~S%O@#_O=+QBH_wa=f6gQ;7|GdnrL(VKV6eW8xn@ATDVcRfv{9CUq z_oRXKrEBC0fT@2|fSv{WX-<_!=+fZ8eD(CRM;LPQ8>4oeNnq271PH7Q!Ji50{UbsB zv^Lt~sYx1qfQkEYv3jYVT=ggz;6o4>!u*wr-B+sBn}I{N4aRD*ob=3BJfEnFZO{qq;x_YYRJyEUcS-G+dI_X+=#aKbX z*;*VG^h0+lWEO(aeWM_;-R9HL_G}ruun~U7VC}7Jz2tPMKEFT%FW|Xm6f4wJ%&s?V zCKF*-3!!wK3(NI+fjagVsIkpS_B&sbvx#O^sM9}Eo*4^3^%~$*0d*d6VEy#Q+7O2E zH`YB8dmaGBInZzd420kYrJhSho%jB@QG5W^WL7%0`wy|cQ7$$d(rCQgF*u~XD3`S; zmrNYes0(mJk(GM^sUHT6c&uDiZ{{d8yb?Y)8%_O}swsMK-ab62QR$j~E1(5EI5mdN zDyo3!OyrNRBm6F6``+hB)d#Zv`=?SYz6aKFd$l|X%+h_;8vdnPgZ%s1$lHA~cVz4E zC0Vl6U^eO6l`mB4Y`aqj=;8soICc;s;R=8U@B_D-R4m<^-3jM=Ci{QUtG)E*I(pNx zmvAkO9{wIGZa>KSdEEGK{xk)esR4W5q%kiRspTdD2D&!F2YD=j%+^m{Mcn~`pw**I zpZhdnX0g`R=4!=m=D`OpO`$w@HiVKn5K4Af+}gWLEGBIkI5L+{-|Nai8Fhc%*1!fqT5{6fMUa_M82Nxt3aB@|a8qtt(pR>=y zPDqI)Ay$!q2)}{tSNlqV)BeGa@ec+mxg_zhm&C&bW>no?}n@E;IZ_NTEc zvO~Sm>FOWNlb>cUpr`WX4}GyxGruFj7~@m!TiIF|&#do+jPPWo*3zv3?-nWe74-Ev zE}#^Xd`}i?TC_tB_JuM(lc9l*d>wxkdd2&0Mg9$vGJ!q6CQ^o1jW2N<+q%svRu4MD zvp-$YjLXP! z5$(*0P1Auos^Z_L>ZO;h)gHr5p977uAVVwe&(i3Hlww7u=_os(Y=4?^tBXiTgyt!5 zNxBXsmudD0o0#6?)4+FRTSIv$2ljtq3WXw!)6%efH3X(86xnhCT%goUMVm7QF(Zwwg$z@_ul+Wt+8Y(2!7|>E8vOkcWc&K*t0=-YMkcL(1$)P&CAt}Z?ZK0 zzFaldv`IJH;`t#h;-VB8{dG$ zHM2dNh0SS-($yS7DF3`myDrSrsq4uB zaswOxk3Hg=owB{fu7^l=i&J$yPS*cRPW3!PRr)rT)U!^lev_>9D^8{Ie+Q4O+>22kjwy zl1_cW4hkf-3?#K`-_Ozfs7ml(GF#)xY&EVZ(VCs9I=0d#4>J5D#1XSqebrVinugLK z4vv2T7wV!st-ZmcQ6Kp`-d)u|2u@WyN8@X2m41Q0MCyv*Wt(6JtLtUT!`?S1cPlSJEeY>tXVbe z_4P9>nDbpah@_5jX*4Oi$$oNlZFJSKYx3p27;<4SyPlRB6zzmS?lNd5uLnIz>e++} zeifICcI}~J{Xa!#AJAm=_VFtsG9u+6BqJg+A~iHZA~HiHQbHIo#u$h&$NS`U3_>GD zMo2_tghZxDL}WxnW=3jAh(tt0Mo316q=;ljWQIuoo{xW=?jf|@JLlZzT<7|}TvgSe zo86I;dX_cdC`R=j*!XrRW%gzjfl8!AS0%~ZBgxW}tCJaeG4nyZgX$rv&V@th%ADvi z$jCwR{}w?xL?ZwKf8y64iy6h^>}rQJqL?2c=3AIZ$Zw;+MZ@#2Uh3;L<=$-#%O2^ZP8xE(- z;9c+sGE+@*GLpxSZ|nCU`}y67OlSoLER|@jnINK_WV(+krSUA($_txB=L#1fGg*eX zsgsPScMGdr6@sMjSeZVHD<1oSWgzE?Hqz|{EaQQ&skRZs2uvG-k7ykVh&xC?ppICO z2MfjG2bK7mE2WE-Y9C2~GvBMEfn1+)MVdJF>ZORqqir)NxfdeD$1s#M;AFktC>OcG zi%U69-4{x#7QF|}D<4lN5{vt|MrY#zGn^o;J3+{+lO)qCTgJx02fo4!@S8|+8iTlK zdWMWgEZgF{Fxuy>m#(E8tG?mDa6AXvZH`oGqvYg=Xt$e*{Xb&+<(fB*GfJCY7QM@J zztSKfhc#06EZ#uwD%moI&g1VevF#;O_657&Eyyo^43p$JWJ#Zh6z$WIvM4qdFd_~+ z<~T7;Gy%PIrpmIzES;~jJTiuzjA55A(t6UT!*8Q-#{q-JxT%skxBgmF4)Pl`-WP)Mu6VFy*}sv{`g(V-#2K%Tyq-E%W*CaOmq<+0-L?4%GPhkX9rSWPz1;o;Y^yIc zu%H^UrPL3^vW$el=PIfFK`l+J^A)g_FAAC*o&#Dj&nUjIu^xsz~M#UWxf zyV_y+{_9oJ`%AiLf9KnhjxGL0B{ASiWe}J5(JR&NfRdxI-N6WIzz8fz`j-okpq4%LC*L@N@)PL%@q-% z`2xVHGFvWw$tJ{7ZU;`_mBj=MOqTrwo{7s<(tmwOA|N`BL-)30JN}1Z;R(q<$Npb% zag(e8WN`6jm)nFHu^6PDh^I7y#BxjmAY!@{nr#waEva`N8wHlmLeM6i~)xqVi zKpAsOq>QsyOuFOiTMZfIAUq+XRw^&7$2|+-87*}ORR2l{h#^?C*y-3KU{IKhB%ksO zvTQAPbq^&KZpxAo1v;-U2{;w_zs*NbJ`au%Jy5ur1w)4bnW1R#+#o7Xu);?|rSGFq znE^F911?neP?k{ER2F~}DZz&>K|ZL(3vRC(d&jd%IkTU$A&`#U9Iez=!FicngcuL%5nWq6xYkHp9cL-F*fbIFchxA!I@ZsS)ru zr=R2~Io<-|Jp{z`(!-ix|3|7c{6$JFpWI#o z(2G0VFJP6*6vzuL@P6m7mzL$}V)Z>Y+X-x=%n{8HSi?U#VvzwTvDzTB9VVVZ5KF~( zoPM!R^N%7yz&OVr0f5W~=jnrTcNk^UAB0&I>0<1{a``eTIwsvLldkClEE{I^5=DgM z6un7yMWUK=;w-T>L2`dy&+sHmYt=?3c(kPN+<<>HYf3)i!yG)MHUnaq{3B%aQIsx@ zJbJOwQga+bIX-CMR!*vS!`*y?Rj$y8*Dq9zXjVBR)~VTeK|7Oha6$17bSpr&D?m0| z9t2uFz!4o7cYvgpj~Xfmu&q6?ZTs6qy%A*2mq7Oi!qu#$tOF3PY4kk%{wYqdn{p&z zH!7y5qh!HrQBv`UL9TtGk!d{`&}zP|CQUNWug3=zSw1-L&f{b2O3` zfm-*Z%f7XE#?6D}3=}(P#oABe$pZVOCTG&~9e9nk!5RCMZTrs{DV#I`zfR-fLaX_i z^c&#tGQeRCc<=Cca5wxpwbHx_b0MQiy(PkY> zBOru70HO^LYZdUU4G`;2VAZC9>!=s*hXZ`Waty|4eRrx*I8l6JEtxMn%nI&_6!UXP zIY2p^mV%r;r4p+(xSy1P4Zaj0y3znqay8@UfoluC-&p#%D2Rh%5 zozi?jDX!JFaC~?@muoaQp*c9A0(@|`3df5~(rv3yNP!tTfD^igL)8X&S_JSk&69B9 zbpUE`!3#;O_uztK|Dg*8b&CcU%)B!l{{LsxYXBIu1Q;wMIUIA0E%i5ECt&to3yfh{&f#w7&N7*;15Pi zA6)NvfXmLyXvI)i1cD20wa%2nJ56j5z61q7E9Jr!LS&Z3L_ox307MmV+T7pq1-_3` zETCgHuOO@V3Xh0n3?^yTtuW?j%RUDQ`;2lr)(SG+5DjRa2g9j`L&g+bx$}OsKr}T# zSHClU1AB;^p2gtKKS>-+Kw}Gl#R_Xua4|+idQ2nSvPJ&@N*T~F<&jh=1;$szV>Y-fK}KkI!sppE*|`rVs}qn& z$Iy3lBUWfZOnyI0Yc)}`o<~oqlLkjNtF2%_vr%H@fLr=OgG8@nvgt|BFC%no(#X~Z zY*{$?8eZf1-wys)cteVq)6zRs)cPVOm=ATb2TefXV}-R zv@&OrR(hEugUk_U@I&i49JRbum<^!~`GoA+K`hw73D4XLowo?z@ju~z9ekbWq#|63 zc+_G53GEjcWr%OmKNv0g7f>p01%RFj6qhSVunA0B?7~GyaN%Zw9QQw1PhIOpQHJ`h z6mv=QOrj_(y`0ISppJ2yM-CX-P32)s$R3BlNb7APq23Y9Mn_aunPuFu3!H4jlkg-)t+W8~lg4mP}2=3zY6iYWPY5?&_$Xo5fT;fM4Q zp4-RS4gK^IvV!EmeMT&|*2xqa9|v_PKJaKk-Xhx#yWM$$Y9}<`3rxn};d0QL&j(20 zUAUYafXwEGPW)lW2JR3NxF){~f`%K;0VUtMpB3^mD7!_B|4u;R19zv(aX{g^cgO?4 z&CqobexD(=0!Vxbka!Y|*o-*9cn8}rn6P~+hZQIpy^Ek{@$FOj)`O|cqs4qXSS^mP z!TSWnYAlA7{(2O4oUH#>klY_)QG5J)*^V&r%4 zY^iU@-MR#0nPreiPvCN2i*&wbqtqbP8RHvPZi8Y+^R>kXY$=Q<18xaWYCSsFi;lm& zv>Rn4H==$rToU8s#I6Y=vs)lImQuim=J#&^HAj$9TR1AD>^)NM0MDKP&!Z|J*-d!$ zL9@5IWRUXrQp5$|*|-=M+&Qw=>~J=K%DyU{R68X~^IA?ciJX7{mFxaTzRwXi<->&Z z;Si1LIl}wqpbyIt$Fn(7o021)J}~Xv=+$O6EEC;Y1jEIOlv`7EHq1rN@!)~R#e`Lm z2NqmJJPxpY@=BP@0r9q5fW-#z{BWI$$c8SwioqbTd-L^2kM7Uk9mo8LmVyN+pjjyu z;J|~QQx5=5zz~{%-ks@E^%UDMWCVvV$p22CkCG_x-Kk^5cpR|=nbpa%0BqQ85+=iAK(L_T&Bt*#`j|3|-JFWRcrQ;=U<8!BVpWb* zdysm&n_;~iAlrZ8Y{C9w{x#d^(kry> zgGlP2Xf>TgW?PBVZ2;;f%5*Io14w)TrX;P~1q!z}ASP|(2}Dav*ov7yy=#%$`-6z? z!`l6IyrZ>AQjwD(o~N|Zb}SQ}IsCuESD{ASjI!l^X!J(`i9g5@t$z*&Q4R|eIkIIP zyP6?ErigB>43N)x*44c65>%4cIA&G6Nk)8is?07%kh}_y=s%+6%=_pAX&3HVhwz*m zvd3%;=ew9t0miJF4n9XIg{PaxOkc3`q&00Nx^5FHet{1WeOS`9y|k0QhN1%6qL zI(`+#(LPi&;`Ll!F9e2d{0HMXZ=4a!@pu0a-T*MHBXDdWQ0#X6o@Yv+TY{QyZwDjD z(8(O&*eVvf3pcKZ{|g@L3?AFQ77Esb>!suay zVJ2Rjj!-qjUTqRjzIh`*`244qs9O-C0l{8+l1S$%Cf{R}VtPCk8(pJl9o9(zuxsyb zjHPI&cH#fih6%~Rb&`LWb^g#gaiMMD=dv#_wWrzh`^mMC+r9zL?F*NlZ^OlsrtT$i z^hdC!@2>(CIiDR z0;qKSl^_JjH6TkT?mMw?U@o;WmwK5?o}c8%J{3AKU{@os>m^vSWgqMD6((c@velYi z0^zC%ppFU6O+@C{XLz zb>pYy&k7DJ$9Fh{0TR&jkns38I0j#^`F3y5Lx;mg&-v+3-cAWo55EqX5 zg-Ib0t6?*7oH|v?SE1Pl%}%_9YIZ=YLaQ7`LUozV=%00Bpb0G>ST%Yxb`4vIY`Knd zE$&G`pd2Z)Ta&~ZnAOiITN=Mh5epDjN08M1LrU^17+C;XZhaj7XKpi@E1*?3gHDF- zC+K);f~D%mQ#b)NnQgSv3bm^)bWh2NbmvTJP9hCRmF1A&GbzWy>eQzJxNcfvb16e5?TS- zaQZYfEG?y1K{ikFttaR6w=WZ#ou}r*-gr^H&l5ts_5)haR=xDlu6;{f0L?OWFG*_X z8zGMpzIl=rr|o})Yn(z#2jPXC2*>~P1S=o#r~}Zb3TX6H8E^VZyfl-H9@iKT!Q^pDXx5RfhO?CpY^nq@jRrE^ z`4_1Z&`!&vsj~kXlk=%CF#^#g&P@{sV7fKHblvtGyS^t)=L`zISSgp6utwZX#9INp zb2Ax`2(>KJsHIz_mPL_jIcN%3Q?&{Zk$=cntHpVvS|<6$TyVag%~9eD?&krLIpxp( z50csW8ygi^o>Mp;k2hf^Y7qv$8!j`K!!a<$LE$trKB`MbilN#SQIc; z=#2n5`?-Oxhrs_8pqE~tmo}i66*`regI)&WP~Gxhwy0JkIPsyN5a3~6KT?g?IFte+ zp6npY0bbbtI)_gyxc^JDsSid`xKt1kv4AI5 zfR3AiDS8Owe;CYm1kBb3P%*FDKpu}CSV7VdG<<4eJ(DqA;;(~T4a3t7x(IhhTCjU5Wq`Rk=Euru&wFjM&s; z&kZEI1W>UINOq@A5B!QvTOi9exZ)ge#lUQK!5C2#v zzim#xUQ*R41-d|zOZdIqpMt9i(JIY8D^Th)F`NOEym*w!2`HI-fyznfd-B$y4Fhm= z2c#SaYV`(;Gy+C$=ceuNf(Zrqs04&8e?}?SczXKm(?kW%xWE*sl^9UU#{W^t4w$r@ z5UKhb;P}nNevhV0E1;tffMYU%V;MK#2jEz_kei`3-piSAOQ0-y7I*BtKvB^y7$|7b zMAQpr*v@qs0AzO$Ie<6t5Cw=Fe3Ts%5OTIFO$0p96tw`Z?zK$vb)-oFAXfrF)&LGq zfd#JsWUK+xPcKD20eh6z3 za1Mr8LzOhZs;DOjW&x@kdZ9~)047}q|2rO$EQOAEvzp^_GcQ^KfML9WVeGFXRUlS< zV1f34Rh?jg_JCDNz^Z*3uK=HR1HXHKc=ZAjj`CDktW(LNK^!ds zoCbkoJF>}oKLK@v40*v3Hl=%@)Bw>IHA70az+41`JO*GY;EQOPg2*rl@HF#!mUIMX zNY8u}R0y4Hjqtw@Kf^ab>9o>8D-DdB_cEPCouRHXNY)e>ZmBKxJ^-MUe4Cj(aAE+c zE#QFDkFY=iKArMFCq!z-nG{XkG{gm(rX2BV2QmIp_@SNTj=5HSMVEh+BtwmqbzTSP zc{N4)hSKo=`bx=IkOfnt_52#7^E_@vfKH3zXl`Kr8UW_=$Y+o0kzpXq_W=iN=jaT~ zmOBGQi+oS@S&r)Us9^{35iVi<&mvuW6;G%DdPEr{7emny(wxAUiY`G=_#;wU4`EWX zpZt#&uqji`xLD|^&6_4m0G1p9mO_AK8t=}M{JXJC1)7-yG?NI}R27fL=V9PebFHWW znoEw81^La?JPrssj# z_*VI=kw+-{EeV;E8TLeU}N(G9zP;v)_Ge>nO> z(Cdex*Ec+F^9~#hzek~F9ZX+2IIXN=DU42Q7&Bm17t%Q(f}Ly zn^P-L#*VWh7E$TqWr!;f%(ae~&T;dn;Fqgj!@_lYh{V&n|3kELjg09m`JX_3=$@qx zZj_+EyTo>_UrdWm)dM{I+n)Q}^tCchlgAvtn@ zuGvf1c&8_bU!z{UA&~pJlSpoXlJ_c)Bm{FEP}(w%+-7rP3>6cJ^1i@L`61{i;xkcZ}Zb`mpdJQTG(6N(L+~|YOZ&Druoh%NAI>DNc zdg0(cw0tyu8PO}P=oO{dm1C#qp_8)9I;@%Fq-#bgCEN6JJcpHVZlpNB2$LCR%<)3v zN8an&gKRYFL>YnQxN`!%7>*d3M-s)q}7oF^qLQ0h&FgSR0i(N6m!>1sj@{8kxY2~(nj&W6B*B`P?=;+ zs3SM5hezV#L{WjfNjz>x@nlaD;|diL{!Wt% zys;hc>Hae?ZTAMT?&UatFFmpl^U);~u(gHq-380y7(#SYCJDsXu*a>zB$U*s@>$OB z$nTRkgh}EQ|C2A@m<(n&2+3SBVS@y$6PtN7HM5a5%pi2E^TTr5pN; z_c;Htr5=VfQVeOtbs$#uUZ-CdU{p~QEfuhz3YSw#b!CDSMf12}PU3hPqk(qZ)1!!t z^TSX%;Dms2$%03s#4us)#CpvC9?}t#awR`_v5gfe6Zv5<19y8L9#qVCa{>5mqV;H)}FsJvRigJ!tricx$ zLBY`m;}-;jCb1utTnYM8nu8H;@H8tblih8Ri6T*p2qG=jGV>3XIJs21jlk*16$JB3 zXjMOxKpG~sGMB^x6f!9C5o+T?vQVmg(d{HHntZWC$>CHT& zMBjsNn3C-1_avit#mfFCsUL(jTRm&ZFvcbprnoFA|3W8g#$c!YmnBx$(0;L?)VhFL z=x`|5h&}UtL2}6nPY|w^_hPlT5MKb^IFo;iu2P8mWP&s^#>W}s7(4=YGRBpRaX<*{ zc!4?d3k7SxhJNMD-oY@JG0fEr^CgDabcu1k$T*KO&Nf6gql~p3(Txz@q)#VOFmRJ7 zkYbm*v!658L-j;CN8$W1-tI&=-Hdl5JQKz{p0odJw{UJH;DR0icvkGlkUfL z;^;$G%bR1^lRDr!P*6WOzPvtzaLqOX-oGWvrh|m$M>#8Y!TtN1IFk1&xDi64Icy2q zvYTQgy&wFiczgqx4AO{|I_Y1b6PH!+z4#zYU(hW+$jF=L^X4|85DSBGOAFgX@(C^1 zI>^SKvng#e$XwVi<;9$u;zDJFNt@iMml>Y#?tSP131U5S$+BF)4KInwz6KM5D?ASJ|VhO5o=N69k6Mq^<>*Y_M230#iT-<*8TlD< zI__=_cUK$6ws?{Q0`XkH`ZRInF6*~Z!GJDv`adMfPB}(9ngb*r-Od)+nNIY6P_Z5{1ndbiK6wU@bEFZ z-!D_tb21^ALqxt3BMU*sM}i;`0wxbR7^R&XJTnZP=9LhsrA04XC;j(At)n@>itJv4 znhH^BI|5WqG$FbnLtIXRl7C4u6ZEqulU4IRgbi0skOKeAMskH02LwSc2!e?$7K3Zx zbbrIS{XEeOj7VGXU)x6vk_#iU`2}uRokD5t6p?rFby@|}ott)kirR6kB?oTjre|Vs z#6)U|h*g4-T7j^wbH}Kxs>ueTk`Br8ks~MsI2{>;#`#=m>Z!X{R z%oTiu8JhGPA*(=aX=ukckfDjgKm`nv4Qrr-X>~K0J=iWA~JW?re_%0DP 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%0Dukpf6*n zWVAJC-DV{)8D-lrzDmAJ(2V&;w%hzD1xx$Wb{IOz z>_SH?$CJcD$COw_+!z1msEoIE?r^eQZCxbc9;PVGQMRM}Ofpj66IWdML^xMlNMh}f z&lWah{hPD88!JyIPpK)%iS7omuKKsam6W}#^14`$m_FH2)RujiLiCi9vpxGoO;vYP zd)OOQJ#!whg40kw$n5`PDO&*b-*Emc3YE48h{AWhIt8x679$+@{vfn%}i<-jj- zC+?nPCN?1zP={n3*}ulC1(mjoHb`E83hBZ+nRSotndh8uj`?gLObihZ;g_kknSbz- z(CAQC^|a!=_%u;7L_O+l-CbQ>suukTV?ZEF{}s>k)Qs)4Etp}L{(yt|II|k)0NY{N zY01i`Ne5CEp}vt{sa@2vhx(2GhL6eGvy$vGA{b_e+vN5s@x0mSIq126CueEQU$PyO z#pEzVLYn*&e5c6^|2uwHOVaOz($nI0M(8WK_mHRT{RNDCrRcXT6A`*JTb(!Xu$ zyKMyfJx49iDb8umQ%_&{Nbd^ySzmAdLjP>@Ec2PbC0bnmO?vZh5&jdwR3=9LRT>h$ zWnPlk6QS5|{h+juw3^H)8_xTO{y*VQ^etIbj+I}Q_mi)bSC^S&5#f90{#;9RYqU1- zAX09|HJ6yBpTz><5=^h}o9&UULs%ZFiwOUuNA*wvvWYK69wwnP|)n5h1QWQSy-zM+bcG;~+~ z*`Pt+kcH)F`9*mj`Eq$xnL)N6Y>NA){H~mB2ve$sM-fL8DZv4{gF#>D2)I^M+ffCn z#~QIu(Hr6aq3*#S!A~GhBkz;qJ`7Ds-UOeG_!I*Dtr1aJAN;mpd1!E0=(y^EshRpF z{xW_rehA*fbTgIO&f0X;9Sugq(Ns7W{t%vTXcKM{?i9^N{ZVhUT{s`@5)~wGhg*dk zh3kadCW}#Lv{txAI2nba@o0l^i*S|bA?jx8GDI762XxhNRl|E_)za_c7~)u>n(AbZ zDaMN#Znn(bMT_F^PG^fgG!u7(e@P%M!KrHsXAi1 zVm_n4qdQ@`VZNY$ps%6!q2$_IwYzZxW)Oxz zqtiP222p;JU#oAG>XI9h*Xh5d*CYC8;KuSR!x_TwTm#-2I#$s`v5R<+_>+hyN%%7} zvog<;ip1AAIdLz(mWO2g#;wBLh<@QQ5FL@YcpD3knBq5P_!4s!O57~mVg6MML^&)A z2u?Cek_nQD*0^=A)y(ACj*>?3HS+y}GV`76koigQcIrpUk*=B^Ecg*^Q?}i-`S5we zK=kRvE!_QB3&YA_9)Db`!H+f6u-_ugw(r#twI$6nyUfwS@gPx)xZF_)&5A7xDdX<= zAIBL@Ev-;P)3nzpG##Do3HzKPR~Hvu_?5{{4eOJd zk=rtH#1Qq;V$G77)M_u@_k++ku`V$%-L7J-VP-ioPQJrLRFCxaO)34I{KJ!9VzWu6 zRFl;HR6YDFToSjx!X?eoG_fhk<+-r|ALVqcPGE}`Az8!Slnlo<6QabasSnuRXjl+O zY>9K?df{T~h>R!el9^G&Ky|Tgu#K=CvE8xFvAwZj@)Fb+{%igt$x?lgdzQ9DU;aV+ zN%M1$&;s0}v=*e6q;M%zaw}Ev!Iju>HQOC2rFc|?EbBD28Qpa*jS4;4Qu`l_Kh&0tU z)ioWZ?qqb;{fjz)YKfRvHf}W>HYG^!DP7d7LTf@zaElOa5SNLUh!=@FsBO_{(-#EQ zbO50R>Z0qR>!52IaV2~SO$Dg{6+uN(d&N1@dHR|a$gqXftB9ixNM4ja$#x;ipbT++ElRLe1&-xl#aw>#G~SC8<~aD_jeKS9u)Je*waM#j-b<%XiB6v;-4u@CSP zu*2RE_h*>AIkY`u4c8%A&UHqrl3bB$B&m@{1}*4N&p44cWPH^ zA8MtsNc9pu#Qj*J_o;sZwFk8ub&hPZ4A8BX-O)bP21@{bE$%MvEN&m}Ev|*E5>XYg zTxLPG0$YQRnOdHj{-X4}bOkD3hH$DG2p-}9tRrB8^#*LP4fw_Sks*8OY4I~Lq8+P! zM%;y|CK`kJ#fm#$5!;d$qL!-@C=x1)BA|{ak|;7Nh9aVlDryoQM^BNSr8=ayC61?Z zu@owfI;Loh?hP8??cqIfcq6uV3pXu_SNB4;cd}n;j!V3(GAg? zVP2FUT^wB$jfcNQzDFYY6|ikEL|N9E*Nd*>Z{__*2l-X_RT*da7iChxY(^c)V#xxl zj_H=)5_Dz$Cp*V&O%n>wGP?^6tpCaP3VX7C3oo#Si3YRpi7v47Vn3&od=+P@_%mm& z{Il=5_)> z`!>7ZG1Rf&A#~6UnB#y$3Zo^DUOpVhA6))9>7k7AHi6>xwO4vE!Qbo!Ck>- z^XAd^iFI6;WF>bNusH3?e|P_I-ZX26p@j6@j=EvH^#HKR{u5l|(1ly-` z5t^ay-@wmsrbYeQ5!y|KL#5Zni-p77|Dm^_H>2kU*}=Fp4R-Mjp^F$B z7zFxt7KQuDa^KAL&G(VbSF8`rJRimUsI&o+m82yaL?oYt45bs$!svqNs&aHnzI(nE zuM=Iv-^}|>|BnapD;dZ6r(`_AR7O?FEXho(kU5rbkslFsWPXz&cq0T{;Yns|p_uiL ze3h^>D<)E~hlu*IFN#jFV`2-ZwR|RLocJxLQ^hoSP40<`n3OCT%+1K8vTNK%#n~(! zXyd)4S$QvLL-7Y;kccC$+ERa?H06P?lu2gPJKyP(xvpD?5Aie+Z9{d&=38; z#O+uU!+_u|!*GMsUTL3h->E@q)0#(rH|?Eu^^6&koki{0M<^6eJoyTK9y!x`<48coJ^#<9k?MwgLj z%<3EKZyLi!uI8NXzJ5D=JF>U2h=s5PYzgZ|U$B4m)$p&=m4CpOW_BP23bQhynyDOY ztl~Huw#iNePX>QgoC*$*N)XTHJPl(V11(C6TX1&s&IqK!|AZ;=y7>C|3_71tpD~)DV)kPmVyakW z))m&6GW#aG#97Jt#hJJ z&O$ZoynK_eAM3duB|66%%f2PL&h~M7$=7gZi63#w6-(u>{O2o1NFga-GK*VFW|8$~ zR!VQt--J6#ZwKo{4<)hEt5SvWkKw7ItSxmb`AY*!{Yx7)6BNVpXhAQ=K*?UxE^cjF z>-5>2!_JT2)tFT#cOB(8cSrq9)iu>B6+!%&jq?7h`Ks?9uj;!d{$biw;R)2s&CGS+ z8jKF(bw8fmHH;#=^JwqS+zdE= zJ?>eQz^BINS%k#>mNxv3j8NjOLW7%+d&akvtu-k3W%UIg7+obdB^Pzet%Ix>TZUQP zc80W;Use7}FptTW%XsT(A@E@8dQ4RM!(?UYxN zKh&b+FZX6;``BUqLV`=Ql|p9kjtz-NWd> zYHUs|=5Z+A7UI5~a#`MoxSDu~bp1N`%M_0 z*qs=WURJR!yTdRyu|_^5-oy9ObSTSFYt?(Q4}61D6#oZtJ^vm5@7Pk3Gu1kEBGnN8 z0hhs*&1C5eX(cTcXqS`Z%&~dNBe`b5_Q9utOR*}ta-Tp*vYC4zS&VHXB#CL=%oIHM zElVV}!Fh0fajn!1WlkAE@6ikO-*mUME1*mC%k(SsMJy5NEpW3W0fYb_SPHFzY`{h6 zH83=J8MXxK3!4brr5pyE0viq6Tv!Gzhq}sUm1dVR_=v(7GzU*}{U)F31>!m4VL8y- z^2XB8dfn=^lb9f@b34Dd<%UJ z|4ILNKhwM<(AV#>asmI53zl-wXmZS~q-nTOkc+FIK;*uUFdjr#yryY+t${;$^O&saSU?ocCegxohjFN*DqIn_do7{XQ!vW_o!FlTjBHj&-$nN z1?JU(av#fEOC#&QR;TTmd9dxPt+{=T{j=TX=E(&zY)p;vZXc2@pR<_^9Nwl%gPwga{swi&h; zwv|9i|1Z-_Afj)`>@4gmn1Uh2dgYBmSg}#LLD_-VgFe{u)glqJvOcf|ZKt@q&^yuZ z%%g4p*|yj@&il@c>rdHr*d6lh_FNZD5jXT6_iB7SDpvVs_$B7G0j5N0{$_b^9%BRS zTxZy`$J5CBKksT^g?U}zhvlProDH-`J^MUOyeGYDeKPZgz)#C3^LQKF9`o$?H1(eH zuJg&w8v{cu-z^WVAIxKIF#BEy&l&OT^)&XL@M?W)d}XP5ec+envw4CIVUK$bc$#@n zd)NCE=1qaknN6AhEMLu&YyxM}^N(kvPi@{3Xv6D5?`8QP|FuORn8v6knJt-RZD74- z6*29$ar|T4X0#{fk%ED?&$g!aRrZf|i=($=vxDTk<_x)pyLiG=%ulX1LJ4cNunViY zdso@rpWV^p@ND#47M*6#;!F^~<5c(V^9p@kDi-)^aqa$b{z-U-`D8^xx;Q|U4B@up zb)%o;pZzN_&S2D#%$3ZsN|+P)cKJ`)|F|t^qXi$N?SxX+5&2qSH&&T_RdkkZ<+PK} z;Y<>L;B>2)DX+slU6GQ~CBwK1@_(fAztle2u~~rpoAig&Tp*^OgBi3KwmH6`;TbFCH3iF}9rm+S<$C2fr0qqMzH#yTorC+yA|$i5~z z$F_0W%ja??i$8L@SIm;v<({cXOBs^k+|x3v{JZR(^pL!be7bxnY!Ga488!q)2Pgm) zpaEIn5#TYoO-~U|5LJO5fXn1GJw`l2m`p~~ZNx2v3Z^w(M_5hw5H{0&M0emCP&EBQ z=uI~e44?!UU^Rf6z;)m$up2fAXa}?fIsol~ApjnxGF?GfOlH$v#2rK(pf*qocmUi7 z?g4qg1uOeucYpwFIM4)W3^-tqfeV1cWH&uTJV3;OdcbAiB9H_UKn`%ia3Qk$?DU+p9brRUR322u zO)=94#Ct@ zeCa~DB&W)#QtHC^f;g%YUb!F(%Pz^bEw(AvE!Hm9Db^|uFAghCC=M-7DqbyKEN(3B zEINwLqP@7ixTW}S@j&r#@oe#U@h^K<%oa1n0yGKoL1k!ed`o~f3h83j}!RZ&nDRE0u*V7{Cm zke`_EmhGB7Nj*V5MHLwN#>v1$U^*}r7!QmE=0J_|4fD9uR;gx5Rq9iER}_{6r8cFuC2z@7%9Zk^CZ(pO0VPVQ zZ|O<#M-f{JLW7}7G=#2#u8Q7C*iE=V*hP4kdz(Y&mt}ETYa2E9P9N-xoi^ei2s=ja)Fo}Q*xr&ps_(yP#~ z(^K>>@^5m#;;X!wqPb$QVheE}k%v1>JW4!51W0sJQkhY%1J{77!L{Iea23el)A=+$ zm2c<2%TCNp$c)cS%6!V+O5RMqEhmRXhDU}*cnNMon`jkj8EFw|9$6h(6?qwd5&sj9 zMWT^N1d)Iz7ztfO8_`6r#4pDm$7k`T^Jeg7^6v2-&>zts(jU|B(>;uPbQJyz4uRi< zTZG$yyGOZ0xl6fCxez@cJr_M2eI9+Rd!)OnyP|8UZ?1pBd(0#9%UwY<0)s#yx`ACl z4O|c3M*2H5gR9}lI3iAq*S9pV)U(vJ%(qa8A|jQz%(C3F)Uw2~*mA()<^L_s;k6yO>a-5F zuD7nUVr^I^l8IvejgT_4%)gOoTMgSTQh6t72k9)SAHP3;9e*=z6Kx~ShYFzlC@-o) zE|Lr70=ZZ&kyn$?70eeb6f6)d63i1U5iAzW5xf>a%xcW)%&N>P%t~g7IiLAV_Eh#n z#*z0A_X{_~?BL3I8_+dr>uFof+svEItIeCt8_es?8_ny?Yt3uSTg+k0x^i$+aARQd@*>SgLf>U|2EHl%fFeL9-XrL*Zq>0W}Kf*yhyf|ltP>E`J!X;DBJ z_?gA!mS=+5K=ys~UGz=#Z4?n}V5@JdXRBkYYddN?WjkTh*)4c0K7@}OVuqTAGr5bo zQ#l+Q568kWa5TJpDRMD#Byl)#Iq?X08TSD9Z>(;(Uif*efuX*kgP}*TPq25eXRudr zO%PAO5!3_~K}ncsnqV4l+LhUv*^$|uA?G6aC?3Ke)*jOSqvhib_#uYr2Akb#x7f{g zlRak7*#EYf+DFd>~*EnBE|42bu142VWBSLq=M&|}6!9{gZTuofJ zG`BS|axHQt86vlE9WT2+3cd=y2tErw3BC)y34RDZ2-3o(%$3X)%;n5w%vH?Q%q7e} zOj4ShR-{|QTfy7FTf(~_J0rUyJ0YWp2%q$Df7%1gMzxC^*LxXrkOxaO1= zl-?ADL1~a1qz0Lx!eCHUs*I}Z%B#w2$}7r`__z3X`0@w*d;A+bihv{_2yg;OxEA@G z{Pg$BdrV|(WORg-BBm-**+?dmj=W2}O(2pdT&G>9Tqj+0cQTPk)JW`(^p5n3^o*R1 zoQc#9)(X}Px^sUW*IbocPFbmhge757I6_zuVp(Vwf`w$ESQu841!rxNZ?^ z2*(P?3C9a32u};!Vk%iBR*v--f?^d}FVPTdDJ#pmlDnLHmDA|-ZmnDA*0|N~p>8NB z4}K1P4SfkoGsG|{ObCz8R>@XoW7+D;n#vl=_R4Ya(eSbGG4N6FsmSTbnaCN)X~-$a z!A=XacR^LW{MBiFJ4O1IY3o(i?hOme*nlME@Sv^TT1ThFP7%>4c62V1q5NrgK zv{g+6e!^}GZwhY-ZwTF@MU=&qZIl%(3VS(gWo$)kPposSQ#m#?rb;T4-efCLOVJ`x zOLlX1Q+5+}3wAU1diEa5UdleoB?`)e@ciAY^Yr(ig3@4j(`Vx+BV;O>LdkHlYVxni zYD9nl*q=CwIDmK*eh+gPeiw5ea}0h2ehB^#(u1{Q9atCEi9LjLV-F%h%?QsB&rr`W z&tOk^xaX&5kY|Tzy9gn!rK+u}qZ*`|rkbzv5@Dm!?5*s@ zs%5IBs^zMaswJw9xlXyxxp6t17%SF@Ke9it`5e%T@WQ&}v^%x$cZ5HL_k`tG5eZAgT#iVi715=M#fc?}J&8xud(^wshtvnu`_wzsccI?tp6MRxk!cgbNN5~s z6loY~RMDWKVa4)_b=fu9?b#)U#fC+Oorc+wnUPtMafz{sd5K~2q4KqIf5aE@M*7D4 z#D~U9zM>EERr-4R7MYfsmYJ5AmYY_X7Mos~_GinwtTuZ%`(5=*^+WYj^;^YNOVm2` zSL|o(7wlt_Ah0vb&2qAPvt?g@-vD1Z)R5E*-*ewn-!mW8e?fd++=+iy zd`{ev|542M^ZZJMygG!92poH5E+abh#G|2nmU=Pg0G5ig#V0tjQfc5 z;{v!WZdrP1dP(}5>8oj=*%B}Z)>W*pSW~gOVr>OQs+6jvQmI_3kXA@#(wWk~nHyRa znj#<%d?I}$?J@2(?l$f+?lYEm7AdlR(MECwoPi~x zbf8YIcCJ>geNLD=p7~$qFYywS#4Is9@CEjP_MY~R_Lep#IXXE%IXyQeH#K)OcPn!< z(+tgOIU2RdfR-4s6 znMc-0#*oouF4=ABCF(`$S?W3J8EQjgBV!Y4V`?MnZ`~i=Pu(vaQ7_fY^c8xqUZSVy z$$F8#sqVXONMUGUSYdGCWC5u}l$GBUUlmaWTnQ?FDYh3L$U7D~6z0QSs14K>n(3Y4{g8&BsZazO27xpf z4WKn3H6+z1)gyH!bs=>o4KofmqJVd>#;~=(M_3m&M* zzN9beA$=#TpX4KXNq+@FQl69}?JYbgJS^NQAYlj?5k`W|gUyA}U=mnA$OZL=Bp?%H zfP9b#l0YI@g$hwCsX=3jIzYA=Ayr*gQYwj++hIFlJ7C)pDQFBd0Pq0Mfipk>mM_DK zFbGzHHG{Q)HHXcC&4$f{WnmduS6CNVH&_o?ci0%%IM`U21M!bCVfu(5n06zoKxXKB zp+}|(ae-`=th)@a(?V-YHL-Hz;nK0vhthqR7?MG+Vf4!0c}wwPiEUgIM^=K_cEx(d z5yi>HYsE{&O~qYBS8+$NytR0vc(8b+c&_-rqOX`M<{>ZC0{heO$Uvpppohlk#(PFN zAfguYL-IZI-LpdDBw!ja4wwto&eX$>mQ9o~vo#e}73}ODWS=1IQikjlvqPQq22B2!_Jp^nXpFLL2Y|s>VHR<2wEfiaceB80Wr<+n{ zl}!HR%J)y(!i*XmDPjuIGE%Z-$sCYvQjkv<%Poqraq>O>3q7kpjG-QX+`Icd#NeA=Wl@ zCv+(D0L6`4;vXG#h{rVMvZknQs?j6oqtfwa&eu}7tTADeljgdkiIZ!RTe^M<)-WmQ znuxvxit-3<3$7(aWiYA!RbDV)315=r)ceF4S1Pe5(mzrs=*#7ml|meAvwV`U3hPZy zt24M2!EYgXh8&)lt(rZWt)=9n-=U5rkEn-+3CS_;jp=fONLM9XRVUV79}x9ak0ne- z3`LAW=nyQ@HuW#q9pMtnO4h1amzX-)TGWcYpMv(FgI|o5rbseiM1nA|O!Q*mnvtHJ zqPnW-sw=AP>=mjmIlTB28|l5SDP#v}tYR6bY^oz)scBD~K^(|Y(AsC=$QoF6X17*Z z&VNuPlxgK>{lZii@ik8U#9qTkquYy*8FF=#q124XLhq8uO2maM;QK&W8R?qvL`>0T ziO1CUpGCg4dWBT<^AGSd@D`*>>pKy`Wu#)9q8+vdg;3;7WmEi7XI$ZH?i>~ zb*ffs2x?oZ8vY$Fh+CfiZX%P*8!FV&Fzp%XGwFcwo$<2K9jKc-k&(u}!amZ*Ca2{Z z2U`b+1_YEtv0uuPt`OLy{U&b5U6pjlRuJMuyKZ#qY^H|pV_;bZBo=W-Tqm4E?Um7G zm#K}7O{xFsu{HH3EL^7Qjkv$>H1HnQ1ojE`#%MP(sO>VHu>sNzoO3kdkVoqbpk%Cl-s4A2QG*6bJOohy>;v>yrG`8W{<7P@bSXO`QVF044xD zsByk|zD>SnzJ9)HzD=f{qK4vtaz9lB{aq7;Oi%~XMOf7glo6H203oO;`UdS7csepN z(k+sV7$YAjt@NWnGJ;Y@j0Ri4tHQUFs=yV5YLQX-cZ|EJqp01qDyRWrSwIEn!+CHn z{BmRvnm{lRW}A*`#hHgF3!)m5futen$m!&%I+zP_M(9Q^yog%CrKed?$4i)ST9y*FHhz(Zkg9)o4=} zlNehK+bO$GyIea0yG}h5^(wGcyHNWPdlYs95UO^Swv~35wwLylc9iy&_LmNp4wU{W z?Jeyr=~Q^t2jvIg6F?!%GvULdO*b-YsiUd8s0Dp9{h)I8B)AfcklT=Vk?)Z^lWQWc zk!8p^=v-lG|sd*5+%1K?4?YE-fdxfTF##M4jtBq##KSA$aX1k!K#GuF zq#x-3fkshQEIRO3&l?R_=Z-KYLm)Hz^ zAbd3P0XPfl4s9+0unM5u3Yr29f_gz>l@J;YO@z8YL!l911U{3jQT0TORI3qE#47Yp zWqW8kGy)m`^@S9wagYWfL%?8CU?doY&mt=k5`+p-foP6ygx(9^2j7F-i);#0!^rTL zpah;ojzv}jpM#&kAz&UJLJo$HNA7|r(L4XX$sU(MKVnQ~cxKSwCZr-f3m3v`YuHFG zvZp41915?7h@(s3LvUQD_xTFHh4v}JK|Wuxqd;-lij;_c$y;+^8-;=SVi;)7xns2-FG`KXpG`3tCSsGOuQ<_j3RvKR#R2otmTB;AdBD^N(^SXTFY?EyLzl{M* z$~4|o-d*|~`fYk6MFT}+MMFh>#R$bP#Yn|)#ZbjR#6!dZxc`V1Bq_8#1iquXT?Xw7sV&V2Sph-5J$ih@t^=Ea9Hiurj%xsN3zE<7thH< z(h+nxy_ck?q=)35@ z7sKb^$Ka=6-E1>_bG!*}#+Mfu78;WFgnf*CwEYd?ErFxDX}@9rJEWql)vnVn(k#|6 z9CSw`M?=R<#|+2o#G6DzVk6=v(hJ9PN7GP?Q1ei;(6ZR_*kHpz!yv<((A!YGtRZfU z$K#23BWFV=hr*?-*KW`*(Ja-}ir0>>cdl~+GMJ1i>q+QEAeUVvS94c0*MAy7+t1L~ zkQe5JSs}=Ru>jWJTPa-!`GoM45K~5!ab;8)R?_fvJOxk1FQ6=>)KFDZ)l^kiRaN0r zgp@p`NMVxLq{^*y*G|+)bPCqY)yP#-R#lD`juK{AY1R$-P5B+nZOjeKzZh*^lQ-)| zxXZ)cWkY|%00TG73%AL3Qg&4So4k<(G=J1E&423O>JedSQkDcf|GAH)?YeJX>OK1%`<&{G>b&Z#>a=QdZc@%5){AvwF-OD^au{B^_qXPcW|^kDpqrqpAW2Wq zQ@^R#}l+ zpcbozYQEZ7RvXmKvn{eue2;y@QzKGrKgYk3zk%P#-_ZZSf8WoefpK_z3u!CKm-46D zr#ht0rp~2W;9KHZcs70rZYb{P{~0=`Fh{m13Mbv^*fuIwC#fVA+o+^_c*nNw$=J4S z+qP}nw)^(ec|9*@@AK`w*1wX2&4bKRKmwcwP65^FHRv12%gI~CTf`GG<1_Wswun8l z1F<;1Bz`J$GSkW5+276I#oyKc+V{#ArWUS8ZbUfht@17M{=R;`h%_wS#@os}96u7T z8mSg}MSM-HPpV6*N6PA9eMUbgF*ng5U0YwxU&UY5Un^aSxgib355o_{55f<}kHB}- zwAXafbkMZZY?5!5ZZ;*QpeuK|2TK^aR1Mh|#m59s5bZL3bvcJhDW%UbXtI6xg z>&X(jfZi9kg1nOa3;qpPZpd^0;0kyZay9Y{VhwUFa#q=Pd4rONDp~mvWaE;CWqT02 z5nmAp5r+_m5l0ZG5T_9*5$ljg5yucG5XTYgksFX(keiVkk(-dK5$h1!5StKN5I`A+ z4x;iD9CcvuaB{0^F>xZQa6q#X)eetCEI`O;AXz|LoL`csrJI;qm{8>&-fsFYMGGZc zFrUw5yx=QjD=c0?h&fidOYlk14O@&AQdpFU`eVRaU;}7k%mNAm6<@(0K>$8N-vSI0i>tBkS0@* zy{d=OJg<}Bozy5BPM}iyP?v~15}sGi8;)}C(&rG!)QU1(Ik|kQjGfz>=^(}6&T|V6 z6vK27#VXe#G>8_dwwOgl3yT&M%`ci)G`DC@QIq1v#f^#^7B?uaUtF)aZtW_50)M%onJP^WbtP;u%@rx>d$EktBW_6G{9_CZc}bh zE}5 z6iQ=ifXa+`DV)VR9qgX(kzY_Yw~QB18~UcLsiVNfNWkYC{4>DDXpc7mN~C)-JOonze9j5N8r!>KmDw zo5sNCWH+@bzn?E*QkiR%tCchzRY%d0bsQaA$I>x%RkT&Lb#--gwRN?0jdhK54RsB4 z9krdbI?)|gb#Yoe4Eve=lug!>w0uc{$Q_{_u3e}6%=%B;UHe$`NHbE~-m_V`McJ3q zkFv~AhW6=3=)S01%WuHP;pgx@xKC-{(tf4p++<)ZX{Ba|a=Q{1riIs-SC|K-hopz4 zN2C>LMCK~`m!IA!;W9mY_Mfy=-CGqOwwb>!LPAHw}B$`_wm5*HTwgS5ntgms2-Wms0iU zEcVaLMr|VoO4&*Ii60fDnA4R5nWLmk)?VJ<^dLV@m(ijE^8?j%t+aeqeG!x0lzlJL zM0HlvTh-IhUEGONXr_LkKETJP?DPJi=v=>r-gd`BmZc9RGdHOb~hx0W2 zS}~IURr5{rMe|+rS@TK5LhumJspHgR)z$HJ@%;+n3uRc`&*F!;DY)_I81EYW47}9u z4+H{VD1Rtmic;1IVL|rI(tH)(ht5aN$IfPg*31jhCBf0bQNd$n6QdKNlcM9JyEV;9 zp9DGO&9H|tF5@cN3F%C2eQw_{lQAs&Lh)R&DB7&71}d%YgBnKY4tIllz&+v5u3GxS z5tT`777N4@&K}|zyoit?wI``bdx_uii}4JS1aBi;!$%3%iCI!(VqfA>{1*IJ;sDYN z(r4m$a&^Kpd;q_Ks3mDg#iTsmgztnuPP#+7OL|Dc6Y7w7IFIBUyVvi>YvEtO5kPt1QSJAr$nSgKmATBcf}x+x_Aj|6u^bRR>Z z3LC=eup(TU+#a5uog%v$5~EAQlV#6B&qGf`_d`!Y7eg09)3Sl=wU9Qf3G2hUFkSK7 z`J&)tD#ptiryeu=W9C9}-1R^q)iqVsQO!}^al?7jdCPg*c`|e-bSw1KmrcQ`L~0u| zlS-%5wyD|Y!DFHGh7+Mvp%Llf=~McX`X;Hn23Gi}USsP8R)9I{cjyQ73;GNFgMLF5 z&`;CE$lE;*SCgC7vE9nX;i}i?yW|UdBSr6DQ7VHZG9?=z6PZ68FP1KCt z+!^&RRAI#@*-u?x#W_igJD$5j@{D`O?e+AL?K7S6Of%n;)dsd_nx;mjhNj*^@1PIR zXXp#`5&8V|)l0--Zx=OZpVK^ZeG@lVKU5#&Y*yzbQSJop0?A#s&-2{$%!JdN^-MSK$o%#N zHNQQ7Jbyg}^iR((&#w$x*+nT9%v8Dy!qHMEKV}*3*98^_7CD5f`s4q)Bmb*8&Smsp!cIPq!r zQ*oIjz?~y0=KTlNf@(twTd81Rsaa58(1cl4xPVz(xKhBkefMtj5<-QLwl|2d=B)+W zF78}e36O)v0zrO=e=q3fyZCsMyTB?1%VNg@b7vnj+!rjiRUS_8hI&tCjT@K=APwcI0de^tFx=n$<@=<&DGsieD8IPA z*vHxF?Ty|Q{l#q0ePMWG*b|)_es1`|{LXC0MX->pE%1K*ncOb@S^ZA^cnc_OBwZz4 z2zCbFf=TNO9?8_)aC-v(aZ`q*p@Crva5`7dz_$JI za%_LS+!T&xNhu!%Lu^BBDA^$g zBznop%JSk@>_)Cyk~&<1Oavr69NAOpGbzs7S@2y7$xy%$>RV}DdBG+p%Kl(pbLvQH za(jXTTizjahGb=quxwy1=G(>EX~G z>3JzX^(H+^e`|kua&R0 z&xKQ2T5$;2#XM3mj!&uyOxG!WhEzR4dXjR3E~u)D3@M>NnL_)!yi9 z=8o`${w`=SS;hoVQKmF!jQ*J+d&?Je=5_|aqsZ9GQa z3my&zgP~wJ8j04T8ln@^HKX&C^9#y_N_BEy=s^&L*dJQ0Dh}flMPXzZ9mm9*hX0FU zku#A!k>io!AXV8%IajGvUDb3@?@{kHQ8g(ILiIu;i!aSRQw@u- zI5IIH7K^3gN}GgOB$kLPyDdk^;d1z#%FfNFncv;`V!T-tp$Wk>Qcb0J!)y>Q(A3M%~gUqBU%E`y36rC~*naXpa#6Bf5*aBsUW`49v@uRLK99|b$fMq9m8c|Hk`N~l@v5=Qaell- zWP79#f#PNHHHlWS6Gl#?RUA$XiWRoRh9)-~XT=622E>0yY>5_$V~Ntp?D+Y}o_IPk zEg=fdkFioc!zH{S> z2~|v$Q@vCL6`=a4jZ(uYqX-{dMY!q7V-hQ`6So(?KSL?3CB4RLL*FIp#h$^rFTTuK zEWz-4%3erB(!wZSTlzHFOxbi8)d

T$t`6>NZmVaP$8Fy0Tb84Sn3ma&YUThR6GDZMA!&Yz^@gjlXQY`5 zW-N=W7wq!_^Bgx_x7~+4qs@nXy@MNqy@HfNh?klbeCsa?UE;O#bnz^;?(vK=AN6th z^XU5|2A)&8k~bqr0y~1Q!MHVPnQuL6L)z2!QTDTTl%uWlob!^ayZfQr={exZdS7{m znGgCXVA`_Kdd2nFJ;FTJLIyj5Z@`2#Wm#bDY&&KvvS;k0?dR+&sKn9ESvc>!?CRlu zFi_w!fy&jr5vdK3>TnT7qT z9-(509h}{`UAWH$=U3+!=Qk%yu^LlL{Ez)D$n|l2O&LPwP-ZXsK=w8ENX{S5FYbZx zjO2Fq978+Iec@!*b;EZ3Rl{b(Lj4xQBK-mV0{te#M#EzLBf~-cA-�GVX|(sU4CQ z>J{lF!|LdosMT`OurOG}viqxOs%rY_*#?KdTyxD(Xd>wx=o082cot?EnMS>Gn5wVZ zpd7C1qaGBWVGsqxf&S_78m9M%e>!m*gU@`euge_CRI&Q74zg72{_HF4QJhiH_TnFFRit0^KGwe8hc0k3VRXM7di{s z9h)8f9IqT%=X$5swZxTjZ*&)^o=G0Pcei)2cbc!cuY={0r3QEoG+1w$yIWsd>)ICD z-q>{Z&h}My6x1I&4>=rL9R+r>M(jTMEun*UC!s&Nes8Grcl7 zV83^ScebyMuZ!iGr8am8G+XbPdt2XI8`_rIKG;g_-RCI-E7YckHve)JJLJH*Vfm`^2G8VcmXW6-ZA&IzO~l3Ew;V08SGu{YwT!f z0CW*@ISShy10C-iWzNk`qicl=c5ihvJ<~iU??LZy?<`+yUsua>OC9hsXtCZm_pyGk zHnJ_VeYBbEJ?!i4IA}0*1@by}IfghsI;uLiInA!st}^!yH^(#619}g8M|tP^+WFdB z9$Kn{XDgM8H_hFwudH=!3v91#T6-t^%7VQZ>Ia>JAjcL*f5&SF?A+kgxt6-p?oDo* zXR^oO-QykNo$hPl>u7mwsR^D3jn>=d9@aP3dbUNjw>G`Ki+#1d1S&umAg5!SV}RqW zBk$bgG`N<#vhFQzhG%M}JbIsZn0KbHm9LxSg{3Zd1q7`R%zdpNt&MGk<+e{Yv%RN% zgB=eIfv!S6$8N_^$0tWM=XR&XwZ>KM-s$FgW_hgM1K!czdA{~OKZb$fn@*rMr=Dxp zW@O4ms?McdOV6jtMiaGtX~$BVzf1%9y9T-idIT_;hZ>>j6iTX`qtYsM%Au-$YGI&X zT4XwnYHVt1T3!f0(Ika;nK9uVrbIbgb;KkLUL$Qy-6XBa+|S?3cjotEY~>@E#e%AW zYRq$j%W}DJHnWa&v2+1wV7U}`g$6cC3kigm9kGN_KJG4|A{WKhlvMs z9*Qq0t2b}+jj$Ah?Z6jc z#QNJj)rzxqu^~UW8=kV z*fTlL#c}RDiI7(ZD9nhuQ=2UwsHJBO^R$#Wj!P*+SHrjS2cQ9W5Yw=TAV_-*7?;^06UGgKdNn3~C zfKjaMsN7!Ev&d9DO-T_BVUCoxMDO8Y82ilw{Vp56P`QXK4io||ZFQYM+gjVv!Bf=} z@z^cccQcJuXEeQ3y$x^KABwNJ4vU+r@2d}SHmZG+1ecK&qD5$=vA^U8ccTfT`D3b| z`A1)n`RVg(8Zvau-ONVxK8APfB)7=;lRKY%71Ncwt7vDDTRSr@#OX7>Ezku)#SD0x#WrD?c~$s zspRG4mE?oui{$g5|fS#=-Kj@*g=t z9-F7+_Yps3mZiCw>ik-aKZ;gLT!Fl;h%5M*z8@FG-bFF78_}iw&y1h=rTFXN*L-ZW zv4u%mW$_ClOlxd{Qi?wyxM)dG_sg-uUd)Vipmdsk1n31fgESW1I#{4oY!kL(y^-%Q z-$;B-n3K6=f8n3>_i!`oC99o~FdmQHz~6~9G7>W}!bl@*yFn~WvTfIv=n~ooHi5ml z{cdcYePX12fSwUXt=ifjb)yeoV4LLr}*-ce9(!(&0_*S%(x{JL! z(lw%sw=0O-at=@lu1I_!X}W8VxSsl^dJkunx?9E}(Q)f3W|Bs8)%5mhQue1|QKE7O(iQ!nuoiDyN1_=mn&~8A1hA;8KL84r=zDCw+oEg_*cm1 zNK#p8(KX}<{VLT8)$CmDY}2T&NLe%-ITd*x*&5vjZ9O z7gYq+WXvRt9qYhC*zM6`))>_L}Q;m3f&IMb6Pe6bbuuc>}ilf4gtiSTJyq5HD<}tzv))HGg z+ZNkjo5w!bzSk~*z5tJ)v}1xpAUeaUaI_aG*vAy>3Zm}p2F@eSs941rA|A+zxemB) ziZ5`Y5*xR@VlMZ%WQxSb`@(JJKI_(dddOxe)_dym{N5Q}wfUSZ1t2M#d>wpj>2RK0 zKG>#oRtCKQV*q^F%KY-YmZRb5IC{<;_SUFc?B&c7FXrqI*AJfx<}ky;;$R`XFF9a|6&-;}rAgD@R^EV6k40aFB7S9pS70(mz;?<^G)Uc|f;fDH$;kW4=ua_(h z+!b7ozRgcb3&TIl-j=;9tCriN8L6VHgSl;*ozb7nX9i{Hwc(XvcXW1mW_VWkso@** zEAt<-f{6~DQgMhaO|481LI(?)WHyyeXJgrTcBZ%wt~YKWZ8>eMriSo7T zr_yHA+<~KkLxF(*zUhvsmc|ya1`Y&nnYIR;fqj7+roDkZfn$Ngfd{6$CdjxyaMQFc zusN_LaLu$maMkoO^dhw1;Rx&w>WnMVaDQl{^yP%fWSA)YEXP|CIEC>^vZN&9L(;gSN3fzM`tWWQy{Tw`FDUv+V8c-R!L_0B_7b%0A9A;OE&JSsF}*>G1xn1g5}p_qE=3icF_?_atq9}I({!dv;IRfqs8}jFn z)$rZ%Sk*#=42i~L@HjjPkH-`6L_8K>8Igu^n_i$yi2IZml=l==9%1^TT9djdurm%4 zL0X7+jQ&&LhaO`-V9>Jef>G!nsr#u7=?CdQd0g2bU?7kQ{H5$gb}9(>Gx69Ssneu0 zq-iNr8l8iJbBrV1A0#sbvl+*kBj^XMrJSi85pMzSu3~=i>XLiK$4V9yuPM1-e7xjv zQF+Nd#lqsd#aNj^+})?NZPstnZ`4<0o@9{OXPGO!>GXky;o`}h=@Nj)lTPO0l`rEz zqPN3sEagk0By!C~|L#GiyCSv@&e_&a>ZI@aj6)wIpFDeQ`9g}o!R z50W@|$u88Qgdp)ZcH6N)#18J10&XWFYzfV+m()=5bsb^Bo_K$NyObe zo=dED6Fq%n>tbt-J4`A~zl7TR*gHJ_J<>FBppe)va{^hq+cz~{BHbJhMWPzBbRMq( z{~Uv$?4gtiUd4Y#?}WeelY;upD%61RG;^Xf4(S&{va)X~MkPfAr?sgT;KE=~v&|6+F>@k}F8n7vRmgY9v)R2433xbd0J zW|4y7t7rrD5PNH+U&I#gEuPCcP35>U@sr{<>SyYgoSo`^8LFg+yO|5pF~%Vh6EEs+ z5zi#nxJjOVvGuWa#+@dzMy=_exG&LopLj>an(7t1+%YRM!@hvHr(=R_~m zht)^aa|!baP4&(6EP5}r10(|GnnzngTR@vbn@d|( zpe>>;reT7&^6&Dsa4m85aX;`&@YnHeh{y0-@iWNJ@$GP3a7}PMa3cM0pr(8vZ2)Z? zYP)6tzq#}iFCl&}-YR({s{v@F!t8e)R)0$;*Dpyw*1gtUB4V?v(!$JS{!NBYIas-m z#3b7VU(+Ar`dCGj6?+za$agafC4>$5S7EY%5uI<5kPcYd3OX@Ev5!hUegXcKzzY6H zeJpPv{KD)ey(_(|O(?ga|{x=DHGLmeRoFVLz zBxFh=$E*fhkno@Ja^`C6G5%GggK>?Ynkgrg5~drYwvo1{#A^i`-Co~5&Hg&}!rnNr zIC3?>&gi3|Xnm+gbPZGibSIJ=O&lj2DNzMWl^j9zs4n3T;(Jr8sBsA(Q4(i5YsRW( zs;EYY66~9bv!bQ!x0#{Na({c(_4pr+H2%-IMb#m4)Ub%?5^tl@IC~;PBcAvo@p{e` zYJZp9U#J$JMEc_@kqlDLSHDxg=bToL%jhIRF5TTy(vLe((U-J_bcE}nE3-V~hQw)! zlh-bG#yHI_h&VDG;#CsA-5E~>aYSrSY-Dn|YOTB&jLZBwf?7Wlm*_@d9&os{I4#LD(%XQ~;Cuj}EG5*EA>$-xOg6@FjF6!{ zCjSz<()D>X%5dsZaU1FMpj7jOX}q5LN!I!7BE=_#}cw28D?v}v^Iv=pil>OF0og#i*QE38-T zgg`0O+F9-z;C|*l?AhpznveN<?_FXkJK?*#4EaKr_*1+N2^+aYwWbU9;#zkuQ(SVRN61sUT9O z$34T`$9%-JjyC3X5j5mAi#Cr=wiL-+(Dvk2G7AV1S0w3ST~Hg;2i3tz7AJI*e1mMr zjSZYJr8T|ubb~$cD9{~!j5aag^xKH%O-+M|0?}%i<-ZZQNuW@B>gN@&DyfIzmR(n# zS9&NHloyq}GNc-z#;P}p|D;L`t@U@w9iv0(I|!fHY(LWQin)ux^A{T?7)BVjs5pzMjWu5hoy}$dUXM;Cl-eBQ?ILlh=ZMz=o z=RVI;BHuk()HXO6QXrm`Ld>(T>%+aIDO3FU))=H+DzvjN>Ov>4*Pl`dRx)g!Zq>8GisyIr$DyW{QYEAl>(Z2Kndq8rv}f=h>{#pvFWE+R?#9|>yRc)hHW}4d&wq<>neZAINJz<4 zrYoq6Wp$7pkgIbD^eug1y0#x~H2Qn0C%+$~hM*>M9<#o5ne-uVK7GD_i)ajc66cAS zB|&h5+(q2^5;Sj+?3v_&tSX?Bj^Q;$)6vb)40Ln!C3;Np6S(TH$J;hhRhE0A9;~>42XfX`YKPUK&1NFVq zJ<_#^wNaB%T?i-WucUW<{rH#owFMUhW?>uYR_Qt6XP!?qP_$pf6TcDHm8_QxkX@IZ z1qy()KmQAFkm!Kut+<|K25JwIlE0uus^X8-239c*l89v$g`}mp0c6r<@OtPGgQ@e$JNXI(tX@h?p^M! zZ9eJijGl*q&?9MM&|Kv1mS7!Obo57zh5uT}L|tw6LzZbVE%R;X_Jy8$a;6}eu{ny03J5+)Ia}WfQe$eVU0W+>`V+$KbwY=MvyKMdh4OUUBaQF zRpM&)vJxHcDdvu1QSsW6JHY%hv*e6AuAKVX4jU~ubns(vuCZ0HQLsZe750Q(;Woic zxKmgVzY}Z`Y!Iv!Y#oQg_HfN$^u${E2bd%J=xFD49pC?KR$w0DRvOX0TP>O)$7X_edL8_h}Cw@f! zDfX+{sW}pHcE1EC8xrd$`{(H`cq{oNU1EMAYYKcyxuglrC>TRO3O|5Fl!5%+{8|D? z*hIQPdQm9g^Z7i!N`w%97gv=Wko1>5lQjX{($VhDYGPoixu&+8wjH(;w(uGA9W&6> z9@`oF1@i+l$aI2_WnNaEQeH#vK?A;)lvWg(j;|Bz?oz60t7|(@I#Sx|n(12Uo>4k$ zyJ&wA?qMiesy0gWF@|Ntk`k^)_J%TAJ4Ook#iaROW&85_sMDGglQ zI_z5P0Bixf8r!dcU4!k99VHt|ohVzGG=c}L?_KvicYLdor0E$waX z1nop^r>HVLgw~foo^f5!R{D$glQ)h&mVQh$mHkD$S|a1Um0bXS@V@iL&_~nNvca_D zd<@endd{jX!)u3TBoY)?kygnD(VCZLGlMggG;3LbVng^y^gMVRR9Y{apICcACo0SJ zQ%AzN%BgS-aldxg^<41w_Y%xG@EEADUNS$n_Joc@CdU&;+_};zcMW#Ga@X;k_xAJR z%_^`QJPE3;SIy6?ry$Vr+>vswajINz-62nX&n3?#?*K2!e9@-_%fJ($%6g?>eri1l zSsc$CN#|;(()Gq&&vVgp(Od8m%@=&nn7=5w^gGpi)m#R@yf&piC4qDVzw(y(&jI!F z9rES*7I~w)e!gSAO8$xeD{VG@Ey_XaohmAQZ9GH#lc~o)!@w%LDR+?elYWu#WQpK; zToL;gCCBbZSMyQKKls85{LS!JJ_FUh1Q%^(;gQx^;(~@ucWky&iJyT#B)DqHQxD0# z!jsILbewcN7y);KCKk_nggi{3QS24=XT6snG(Yv0i|x#P6ZLb>Hg%tjU(%G@P%($Jk!zx_M(x&dvL(iq ziGdO$uX*gSG32I2n#7Zdm2SM}C$V>IO{|b?BU^2p9vh?BW+G~adwM67-VWZO@vo7Y zlC7nG|1|8SQgZPe*F`HtC9`t9TpYfyhQ;h%`Cz5!Vxg z@Z(6$@pgOjXjM1!>_{M2os_Lk~gNt&%)=Z_Y%Jo zP_kO`N7ze%jb~$b@sA=cj4(k*7-hUmd>pGqnjTR{UC}~5+MdWw4kDhx@5XneA`|?? zuUM7Xr8qO*I5Lm8hPpA*J7SI>rzYYznuznN2n)nk4oo>Ji@J6ZjNe7nu@271Z29q^Su4sU_Zl?}f*d1t>B7JoyT_ zxS+=&uPFym?37p9eM*%#V-{l|R$nOxG-=Z=`Q1FDZLq$=IFJ zT9yOM*4(|!7Tgl`|su$4`3s?T;4R^=imx)4C-lHDXwb2FL)z)FTEz1 zDVQ#pAgt-_Cip1r$i2wtGn%0L8bLCa;Cl{eKfTv#pKVT?~-VEHtAB{6V+2y zi`?GizN9TwKUz+ zewf|`d-;%5X|<`FqV zIF>nCItly>a#^bZo#KG74eO(vDLTe#A`-B7DVB)Zuz!kxrDBnnEf-M}uH0HNv$OY^8KNvsxX+eg0R8AL;VNQ`w z1pj~>)+#`+*e`6&`XpzGjp;Dbp8_hQu?dhGNc5|Tee!y*mCwU1^ZpQ7h3A* z=&bH~;u_?B?+$uSd#ZTndl#C|_?AeE^0x$k1PcZ81j_|W1Yc9%QpYp*k$+2pB16$Q z!&pPa5H?gZpwln?qk)BlwYh&KV5VB&BKk6VJ9;O&+|UIcO>3Sz?(Zbup^>StDhrg} z#(>s9IqZ0;-J`VW3RYb~*I#GW_0jEAKG*J6T6CbUudZL^7-Owtonr)rppElR(vNZP z(-XW?^yAzI^a1c2CMs>B9Z4NlJ`n6q9Zd0ng{cs2eST}%7+MD!8f*`~1f$k}<{8%A zwxE5i{fNEDQR)zB}Jl-80(L#T)RA_cicM zw`jmF;5#s7Ra^F2&)H)3@%DRmTV}c;i>8#;;XZ|Mh z%HEQNARkm%0Lv!p2CLWj&~@K+!*k8k+}zZB-FMaJm%St3wOlZFvi5)sj)-%qQ{sxd z*Sl+b#(U7_h;OoQD&cj3+`-(=+=JGW##ZW70rhwlL&H>Wzz)Lh!;Z(6@(qlyg3oG| zVY<9RcU4EzHz7Gm*GV4ISXxIK2J8U70%O(+^Gxf1wmr6xeVqNMz1T6$fp*56E1ZDq zq^q6#iW~Ga^6c=$y~n(Ly%zHa@@`*KUuT*}=8~CYh4wUwv#q(c`IaRE9tLIBljio; zN7k;;5lHFi<#^}_I_Eist_7}|o-v-T-k@)Suc2>-MGJNX--BtZ#lw@QQ!Ps=97ISi$DRWw8$-+tsAXA<0IDt z*G9fmt?nh-c=n(m+BucoQ40Syd8*8+O5fj|Y(b5euTgVK}Ilhf7G zv-Pv|t5PpXjnSi_F^~^xU)-}~2X+(|l#S(&W;lfhnJuMncoXPu(PdU&k&V4mG>QFD z{FGBgQk}a>!s9)W{RdbCBwGa9j@8`pp*6XIped7#%b;N_7L7+2 zlIScJjXthyC}_r{;IimE7KbLFQ|KHP^FKr9z~I&rK;aE;uY?fj1cmuy9TS$=809&!>e5zwO4C>=_P(x6l*1xk+m=$Zmd1}rEO%8D|e%qSyD zk4k2W8L3%d+G9Cyt>=2-I`5fdJm*s&Rmh?esYWgWYN2YNYNFQT=H}Lho0>k`won7~ znvAW~AiWl28#P3)%~(XOq;98%>2(-8s1bTy#!hOKUXQVh8l%@|?54))4Hyj>GV(9o zZ`~8!Q=NxQ)r<7?Q;YSB^wm@OR2!f#brW?jbq{q7bv<+=bOUr$sCy_DiiUI{ZD=ujA$tLP z5&H}EGxaO=CuTY31-mB{14qO4Fc8nc*9cV)J(sIYT&GDLG^wDUojFZsM-zZs4xtZse}z zZssNkDzm!bj_U8`Z3Vcm1%`MF=|5?sh&RWh{&Rd*V2UZLd!ER#IxT%ID zHi2~$`&sYeZsIOl_pA1*Dpd(}0~Nwn-!|2DL3LJj?jIaiWl>pGX4Or84)wN(dX0L6I*jn3eP}QG5MqpLylb3mvTLlX+%?fP$yMf> z;+pE3;M&RG#Xrxxz`MxX32p~>fZa0|flcsF2*XZy6%D=f}Vow%p1&`%!%%@PB*Y+dY2Oav3gtV=FWT761i72zx4Gvy0q zXJ&*i?<@GK`834Gl+`#ben-d)y^i)kFG4Rt0X!0415+Ji!I&{UF+DK<>6`0YklT^J zCch-%MM^KiSp69NX#D^JP9N7MwDq+Ov=IUm3PSl%9+V6vL2Hr^LAK+UP%qM+Cg4eb zvSCt`d_eM)T1pe8)AjSHmuNHfGxS@y&FPD&SX53=O1Vq#b0^Wyu&bzB85;`^Nd=s@ zbVC8~qWwslBcHSz;Deo87%!Bi`eIh^E1l&2=*D|>{!am&a6PM?cdNGv=^6Pc`4{OY zX|lT!q#+~*i9%ZNBjA$+Ea^)&ND7m~VqHA1$S=vg;#oW0I~Dny{DK_yeR8v$uXvjo zsr*ya4fHeAb5zjEFdT-n49DQ#bH8Y2e_mN(?-JN!a>Df`wfWW}Sckt&!wx95``HKB zWEzEbg?p7dj6R&M;puo<-W~cq`d#{LMuuO?f692qATsL+>I&*H>oZ4*UW&$uCfg_3 zx0pWD+j}~SKYD4_58lsSy7kk)#S&KfLp;?!#eP#eQ@c;GUr`qsRF6}?SGG!pQ=C)- zWMgC_WJ6@J3G#z$2wH#?B1cj;&{Xs>^jZ-cV>IJFSYVP_0Y*t^6m=s_O&?3I9kDaU zFdl%_{$YGpM4KX)0Yx+`#13&n%n&!k3pI*X1-}Kmu=`PUoNa6v4Z^9(sY|=axgb$; zchcqy=b2^nsT_y&IJY&Jt8H7b-`HW=lVLE{ZZ@u5dmpEs;u|6?U`AC`_0Q;R3MeFRpQb5S4&_fu_+J zbQ?WP)K$2a*-rje{X>n@)J3;OH$o4UjF;5QtMc9RRXJI{L*A3m=8G-z#XLJdAP?jT z`45?z?7GyS(l#;#21)=qcjMgHn6m9W_V4*p8|4>K3Y0w3U5} zo#trc!18v{$w{)j4XG+lN_>o6bR=WGOl=;Hu|+za*NAa7qj$>v_2kFnN0j*ZcxQu1 zC+%zA64Fk_sz~>UD&B$L7Tia|xKRFZyg1FZo8M4zRj~_Pq3DsZ3#813(jiPa1y}l2 zyD-s9aEsYEwpZtJ<0AS@!+0>U&<*wUh%JltOEwoR)lG_xmaa7XRSflXOB7fMpu`RJdq!NcHlE{Sk^vp=#p-6S#cR@$i>bNU1F7erCS9&FMNe6VdBDy)c7y72M z6ZVF(GxoOfma;vz16Bx3hb=~2hTTHcHq^oSXk=YSLj~@eOP~9cUx7a%&nUhJ zlIa_i6U@7mX3+?Bb>??!OXfykG0*^257iJ=A5|As8&wDO%wHpr^&6?3C@#iC@DFoF z$vwn<#2n0e_-FigeQ^|f4z-^52JAZQ5#k}@0_8t;d#a4{ll`x>M>{H^aVOF$gk@$H zeKbca{Un*{59!FbmOLk8k8~-o0|QCgqy51<${57&4W8j21F3@VU<+v(bF|Z<#mZ)ijba__L44=`ZU!f8c7-dGJz((%GT3D+%vT8!r!bFln*W|b4H{=d_XKo5rN;}ORNPo=gLXp8bGInd>{9}wsAQgQDY|30H_{)S) zMZ!U>5cDIo3h@o0)Kvg3l&};4PUIiuleo?Xw&-)dW6ql^%^f1^Wzp1q&NJyGWXjypY;Vo(aTI5mXcvMukwXAkCw5C37Tj&2mX^Nnc4H zNq@-zNk7Rz30t#TvIe;nn1<{uacX)xdpOrCLmG~#1tCa4k!}S#b47-8aBWE`5rkAK zYZ*F4M-he*>Jj_|AI^*0oY{q!C@~O7LGI&UjQ|#3zMI z&q#Yp7fK5fwFF38(t`B5)S;}KejO-5L4s{c+Lhde{72={M$-CGhBAJGy;+o0(`aK& zbB$Kl5k3u=4$J_2r~t~1@}oQ`FDi(_vCAowVY3l)5LI|FP=&k&y9s-Oc#LR57Kj!X zy$cZMVb>8KQfGn-f>R{F)CA29NkEE}o|E>LE|&T=)g@X<4buZ#SLbPGL)UB9DEE7J zE$Al(Ob8Kv22$ybnN1lh z)C4s`%}@*UE`bw#Y-p%Nht_9{>#{8}?^1sXYmz&YS75ghBvKgq3Az@Smi5S1$tz$B z5f@=M5Yu3b5SLpU9MOJIdg@C}&|gdP}-9Yh|8En){@Zvh{HmaO0CKj>DdNPqgg0VP8e9>3R+DBSQnH!sHljG~bAC1`m5T zdHbijrdDaTYkk&{?ykmi*cixk2%cR*f$??VSi*2ZQ$m2ygAgY43tWI5HCe=j-be71K zJwxk4@1ltiDrN}Xh%RHlqyE4w!#oJTW-o)bfZ?M5ndtb{rT`~Ov&L$L%4H(V1k3@> zMN^WrUxMZipk;-_gk#J;^ATfZ0rW+QKXG-r2n2Sj-9N_S#RLVtou}Pb$!(XE7#WA_Bb}*rV31r3<(IM z?r4?mys8#ridu}AMeL(0MHHfK>@9HH>GWJ!5hvC?2@odK>K?&*9*O=L!VR%W#CNTjCkr=TlqXFL&Eh11|CBzA^6LlLlFJ_NoR#)RpP@f4MQ z2`E{kKTqzbPv~Q^+WMqEPB!W{=$jFG5+>>U6P$Xo{+d2di0F|ftTkZ#D2mI3K9#Q) z_c?7j_9@n*!}*=T9-f8S8L%aYJ${1!X5a(Ra)S2<_SW@>e^5}o<9RFSVSFWOBfjbL3OmD}=pnLBGJxEo61kcr#QQ5my^2i z+kpp31XnJ;kKbG|OR-)tl&PXDN%RxkW;Tl*)A`)=NF=e;jr0tTZHf&~wi0dBO^pqS zt~Vf*Qf1G?TaU=wC;lU1PBctx6Y0fK$|-%Lj5abeK0EVU(3!O>o{UUPc$L@ngPg`d zvFNOFjbKlps<;-qh&_ffoOg^@#<^m%jTxQ<|59(t{?=!7i8b)ZvSX8 zojaMfUiggFmGY9`M9_@6NB}9a?ueR+t81%^j_RdBS+J(=dHRUn6LbgvvQ|@8QJ$q6 zvdP^2tRsAiZm{5x#YD;(PVQay5!)CI#cGh(Db3b<<@htP!F`luej40n; z|5$MsczKS&okVLV+#-C!>P(sJm-4RB2lCvE1H563U%Zoy@%-X&5byuUzYYEXHw#Kw zH$;uZa^YlF^USCc5%3j>3EvPW{$+mxUISYR~VGv8UG+OgQ#*9$kEHYyzpoxQzK<1E-R z#8ucG#Byj$*eBFSRF>R|)tJ1`f7_HbA2CZTr;J@Jz3fNr3P*p(BS+Y|z{z(la$()| zJQF>=ykXx|Uvu9%qsFns+0TnG{^gwc=O1t^a`y4UjJ*t1`kUf)nSy?tzNUfgQAJP|+7w-2`qpDOgt_sKgDQRoe5O_&`KfnJC1%TA(b7!w#Es|-CE zNhqw%6LEjBCAc$%MZwX*!_foLW6{6Cx$0T!IqKQ!Z%Q2cSMYc6d+=xQNAOSZX!J<* zx9fQHbo5MgfAmP9crbb>I$u3c{Y~{l^c&C!-Cp=L!_o!6Xi zf@ltkQ*QqaK|-O>Z;(>O`0%)Jt#Iw|i16_6u<-8iuJ9EIsqj#h47|qnB|cHT!VV{% zlAo88sZ&vtQBzRg7#6UKfv1(DzB0;C-x-Ig8y&BU{tbbMpBJuWTJ8w>gZ?H)rZ((9 zlQWm9v`E1&|1bVeelz1Y(Ra}Z@klYCZKkZJ^Wt~sW-9mOc0^|=rzP zpm3PPZ~@*WctzroLZzpq-KF!TIf+uT0XG0T2wsGD3;K;6!s8X=6kWuRNnG4R|1HuV zvLf?{bd&U(oFq6+#{#g-lVb2`uwJ-lcu%-H`y5Bgy};_ttK>c8FB4o9<-|fE-2BTp z&9c*a&<1xNb+vPE^+dcn;||{#6TLZm09-K6uRS&2e25GoHA@B0Qp-LUrm>{`>pT|!+$ z-9i_Gmx42>5ML0G1WWWUbhGsWGGD(`|4RQgQ-|G%`bXMc1}AKRaygIV;@FzdrzkFV zC%TgTfqEtUT616Xp4}VT3u=U!V69DIPJ-r)O_i0B@Z3SPQNnV~ZRA1Z1aru|!%U!q zmJUVaZfE(HQ>rIcp{>2` zUTm=q5~zp_4Dh42=ug{8m0eX`O+-v0wpNu!JJ@&G*^W*Q5^pb^mVAk%$vcs*>wd=> zi7&Bhj$AC9X}}{h=J1*`Ze?uFVyQpvZzSIevjAHWSm12@xRMetbM}@ujr7!h;H@H6 zGS)}>MD+1){7&Ez64@1tzk!`WoJAbq_fa%a+*0fX*C_gCQ0}QJk3h*B#T>)bQplxC zfJP_{Qj7elU7i>qxW{Y}+pi0_iIHaUcw)I5<>?(;5gV9nBU(|^Rm6(XH3q1%PvV2; zmO$d|7ylU1CYmI6Cw7R8;sizK`$jmfmq#YY=VgvZ>iGT&y0g~By^$%2DxXWaO5a-_ z)1$QpEnE9VQ&YcAKS56?-`0opczA2XzcUJEf$i4Z^3TSqL_C>E9#_niAIP*peDhd= zHLxv+;yWb4yBW3#b^v_{-8gp-SAwXc`7bvT-z2vW&8#*jP=dbZVz~#3d$5O6FLTt~ z)wIn*J^d}KNa49TZlOCKm>HNAco`76=Qy0a3-o?G6Js}TBtz_$x|96(47vL*{~lOd zFrT?bfM==P8n@PMbTix+L?+=oO_O4N6^ANQ%6*FK%JFhPn@U}2Twz?T(wkzb3iLEI zMh?qCaBp+(vUH#=yDPPq>4k~IEt(sdubM5E!>kj)R$vS8$T-mQ(X!&-Dr-jIaR6(- zYWF)nId(YBu5Yj^*jm>m{&2%YL3Q^oH^?V#uZ$izcR!YAAqw|FDB(=G6 zv1}f353yHKr&ae>8`S;Ot7MCa`-%5ttBEIw*MsmBB85x=sei#Xg-KykxD-A`ND))@ zz&uzJOo0u+YG5hY7;FMA4^9j&3r-03WMAV=rPc+toSp0)YzQp}A~zx;Ps=|2CuVjvTm??Q~FSfLY{zkm|h?3!t2cQF$&-Y-g@3ZMs@Hq?+Wjq zFBWVF-s0cn-vFC|{{o@F&1?iNV@?xP2!1o0g3ko!M5o0}At;>9ssr9)-3Z%(Zl}9<=_oCT%I(XxmBKsiI9{m)cQIv=id0 z?>gr?@9OUE;lAs>=l%!y@U-;o^;CM&-sj#I-n21q%oriD*}9{6blGyW8A zI!1x5jqiiEAgv-0%hd89oMG)T#TZi&>6o zzN4Y>pqL=Ziw^vQdgnxHbx-vnF;P+w9Tt-$)kIO&70PvriT#XX5YA;?qZrvwDSF`? z)-mdQ;$C8J-D2Va;!5H%;(gqExjf+ZD+A9oUkWwDHNvWlGJ`gfU@xJ=sq1KXs+lp0 zL0}Q!kjxY#rbt{D{OT$pH6wOaJmLIt;n9Z+v;EcnIdxC_PYNlfX@QEsOb1DK$xz+s zD%}uWNgF`vTH39&CA%Z_0CYc8&RNg?LH)(-Dhv(B@=uoe}z&bH;Y>VfHjM*)bvHln}%wB731 zSuP0^Up-V9Zj1cq|*1X8d7@lM}vp?1i?3O0<$Ql!UVa)=VDhU_Z7t9{__ z;Q1ko%6`fop&z54pr4{2qMxCkqdz#`Id3LkByS|&Bwr_ACSN62AXgBN5|zVg91|D6 z*VLBNiRO>Jl_@cEG0!+gb1l++Nki@jS~uZM;RVf7^I$VzNz!XtP7_vhs!LyS=g=8a zF?$WgZ+ho{kpPmxWaDH7w@VUL%s?ZD{(XS@u(fpOGZ$m`G0!3$bSwS%Ob)BTmg_>+ zk=BQ}OID(-fvwE;Jod!aB(NZIF+j~IqyA_EdyVKy`!8{K9LCYqal{el{h~{fBXCan zIZP#{Hz}_`CIks+ob0Um&q|e-kKjcaSEVO;ix@96L!CK)dwKD4{JWAD|LNQ)?+`ht zosYBgHMDGVd1MeO0ugGG8Ezu@%Jkc2Y0h7cGG7`ko#05&+ z*FGc27$S4xhcf^9JNTdaE=H`G`G~}sTZghsS6l9!lOd*eaW%BpoCw-Eoltt0wG3yQESP)!u`VQ!pFkf zlG}wRg*%1!g;#~Ug?EKpg<CZf@*VPJCG8-!AZ;N{A&(263-9s|3Rer)3r`C- z3LgqL3)c$I3oi>V3VYdD+DUFt`gO}JyOMX4j^{sM)D&cy^SoVr*R`uPYl@o3TB1xU zcPn-)b{lx4q*81J3A+j%!)G<`lQ;2nz6czZ-HP8$n66lx#&pyxI%U*I0()Z6jhZOaf#e?;o(;35e_}1i{YcsScQEWvi_@is zA8>2<8o9myOyD4!Kr`?j(zEcAp zKh?inyF&X$7Ew=BU%}f`4Fksm6zdOfSHpEY)%w$G(A?I}(mHfb9U{Zzk5d+>Y5l<` zv|f@i61mJSA1)uHz!hEv<|yr^a!HwFfdrzVYABj}>PZqRv#;2YdYq*g_QN$LWriEX zhr}((aqyw4mUueEQxYjzOFTtfPdr1sBU?v2O$Rse6-cwMZ}xNYY%z}p3BJmL#g_(H03TfgJu+|}xecl3%LwySztC~WSMn?68r&sT zPYT^xjc))ONnYpXk@gmVaHrTd{|J9fJvcB~?I2z>=!r<}V9iJTsIVY7IJ_x5BRoC) zKI ziEjx@vRd+M_?jiH>ksq;PR8ynR5jF0)nsBDl|+?|cC_!da~z!=WL_nmo_vL*%R7^9=>EjP#Mjt$ zM?MzGG~`hjb9w(UZfESyjK8t`U)c{J0C)fgoQt1OQsd>$KJsRfUfPem)uerl4UxVP zL%ch`GkBClamC|bP{kPf9O59qucDu#sbZ$$wxSYTtLT?OyUSEwfr>erIhLuTP)dJk zS0n}s?lW7)4(NhzQlxo2kyznId-}vy#s($ZidO2T#Rf;$8eq!4iI1M!0;#uu{8L1i zXqwoQ*eNoJlN7!0gkBMu5}%(r5vl7d5%yrMkNYB1i-}w4+vqMpXW*O9tz50|qmSz` zTBDYu{i>;@U$392C+P3!!+IOQR(;BT&Jsl5g+$R0AxZR8$d{Sr3B@cqUi3|P2W>>( zMfU{Ck`2P!!%rb)`SSei{G9yU{Ji``B+JY;t+$-Bne9dQNlydgDc_Y`W$t$FV6J#H zcOkbAx&*oyS_{?*sR6dYpgPB(Mu<2Xj?A#Nvnl?28f$z}3R z(A)6%jQ!FTye^C|KMqQZ;81>l@B;rN7#0|rZKTtg;{|V-okiu++N|TEu$Ukm!b(a+ zl5OE<`NhF8L74z#$^}g386n-2RlXpd$6P$bkh+$LI`NOdNa zbx(X>Ji~wrPlHxKXG3Q}XF_K{r$ZaS>cDEl>ci^6>cWa4WDqHV5fFHU8DT*Tfc1y< zgY|{=fpvxTfOUg)hmAsvM2tWTM+`&sM+`y?Knz5DLe53aM9oIcLA^(OKzu}eLcBvX zfYyiBgVu#Mfi{LVf;NQyfPRO5gH}QRKz~DjL4QKgasq~kA!Fzm3WkcIVMrJosSHY> zz>0abwvOQ)=@zF6#zD7`!>nVJD$YY-B=SXSefnPdcfOP15Am(ip9-Y>9Ou>*|o5Tu&1z-hyXf-UI%*wdj>m&2%^L2P0%CI8i@6<2e2owGx?IzCmCqz zv&X*vOY2~=T-bi^iKF2JH=^2 zt3e8KPt&Ff6XtzpGo5ESN%+M&VtkPJlW-;LB!7qRS|;hd*11->ZFXe8UF7KD;3P%z zo+O9DrGO`RiNCQsjzp$8FTz;Fo5}Dx+sZF1S@BuUR*}Klue^;#(jmsS$e@TV-k(1Y zJWXP{O5-Q_9Td+LFTq`kK^d|D#@xcRP*A!N0wXKtZWAv}taTGSgJK(E>vg*fM5R($ zOxzc!yidGi;;j+~6A7#z4Sj%cEo*B$5{WBerh?06G?`j!rqSQf zQOw7*;wtS*ErYxkJJx?1i}Cl+>}4(>EFzp`D`+3cE4(n|3jNn?KC@o4L33aGFyD8M9IQ1G3R*munVxQGz<#7lv8?+@{RJHGM~JZ+*RLQ{|S!@A`MY;f*|m>#PCXW z{3HnXA$+$fm24dCn4}(eAx$Se#qCw3f0j%$P;e^VE&5(5KqBxTGQ3o#0ARM1PLX1K zF$rH%SKmMnmM}_QmOM+n#7h7fAO*f7e(kVbjMA9NB~PO^|(Bq50$ zTmV@Jp^17%Ux?|VUeT9gQC>=1hK-AkVN1!63BUPGSXAO@(lY)!vc`X2_(xO}w?Ul- z#*!wIrjo{!CX>oZW#pM;J?}o9t|d7;>O1RO!3QXhrqk(gx*WPuxfi_~g5Ymsy!AfC zmuGF{J;ltc)K9`4_HDMEW}_{~o5-TlrkD@9^AsK0fPN0#2Oan8_u zOCqf;mn{X|`Pkz~JKa=W-db*bXyw`3*q+7~*vcZJs3-c(RxR4rzP)G%9qk{fyHR-(y!CrLkb99*@A-E}g||!nlz!JDbT*$4@Cq@yX7{k?z`eycMK9jJ1(o z5pBFPzdd+}gm>lRvs{(@{}gu>yTJ{L-Wiub#%v-T_D?NDEd8Thn&>OI!)zWqs`I)T z5mTl~JepYIE+RZbVw+>bk}XA>bY-z|(hUZra)hT>;*IBqK$FW zm)R@FvS$IafjR#k&yuJ(DuGI)GAKMQN&Jbul5PXPAFhe(kUU4SQ;sBd#EbFBUhENU zB@N5Mj`iRUZ%wV$m+W+IO}~|UDqk*Y34Imf9YuVa_L!5U(N#WEaOb$8Q&#J zRQgSP>mL;VDYu-CDoU%w0p2_MC22!G5`5={iu;LcO7xPE>;u%}yq~~rNmz=OUX~7! zu8`J}7$hOy2YO|uHP9AN`yPf`u#9*JOM8ojU*W=;lorZ!fVo7(i;+-bSwQlML`wk122RlG(}BO{=}dl97G>MoAY&X z1l%udA7vS?9JdVr2M$F*5DVa~GH+7du)VSWNm@v{X{wTy*(Lb>=v9HmN}0NgI*%Ix zpRD|h00WXh|9~hU4&0M|(W!u&lAsijUX=EeE|b=f=p>cU|1jU=-{rG(K~l}&m?C~G zp5z~-+3)uR4nx=Q>)EO!v}is08L$EN7kMA{7MQUAxQ5(|lor*uiJjF?h>z)4byswE06%q9 zW`b&>>N{a2xua%QAvrk=-Tv8pIBc0am?jiCL;ti;?5e`V9)oe`L5I};n9;54Io zV#PT!Ic6s25a+6?KsqGxaZl3n!b!r(=BRm(*+^$wju1w1RMJXrk^WwC(D>AUC-Ea; zOV&sZ<^Bk_v)rTN1f3>wlk`_MKw=FQFThwRS49VtLvRB5Da%wkIq(wZHSmnlge&G7Y*2V@WJBZfkX2d2)w;S-v z(VqSZxwngVWc+Jn7Qv8co~TST#J=yM|58R;>X}yW9*ndn20hYVN1HDrsq(F2IzL^rs&S-p6Hh7zUUxvG4dC;XHUPL!drTM~}&4^8gu2E+NCo2y&k&vnZacts_DNO@ux< z40%xeqWEdL3y!X2D4(R~Ynx%>_SO8lwppC^@E4g~ng22kai4ToIMXWLb`=C+Yi@yehFm-r_4$boOk@Zh?wr6E0)fLp2DFP)&kZ&n7}mK%jRNPcFu- zMjCM%NlPf|2v_y(^(Vwt0X-N8V|J(XESz!-%ah!*@PYMf0Z-yp3<*~Ao-P< zRhj-|1$3cyfp&*L&e|@JvHIm+`sb1sV;5neNK58AvJ#2R_)_zcn$b6i3!mL+7|kRu4XH#FY_<*R|=O47{zor8@>D5|Cje4tZD>M;3|52ojGx zBCCUY5NB7 zE8iwRBzq3I71=gq44cC5L$5-gLhnL|@cF>GzzfpqFbepP{g_1q7@#g@0x%wsp(H3d zN{SMrM5qVi_Mwi&P=`>b5E!C|7$I7y)U;4E-Z&;ZQgt|N34c<`HN}ZOyLEuF0;=N(~p``;#l9I;b!Bm)Mjj2o?FE1l`8RgYzKsA$2f~F~34Gv>e^v zx+=IbxHY^qI6k-`ygqy@$PRPDi^2=T(crhx_fV*GIbVq_oa&q! zv@@KG5;1oct-f%PaK2eXcS>(_yVL(l&av83c)YXpo;)4nxO5M%H{&<&0%HV!Xc4^6 zzW|mByv#1rmCPlAFU&d8FP;~IOQKRSUpShTk;o){jnBC==u)SJj|DG#uXsyKM`tD= zN0>^??aWWiLCZJeR14C&)#|kkw^iDB_KahSqr}5 zAq@5$${vB5Wfv}IEeApNT*_X7hUE~hV9hL9lysDScm60k?}+b;YZ>PT*9XrE-#ER& zo38VT{=weCp25AsW8(MDPomGF*}+FdoCmn`{qa5M`oMc_ST+x zk#-S*;S8Lm-$LO-_G%os7C?Wo2XF%&>qsMi@J)@#!PBg-?-Lo{A7KQyvEB;(`!`hp}c&Ql6_;>m(8 zf1+X4NyL(CtfskH3=P!6enl~}Us8r+_d|v$n-dv@v&b#TRvtwF6N&|wW)=Fsy63uC zdYZmvYKi`({!3<4s5ZMH^|!R03`W=t=@j^7hcF$tTDcNUFRe>6-3WoSyg`yXwfs zLYewJ5@R;68RKTg>P-0?%D2PL#E&V-@u|*U@+OfU+V{Mbq`i!Fk=_wqyeq#Wc$h?T zMdPnw=Mghp`}w^UjTJW)`@q$TJ{iDWELXV&3g$@W2&RTYDjkacpa{HGXU(8& z4ZT6XRzF_73Rb#?Uxu@8t*(%yv8o*BqS}}LA&v3=Ozr2KCZFF69I+a6f zL>o&}&s=H}VEBUTBg0bByst@HYQC=wbGeu4c{@JZ2U}OQf&8=S8E$ z1u;iBlT}w@lJuol6W^x34R#ja@z)O@jHAU@#WMXL-7}p!-y`qM56%zFZ%|E=jVJ}U zeQ1M)dk8yO^(bwVXEQb{Cwfn1R2ZD~V0TcG+xfMAQ(u&!i&7?!=UxB5_ zt-&q9(}gc;q~`iR+z%A8B)Ta5K>b2}9)~Plk!EL#Q`pz29@!At9z2C$;e1NJjjLk6 zqtw{r=smWR2E(qyJP+eI4BsX_am*l@RsnR zW|?`28D&Y)Ygx_^)^KV_UvuZuL8*kjmJ%?%_rFY_lA&ai;dL^q@!+?pODF8wb%+>*gDGk2zS{^vNg1o+g`+;+L{IyMlJugaB|neZhv`EqD1d}80gF?dwPMvW1^Gx`jB!nRino~Y zDl^QP_jizAiT_aYC__0WV$GlQc$-paYA5cwU3?DmAhFHTc%^Y zkofLSd4A(Y#&*Re@o->?JzD(e@q)?6Nz1d)4H@g?KjPB_42|c>SgK`>Ouye@UG-mURc5_ zDRN4d6jl~2B^L{CN`}WTLlzhMLncFZ%11!TAY&n$vP%og3f(0$^Rx0PY)Gc}8~vwQ zUW3Q*68-}I$k*5O*3`s&-E6ZwGY+wQwY0FVvVOLjZT)PUZAANZd(bh;@x@WwxyxyH zZFJRkSGxJ01sdwwsD28X!>j_wlv>0dn|8^qb$EH z?W`NDKdesMVB1a`-G0{|cT8~nbToACcY0mhT@Brb-BQmokH>q)Tkho;SNaB;KAQeB z-!{7}uZ$xsKP+vm>#X0bcH1D^b{o}x+a7g{b9{HycUC&xuC1>6?t^ZzXNkwrbo8HpI5e#<1VBCma(UzZ{L62b@0F4p$@h z5x2~<+~f70^;USf##O$4rgx^M<{M_a<+*XFrOMLMy4w21YO(dVZLyK;H|!zDXvbGa z9p`SR!?np($Gy)j@GSJ$y{EjBybR+K-vCqbgQ>asmf2}}X&i3(ZfR{@Ypt@{Yy)lE zY!v$~d&Du;@y$`sx!37(ZE@9eA8?C2i#<;76fe`b%s0gJ)zsR2-|VxzGmf$Rv2?I* zvi`EVZ9{FlZJ_;s=H5G?>8lGECOd&FNC*%TlCYHQJqRP9C>yK`aR4eRDnrGAwu)`F ztqW``$1S#6w_?}Ty6AXn)vDC0b&J-vwpPW~wrU;k`6X)mJm2?ze}8|7=bk%$_ny1X zyyQd3Pgn1g32ZcE3>CrJ)PAB|Hg zTBQBeE7Z%as_;e8w)h8e2cs&(-ca0$9jDaDK9Ap`oFtnWaVq9$#JKq7k#^%|ce>{j&zYFFRAtErlasttG@HDTqBd_$4Ob_c z?^*7c=i7qpQBI#{kEcBOtK`jIjd@$@1IzE`g*Li9)3a}uXL$1A__wKibmTXwh{JC_TTLmM}y;ihtT<*Gu<`U zC08B^zv~*SRLeFiC(8P}ce`JTsP#BJZJv`cUq>v9Y*KYa_D|lItn^OQtnrpa*;5v# zEDn*H4{New-$@NszY;ZGUN1QueGGq<5u^juE7dDzS=Hf9(f0ViUt;%}YtccSw$0Dqe`^(S+7|dUmEqbrgyAFJtrzb^h9_zbb9WB+#t~- z;bUQ?LM1sIMK~XG9$C71W6W2q-MuPvwS2PVNc8bIm13!Mpn8>hrBxH&9PNmI7ELE@m$ps=83UT)B}pf&mL^48|B<9*2UnB1nHm_IW6 z22BZanVhB{nAeyllhJgMd4ahpzZ*?!I>WS@I%hF$rYp?JLEi-xnjSLsrgKbbP*IS9 zHXvwV(3zm0f_Cz@^1kJLLR*zTJ7|2+xS*P#>Yx+6Z+P8wuLLdTy~*3iTgVHcbu*n} zT1;lsW#%PjY0#jclAx)$G(pKaj;uUe^oIKh*-tnLf`S0bg&IjHG-g~^({H45GwDr6f zf|lo>Z-t!1=bN*4@O5QQvGTtiQ3f^+wik^pgU-PB~jS8v^($Pi+jR^XH*Ty@= z+su0>e+%#3{H^(d#HRelpngI9gN6l_usrPgSwU-g9lYbb)x39jt-N*lYk0kb*5^0z zbf&Y+oS^KWQ$d4hrL+=SUs^xf0NOy>B3?2r$@Cr58Zc2Ao<)Cf6PYNalwROKu^U}8S5_Ow+Yq{&WQ-e0s;l zDrCbgyEGWi?@OwLYrPAjH>}>DM1v3aB}-xU0lJzbwz|J?GpFapYdOk$D3`$^5g zATcYWpw%W~!9#{N6ttfS;=s(*hCtXCg;FRtDTOeUspW%pRzx)TrV7}wzg{4w^?}Bi zB1NE~y-aPZ2+VIQS>TpxWzf+fh=A6@A_)}Bv|{LxYI)#`@v=y}R9i@c>ebjsuM|Il zIc(5=9~@3nW!lpWC_knRh80oT?xaJm9TyJeONs>0Xx5&l`%1yGv?vA|L zU;mQCC7UeTQ(Q>%Vq10dG%Dz<;IY8t#Xf9W!M51E9Uq0Oa%_Np?1W`&A()Y;JWRgr{rW@_s=q))CE$NvJ+4ujC>J`~JwF{hxH9vJW4gWQVh;Y#FztG$@d%`9Z5C zP@nX`d=PKc_5;015d!DC zQEQ+NE+O+~EtA5D;PxSJ0@QB9ws!60+Ti`2+$dPHldC7rHtkwA42kL!4MkV6Z9UPd ziE6iYBNKM%`$T~FJ)FHU`?XgXFwN74Pi}vv{hbBEiHc~FdPuuZ0wcp^N+=7Ht!(ik zt6?#)Rto+FoH)d(_8!O|BW?^A#4B6@r+E)$qhn?4L#^xqn}q$cKAuofL{b zbNj8Qa4OEdDN)Ni7!-EjI5mK^Yww9k-F}{ zkJ7zOc1P(vAz)09_a2vAH0Y#EYEy;7A|GPAEV}+ob{|w~mEbPl|3U<;d(4oN z7oEBvXmJ04CIlMCrZ6ErRmXzuZe1|6x^*ImJoG{gVS02n7JQJVlfa8!3RNOiU@ zhXMWKWKraLo^B-_=9;+?AzTl9n;LF!FbmE|o@JAE(O;{&YkjbxGcPw7~};Leb!$c0YbK{mW$mK1>Ty6$Dzvw(99hLThl zDgQ;sW5T0bxVYLL;HJBKOZO4{yf(&0g1dAhXyo`ET>}la{;n&B)Vn${bl>k5KRED zgU#3XP}36YO=VNiEoz=ZZc@z2;l1wu%Btm6>H zW1WTu^h1fIWY-hjL^>^l=;->%Qs@_<-vE_)oblvlq<$3x8&cj*jfWiyy)MwGlY}bu zJ9*^SM17n9jBoPsTC|4X{oE>Kfw4jvjklj(0$ZmKR6?Ca&xJ+IFl14hLsN*rp}$Cn zu1)l?4GOoO4ZAXVkuWq(pMziDkfvWmgXyQ`8F2fQoCDFBdPPfrftZA6>e+M-HO%~~ z8lY)w!=d%Pff3MlLahTqf>i_uI@BWYW$6V__APG1+LP+g=jHEwQYro40$tH*(Gb4X zB!M>m%dfdck;wh z6(cGqWmVVJH-M%OR{?{1>vxe$+4^ieK8_4DLV1xs6sm`?6G=y3{R1||V1!;87!PNU zGvYuxSpNciI9Q(o?bX3{a%qU(M2FFtVHz;^z!P+Enf@1~^@8*29MV*-&*T$cwZ4`K zZEy6_!sjdE2SDe0Ha67dr*(�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�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%0AL)8aPX&PrsHG1d#VU<%0f#Pj+(zLcl)MvU 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�((DI}zdZfbINeW|~7kO6RYf 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%0AC(zlE6oikD+9n1!YJb}t;TZMsPxDs0BzDIVH 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 diff --git a/C3d/Lib/x32/Release/c3d.lib b/C3d/Lib/x32/Release/c3d.lib index 8cbecf6a92e019a4956cfde7ac102e88624c899d..79fe122547af9384bbc373c5d38b0bf8b0e7d465 100644 GIT binary patch literal 9074700 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�Kd+vZ~ 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

S%cACM-!mB5;CD?fGSwm#WqUt;V#oA#JM|+ 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%0AozOI)Y8!Gw4(jxF|QXZ9H5H!fqe>bMQQ9 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

uaEfnuQHHl#2=UP%)7s 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


RBid5fLdFkr^@^@Gy@ugGYt~9OeU&nGqQo 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
WOt=C9m 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

mUZ@&}Y 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χTZ4~gLh;GX40cG03*-1%u?IQIV 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%0D-eDK0(htbqXd{f4Nk}i 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%0DbRdMjqQOdO1EZtzWK3p%}w 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%0Adz|uSA=#&pL}58nK#b( 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

AGBhi@6qtuJT-yk#Bw=B`+-u1;u(>*Uai43X&fN zDus%g&_274*+dk4fa>W78}zUg=#P3iyO=wwk*9jdBuep!mG>ak!iQ5{c4I}RjX>2$ zY^G2vk!O6gK@8$C^(Ow}Vqr{ug{j_BJG7JR&Bs!1?LLl-vYgR z^uG8BlUc7Pu1jk^O3(I3XfOURnrS{q&u0?v(I2YX`R|mud~~!NR2Ed@^;9Uq=Azy3 zlPS0PR+G$V0xf>3K{js?IkD^(&j!)c(=FV_R9)@23qxb-KMiUWmMWyP=eHC@PXD!- zi(|FvB$~>evB3ZSFQw}HH#54isy$;8g<6PW!)H^Teag`ox}G@%;DMh@$vFY3WPeWE zag%AMoi?w3sK%7im)K7op?u*+bz-F_9;>R9u=qV0;_RehBuL8LwwVT5Gdwjyu% zf|RU29Hx$o3TmpVm{e+=Lovk0DOtC#sog3EZaF4yi_wmGla#Cc|LTIiGnK0COPxdb z2%Z1kv_?%fk?o{JxGhE*sTbDRD+?o?%}C89*1nTe`!`E%sHoG9Lb12@#lmFKZ$}RP zDnZzyBah8%wluv`RCi`}@XF?*v*ImMF7YyvkOd$cyk*UT!C*tBGpd8Nc`JjhzTa2l zJDbcM`^dj-U8All2eCf$=r2rKTkWRwHR%lErrH0|4B?~($(2#_8G|>QSWq^lc~Oq-s{JlF*7|Tw<~%} z%1<1gGW)B=N~kwj$#G(OvZ;%IYFjQLQ72&PTN-uSps9P!j%E8qlyy7aVMTrZ zm^RFx?_~k1dYIX;K%a5e6} zb`C-9UF69GS(+E{tn#%4^;1TCh)TX9#nW$3egp{e4^hs6Yp+9$_Ydm@uC9izd zzJB4=w}^fsLGN9p&cynM1n4Mtds0eYwrXYXK;sxt_wlNQQFW*os4I?>MRmA*x<_1s zEI8S1fez&aEkKd`DX9g^<4~PQ1|y3`#%ibOEv|$=tHDZqYRVm`#-oK{0?8fevx#+T zf3;4XmJ*w$v$YWO+eqX!_fy3A4r0?nEXwP#d9FZ%&6`E!;`dSQ>uurrwhrC!SWu%4 z7AAw0!C0t|vC~^UDrVO|C;JVr)fSzzEUfXvXs9~6R=rx2Mi&c;)pBx{?&T%X$X%3> zd;5oUF4fF!o_V&%onLg8yf`5)Fz*tr3`XnA!fNy9`SUwv!;-{?g(j>sHW0D2{^sxL zH5;0O*xGhh?_yPGnN@&#tPyD3_qEuNj6AiWwk+_f9YLpjX8`^(pEeV#gRY}EGiBQ6 zP^!&YxsDGTwRfqTt#534RjZ&Xnf{cSMApUvr6NCHF3J`zOS#6$TQ<796b8}ta*FD$ zF^Y6nq||K28WY*-TJ|c5>N3x=xtYAp9m}So4D`yB+I(J<`VFhW+&Bx3<$)G5-DEbG zh(=~8pD~znt?83CmvGc<&CyPEmD7TjLCt2O9n@-@X-S>7*;*RdbQGDaq3EXT5G9`N zAPOi9)}nI=V*ik_IC-ptbBT<=JT<1zt+QR{NP00$sz$RjCMy_Y&n+TPt?$UaD6wNv zf2$p8v#i%eBM9Vj(fg4vPPj`@{FTjik3K;D`z6o@qv7mvj#_>mpr$d9&M`JGB5d`E z{i}ZUdu@viP42}#0oXdA==c0Q8*2S;xj3!{$fp+o->2_Un}?2#~$+?SrQ;i`iGi~@ST6CntaryuIESb)y4U2!T1Wr zIWJ8uc^=}NmpRy$PwH5l^YWCO-|cMwht`2tKyK3#<*^o_jK(W%8)(7VEk~aBRe)-= z+iL|z6=Ihp{Jzx9XCXkECe^<*yI{5g=w#{Di3Mj`jZOD*X2O)~XV7}{nv_Y*ZXYf> zS$eI5n+-HCDyZ>zT}n-(2$#IZbw{Jrw}#+n6PedYB{HD;&o zy@FBp=9FA7{7{3e!|%$zrB3YByRvA6zqQ6zj|z15T>-Rqye+lid3xRT?KS@M^t$Ui zYIN&bg<2yLciQRKUEf(_cl~wOch$Mi&+D%5uG62-*InP!u;KZ5-SxdS`t$U<>-*~T zZr}2Me+#!h$^7F&VsEZrcl|(0&GzO5mK_-q*&zKM=Lb`2>Q}5MGkjmCME4dxl#(Uy zh7+gg`nARno4hW)@AnatY1VA6UDmPp{XR-F{qCcKocYI6rs%6^s1EnzDX}B3l%Y(? zCu-#9<#ne|*4WSM>rS6aZRpbLPIHJDT)%k#>6$Ii%Ns|ZG1%REhti%(dTO9 z4!?1PDiS`QSg@?M0=|RZ^b0i8%bOzK_Qiy`{CU{Zekmo3?rG6U^OtKxBO6vv$qDSG zQTFpIEnFhlV&{oaKJ2S0RXoN+^X6+QwfW{q2j|V#X{Oh_LGAoT!d%f+|L{$k>DA5? zh*hsot$N=|m@5~|T)1K)9E^12K7YRYppq&`Iqjf<6_t3tx*$>eb^d&Hfv>;NQz}L3 zwRipTspNlb94YX}H#pP(cFnTdesS3{HKp`5%4L492LRr8T6p@>q$dx=x! zTZq@h>OQo7A=jfO1y#=fsAfw=U0Y0+AN5p1a4xFE(dGkw~n>4nid--D+_~BnA#5L);aYAejQMtW}UK06rLOi=U zGVWP)w%55SF^;CIarB!OqH3lm51mT?Hsv+X867mJ>m>a~n8H>TWCDMea*g?t>^eN# zihA?+4yM1lXm0)?B^oy!>%ulWBmPVwGRpeBgFiaC)}ZC{K=tgOY+fR2GJO173D(g+ zr&Po9)zvkpC%>gc+^^rR_>0ZVTIxkanTWq8#B&_)+NtU3uPPi=B) zkDi4=EkZlnp9@CQ-&3wU6`cM2qfTu)=~Divxv^Y7h|be5Mbvbq(~i-@52KjrvKD^L zBPI`qrLm$18^ws1Q*6)s{fK!*$~5Crfj&l+hF7M{MH>pkl@(EOYAh=0K3!D>5;5BxD4z=a$ju4$+^)w2x1`*z#ss(4sojkUZbQ^A#00mu@Vgfi+(EHB z7!%x?GKFITgcbwd{5S>unld`+GM%O-6=$zrBi*mHIsk&!(`_42>lv&ERu zzShhkJjk)inkQ*%45#g<2D$#X#by&cKBE|hwuZ(D69V4 zunK*P!;4O6qF*xmpNro3w#NMVUR*HtpJ{O6CrC^@+Y_LC{E=ub1W~i^H+G29shpJsSb24WtraDtIMP>IOil8@lQf(l+ zm0*N!Ve)i^ydI@CPPBbnro2Kq95z00Z3@b59~z}wnY`KuhD{W|ZNv(xW0b8^RumQM zX;1YnDig^ANYNH)s9-ZD49*C@Q8k$tIBBKCo}yu|XW^SKhj~ zFdD8-c&`vBX5T3#7KX=z1;wCH@*Oj1rth56va9}HTB3D!7lT!eTiptKE)Y?_zJs}I zgEcW(hyz`WUS1iL)yD+w$-rfzFO%(NFr$(hBz>jPNaOm4dQ_km4pl2UbgC58N0^lx z$Tnf@5sO}8-<`HXeb1s^m7nhW(!aO8|3fkV9u0D7y=v!r^;N+rPF=b_hxZy;XwK}} zAje8tms{ud^K7vC@717{!&0>zg(K?Qj=rq}UT^OPuUe`s4E0hHbT-=GCnf4n%dJU1 zwrO=9TTQV3?^|b?A6~J=0nY!%p5?4OYql`5hB zn5Wq&wttyLoO|q{T6I9iTAX}H%Zr5eF9&9%WbF;&B|$V1RN|)QN%q-68MB@(HJisJ zp~~`uO;T}6f0V30D35#g9NHxvViQ$)Rx6O(LiD|^Lv3DBEqlJL1CiQcDXrz+- z>%id+qV-IWiDS<^)PF~$yyRPf>B4GD%I!b2o*$X=s*}ZVI7uGV4yarUEWf41E=||u z9_1jbxgowf!?P|+>GWXtnhE|JtDQQ$kO$)+!xELI-9&s~=Cbwm1rk`N(YTm2`DB6D~x`^r! z#=DhZw{nujT&hAEZyMP7o!mkj2*#R$d`&@2bBaYwLSSzmiV;sud8LXwpy9U;tna5~ zv>;wzC?*G45~nbLIlqaS956{RK`h*pGArsmhUG&S1jCg9<<^?QZ}#j5>r!t zI?&UfgFCv5>%=n#)lwA{COUn3u_WcGBBlCAx?Q0LTU8X-oL*-xHJ<(SS|33V_NA-nRLNJn?ZQDq$o~SzEtc)0{Z=0*ns1w76-A*(QR~l4xcBuEHb`#k&bP7Dspy}w8 zoEW0a!3I-37#S9dQmUdluT>4^z_@zVW>9TS)ke6v2#P&=z<6Dq5hsJ;a5NmQMF4hZ zvWdXUt;vYhSZ9b;z&aAPmP2uwXiYr3!Au^zOkVNw@o(rP?Hr4>RJokEP_*Ubk5dr4 zo$KN)(y1w5KZg{3QG==ufj9V{d!uUC7pJVDp3?Am6gLi@wW|F?5+}e!JbtuVm0yDB zgKCy0VXN24H_^Mg-upa*-Wn4)8_ynHNc**r?`l4G-l8{d&Nt|#WF1W^o1D$&a?u^X zf}87VE?;dTQ=#7zA2PZ1HwPDuj0N?ysHYb}uUQOdcSpO{V8eK{46|%^VpuiI(Y?i`F$0MYNntWQ31b#9~IQXTEAnZDZwimV9yj0rJ2T zgas1w60JBTf-B*L(7o!2;3fxQD*dPSS8D0YTm)NdkmsCqvR#9WOPx#jh`DNvU1(B6 zb?jFftGW43)D{vupre!>F|xxk+_)OdAvDmjWf`$uLyo-$p}Tw)M5hKG-vYw3=`Rpe zX$xG#kIyE2oW8}jK4YhatYZ5KEAb4A*Vawr>GxjhoMkkE_mQ>{#60T3O0`SeToHZI_h?>ia7!aSr77HG{%Sy+4C+^) zUqiB8&1S~{Aj9)oi@aRjFi`an(k&mXmG^??3*i@vHf6qJNVI*PyEfatEK z^T!)Y=-)!(N4uiNk4Z@veDC&BPljU@! z>^?>j&|95cm$cm0&LU=&uE={^6V*L5!*E&PxUTDcQu_oicxmr-5FioXzoDE!5;BFG+UZZuDL74`u9X zSR5G`4^(}4P$?9J#~I4?emK_#mkg3U@R5cE3&sl*>L#dg>(RN>M@hCstI;{a$6Cm> zatOb*8m%E82Sn#IV~=&ThI|6hMYJX&ZGFS!lP0?~76|U6iJhIUs{NFU>S`)mXEd%p zZBa`>v0ixExpDOwKC~42T|#S=NB>I3E=FZl$QtRFDDdJeInuw1 z`L5=(Jt_M3#Md%@R4l6dCiNm$@wymL5%hJ^2FKAPyY(nC`UXjM9Za(O9e9{;l4R;o zl1&F$_iqt&*Kp%<(OU9tlUuv4>2xn_CUO3(>vX=;%5*i8KdwOUe%D}5Ohly)WD|%M z>l63yA-YK0(S4Hd8}w2zFd4R#26KBCy>;*dg614n{5}SA=!XQ^B`~+e=!EV^HrKVG z;Lgy=Butw0oZIJN2e=ienGQc&1P$l?*9DJWLMOijQ+Vn;7fkx zrimzwPK)&5}Vs{6wCgpl`WHnB1VpAt;X8M}2GTy6J0E)SGMW>u z)}HN3%$HG}h&Afz0Kh+JYNNo{+}G4p~>U-bAuR zR3`kTK5e}@Bj=<9xLTu1v|C(MS5w&|8NCp9t3i#Tb-{=$smCUwxc)Yp=p0mBjnR0% zoo0(DjoN)j#;y*9!(Gj`+)1-V+KoooT{gRRqr*Sr0#@IMoDMbt)jqyLp>fr$3#l;z*H1^RA~=ZHq{9z5x$iRg?* z*i&wA4jACDKVZUXDjfATEoxCZ}ivS34{_PBiaH27bh1tJ=t4DvI!1xu5M zKCu97PlvL(&qBVd`P}^+x_|JTOBLIqScvXO0p z%nlyJ{V$++BHk8NjciPjT@tf<8IAKzn#dOglNCJ8xZ@n%eApDxMcR&{;TPKU!9cyK zR4gXnp7C7KQLb|{o9cW*j;k@+)ogC?Dod*qlb&rud8jQ+mM%cTLy138 zw?oX1>%Bdq%jgZx4R$c-rQ!M)|3uo1yz`DOsz_T=b^T5TwW7X?iyau(i=Y@_XNoM- zW^n4aOGXZjH^XImnAmCQFR$(D=E}4eoFwmNaP{j``hIUQc?#OuWVY`@<7;=4?P@lA ze1STsJu-6gO~3Be_wR}5uBP)<2YL0qGJ0GX%?WV%WH7h(CdjTPvwIuVrtFh(vo9)Q zXU1q>H$(1a`4GuwjM-~B@{-vmV*s$pqAiDXWp?P)?$77c1WHDE(!SB1CMd2 z!vgHExHB1LA`U}zk+!2)_iz_oY*pZlltHI28D~*_qE{7E)rpJV(4-gjXFaJw&ZVLnv_&~oA=QZ!8GZ9@agGHNx+A}~#NewB8tSi% zO6Smt?4Evm>vTXDX*p27&tk`=LML9`1@rw3%y%`PJ>Suqa;C|zm8C5$ObL7~n20Bm z?=QD9JMLHeAya6E6K&S7`7MKdS2uG#9O~ue4f)9L&LJGAc%6Q{LY;Bs`dYEpcTFSE5Hzu5Z91_N&~>Xs8Z8yY@|kLCkkGpY69$ z)%Gfj-&m}&54W*tsA_w)o91*I?0HLMxAckn8aG!&T~sZ8w#6L`YDH$w=8Eck4#Ac1 z8|XyrT#GAoA_lT3FUokKdhxQ-7{5-5xg!cyrM#FTi;O8$De;nKvaGKnGDrFr-gy*R zq|N9~>Dp$pD@DtnLBvX=W9jp0qRiL=bvOl@Euu8a&JJblVpMp(va`dq6;yhP-}}I? zjL>{XxX1M?=!QdJlb@ID>?mb}oc1909HZzG$`f(6&d!e8+~+Mj8$vs%F5r$ql*PCJ zTHxpg{@f$_LGSR^W%O#XxVW~i%dTXI5iq$(^qwz26P!A}t4d)AyL{PP7}u zaqlJBBCRIYM4jP#pGj`(Q#@RMf=1i>5#7~v{t5!x{(+2M2_|YIywUM8zLZ!g^_zSj zbkHUID60Pakb^1WL&5&-yh zS8YB`0hT=H`d#=K0NAX#TETS=52R~<#E4@J}r|ga0!zk+dJfORp&ea_4 z7r)S8FD(oO$pa_Z)`i_llymrEMhXh*{VTbM7Ol8na!@5SMJ@fZ!5mXVsXHzGii0ZA zQk1d!YQ|LCUhhmGaW1OAk@YnP$?1^zW0r_eb;j!J4yuTzL=>anCHaQM)MF=`kldM% z^7G$p;RxwTv`xo^-)dn>sEOjTZ(GFN$LiUm2URD2hh$5%8r2AX*CG!DWBL+(^YrAm zmFQU=dw-86I)@c^B%?n5KFxLs&2BfU^8JB}ZBJS@4~?lGQal-zi8xfBIQ@tsi)f6_ zPk(HY3-Lnn7wAxI_!C4IX*)V?`>8=MS5_C|rcXk0=MnN6Kg(FPr?h4E8o+D(oF>YQ zXOM6F1n-UP#tRer~{XUUV#2xgVkS{oK&wQ>sOh5=RSz9e{b;Ob=}&X{lP(% z7%gZ8{bR-qM#6${1_|Xi{zQ>oO=izYw1WQGAeYB$`_b-Y+W+F9cRJJlR|m7xnfAXq znBB;<|GkCl8c=NSM68|qtFr&Fh%4e~SiOrMc~={NtnZ~3QTA>d%KBdB=DGxC&semt zxZLK-L}DQ8dj-XF1`OP>guLRF6j?@N5FK5Wk;77dVS}z0To{%sP0K91U(s7zS7*#< zq?T{Zd+vzMNBPieTzrQ!mh9ncT};X`@>>gf__|iI%%}ps@p_v%7ze?8rKWFAfQU== zOD{K=#N^KYvT8h3u02kmUo(^FUm|wY-#EV!^FY&P0u-kGt3awUt8O(1v1pzjRbmGQKP^3F)3>V&&p zED>Lb&i?-CqKb@Il%u?-nYtk7Wah%ZDwyV8eS(`H;+d0Lx?WZb`VKDY}0E`APqOZ_9oKb zXm{`k=F7C7So!n||Bu@I`UR>o?tcF013mCqjs-3Wxc&s4fjr*eFCQH(2bH8G*L$5p z-t!5XC^Ozbl=Ux~?Gl>Z(72Mq=EVT49#{4&7RL_AM&1>_Zlxc1SoKJ|6a4Yng=6N>*Q9OaB!(&j@5vE3ezfir+eYfa&%6xr;94nR-$ipWVx43 z?Xd6Oo1i(x6&=I-5M-CY++IZy&%QQyaa=#2%`5TX+JIKf{YV=e6PT|$5$_$LM%Q$b z>~h)cHWPaoeP*#gNv7O2n@-Fs?J*A^=n|t1tr7>?-15Mb`e0_4zEOV=$(Cp}${QbS zkc(47`FSv^521KYH^v@U#4cIixH*&}yCi1MWb`GB!&=F9h|i{>Nc(U%O+;T5aU9|1 zil~c5@R1f*C?gF<@KF>`L}9cR9Ziu%G)8a7A7hc*{d9vp16HNu zD7qsI;Cd}o<1?d~>*+aAF5-BLr3?R>k7~IsL!)^nL32(c{zw9SIg22>rV+Qr==S04 z7OvfyTo&@ibDS(0ZPEU3u9GXFEIRd{XHYK+;@perT%}RmcmmCKHJd$>kzYPBW5=O- zJvOWzZRBl`*fyXVqmw8by5g5lrsx8G8Pyh@VsN98s`}b^r4olj)e2oK%~u`l(N2YY z*T7?sc@P7g)=Y=TAZ#*-;^$LjSCiR_qbR<|Bwyf2_ONLvitlyPoDQ6;FIqPixVa+g zqWQG2l`CIa1Lo5r$agiLtvXTHbBNlTF1F}F@}-dS@}UdVKQ$Euu&G2Gr1O_cOzLD1 zS3=*a2NeCB-b`!yEC{z{=!R}zgSE1tZY3AH`^Lo?i0&FnY|nvq3}Q0EQK zgeX^c6g>|BqRdD``=&ua7ttEba(0RBwIpdbozd_K`yIr zanqoyx3$%#cD;ZU`q>AQMKYX z%g~DX5(iT#Cr`xB`p))wnChJ1Y%dP(Y_G+9S4Xo~PPA(~KjVi(!uN|%wy)six|+*X z8;$Fsj2czP>JRV;81I8EQ)C@D&EVJJC#*_8FjeOC(#QcZnEF-x`Wq z$Re`{#FUeOC^V0V_Uc;V3jtk3Yhr}yx5{FR9mPVi9cXQtCOV~N&t#&t`rVWY&2|aR zp35kvs~YU0@*{!d%lAYqt#i@qom^LQ*-C?6zQ~})>w~VI;BKJVu4c1)8Qm?K%Gl#U zP+6?5)r_fYfE_qZM3uG|w_$}#3ib#9ConIyZRigwg(BX{u<5{~zYNh`O=ph+bYAgt zo4%@~cIw@0`zsJ#rtKhFdZkG(=5#q$woJRhUi7UQdm@S|kww~!-dcKlGugEX=FcuL zy52z(WmXj6Ro+RnMU+OhO7F_pS%R_=u^TTXTWBBRgHY!RhV1>a|FwyFj#4pa&g zlfv;g@MfROvp_@x6rFwEut40`&-T{NmG`jUg> zoJRZzkjR+nmjS=*pt?k5x0IN>`gH9pP0Y@Fs;|;Sr^MXWqILgkG}|RKzjuMsUpLtL zrfcVw{sv9#xYFOG*&<4#dsE-C*p-EaO2IBaWcMrDF@D>oN!LGs(}eF3WS79~Sp;&N z-_5vssa+WkHBu2>$#H%U^Igs7_c+KxeZPe-9BmL@MArxWfM(0I8|>MCXtQyn`HF*) z{v(3yYBGDIgP#7e!41{JxvC^G%fCtN(LqoDgl4;%&DI-L+5XgE$Az#Yw_ih1*Uwy3 zk+!0f@SmI1whAHGBaDcn^-GDrKy;C|qn*Pq4f<4(zDUk(G0JoPs+B3yPIMFW*BMit zQRr_3+5rK#edspiZ!DI|s@f>gdHP!yOC+{K^YnKvsz@KAy5-+DQym2i+aa|^3w;1gm|6%h6!Vz!( zP2|R>tC4nT6U}vi&Ys;Mro4>g$;>L`(=I33B4Y@gBwvw{gb$Nti4#F3p6cEX=^C2t5}NNPh#bh?O6hA2c4@fv(F^vhCC0D* zKJRr6*5G(pN)k-+H;Re8w0@oUddPQ4&22r3Hg7QKx`vWWF|qp>&E6XwRFRevV@ltH zxG7^s`ubM#%!t!5OX_>Gi|T4BUth5D-C|OsVl+0DHaII`;br@-vCqLuG9 zlI@b2+iG+UeR~UeMI3EtdIpK#Rw8!RH^uItiB6%ptwnu&C(U*V&2Bf^o!(`z<7(M? zxFgxtyGfo?W_DZAc>X8JcFD|bHB$MWj2stAz}@}@PNfzU5LZNP%SChR~d|hAp6B*jUd(}{btUC zBws{nqM!B2|3d~j>tjd_4u|A}t5!(oZ+oZCqpJug~CI`ac9&q{U#A{Ws%Ij;NkaN*>9LF?-bj zTK|tCyPC|_8trAC$;jm(wkl4taDYujxvghuqSKMEbp}4{Ihrk^G^$Xawrl!Lt+*Be{ZHj0)-$Mz zBzF5yPILJ*pj4N#VQYZuieF26^?INHXf zR%;dY&ZhFI#LiFecDHpqc>r32%IaKxOj&80)liCNKdIyoT}){X1Nbs@O1|n(mir4a7n6mItADp^If8|+dqrQt?aIR>pm%ea?wb0F$cGIXA!e{55?KH z!C4X)CX+SNqeoGT_G^%qM$x1llCxVxtonPZ7ENz(RtrSrXdT)=B~^N(4eCLTaz)q8 zCu?tCCHr&jklRC5l&?5|wxPcwuoj^&IUd+xFA1UvRRg3RzW3`_lnXkjPFuDxs;UTG z9ct^33HhFDqm##jQ=)pzcVRd>sxEbxnoGO6BNyp;NS(JL8eSJvRu_s@-!CO0Jr8Zr z)R?83$M4-X0eGRJdDX7+Fa*60v{>TPVe|4cy?5)=eSL5}8KN-C!dQ?gd>eC|oHeVx6EAvJX zYX8x+6%xJ=^z|_f^7{HWFC^wGx?^%|N(_fwDE{mr&Kjnxk$ha8rRolYin{CR0&>SO zx~nrIx}7Hi?#@HDqQ=Q{U|@jky;swvxo2d>_@iSt(Cf&bu1N zA18!I*Ez}At+bxfh%TV_&F!LG@|-$#uwTVp+MV}RbO17czDJCOzSlLknI{rip_h{9 znZy-QS$%UiD%F;*zDoM%{z9oRrhm+u%^gW7vOIydL8Sdi@e>>DC9z6pCG|(Goy=`5 z%Eg}KAUd1LoU>a_gCtY$90y7uJcZ6i#M6Mx%34N>ium z`cV?~k(SniO8)2sk?G<(akaX?8O7Abj;$ylE@=?svRbE%u-~f>!3p8%HqqUUd{qIl zuTESOR!V9+xm4ZCY@LQ|B09l7qfQK?q3WnT0r?yvhSR(9GfmFgf#5=47BNzER&!~c zH4#j-G>SVSk+^ zY=w*2+(ITUDo|ZJE>CsadwVh2+~)XvD9vX!Tjv3}laZG_TKg5mQ+`<~2HxIj6>) zm(H)by%~r-?!o$UuEj~zG*N6Fk>_|(%2J+VZg1K$k9L9i{9=pM+F5K3!Mgd9IxD4^ z0fJtKH0n%d z&>)WVt4nXih0$nL4%iMOPZU;W}zNjXR*|_ zcUbL3qgbd1QH5+VBZsB_!UlcWS-+{~-i?7~X(l}3K%1vT04>i=(Pfp7QwmP zxkUE=L^ZPBna72)b1gwH;Jqu)5?g$Bk0bTp-Na5p$S$b=o;+^X)PHZDB^^}%eRcL3 z>hp^A#|P6)U`NOP`}4S6Q~v{bmUK}457ycJLp`P8@yL9grlabAD39AU^*@|viKqIg zZtx=pyOb=m5>Y&=$^Ix!6jdDM-9AR!;HfqGTF%E!YE(&fuKHGL<6RE#@jj0@`<|~> z+E27c7wrRdBm9%?+2h$C=q&bAnFaCi%!Q87k{w?Vk&b?$^V4pcsNN`+{*0UNsV_Qp z`m9NmP?yMg>K8FT=VHlfOS~qrpQ3->MfX$|_2m~bUb(DlK#S`7Zj19^N2q9D^hJ^g zMP}Q8_C;SJZLp>0&RTR<@@12$&S}-t5n~mcu-_^ zi-|F-x})io*K&zS0Vpj^C-GPgfKOj)&wIi)kVKSWmeDo*S+^cxO8 z%DC$8Nd3D;-hLbHD}PKAWz`1fGC!eh@YNbne`-)m>V2*BGg2*bv*#2#4f+|RLdn^7 zpxo)tp&hpDTt9|lqhDCuqf0b*CXAcnP z!G|gKuPyef(omq@OsIWhx@G8ddmf!x{3eeJ<>y+0&MbbLXNfI7dlaGw{dX3-+&iUH zS!F5xQH1{ce6nm05Ydc|(EpHci)SC8Jis3fy8iHqOpK250DmHhP;&OjLwSHdlQ!5= zb7vab3;m^u=?K%=ZA978zY;uQl?l(TBe=g27Wiq5R`S1R#IRI|r;OV1j{5U|*xU%C4Y=^JzAbP*jA_s*M zwS=;J8%6I|K`NA;Z3mk3S3^5&*|{?fjrMCYuDabc7BoI#H3UZ4U76KOF^YSs1%A~qkbN72jMg|%Wr6l4!eP`1~?PEu`PS-?%<9C z6iMCG;4dh|>OnO1f~hlv}JG-(sQjqHwA7%R@j2GdmLq$Z*Ao|LP>5*QC{pe znkcI_SmSP|ZSd6^Q18g7kP0Pdk2JKh-UaQjW#^7JG_&q*;i@aH z1^wQ0%hJkj?_8p{Pf))7pE+D%<-rY!dvYxC)Se(uR~p=Fl1pQOy4cntI=8RUY0`ZZ z4~osT0KNEfKV^X}GJB+<5&nS1ECt2J7pq&uW^>U9e=vs&MQ2-ra(@rySYnIM?Qayh zK5UX}U(ujLcXpdm*8P!OvatR{ZboO_AI-JJuLscRe~e%|Tn%4;;NKt5;R>rycv*d4 z{D~Y({Io~&?q3U$=-#S~3xTgUV%Fb^&v0FVvS@9oWg)KO@w^5bue@$v6QT;WCXrr#u z{hyl#MP|2_=xO~*!ZU8ZEib=UiHwY{$9dKumV$xFFxKBP6o_PqR_gKm9AHAJ`7I~< zT94;xyCpBDt?D0Ax80grZVS%tVIl{pYmqjxspRH}%S1Vs7a%4SoNEP&95zN)*n+eB z8GVCn6O&ug9=kM_1dR*Sn|bPOXNl21kCe5NlMg$Btq9t;y|7J->`q~i1u*wE zGc71rBwr~Z=AJ%n+8k412XI@Db}?IEJ8aq6J&*E_TbgA3NR~{#mxz{UDb2T{iK2R= z`(9hqHu!2yw0yd1`8Lf|hoj++Dm1INrFgPB6P`}T65CN0_^M1)!|4jw?VFj)ODl?n zQpfX+JLGVo=M3)3;6R7)S>QS(AC#V7Y1>- zUR~~8?{Y!hR--!9J@dG-`V;%PBbEMpn8L&@2S+)nj z4A?i{7SBFF)inDV^duuGx2pxw{B%r})gIK2?T_v7)t%^d9d8|Akjr7IT8={3kssUJ zp)Zym2$)cEwiRf^9R#ef1?SFqw2wI0py~@BlbgcJgg`x&4Yf*fAzA9PUyE>{v{Na&udcM)h&n4qJA9KLc+v!zS0h^evMG1F_2S znA$bnXJR{ibte?i=N_|MWQmLzu`{2en6nX6RC9FhF$Y=UsW^Hic&>{p=x62;yRuW2 z^7GnZi}nCoTTW=lnhy5Fi3UGjAN1;PF{?0!3?I3k7WKqQ?XbJn6DPN0jb~4w^N&+( zzP__6;e&{opwB-}rHP`dqkYS1v<;qGBmXnMiMl>2R&}+ESiKUVy7oUk?XX4lNB*a` z9cwz+6APO78!B;Ce`2^BJ+ZJIcGr4hQ9IW7_5{ckEw=efqtS|52#7}u*l{naUtN+* zhMmB+1$lcKvny)wviOYD}T8)#>=MHlV~VmGAg-Og;w9>3l|H_(>a ze7VdH*cJ36CX}8#4$-b)8M49_oIL_jEU?_>(vM-WsVIlJ0#c#qY&%d6^DJnGEjzon ziRyLzUCfmRx1tNs0x5fLZ=;jh)p=Yf zJ=YTSqQsg!OKkB)=i1o~_QGH^iYFv@yh#0X^0-}7|J*!FJk>|}>=zmA(r{RDJrT?u zX{h?_#S~9eZFDo^C6ooeDub-%c?PpmP?ZNnD9=_J#4l?RQ&w%F0!!E3osX>WRh($O zewV0_al@Fre#6$A$R+3u&JazMRU5<)!?X>)S_A5cLDg?B>-Rc}GPgrO)Dl1{lw5TE z8ijV)vUA59idx1pZd4sAn%_`vxt7D_qIGjTj|-*eT7sgMFwYWOe0Hy+Rpf$<9T!F` zltX7TQBBP{z=Wc+tw1NzMPP+3IJ>VwMr=sVql{RT zYl~kGpgNDq2AenzVUIGD3%}4wlT{kU#j%s`r!J~=C|kV6>LrwNpW2CT2wVl)Y^71g zw~|YiRUM7*YOXDQJ%BP^>n-*`aA7ql3B5-HqL_;yRakjYoxTCu;i)><8BJMSI?c#k zOVB;nixCq_&b0z%c3+CDumxw&cNDq2EaMgn>Qgnurk~A3k;}^g6AI3@0!1#b09M$7 zbNd;^>8~`X!G!wgyLz*h3S78c6sNx`j|+w8T7u&AOY$tS#b@_9T326fvh{r%b(~EW z)Ue5DU42b18Fm2M7PPLuHrE!r3)sC+WMcFyM6b)(6GPERsHcGByTPcc>GkcfVK=aq zAo3)~Dka{~jx}~i@J9jIL%lKMH%_y=GZ)^JONO1mR{@O2H|N@7cL8?~5BxS{g)KO{w^5Y!cAL8*Do%|>C8y|YE_#LU z9XVVmJlhg<@B5uOme}HR`y53+@3P5o>E&`!J`}gINVF&P4AiSTBKHi^ei`@m1z5fA|U5sMs=yM*?`=_XOf3OXEHY;xj(#7+i@I9tE*{P7$vl%A~sT6;f{V~H)kWUqhHB-hKhyW{;ol}i@ZA9(*y z=i1`e1L(ZuGYz)R<~y7Xf20v(UhgeG>!isljm}Fx=j8jTi)Pm6EndIMD~yJ8i9B~^ zq4zw#kWYrPb8SIY>R-&a#qI+3m_xUuzGTs6QhnusR! zz1Xi>)b5;xu856qV!hJ2nr|Xz*A)L2vZ8~Ef4iC6 zg(}JKAZFJT|1Pq^S8?KAkAB3b$>+Mt52V<%;Car;IE6oGhdZsi7h@i7C?1QR~zI|@C7qe z)qM?OLebeR2ieMNkrlS!?7p5&L;(7WVAnOcrw8#w=>9-An>d&7x-%8=dXfmmX4`C^x_5 zMC_!q-**BlY{A*R3_SH+8MRcd6r}E$5gto>>bpszu+~JDLT9}HN!sA4G~qdQ&G9`3 zvodEE+7_|L6{`KcH-{^$I;xVmFUJy3?SU`5-z3W_PxvzZlGOu{Dy%!WP5B_S!%uaT zb$-a?=6t-8>(fxh^}~5wC_UE_RB`=Co+Y;U?0JvsVIIxcm0&`w#uP*?kI_U?#Zf)X z1@xK3a#u_hRUM7=-LM^=x}#NdcY_>FI)*rVUZM9L_i)mn%xpfouehg^Z_CS_ z)3ZU`w^xJLUx?SqoUqLSao^qqPgr4ahPw}8fv3jcWNlxAScyaT(>m-?glZu6BZ^Zrdt5pMxT^8o};SYGiAW%UNW<5*;cuj0gs z|GujA$7S4TByfHFf;+0_5PKy3J-!)knykto(mLMF_tY0lQXjRFESZcV%<1iDSisY z6V)2M2Y)JMfv?J7l|HSRxgZV->awiEm9yg_bOJd)hYLk#TY}cJo*YYT@wq;AHZkIK zq|$4X9g=ftC{kJAra_^(d{i^D(9O5y<@YS1?_o;cMK-aw7!9wZj@P*@MKzd<^SGj# zqZ-U5d6xL9Pxx0kJzcQqj+} z>GhJHGBpX;|?qbBK6KzkA!?hBbCaa6JO5(_Ge|FCQH(2bH8CTqdF+ z+N@*9<(LY^=e8cjkSnkqw(RU)M`y-oxyS~DuTN0SIht8eRL{-JRfiHY zU!VAmAtn@`>xEIwIgYHb1!wzUbQ%)2avcMo+fsBIasf@0RU4dVucK}7)tc~hI%iol zsUoLNC}%n0rpandNh|lo0udD9D|uXf6yBslYj}OW?O-FEEfVR zY{9uR8Bt@K+DAVG!lj~W{xYUQ(Ybb@eNqM6Vav|%Z6Z57U5)c=ajYr^I_ zyN^Mv|ArQ-V_37>iSDetkt7OhO=Rr#os~C{Hh3xxy!M+7W;v{=uMsaSR0<+$6S0tf zk?bvyDyum1+HZw+c&ZK}qPH2`i-H(j7w66@w9|Pzrb5}dcA%ZkJFp$L?Ce>N@`3Nn z$Z<4M9j*|GS#E358T7jlQ&e-54}3SW!dG!38>;WOyvO8TKy7;1dJ_?mezW$yG*MP< z;8ov8+u*4+npf{{rAnUt5S6PZDa}8SONP?3=lC3A#Oaf_59ZopcL8_op}TS)YOq(T z&)i6?qUglw!%mv4(&(<-N1S{=b6M(fVNdWDqAum~wTBMBIOxYVRl8qRZ-!&OAQVmOXyGfmZEL8~n+F%3#sO zifkW>ZUlTLhb!Cz#A>c9mp_|hiKq5p4gZ{zEa7udKI`+4Dy%z^qu2HNUx0S_sg7zE zzi4pF>h4ReW~)0@(qC$WE!+b{Hb7TNf4L27{Q3cTp0616^{SpFkc=dWS*KqT{%Q_a zSbyMozLsN&pZ4e$)YnaNRIKW(j9kqqx~=hzd@}3+ZY+%=k#FYPVs`<11w=QbzLn9# zA+gv8Wgx#z5~1Y$Rueu)XCS{r+F(n~?Pru*|8B;t7K0J$2@G~SQEvTvfGMmt$O3*J zSkXDXf6zoFJ_#zS_YVQHdwTx}SmCKRv7+g>PJY}(C7&JPD-JU3KY>(P%~6K^r_heh zss6JDcR*!g^a45_6gz$z{c}j|p6b7Vc6h2zWK8rMRljWE3XXPSj_IiJS2l{lwwFj%oZ!B^Y%SDXnOzF3n3WeuJ5h(ZlJ8Xw7JKNKuNb&auxu|?iFd~v8Al@0& zUoQItNrZCqTMZ(`Kaw`sQgizl)gt`KV8-i%E}feG8B?L;+}5KJ{ugYAEjzof(Hj2O zj65C$mHwboC{kM@Zi~^|4S&ny%4$#KrS;wYzvo%fLG}M(vsaaLFhd0GlImZY$L*T> zm*rXFsXp4BUT(6B+0Rg_~5vDgKA`T7+fR~)u0yxu4{u0 zJAtnR5#Q+S`}J*DV|N6144}DpgGrw(g5*1wiemX2F%^o>wF5;uH(@($+4;Q=c5gS^ zS&}thVAgw9n~5?Zj-}dl^ClO zCMKowGxLa8^;G5Eo@j?H+Xuu6jQ-x^zuK|JvnSB|z5h1&GB2>B=;cW_O;l|Zy*%aS z`|1lk%+n?@R>Pb;*ki{p#7d%H&HN9hit3F#%zv>R9n}4Q23g-b?T+rxU~1QNe-_)} zt2?m=)^EQ&XOSy%)dNK4Z?Pp_7Gh%J`Lmf=BJr}wWwhN>}x!HE0 zxxFj2!+~B}4hyqYs@x9Gq*5-38n^hu-5j#9-?xF-M6EzpaGs`aO9e%1K-*QyOjSHpnXcSM- zi5kB0#2DAU<>*|pu=>R7Gx{Z^V{&cr>jCr)lVc5b5Uei5VPVL*rRA$nM6~){!s8%S zSa~9Qq+j2d0qyWp9gX|r4Q|*QTpR}A8W~@CFz#pOl7-a=<9=4IEq*9pCPh@KKi$8O7Eb-Ky&_J)X^PFUfs0OXICqSyO?!f1q2<`Ax zotXLh&hSYFw^~vkrImaGg6M5M;!n=u3M&sr{3$t>cxq3qclwn3)CL(|E8}~5B5$Kl zxlaR3Sog_lJ z**2i}rTa)5Y^fz@))^KPmY=fQir$DkGlwgyI(jc{X^thH+7t6!`}6(=dG(^j^@O`f zWdX4}(cc$bhL})v$x*i)Sz!y#9(Cx{cSXikCxB5bv@;@37W8WyXOToGHow(ioUJ5n zu%%}AFwyS)RJ#Y7m=gXQo%0S7JYkiInWbM_UPV~YIgM8v#JCb{=(NUb2wwLzKAW(> zQ)5uMeohmyJLjV3Vk#7zJBv{S_#$kFEj!<55xd&yYBs$%BZuYCoxJ6?6n&ZMB@|Co zYgBP}9%X^A%D_vnHJR0d$P0JKOP^2hpve4j1-x{Du)r3XJF-w+-B5$LCLF1ZJDfC^ zhU!R$-83jLmyaT>5jWqKSF}e1gBS-3qH4+Y=?rcgQT5a)O_bG{$Uo|A^B8SI2elrz zsMJR>CAAJ|V%N03fVRO?Yg7xoE~Sq2lnTYEioPBSp0i-jDfF67u?;qqpKZ+?V*jFF z)0t?)8oML7BM{{WN)7sgarF@m^+coO7uV3ME>S)ib^==kBG<30H7E0JvAcjh0??O{ zE==i@i$Rr20U(=(M(+kE4a&>rqtQF%3jKr!^|O&b~?1)R|n zkLlIWS8tTTd_ykTOLw9&N!Q}OF_$c?KQZd`3)F9N+A>q=pPV-~cYtW$zd4_6bOCp+ zq5aca^2x9ZxVE55lD9f-kr@4`4*G47YRJy+aa5D|c1VSiv+Y2we}`$uDpkOf7Ls_6 zceI+m6HyJxxuXlMrtd;bC^**&w3@!VZbedbyk>A}GPyq3EH;}+_%&T8^d6FE2+bZ- zXf=H=NrW=9Z9r?<`|36%SDDurib+7eEE;jj%_pbf`SkO*R4*AlH*MO|jSjx#=(YN0 z!Ta;bhV0yaMlUpcAdd^h=URfa|6r3PNzm98?LU-9?vVB$&f|7X`;XKuIYT`OTEEJ+ zx=^eJLNN>&_aDt8cS!q><#D^F{m1K;^bhrvhQ}kj7Pi^Ni~5{T=d>o3DD{^X<_^djs7N|3YR@JX|Z)^j068gM87=^H3XAYJAB}6IBy2R9ECVzn0O<E-{kzcLBbIxrXH2K1MI)d>d1t>|8rg zRmyiVJJQ$rR>aZza3qLDlt)?Q@8+{T)JK`b@8y$4djOrUem}EiLs)h%i`*VZtMCs9 zo*^}RexVxC9}+w$Fxvv;Pk+>~pttrWnaIdSefndHXo$@1Q&jEz6N(2#=30Pqvp>x& zD3nFxYm}S)8KU}VO+?lD#?{XeQ&w-HeoNog{zYbmdTXii#+ZLRO|)F!i1{VW^wApZ zA%8^^Wt9f;!LJP)N~(4}Y{~ib8%Q((_wLg_5)FK#{@kEIXF3R}aqw zW8@&`>W=ckzt1ImX%Ehq|By=-*1u83W1kD?@6P?vvSn3iC@2Z~81%;1pYq6t{M>#= zClY_o<3jPdmY|6IFP0_c-YK21DwBg>bl&|npY5SPipc+#PZsR~wB!G~VT*qBN+>1= z=brx{iH6+lo<=+VOGzS>nQa4#k}qqr!4;NsXBOJ|Tu$(OG)6n0D+r#f!e}+SGP58o z72+xD#gpdnh26Kvk6)EXHbm$4G4kVA=W(Izd4xC@RV-k1CU{#atiVi5#td z(^U$1ceg0aI*Uv)cM{3sfOh2IfkP38zB`+&b9-M^qVp};!p)E#v_H^ zJ_e2Sn=#jro!i$ab9)P>LfN@?pj&jeW_DD=;)wbtSy1T@D(Yi==_iDm#b?ur{DZzf zc3WF?qYKz04!!n&ds}qa6>NJ@_Un#@JqrqPCHcgyJ80xn37@Oq8@dxw4e7c4jw%!H zLQE()*9ufYad)c~uHcccHpqVclVnlC%37?SUVmh=$l)&xuZo9;J9tWUd8hbU$WUPzubC2RBR2<)YF3cn;SPoZZ9d zWyB|PxKMbuB`B-@FVm9R%LgD-=W37g5dY34d#Mky>QCm9h4lwj$4?QqxI!1M`e@`o zox}Cg9{BbD}UBI;k zozy>@*%B3twa@j5jC*2_ntZhzFb&Dsy^X$RJZ;b9tKF;nhZeN76N=5Y0%f{4GOdVY zD!EXl?F-yILuBq)LY1}~yJ=8fE+38MO$>f17?=#>+Dm)h(F+*Mn*ydGGkX-FvHU{7 zgkrO;KzqT>Y%A!k5T6QiE1M&#Avkwjq1?(AhzSMfT7hQQmZlXY?Ws#+NtkGSPtSLx zqdOa0waGU+f!*`-h?&--Vuh{SG71?)0K<;N-%r!cIJN{69eLGBr zvUBY~ImYcxJ1S9?CrAwIx+3mO z5q(r9c7Dez-YyhRRAZEN*|ph%<)szHLdki>oIQ(({kQ&}({4FjLvVHvqpZvBIb0|_ z+Y*#@*`w7GSD=Dbr+cTH!r9RQ?`mLP3a>>H_6W&Gd{r7IRWldNa?^R!@T_0{JBT?UH!hr^gObM z`lt$N|2(d&_F(iMU|X`HP#GQv?uV~FvC8UiHy)VJ_Rt@V{)6(#qCJ43_k#^vlB}cT z4i-f3hhVOU@+f*g6jNnY2mANK3_HqUsalRg=V2b#&!HW|;ect#&7NZ@M{@*VLb2Ia zph~PG4J-6z4SI;P^(M|2^qcudL8_PHM1DYj75ZpM71o@Hv242ju2i!yIBoir#dn~zq? zStdUm?pmH}Hs%^~bNd#J=sB1QW#`&~RG(|xQM(~6RlNw}lX;liA=OX7R9V%*+I6DK z4w<|ch>A`^R3F8OoTk1neKKN->OF^8wROGnDK0BSJ2vcR?`{R~TUI+CqE zx>vc7W_oCjzU;M#CW?fb!7&xn!dQ*t2jhu~*mU zARG_cp3Cd2t@uke)lo(246y^0-iZt|dtO z^BR^c3`V1PLU?xzPP^CUkvpXQ`FUJX?a}?cf?-K%IIK9I>Sd32bboJ%BKl}e^s%la z8>V=&8iUnr#IT@JP}Kxf=+4&}#4G`#`e;pL?R4gH6ftG>CNh`$7Ry*>MHtf;XZUIp zE!QtSj?+vZt%+Ts{#rmt6J?bKClVJJHs}|S^_v>SqAMxST`y3ivJO%Wx!FC8qQfGj zLdn^7pw)6Bvm>ew6$9CKl2D{l$|D=nbNd@bDp4L6iqEwK-D8@}EQt%F71EKjnZTP} z2$+W8>@kLFWMaUCVzaG4wV>t9it1!B98Nw)BBnW-W0hR8m+~O*Rm~;K>W?Zn);DaS z4m-Hx993>ywCn(>MD{9z*6ddy zsv$Xdj-lM`C5Qv07!G!vdx_Uj93|9DLBEzBYY`i{?YzWWoYZRBiA&(2i=UReR(>I!y=$kUCSb#2& zVUxj)u{Y(C4e{B%j#kq*=aQlPY+KM7b*45FP6=8+BYxqXhJoe$-4q4-=&keC0kVaa5nl$7Lk$IE{t zmu!g7?seqlKblL1^0RG06`~(AZ7D{vc)SU2Cx5&Rwof0-BVyfCRGjsRHrTSgfPC;L z4QnduYfHgMl0b7EKCycrWsW|DxrX@MIgjo>eHv4t>|8t0iu)Pcj>Pwfo%s=Yaea65 zvpHNtc6M*0758&FTqr!-66EziZ(33>#O{vQ|3WU=OMRl(brtXzbIG#$qtlQtHEhw@ zdY7Bw&NFlx@?|H@M`v^z@)ajdR$KH1x360Kew9HO4MB;!=)C$`KHHF;+sEk5jj!jE zVHa?1LART}Vc9~Kaj=y~x0}96Gd&bXx0}926J?bKry$?9Z0Oc0$afIcM{Oc6r>nuf zi0DvU(FSl8%gj z(rg7C6!N_{sQvpX=K5$(%q#uXyq{sJtm?pv|2(s!7!|se>HP(w`X~;(_%9K&YkL2x z$%+kO*`4O)k8xtO>F_iqqWR&Ox7e`{L-KG4RFLcy&19i$q9vwIm;_WmAH zq2z2kP;TW9&31I@G~9R(X_5{IhMzYISKK5_`QG%(A~=t|2^k zmZ4nYUojQR&b0%bA^k10qgpI3u0`i!(IT2@f6pWPC{OhFiE70EBabVqJvc|W)Mbfa zOei?l3Y7Q0rD4VC zK|B#w+#6Xoky!2Z_d;$ZiH6YZF@^l>Z6p!O%(emDG{3#c28lB}^bNT?2%e9|bBN5} z4ArZ55WOXOnufIR=7^HfsPWUl> z|M781mDL>OyPh!Z$ooJf+n=F%_OCp$Aw74Vp_=J`=W(I|Clskt_weBcXh z8w5{<&<=MqMDWx;+Ei5Z4z3MUxnMz^zGuQ=fnZq?iXU}A`Vs4eg zg~GEfK^elW8!S__4uXn{FwX(_ZQ-ILT6_n(mR0QdFe~cSbY|JAi)z>nDAUW+j)>-K_w1}hi|we z7`~5B_n?xfS7vdR7 zhKVg%vA|9iv?J+Ans_uv8E~vDGcz+YGcz+YGc)s7)qVP$?mpc$Q|>d$=KVgucki_| zqtw5vtE;OEi_jBPR>N&P^g?({y;*MSp;s`62@Chs3_-M1uNCQQ^4vmLYjQh^s>W>Y z38M0$Zcou98O}vPRo0)Tp=hrbA-e__A-vrQ~Xon+g4 z0N5vw*!@F&(AKtqefFsSv(tUhCO3hNhRU{_Nzoi!UT#MnkorXP*g)W?m6Y?WjTkUSt!4$Uf1WkP!woIQa*^g z^g&x=KKpc2y5_EZ&?Yy5jfT=SceBtCrSW{ags!vYm^AdY|7T z!_lfknoqP;)Eq=g!_`Qt8q3)`P32cDkW@*QvvE)s-eQVls@9SNR-ss@4&xDBN}jvh zRCU%;Ps%~O_q>*|%XQR~aP zTJ5-bu(eWa)-M!eC92KkxDd(r#Je$%^}IgmhwQ@+&puRu{?R_^7rP^rJT@{!4QTdW zWR9oDGm9AjYZO(D`CP3>WdJlOnk2)yD5zLRoJZjjdiG62#X8pgG(&jp@jq{Y{XO4L zu~<%fJZD&e(9$s!6;UGml5ID=01i`{wBx2$EO&_$+%@S7mw-!g^RMzD^GaPZfxY`Gg z-aHv}*1dcbLpbfxSs1sww~t=2JSOzWeM|(KqDSsa(9{^sK1q}wxgSB3WHuWG<=wl# ziDFL+vH%9HwI4vx92B=7NYE^p+l2Ofkcpx_y#XVGo#|U_t>t;+VAa}#Nvc5{_q=Sd z^UOm?ss;1h6Scwqe-G7gh^FE$@#dc?LT5kBM^R%ncYmp#st@pWjn59Ibe%yV&u$UimyHEd8faR6{sU zM3$dM(JYqVp3{bR*VA0zMckcKecSV#)I#`7)wez0Nv&Ys zdd}C$aK2uk(O&`UqzLX(dA%?6&f4?iYFJ70cmmo*U|PK=5J>!CHJQI=CJs z6YqVadbqdpHqR|_8c=^pU(`dmzr$nm?*i&C?TdQBd*Sg+-UjfzOvAHY13wj!bjaNW z%G3Vx9&|(aPv!H!q6gh#IZx$9ywZuJ5N$)egI+~a4dS}z%;7ue)g;woc~0dXzDC2* zhN=~rf3-Pz&adr*b_oBe+{4%PLA&5xAau^_H8jT{KP4*AcBFL98+ylw6zW3-ZfOE#ant)Cbxi#h4M1KHN#S0D6GDM zimtxROHpGt`*lL~w0XOiBFSbp2FiE-jtoP$5iNP!RIvBfbIYOs;hhA{AYOY;^j4tx zE`sLfar@mCilXdzD#PnN1kFKl`@ICsg1Jq2Ro`c!@HYlwOE+OTzMrHT#BtA=Ld)?3 zB-PF1`3F-R)8KKzYjZVf3_QmCA(HB#c>ZCMYOy@0Dq}yA!%?85dpsl4t z3h@SbtOv~E__UXz#%i`*M%jj+@lqt&%*H@T_h&T>tz*%qMMFPF)74ncl`2ZQKTlI7 z+0Mm5MQ*;3;aH8L&P3FyHM|cX_I^|M$QOH24&l7VPQ%&kU+P77(|G@76Ul574H%!1 zUMTOs(u?wdc>mR2bPMG@^%DJ>j-=ridG1-G-bP<1Xle{+?=+Rg^$mh1$!s@~r>3H|+rKQ=avo^rl?s4p4IcQw>YA z+uk&*{%17Z0M1i#|8tsZv0V3L6l3-0FESj3*^^Y}(=Xlh0=P_7fBwo%tyu1QJVIDw z^6QNLI#i7dL&n**f~`*|i}g1Y)c{UYRpY;9v1d( z{R2(4SgsRN{zntX6c&LFUdsPOQ4Qg^CtDahaQ&I0SuDQ^DgTRxVl`?-o8{g8D@`|q z=Y*91jiy>C*Qvb2zneJfi{Sq1)YjGu^uu^04?8^mqaW%4JFvr(QF|uP{%1e53*84Q z-}YY`n!-Aud$OIdkM6(S^a6NISuFo?Q!AFcgl_q-PQL{r?ho1VZ5HD7dD4y4%6V>kP3QJ&q)5;RF>vr$m*`^#l0@Y*~?q|fo`5}M`mG+mA9Tz?X!S*}1+CE3o! zLA?d9XyWi1D{PvCj9-bMsj-{A%an{?nV?BBn~j2!@vCSk>d-6^5*Lf}st_`MRhq8G zbnZS=GJZ9hD#>;(4k|Kcw#t==r??dh?IPX;Z14Ijl2PJ3)n>_2o(AH70(OvUA{ zr6Z^}S1KKt=(*7CINRc|&lDAxyLKsX3*j|&+n?^CS1^YO zt$Id=AZ~TrgoBsOXOE46`3FNJ)evq|R%VH$S}e~gtumb9Xw{nS<+!y*IApkdr(Vk= zJt+tAp3o{=dQvWy|5P>5Rt-xO&DC1<+JgJ}%H=)PwQUF~AdY*yGT0wu4?(kF zZhQ6}Px;;q#rhIRxk&tSsTTgLjL1*eM^V)n&fQ}wkGD$EBpJ>{L3v@$%22eURJHgY(+%OdXP+^T_W(_`Sgw0=46zsPgnj>@!8xuOIYM zB-zZyK;4>C1_nBbl+A78);zlh-5{QOv?t!0hkDR0mh;rxa5_aYw|h_GP%kK3z;nJa z&uoUGsxh6r*OVTgrD&22=c1rGV$5YIAhs8`3VH_-8s)H;qQ+?UX{I#F5idoO&1?*m z|MnaULm_&b@ZX;6p%=nq%71$o553Lf@Le?otxj}&&>X&-hu)w#e0LAMf;mjwPV*Lm z&5C#4gQlx7oU0kBcxR2KO0u1cgUX#-$Z*u#b&q?tP+hg?qZq6yX=cq=%6^+H+rgLNkpTopkwBe^2z~$|pQ;Kt0*8CJVjnhp7 zgLmv?p`4EWGzY}#bw9;IIZgGtIbUH|sx)hjjSiM`QY|OA=ZWey(&~e@#(MT{Q@w85 zeb6R1fsKaZL?=ZvwhF^5pk-pQv(2bTX}33RZ489j>>yn;iQxP`(KFkT;i7ZKW+Y=fZ&0MpfWvHt%`Oj zV~x1GbVy`go>SRXIlN_J0SxWtu|rXNW@8vA*+6$He$bhS zm!igayx3if&^DifpTT!lT2Z@QZ7zXr1HV)*hj6pBMH%bnTJ8mFd5~i*hg)^54cnB( zaL+l_jq$-^Eeq|qwIJ(ICKf8qruZua3eK+VK2oUwd&Lh9%dOGa%0;r%6E!#hTzyD} zWUAJJ*`rI~b(~nhzl)S~v9XZ^H9sj(nV+JQG5{N8!8|mFt-c6QO#0$S$TCDXR|>&G zz1topuq-Y{>z%TAn~v9)m!T_8a}nfkR&3bZ9Qd~EBf#A&>NW9j4Fd>FYY|Uok+ePU z)>A=F9%0iimNH>qJ<_DyZbxeijSZ4K`<9}1{!uplI#kFsSE`+8jRe8odCw`uS%{A| zFpSE2sbZ-UJMb8jx=8ozA$sf`penW=>!43Ao=u(F|HqlsTXlHBr0JFgo<6GYz~c=J z@QPgn-IHeb6Cn7UP{zc`<_(NlQcti^G@DV@x~EVSL{{PxZ4~lF-_Mp5%8e%xIEs}{ z$}9e46Ghy>kq}}YZISyyA$W>~0Qw4v-b%&tV7tdpjI?@+?;1yIO1k|JZjaC)jIWK^tP~NgW$$<@+hWi(6gqWZDHf^ z*smCOd9H<{)$H#ATV1A*d|28!`0DdQc4G7ZZi38iBE?=q-oqe_ar zy(sPUZj*kmpb4Avlx_PSgZ5c4Fr**x=iZvs-oMvCZ~&Uz6)t%m4S?3<`*PHal{@N% z{r(*7>1YKl${q~ha-Z7&5BM;UY+xfG?#B6 zqBf+b6-k@g{g32m4}|j`%LJpgA2kq6c3TVWf~BAGuY4>|on(HIa{qCI_QB=lcGT%7 zK*z>FSRSA7Vc0Y-eA0nn5Hd*Bjeg3+(5iz9j+Tn|EKrj8X#+#=un<=YD4+Xh^0b8@ zl0v|@LWn!yvv~x4nPEk8;d3Si9Gx}*E>QdM`8@gD+SJ8XJSqNK`KK7u^U* zQRGSibt8T$M}H97h?0UY=MZ4y=N^|L`!rJTp|3a*kXyi~Ph<*w)ui8w&+B~<*)rgH z6QEb%YX<#8(K-~wto3sc*j%7)rLTKY6uT2tZS*%R3>*h9dq;?v>^Dsu-9~>;1pa;y zQt>SV!?a8x>PI>7Q4lxfw{s}szQWV&-SBwvFdFq86GOeR8h4{kM`rwy!pTNKaN@fr zinTcI3?yTMvf939VvrMth|9Xj^?%=_jZ=*WVlz=5j2~DSj2Wl}@`2ieA6gjTvMf{v zDc{wPY}zm{ktANIF8Hy7dOr%ONGbe@g#m4qL13VAG=6Gg7=Z7Hx~G5UqTbtJVN1de z&)tL*Gk)&E;F#n>97#4FiYLD?@mv5EA^iwNp%{K?V3>*5<3_x)(X%9>Pz=AaFzEf} z3*8GMD*bB{L(A?(T__HZjG|Tj8yknwleth7geCb~FN$LIHg#+K&cLARI*a50^}_zW zLH%&O74>#9ux|`v2mYYZpRBD#eHMxF&n00y{!zmqsy_P!!R7;T9{!|Zm=itu`UJt| z#7+;2DxAtc3lvNH;>GcLZKWBv!FZCTqI<_C=Amr0+mL0f6P>+Lyax4-kPi!Wt)sJ5 z@dKl?N0?Oa;cQ*pG z=zFq)dp4=A$N!Kxmdd+MIZ&Ujv|6=|@_vC~sD>N3R z#IlqZik}^yoqJNkh?CemLv^e9mm679{tj9|$CUjv1dYc7QHHbgZ-r(oUIXu3gjQV} zD)fc25|UDxoodw~iv{X9bhWB&99v#IQB3SB`yYh|(yE}eOXQaUjHybsGFlCx>CxQS zvH8DVnnwb-o+@N33;Fgy@uVALr-Y@Yauo_gI`vx8{Ggtcz8?WCY^Vk{hdovU&Z)YD zqAj)jK<<6EVfJa zq#i zGjVwbhJsI|XCE+A>lh-HC;9cswSR)NmLz5M0eqRnA_S z#vt`UG1`ET|En-iXpG?B2}G}?tM-i{2!TgCW1jHU`a}>ypQ_irx<(%?zvQ_j4P|jP z3!6CkvGFwJN+i{(`5GCDz0m4HHa$bbKsWXHJ5T7IYi1Pp#cT0O)QsY;NR-R$kszLX zqVactJ-=2j+VE`EEu=tA;IXo?YW&(XV1|8;4~+D-_C zsKMZ_pSl&U$3YQ*ftn9=eFDQIybiJ_5O?L&)RG%yR5Lp?J%Soh_O4U!t{ak6^JXx2 z#Z+(B8}+8GEt^f*lbee38*ki;asXXQvTj1t4WLTBdu~cl)sFQhyn9Y&BGDMamU_zW zyBP~d5DLQXyEy|z2nMR_`z?GJOhw1$K6UO-v1#hpTh8@Se=C}9 z2vv_wgPy}%Q&a%i>440))_1;D;#E0eUcHt`*HX z4Y<3-LmMRnItM4jL-v+B$y*h55nJv@3x`J6sJQz!0*$S6Lvc`@wzn%BonkH~?!8I% zuGrzjW-~|#uWAf$Dm^Y9>Q363;gJ`TDiccl5!#J}rzq%7qTXqD$Z)u9onv?H}1p&<}ZpN4;pKY zCkvl%9_w!(pesAjA%Z2(wk9`Owy)Y{#+5Yc<=#a6 z(KAph6-+oC8&$ucKg_xOqw?JLC}c9@bfUHnxu=lflw{+xQabr z$lf}h?-;KE@#+cF!juPKU%F~&2JJmUFw4Yxb`K2&V)8^I?gTqg({nQR0K!QMVg79-v86u`K}A)>9aN}ljYZK^-mOQ3Y$CTDN0z;h6qlwH zp4DjasF+p-#S2Fx=<+^>KY^_TLk=9W$!IIAV37ID!IP~@CAa+F1@`Xj6ji;k1RN69 zq&mgonvthhC|YS(iPwQ4hROHwlvYiJ!{*=)&kJuiU_GR;O8@+!Xt|twZ(a%0!mbTt>OOhPLW(vMX5nkPk#&K;BZQN zZ7pu54>CB;=Ts6)CH)~DnB?&-DwWa?k^zIGa1Ach+V#bG2#diOt@8X~F!2^)F7G^q z$?Bap{6N}c@Wsq>gC9b?O4w#%${SdzsZZE`;Tykbf0)>MTS9JK%FA~oU~*m6Z_y>nAMO^lI3Qz~%(tG`avnzfavH4LUEFi9E2>BnKHN+U|`nbIDE zwBIF<3Opd`*g5fjo0D(43@z5>;gfE0z9R=|!mT2neU;WqGA^?OKY^=ouGXs8aD1k= z$!xAtcHdoH_%o!_8xR3rOW1k@&Wef-WnWQoq9B9>hwtV-DAeb4|2x%-LYH)X_FO^FZ>?hRHNR;Gq2-D`k-eh}C#$sQgK z;{1?uu9)a&0R263=!LBI=V7s>Ndw8{FcnLwWf-yE74<(xi0%0z$(t+n=@|-vjYnPH zmKBGu`vntEI~hZj61JU*p)oLOqU)l<0=3SV5(Dpq)hO!BHnDNO>@bw^CHao7D2wFF zQVz=uc%s^Jyn=kHs9Q%N3Gz5le2MzOA`FGGLaW#e*6pr(B9K(afLB%GH>No(#;I%AuY8oek=3v&4K4iD3?%!(`uCxnvqGwBi z5l5<)r0JraifS-rW#m|QUJ35J*rE*KP1R=;@}uBQSi%g*Qh73{+W{3PU$+n>hxJgc z4o%duI}R-a%wW*ApMA$EL)GHLIPl?o9mPxx7LYJWk}fEw8_v`oX1IZiiOSJzoe(DB zK~HU#a-h_fhTUH2W#C!c!Y9)EK-9JBvFD{V`Nc+V>1ZX4nhLE{oqnh{1g>b zc!gq8j|hubW#H8@9TVC&rc$&_oAkq=Q(78!$FQ=f@6UW&Jdr8FU@PaJ@ ziE{s1w*z(qxu1z7tAH0*{6S-aMu==v)d%-CF}Uu76^j z6f`XiG3BH1_^^fn#RGeza4>>>(|P!S9a0`-AXv|&0s~CzlK7yc;lUbhS=ltPkO2_3 zoGF%i;sJXfs2cHyC?pNM=+mBrC<@hTQ2ddq5q)T$s?-UgG(Fj}w}kzESV}WmgDM;B zzL@mo3SBAXTY0!f6>4CB$zw6h)J*J`^u+C!!+8yZl)cYX73Cv(;;1HnLS(+Q#cdPc z2l)R;g`m}veqxbNvK#*;eX~dGG!k*^Cx(@X`ienAGM1IMlC=*DvXe_gN`aYiiz!&m zqtt5Ji*XBL+|GSX&B93o?qosuC|mE*IefZ%6?5FszDt<9-+;H@1CK3_6u!sg@agW? z&A~_YuzIY*cO2r_u)9Q>IGZn2eahn!x;XXMZ%N9Tz=5~3=WawB;qe*@zZF>o3QA`^ zK||q4Y!KI^#1NvhC7QgFNM4#^-6OINhTMEu;cOH@MD=ZrU?WaiN`0m1|Lhrq34>|Hukh&xAnZj)Td)KRc2$LZsX@E z444?~ppCb2PW=~(;Q2WOd3zun0ksb=un@TIfp82|&h!g)3^F_*c3-^FBxz0AaL+as z`+kv!vaB5qUp2+~7i+YIuSN-5Y6&8J>BZ5^54QYK`MxhnFzky#4Z#VJmt^8afV(wq z!k@Nx5us>ZVX^U0(V~|oc&g1ssB?gnFITsZ09l~wvR{_cZg-&GP!x>oIlV#}rLzFN z|Ck;CWkxuRa8HXS17zVU1Xw`=8w-r z$04g3r3-mk!+$1;#Mvdo#zFY$UuoblGqmm93^`$?VFkjpW+T|USD6@i`zCfwAM#>| zn^^|HKZiti%&Se*qO3Mk$=V^%lNHzqD1XUoGz9H(Zm>j<|GJdAlFj{kDD2m3b-K_# z$?i5(Z0{tI0U{=5$m9>>k;mpIb=$lyS=WB(1%=MV?m~TWdR;Y5sfcCmt5hb3pl~4M zEKv5=>%ADTtPaa$i$FkW(l=-bMAn8H_hcqI3FVMxbwKewIwa~XkvAd3_LYkGPL@e= z;u?&~V0>eO_dryG)*A<~g8}GMEPCoDniN$;Z9D>}ajgT{JTkpV+d!^9Esdy;Pu?g&m)J9_Q?nK)r0=tX3hoS#*_LX;`^IP5!L_|K6e~$57Q) zU^c>8XpaPDBT)AFTQ%G>@wBYU)9_c%8V`^-%07QvN;zH!t)-c#_%;i-jVW97?FQ}j zy{+2%YG%D8@!f!RQ_=l*DB9qfEbon=KS{e&Uy0fx%|1;W!sZvi1^iBsMZVMHp~S4g zcj`!FP|9z2V)3yAl=)KwIJ85V#FlSk6mY#O!IhM^A~LA_1MkeDmRbgP$`o8_!m&RG zj+wX>m`S@+DMt$tc29b@jYw?Pa@>LzMRw&13UcD|7B*fMDA(A<)_PQju=gl*(OTTv z=+$2iI;+f22H99tOzOQ9mb5p5=x zETk3RBFrsXLL~EvMI#6d!dMPwE+44Z>4!8VLY2zK4;cD50G*f<6_0Oa+22f0^5f#5 zBK{xNa4caqBy2~@8LQhG-ge$zX94m;;rmGc_@uq;>%|s;kGh#YYT#2j_&9w~Siwms z&ot9hpjIjxcVDUQULVtOiOG4evEq#<{-K8$4X>JpEr4T`Rq=5RSvzT0CuKZo2|Qh2 zTW>_-h>FInK~@Ddv;GqbNd)67;mk?br|au^kS8F8ZXRj!nW69hh|$ z14S&h0)Hafh@q=5iG2!vFf#!rm@WcA9mp2VNE1 ze}>A2`%H?hwN}H+)R_$DH2wIuo5x><{Z~J07h7I}v@zA4>q`l;gUie9sB>fp z&XJH`EV-AN3(7wyn+|5I5`mBaO-P~ovWaSzLWTB`hRPaU!If*u>iCLjw{Ijkrl1Kdcy1fDb{j3PJ#h=V(Tdn76yai8t@vm>YA?3x>tagG zP<4M$+=Z%2{$`4*RfFK2(L_OIhNNbz;dbImGqp$G>H~#PkJ7#ll3$7&-?nhLF0sLL z<2!wzD2f|Yci``4IH2@Eyd31SG<~*a4ixozHVkF<`JrUwdpR_Cr;5i**7G~_s^|tg z8wZum`uz+CK4fIOO%de=?JryDfLp_C=fyoMR9}f7X!K=nrrG_puF_hChsXR36FEb4 zKlR%Dp@~btFsYl!!t%UICFOiF<1P!JX@8U==`VMWYQkmZ@oCKC?kRO|{#YSa=_Qze z3Bn~VsohXQAd+L0S6RlSWf#}5{iI@%KglpPBas3i(qG{3(2!3g%%IR$#oJl0oyklK zE{CX!>Yv(Z;PB_h_y8WGE=5Zh))9Kg9T{ILDmF>!w4bR}#n|c))JJq-JrY3+d}oVL zL3-U-p$xW8qwc+*J8?+YsEDSdD4-8R(7ahHXWRlzv1HWvxj|_sD1k_2NyoH!E7jsU!@@x zurzPQ!*DD9E)-XR{G(3b@B6@+S--(2P%+H%tYL)r4{RiYMFO1j81m)JB`?ep)yBVU4Q8pO<_Ap z>H5D~XarH2Cf}1S+&!Xn{olAa3e;7UuKzm+*Cy)ve}v*Hkbjh}|7RaKy@Jlyri8Bl z7aK_r6~#xV6smO-p|1Zo3*Bbw`u~I@>%mB_9-(yoe_d!VGF^YtO)hoPuFBGqN*YKc zJ%96fj{ui9)LZlt7MgugYpvcH%neAr0{_p6!*ve_z5+cN13TcnUDCpk=7yt(VV@9K z!(`W;jqE+~oO_JzUdo4~@Y|Q#kxRQ!B+dcj^#aZT?a|5BfYcPQ%lI)^`(c7$?}jIX z5PMEt){P;*7lvT65qNU?wgZC8SqRW!RclFuEOCBmk4Xate>aHparu65WLnDr!sdr^ z^jh~KW*J_=#-S$F_ocSjNQjeqMIVx$ii?fHlf8}oC9dQ^0nZrsS=Ejz7XvjP^~w~6 zAm1T1#o{VDRd2GzF_9DwmHMW~#VS)VKP<`*Oyyo))kZHbUlm@%?v+WN$Rrqd+CTM` zz$w#%JKQ~`?53-w_=Ir+o@(Lsb6HN>&olvJYaa^D)pKaPmQz7!wiCC)HF9XM->uj> z85_x530i6acXz1!`I>$Nvar1%E>M*_*UDi?h^D$HEZ#?T5V^LZEo%v2ek};-^jaKu zvf2=FJ7U^UGO8ZGG^o>ZodnO>-P#fi5Ltm*dr&y8wtd<+bTI7GLdoZK`$2=f`;5>% zTRsV^<$5jzmeEoaC#VXq>(e-bc|cWx+#sQSsJ2w^qI_y2V?j2JR+(JhQ@ZkoimnPM z<2;nqR1{(6f;$^fxf3@s(deNgZ%~`d4T}DaP5R>6NZgr)(ckK^5^oOn*-cXV*cTnT zqs!8INXOq7a5qqO^*1#rVvf7$pM?Ff%d;j~w$`EK{bZePXEZ(znRjZWpG}y%!&^PK z@lAlkH`D0OOaR^7GP%^KRo>jDHWkC*M98C!TETR;s={LH-E%WoJt{&+kczKKYIqyzhy>0ahGL71w7>0@=V>Lx60@zIntoS zdqUvK8WkD3b%r1r^K192z?6~7Tsj80+FXuFG0aG@NV?A)X;HP8Nq^io*-t97R@7^4~~)Ha|I{rx|nkT1s0A zn9}kX;Op(uRh-|gw?qS_{^cc~=jHACn9B$Q`IjRx?7<*#Xrvn=g%kGD^jHZyLC*~Pa9BJkmq9;THjAg?1gjLc_%LWw28B{WzesWkgh&Wwy=*-JET+J6H8xdsyN!d!yd^aTu@QOp z3af**pEx3uO?(L=&eM)ODl{pQ_B4qjL6aWiV6%}>`fjI*WGr6afa^&Jt%*hiB&FRG zh{p4dFT-x#!9;VgH6Ashj%-ALu+&sVI9=YO&Xyj}s~0Ca-%;UNh5GsgKr3!w`Fe66 z(8=UnEYw|aCqEX=^OT0q4fV&yL3kbSoZ`S%^I*z=2Mlbni5hfILaiq>u-2GIJJ@W= zEe)xsvuXPtfxS6X;SkfTr6btiQc2kD`@mRF+dvlCVTqb@xhoJS+#xAu^s}o{78F3U zbtQL~hlkzV{Ty(&tfLkU*FBA$4Lp#+k=l{)H`P;KdgHvWiv{($Zfkn}Q?hlL|39TZ zPySO|bVIR7{#4xj$+w;Uf0#d|5v3-~)7b|38P)m27(bvy!FYr?L`fHw5Di1kw-{4v zNSopKx|^pKxCVtQZ4{Dm50bpN6@i4B1-V-x$#pLj;q!%ByS_LN4(U3yX5`I+#J3bQ zU)0+~oxpveE^|{NaWd2%g}l4a0e3yzT%u{0*04sjxtptIKZbrVquRfdE;a#sO!Yh3 zo8oINk3$2kq<8j29j?~nXe;9nW!IzmNf_E|X3Le!rLajNL+-OY3!K}hA%nJ1;G%0l z=S|UR7w#sUI5XDmbYRERwqsxh$dt9Ig(ba{c!!G?;(dtYHU)e8*eyY=tgO&{SM~YkFgC zA%>YkL0s~@g~otO2b{R-;Et8e8-%TooudlIMDc1O#RQ$}8^YHJ`XU+jQEP1^PggK_ zL1#=>CvlFgo=OzLt5Qjcl&Qn+3cQ+h;xf<(wJ_a@)nLK?f|E3@wcs!!Fh25B?Fa)oj z;{IHovYj{`Ousbb)`o{*<%cu+YtS5cCI)8XU(hMhe-v+MezFu2pWCS((U$g@1cXOY zgx%&MTqDhu#2qX<5+#ndB$BY}ltWYLpcv8UQay$=<;K>~!^$~o-X?2+?wlODX)*g1 zoB69IC%U&mTyh%5WGzYNtoDHqJpb%@c#Ape=PL9Y>yhZ_uXR#~>1GGeKE)`T999p* z2aNVyY(9>tv=Nh!=L3?vBuF4%LbP>a1#xLpx4p9@i4z@X+$Xza|JQ1 z;I7UUOpWVnfSD3uOMr8x2{K<6JhwDfklZcB4_6N6U_tkEcr8InmfpS={%M%8Tp36M z@zzN{6%gEAA&3?yfO=M@E-mW0Qdxi4vMDR!vnb=d^!eVDgQ|z9H`hH<+R&KE$i2jr zB{15jp;0HyWj%gyInB$=rO)ssfzS#y4H0x@0;3aK!0dqx4Lv_I^^&9N)r69}FI3&% zLSINEd#pCfe2x%jV^PDQnn#&D5xPFGQ+p{*fiShplR-k4QUqf9f;K&IX$6n!-0nSU zFoUpTMFhe8RvL?{R)r^jCTVmoZ%FaxL0w;l_fF!Mz`o4sZynw;-CYnLY}2=!&F9jv z3SOD3T!mFe<|^go=vmxuSa=Gq3gaF?vugKeda)+Auv}VzoKh3NmxFK@)1Qv&ud-s| zL)Q6ZCePN11yfQ2@1W9Lx~2AniPWQZv~w zr6}6I$N9-1F9I?5?K~54;*9d}Req{LrgcT2Jw?pAJ!+y(l)MMEo6)A+<{2I!Aymh_ zMuPmDxZB!WTd1|_orw-+tR*@La-n6?kyhf6%#I1ttW;LXnh%pO`!PMcA4uH0ovMs# z<`Kb+JD5AsT>AZ)F81MBkB*~>Qy>cUh_EbC%*|T&p=d2? z5TxK}j9}lwRF=f~J`Cvy(&BhQ<<+$OIN)IdjsR)7_`$HbLZNAU(2T-l+!dK#LyHL? zjk+i;IVppjbUbLrAj_t&v%mOa(Q3Z>TP9_xB;e0XLf} zB@dvG3`R;k)neEs>VY0KS%d5XCBV3()|d2ryfqgd;N>d^FH0!5+sj0zN477|uG zJcPiYO6M}wn!ED&8qMSH-UV15N?@t=_w-`pq268(^Wd4NwXoJJV@pn%6nX1C+=oK+ zOeyfZcy?wh?93y47?R1H>Ybir1b;sW{r*TFif*G*Z{V=%Y)cA|RL^_(&cKgHd63BY zP<=dd_Z)`teeB($RE;~?G@kE3w7{coBZ zg{@5pB#)<%B*8TQ=CF|vQu_oilE%hBP7tA4p4baZq34IP9G~Pv(u&XPDJX22-Qm&s zcpE?2g9Q8W)Ea#`LfLqz+xRIyJkbgiDN!55mD(M|d-$nd6ne9O^aSOuAubkbO8C<} zSdyZm6h!!~OnD%m?n8mK75y0Yg-XdYylCnzNb}4Lgd9`YJ44(%&-9^?$!io0Ha93~ zeHM+wRw%(dp*p`mo5oUYf_u0vD-H|CMd_92_;4lJ@&04PmKfqi`dkl+nblTQ)4h(t zdqkb(=lM{m>GQ4l_{OG5@AG|VWHP8NCE>c3*q;}8u&6Ab0;PnoQ(ovnaTr>}^fcbs znuW^HeUXMlbnk=^sUC<+@>{ZU1u-EUMt81F#vOR5LCPUym5C>8fTc?zd~qJ3aT{$8 zLMn&&B@Tq0Xic22O+x4K=-`#@(jz2Ry~Dy!*b4qT}lq7PsWvE!eN zk}{mlY_Yom-(H^J6J70Ujj>u|5i$olkQRjLl8{QEH9>=O0Im~kqbV}uv`;GCIgGg&I5(?jleZiJv3kKMIqbo!i^eCf2-%#xFzuXH5z@{O+lJjIih^Y z^7#5s0jjSxsOpVN!=!7Q$1jH!Gq20j)MFeb;X+sYdV{jwCegUAZ#F2}X_O(_k?M~67K7^HsJ^n= zQ8{?P-}%V^J(;$qw4y-B-?wUX>+wQ&d44)NKb*fF@87L~;@dKcN$(%5BbCwOP)RBc zD`jZj&PRdn+cUc3QLBCoO1F+h2cqUmXBFsAuVWZts@7Rt9qrZ|OBm(B8ybvP@xz%~ z`)IiY#_O#3TUw1Krro5!p&7L7Q$XD#@5r!EL~C&i`#HIq2n0!|=M>%^c=t{ReNhl5 zUSdf-PJo0`oygziqb)L>!)a5w)$f+Hk73`2*>=>D!`y-S=~Qfh2HmekRhM{w!|JOV+qrb9 zUg{rA=!!N4G+Fj;Q*=M1=wh6&V7k;yj}NDG8yiwFk*sE~nbMFSNoZ$tA?Ss3_nE4Z z{Afa3)x;M|nR?rNETJ4P?x{m{=vV`udZ>o+FjeXpswtsejWE#M~ncuEy6;*4k& zhz8N5-m;%iG%uLM{>aK(4&9h07Di)e#8GSRY1CHmDh%wdO=aPKGDR^RHI`@M?jqVv zheQl6K_L&j+ybKzhbwVZ4d$m9I4pJ)DhZz!IMx>Gvd8#bz1_ty6KugmHAwoD32RyF z_@4iaMicK3jp)?qgr>^bD=nqrM$>YBI#B(rpb93<#)VO0MjT2y=s<1!1<`uv;Cj@O z9d)41fNYu~4q=Aj#y(jdk0569exVm- z5IFE3#Vy<%ZTVab)cyTM9YaRVwRHB*Z}DWu+z8hGrJGzD=F4K|o89YhvTE=sEDujs zPD<9M{_hzlCx1#?cW&EJ+P-}#^_9pElmnEq6~3IVvD#d0beCjzleE2)t;E^uq&!Do zNomi-LS1WCsL16D>MKC)>{s2Cg~o(hzaY9)H|?)wbfJ4ld4hV4;Qr5d@N4>Wge%`v zf1R(VXb#sqt4ZOrEMpM+F3xu#H&Rt@-5{9H`3)ES#QBm&cO;%b6ciV}X`m3~Qx{Nz z0(~t(GLbefu((hHE__SSheAqq1d@iw>7^z10xq5)1eEpqZG`}?wFoNX;237k)G+&j zc!7RLQH_eV)2Y6D-<34iMR6qE(s^_@G(WbxI%^jVTN6@x;d==#=>nW>);nzwL~Nmm zgKO}AT*GUE{IG;q^!o`Kh!u(!iMlU#boc%+iST&#(Es}bi*Bce$`8cfAj#E$l#TVn zgkoa@!f;(|GmZ>T=ABKzjOLZ7HJo>fu=k$wr~W9x07)&Cwn$Kh^Cw-vsR{t^Z;;oW z@{s*FqmT6PDu}xsY^Sro8AC(Z92h$Uz~%;(pY@Xr&CCT*$vi4j>Y=fc9kIrjwfG}) zI5O)@JEB}kp)|)&J-FtNmE6$$u-N<>Nf~CohdP%y>7<`|agDfe?ZbzIZaTvqzw4AW z=&dhQop7O}MeY3IOonKacN zNrpRl{&OSdU@`Z9Yf}JPc=h&TT-l=LC;dFz!}=QJXdG-tV{r@o_~Hb(BrHkSs9KvD zAp4Zp`WGfX(2|%?1>%)2sIS7a5MIiAw;{qVKK4|p9vHG(F|LDS6TkQ_w1> zpvhV6PW~qc+L;St6??Z>YfC|?Cu_O#O3fVnv&0&WtwI7vE6PG8aBX6bGr?o%fz=lC zA#%YIzC?~FZ=cDxCh+Yq3JW^A+u%=x_CVk)PpUw)bWb&qcUrdRHF6cRYrr!9&?`PX z3`0n@1I6WJX{QQvndn~&=>Ixc^x8xrp-2^5Oe56%$W+6ybkM_FwB@sRkCLRnCF@Uv zZNeRJ9MjKMqoo!+SG9i1(r=@;g5_gTs?G9MVF#|iD=d&8-rjva2AOb}F$^z5k4jsH zMZ}Zcc&@TBQ19h`Bp43V*XkX4_9YUyDODuLc3X4hahu0)bvp3ipFQa3m(QlI6t=Yk z*p2sJ3H52vA!51sKu4$LOtgjr(kw>-pYxvG$Gp~mC$wv5`pZ#yT5lvEtvjUHl&Spi z|7eu6yi1cHm&??g_WxS+WLbac3U?^;#@bh$BYDzIF9k6lFc+sG2?-B$a@Q(}7PpxS zMv8xAE)OHT&E`9mv2zIm6};yS!d(7uQ!3x$#%aKe|C4lAy2;RNC^xGw?tvcn!bOMW zlcD}_YSovts8fX5tM>RmF|+Yf7G1Pv3#CnP{L&WfX$%BV#MyF2dDkwZQ=g13=zt>@ z)LOdXaPs7{Hj*Eziw0c{-~`d#4QyPc^s+H+k0-&7Tvo@0nikYv;vE*cc2`Atr@(5T zmnU+55x+BEhtwG13)4P12KOcW!u)i7Z9NI@waasR7b~qq(v1h6v@+&O!^uyzlnS=g zDdW&OYqxs1VzvZHhnG^xDDs~R-T=E_CTro5t8(`k41M)6v9ek;4-*^BY( zIdQP@L&NpK02k$JDWQy4#IAB`%ZSg6K^{yhl56%aSL9WcHAL7`DGTg|?UE%qK%IMkF zuRzS}T$Q+W68lamZBZ)LT+Mx8HP;oZ>2zv~t9wiHle33Lr?-HmB8q{8C?y-2&JWBx z{9&8ZPbU-CGzdZytG>WjB#M8Hs3GK|Y0Kg25Nb8o(^dmcrHjF8QrDGry7t4~Twhy_ zz9;fVZHwi6I8AG0+c50sjbx; zSYTFZF0RH&$&)Bqf+t||B1mhSWIAquwMyZ-ksB9yzt-Yr3PChYC?1zO{1(3s0KabR z#+8i{9W*x2d-g2Ab`uY_d{Np!cGKfUs{*o{x{<*g;ic|khsiRwHm6SI$!=s;!QMdl zMd*r~8K}fT+pIH5pt`vW)hNs!Zf!8Pkb8se@MLd~gKXWxi3&<%28~Gt&MYl~UAj+k zVS?-pm}Cv+%^;+S>IZg9CmI;~4G+?RU?DhjD;JjWsJU40$QDz@YjZ-PZ|y`AcVXn< zpruEd%a-)usuS7f+<25$ zx}6IbdIK&(xw^dzQ*{Y8u)bW!^2vUb0b8!9D9mY2M9t2&Elf7?QF$uuaDx5m9#jLz zMBH*`xG>0gi&M@l?FSNQSCxR(ld~_a50bizDoaj2Wwe>^wtOrMbN{hXV z9$aYKZFS>nK_?K%3>kz>QSX;+9yI*TWAn&kSK<7i?QT>XkjXd@4pGwvcet?3t}lTr zavG&+Fo>uQkUO1-(iStq-!h%#6=(Wgx?4m;&Dk!Fqzdo|QkKgd`am}bDW~eS?&!h< z1@Lv84L%r&^5o^<9Edx4(1;!Y{ji3uC#l!non2@S*O#!0xgRj>bMJX4lz}s6y0NsN zz5;vC3OqtNaaPK87bJ+C^PzE?J}NN)gvMRTn3<%N*miiJZKJNkf# zy70gVJ!ON8+Gy5=`?&*6NzoyVX>Uo3Eyzj1v9KkK4vF3)<6Y?62K7&%0W?*}<`va_ zWz0q=J1C`QkqIxPpAwY;IqpWWr+yrPOh>YpRS0z|A9lB!x=5<-gAx4wAkNSp6U#W* z&anOd^Bi)9D4y&!aZE%lF~r=-4$N^0?YKtGQ{QJ{iA6!V7Yat0vs6ZPmS$SDW_ua> zgoT|YYQ}Qa#CZrC-1~P9ScWa|(JP@HZuVV8=)tpWJnPtRg@zQCEmRs(4}3GKd<1TX z{T7frLo zxack<<9N;( zz8OMxi=sWNXrHtBwC5DQZzVX^+aDrNHmB#t7cB*eVJ$D>VZqWampm%6)9! zb910UjtCUL(!B&cF#}C)j)MXZwG{vUT~AfMW(th$P(f#8I64hD3Dg95PjU| z2$YZ_C+e~wo#H%+Qt=HW6uBm{3Sstv_!i`wRpy36tSZX4b*@4rZOrx%K$%__Q#$1X z4oV2b78*71)~1ex)HYH!=v^#y*h|ETF0+t_#UD+~*!!o9K5PFlcj2xUp5cD+AgB;b zgb(L#38t(72*rOix=@4&VH0r!rQDA}B}EI0?Jozow0zh<->D=anAXYfrEw- zD5ZpaL?#Lj48ELt;&s^BbXmtiq6p-LC;t&UFRmCE7$EqZAmn1zM8SZ;l}@VvVqKt! zR=`?m#G;S&^jaKuRulC(4wV4HYzeGr_tgGk2gvgh1W9ibymL1h!O;Xk9fJ`Q$sDr^ zWLS&32dI6JrHrL=*vrOad0-t%qhAEt^M)%0R4&9?O24wc4z2ir55m$HF(TOzX~?t_ z(Z(>+;Mr$`s(@~$c(B0&lqrHUW&FqgUB6T=y5M3RARVAl(X z30`?}0<5qS(bHr-MYtO{(umhZXeN24tHP!1iIH`;6VHvb+6CTjW39N|PF^D7Ls8`` zU;H69?N*OJ2q#yapQ4|EghkO6Xliu+aF~VYp@*FtS|&XtIupS(Iw3^CluK!Wm-%je zC{S)o%FU%p6MJl8o`ADO8NQ;E&qYnuSiHW0iU@WDUyJA;l7COPZWer?&5hBhiKz$j zThkZ>ny4Mc<01ND8P7`g`J}vQ9ks41G;0NAfjCAtuvxKcqJ!sia#Ab?I@Cw-D3%!$ z+JRsrDy4yUlhe-wPInceY0Q{9VP0oVe}KW_KQ$3`Y7K+WvPZMy84tffZUI+gQ|mpJ zU$5K%(GN$aZR>sdh4%J}Shj3b3D|W*HlD^(Cz0cl-(dM4EOfoYp1xx5h5fzYe{j9< z#yylYRzqo-KqetWU<(R4fu)_wd^F|#HNcPKd43#(yKy>N5rw2Dpk}hgfDA|($5kkS zKnJTP3<)uH+y@{HR5kyGT)l9DB*mV>N)3d|1E$P<{2L<5mY-EF>d^%#wVHZnYparaewvu)0rR~iW-%ZKT5TMNV;Jp_%4np1q#Bz@-HK`mWo}6J zu2V-%38V#=f19LD6imDi1HAXv@G4Nui+iYacuP8rMavd6#{`L$7JO@rD5r?It`4m0 zKH9qAc5A?toFbNm;#BBOP$JfFUwsWHWK%h}rtmdTy&~=>*U)V))mo@cl38$jTXk(Z zU|>YHRC3=QcLzDRe@b5)N9dP?q*tPBoa;J=d2_Q!?eT!V(I&=n77`TaOHRg0u4E8Z zwH!^^v9-rGkB{qA;O+wjUbrES3ey;GDZD))WF{VsrE*w53w1SE5e)2R^q@TD#Cro5 zbRb=764--vx>ma~h*5~Zan{euSU#KcR5$;JmP#w*_s0AGxMN5jo z%fZ9)ltKK9p-XkOdbmzE?ZF{=Ziydz-zj_W5jna->_P5AsZ#dfBXz0=moZ}^@tVQw zR~+@!Hl3W*19I%Om(ups=AHf&IP;H6QH*YkHR3ij*G0=ox38eG0vu?XF^|Hay!+SJ^y>K<9%Dw|eloQ8XWelr)w7yEQKGDHT z-C0;X!dfEeL+1iD`0QPxJgkpN@sD8;K06zUq!_O+!ERI)hC$B8L*>jq)`3Sj#Dv`D z(S%~5`X4?{!;$)PYW^bSuf6Dv<2@5R-NQ(>5>{Ng8MnD7Z2LksN~ zM2Xp) zmt_8GyIFavenL*?PW3FZ>?Z6Z^CSyxY9yv@r-7dd3gD@{S75XSYE73p$<}-&6(BP? zxd_;vY+{>)UX3u9D1EoNG+%BG$3V25dR|!kECc4Jq?l97OT-o0o%G-3S;0rW&uLgk zT`JS)sZKmrTit^3P<5hD^Ww4X)xx=?j;yt7yI|Lz?m}g?;qAdB_8mp-+A~~urs4~t z)UMl?ib*tIlnvI;k#i@&oeb0G#Nan!rjs|L_SG<3u|(WJ z& DZXt{bz%8f&sfr(H*SU53XkQJ(O!Xdoeu`OzM8pLo1KVg8L5ieIQ`81Q8Lycz z8;1Hr)b-&2Ipu+bw+$Shpv+W{=0hYe$s>V%j0c3IM8tnyDv)%Sm!~@| zeR`JA3=25PeI6RiLzj&5ICO|#T&=a{JJ7-d?A-CB54>KKHjXW5MIyhJ4#k(dl6nxB zHupV%b1##4VR#Y7blXrd1XVX^2?@hns669IrG-n4pX0A2`X~3CXUm+&a>IiA;)D=a9Cdbre-gK}A%(1K(rs*dpWB@6a^jIq zk44aJMb)*vCSAQ4#g^p1iob(`f>ENOnG7WrC^Pj1PPBWipnVKVH5Miz*)4HusF@;o zP&$XAW$CGce}M=Hr7upsCJlu)a=U-4Gcc(i8#Dg;{7K z9F-wA5*15&y`+C|9X)%shJuilOBo5;+?A){1#9KQu<|!Z%1Wg}R|3d0*?laT7_Jh4 z?8S2tP-}mq3ju0eRmc{!c52t(bkj>i_2UK%C2ql`b)XGfju}5^&43*f)z%IrJrRa? zkDhpUdy4*>4f+#{}$~I}v!9cSEY_D41|jOD!9GQ)h+9I~CD;D^eaC z8HY0#VQR_2CUimvr{~n@IQ)O@kh5=hs+R0+f+`Fo$`1m9VMkbly*OWkPjwTDE9q1m z{q2JOXawE<;Pz3a2-xghlp;X>d%FYru#kELs?*LpcsMj6U~_}8YTqePpxh0d7wVF- z&Hz>{y^Dh)fD@&X3PqVWWeqUATVSXyfEf+K=YIq1<{#J`dTI6$l;(>{QIU)x?hj!p zXQj~WlSG~0_ejKxtI-%_e>c19l|}29p)KK&AdBZAv88q6V$qb6RjV=KFK>lT|046Km;wzI)CHW;}gB(r$o) zie@HiqJ9Ye3O95Mj0HO#mdkZ2lJdcX@}lUTk$Q6{7S5_Q;%>VRuTF5njMW;8-G&%Q zC(~diMdABoNxMk6sHo`rhf-9coT}266&&i7n2ICFgNP+otNViTL#RG5A68goa)^AV zqI6>TOa3cm8}3xt=`0Pqvw-U$B_BzV$eto%%l5@dBLrX$mTo{hS@k1**HjjO^~Qx4 zldj~n?PSY$X;_6eO#QnC$UkZ!N3YO$wBA`&P=TKpHF`haKW3r-Uqh}Gi^=z8;QPld z0c0~0%6`RDns@&8pckoxd=?b_MPA|_B5w~P)a3?W5aHaq0D_&p^>hXKIKg&vWlRO zGI-17p&{&oGAxrEWy2W5X~veh5`p`33O5AFrm#F2Y)s^1w*mR|SXTi*$DI~Tl>t&P zjb!jq*yUgkSV`)J>o!P2M^s42JmbfJ-=7~aZtKmVErYLLRhqlG3ao(og0>3be;N?$ zxtOU=OkY%(H5nHM*;w3aMlFyR^+75(;1Us=NcCsRa5gN8=cS!4r!K-dJHBI z;*6+OGfa$JSTshI2(QXsXxO2tGTy9c({S`kFHMdU>Upt|} zQ$2uBRFwa#3LVUzmY$>v8nexK4Q7WyLg`@c&0&u={Un^{uPH=RN#tdEwZ4oGXm3od zSNKJQ2((vL79=CO!hxjfv9zq|m>o#VK1rfz+sb;2H2zIP#7Mrbcmw8Gz1tq$&}{`^ zk^WCx=0)|r{)NQ02$3x6R{TbawY~swG1#n>8gAxA`?@3Yo4#q{zF1)XmWFv68-Nsm zTIp;}@$hANScMbJdpm-2{%sAj(82{^*3M#pCzHBYzN1jfI%{}k)UmpzPX`M3UZMOa z-&IINlK-*sI+PsEAH%MR6H!}rY{Hzu-AABjPgQp8$rw~Bm4xqC`*Njw91;y-Vx;I+ z6ZYyT4ZH2m2JHOzoXD}iq6Q{KK3R2PuQI@MxlZ}=zOS&)HaEqCM$I1hfx?2$%&1%i z%du0h=?`3yN};)!xtyS?`+jIr*Ni|+Sq#gUq$b<{$fi3ENqoYuFDqQct0R3s!BbFE zVBGbWM%;e1Hc0W0Z5*CXkK*1+_LUL~_T8Ud5nh}rtKM84n@*L3-S9x@Jscs@Vs%!VJ^XRZ2Z0^d3Z3V`0j1#W>l}c`J(z&3C ze&awnf6Qr{BtQpgiBSCfZ5}Ch4gDYE=kFXSFFJlwbr`=lkv3Ogd>!_(t1ab%#$%AQ z09qc}eBs9`=}lAQT#2MSG=Fg7>JiXJZ=wFMXX_bi;@=;6h>~Ef4u{JhD%0XmPBb%d ztJ}`5i@>~9Dog9nCVj{R#H?(6pr%GA&0RM&A^Yz+%3}~}L^DwCf6Djy$^E3`o6QfZf5Jcd zLM7r-P##qPpGT4g;Pk|bTHImd+dH!=FW)Y$LzqKg< zlSr$=tG5|rYs8VA?u?`^?B!*1dnuaZQ`?w&o{_Q8RN(*L#%xTSEoas?Y7c1kE^b$j zQ0rd9nvKg@yV%5;5e^S+Xtac~E-!ClSgkczqJzs>Hww!l(cRf>UQlnAE0{RqW_?jr zv*W#lg}13!U1&?KXgyG`6aD+vt>dW!G-EmUwl`Z6PYY7v7(DgMILxGtnR!<-C z;?l274&k2=UQ&}#uR(9Od!4DuoFf?W-0Zia`=m02*qJ$ki+~ ztt9@bnhzunf2f^Uh@0jHf-epRalpYo!Q3W$U?y8Pl~Na$X*oRxWbx|Mx@4vWMhrG_ zd0ZJPrN#B;>K+TLyG9?Y+vMe)!0x$UJdcC*U9+F{ZSn&7`{!TZwfb2fMqn8qxA*$~=U3-`iJ|tqULUL5M zu5?mwup5}`6J3D88E5uP!J=fW#SO5V(#O2@4y=L<-O!2QK)l@1LJu%I{K!5T7Rfv^ zLtv8+2fiODoA*X1jBI{Ve=2f!jCv{F*onz{rzY{E{3}h{LjE}+GT?8LM}?2WYI7Mo z+4YM~7{JNv(RBE8IY`Z|ylEaQzG*FxT6!j%Q&c77$qwpJ+o7T$1tX!V@^9uq;@vdU zVGo<5KQy_!LwV?LZlFqX4Q+*lPHuSb3QvgP*-NsgGZleT--kg@OpX-r7NtB%w=i*I z0=md3748zViFI)OJ~b=&6aq&!-UM@jLnNHa{J3RL1Tu>?gg#X#cPk(LiW;6Qx&S(y z145p3*%3Y;8FEg(PNV>{PXUp|eCxv2z*+)Z(@~X1x5=*|!iT2&FbfB#b(Y1?^!vBjW0LuJjo{5Ql~nx`@v&_PH-ve z)vG1hmRDzcvFP2eiSq&d?VRY*ybh2V8$9w~Xa5Pvyb9KvuRW}xxxH%zfeTPV?-G2r z>=6A^PIF=(Ab39KiTc3P9q75(Cxx{D|I4}v!ZeNhv|^3?87|BN0L_;Ye|_MP6Y~H- zv!!F$)0^$X!0D0$b-O%@EmR>Ag~=Z<_zwp%?>@haKZMfnF*({>gjGOGs;K{LwF42D z7G!+QCRK(i(KrjwXWt=X{n^*-|5Orz-I4_UL*x&v-;?DZQ(JuuxH9bLipm4L*f54G zs5In0`DcT)Z1HmiI|GXhLpL_UVNX@q zrvc$sKf-j%ZwVWR{y%)$-Tm@E1Kiox2X{t*JCH?oF;Rx>Gw-Q;|2S}Gdmr4n*f8b< zPL=2l>ot;j;xHC)UW&fDOiI8O;$`&{I)RK@Je}~#i%h!G< zkn%g2n9~w%Y~vytbz`Q4?Pk}&aQR1h*Y9Xyk%h$Y*b_w%bY*dn_i@PmemesExswwU z`tZ(*>&*_fCYpt@PoN@2`IN{P5(Z?eD^mO?qRwi(1QR5Z-lcj9B$szozxO*EIHCKp z@*e|IzayKAfSML_ritMAdLxDs8!|mfh`Hfw5I@m84bZSr6E?OMqbf*udGUIZmwKYr zod|5s5>q+KUZn1nCKgaWPd)Dke7{yaNOw0Pbbf0sowEci@w7ICztiXHV}cElUOSlzb|53($G(&Th_7$OQ9z!TuA8zFY!8S@}aE)U0)uS{uEj1_zh zJ6}WUYBArEQ-FHhK#gUUN$h5-**Oqnlf|qpo$hWF4oLiZQu-DWH-)E#^R0Uqz_;7P zCq_hsX{%BYFR2*s9uHPZQ)SW_B$q?(_rn};aIXi|32G$`Ci{GEar=<(2`KkDQ9^@{ z^O6?pcvwU<)AD;;YDl;OK{%mVso^5W5*zg7XSE?=`PWp0w(4BVbiJ_}ccV^6joV9< zYqBV(AaFTMP24`qjY{U)sy_BILQ6qn>rARY$^JYhFtX8>l(0ypSqR5Ugl;@wp*tQe z9jLG2>Pjh$e7_)bj znr<@b!zB8uVPUfJyN1?>l(x8UjSbKy2R$fo=W-PU;57k(*BcC&6T%!qm=s1-TCLiK z$YctOYL2KfSMz?p09GdhwRiDj!CJtg&#GKj)P4(MTk&sTv^$nieIe#DFavT-V0410>i~a_2HfO;%V2L-FA$fQc}?k zKShMR+{1@NRoAC53OGT;4OpXaP|V;+w%ft)N*C`Myw+M(sz7s#0%9?t9nOp88D z8}K4p>gy8V^N2W0OCCJw7}WusCF*QPK0L=e&_qr5uKG8OeYQO^gm=NR2ao2w!#dWY z>^Z`gTJa#s@|z3fh(}_!0Y_Fz9Ms`v?+byV?m>Yq0_Y2xjY5^VSvL`Ob2yJcQ6wj* zxk*O}3`tK1njvg`LfLVR-e@Ssu(42_S@U5rtE~LThK+`@5}IB#@w##}q0=>0xIwjN z+dX#>zE5Khl5DDPVfu`)Rn|Qy#JKvRC6?gE`4o;qWtBQNEf0!=Fi9cm^n8l(C6(Yv z+k>U2k4#Ffnv!YaDJ zha-zPrCK%j;9*M%^)fx~L2@Ws7vpt%KYX}+p)!p&`ao0U9#L2>^kEUh(|Ws9*nIKW zNoT@YzNZI^Jp?-yn&9(H*#-9^(8T??;rJ5l(YE*^ytfaH9r5%thmD7_4({W_6E_S; zMsADPD5yQUuMfrgYSbExN06{p?&m|(`xx=LqS8&QI}tM(?@!|>{8prT)IWg6;)whC z1(>UADEWP$2iMFh^x6bZ96Z-$a)XGzgw*3w+V?>|6s`IibeFbwDtAd1$&m+}SR&|K zR%`UrbdW8(RE_pSoG2!$BjeVTsIVGsj~zDzr{|#_1QvZ`%sh7=s9tFgGwI7QGBX#z zFd>ZO_H5Nu#g?)Ei@nkw?q8Rz7MtGe<*|2-=#}<}o~zsR<+0CYX`6DTnK=^JEA5fJ z*SF~lfveh_tGDY5jpz{eZO4~`Ya#|@Uy9d) zyD_=OK(@S5QH-ZsNEaL3rATB|Yd8-!({08kiy<%Y(y(%tn>WvSu$#}Yuympoc=onE z?_ShsMz3}?BMqkOLH20Raj9H6+@XM5aySaepUFlJDeFS9g`8Wej_6qyqGM756T#E= z)@Y8eWKf$Nll%s#;Gn$MZJxZ>+XDBVZD2hRHCH;Ta7ZBeR(RfFJ42*@8{uDgj+-*9 zIf6oC^toUfmg>VKyly&9t_GZ$!$NMx-zvby>UC>zzYfLgXaE=+_W(r$4o*Al|NPNVXeOe>wj@i z3`KB(+W(ih5$Gumq3b91|D^==LRL=bzL&XaifRU%*aa#N;WcgqiKXifhK2E?G^Cy~rtNzOWa70Zn)=a5R{Db-M71dp5eBP5STmNm zJ4W?Cf1QaiUaU3Tg;Mxt&gwA>%Cn{Bh`ydgmKH9FU}ityxLl*Uk-eb@HV7rbY}RTg zS`*?f-86-BkCMDMQV5-ePJ`ef<%4(=iBOwcJFp#22R2$Z%t1K0Z>G`N#Zdh)kFD*g znVfIwflqsB3`qJYt@74B(B)2sC`X=H$c5nfeDh?G?1z1Q8;MMN9}J3Xo|n^J!1nf@ z*apY5(vUK3%v2l`+`Y6StHbz_4vea#@NRxTzWB2KgWR2 zF^{)!5|DkOC$h|E*q^u+c^*F56Ws}@UOxYb*TkntgxIt-Eub6B-V%4#r+Z>^?`=_< zj(YolrYE*Nt=if^Z&cz`e3nLM6`S?5vD{NZ-S?mC2VLI^n2n4$4WB2G8GdkGnfL^b zeNriZ*cT{l4j;J=usCapvVp%yBFq%xgiM4CX*Ume zB6k5%SPISe9B4`x8Jh1q&o~5}BNw1y26lg>cN3cfbUB0y3$LBNHXY<$mEt+04cFnIPGFO-#4=C4o^Z zGC0vqKLLI!C$Y&-%*gtc6KgK+KX7ikcl_>v@Yi01-YENk(NeebZ(L}tI5ra=d%r1s zzjfiW;;@6pN5!;%=fh{mW0@e?=bM`+aGDav`0lwT{AI6WK%=-2{W?C6#0LXD32oAog|Bl!7Xs zO2iz$zqpWzIA6Fp^7q((7yi|WGZ(dDZZaRC-$whJ7oqJuV1g|4RR7(D@=$H5UTYNa z#NqQ(B|exs66d7-!-X`djPC2ur~=23*CKIp{^`W$@$fRYoXZ{XoMwI zgk|pZBJA|P354z{2pREWLgt?%V#fD>oR~M(7GhbkdC?>MuM1($Lp^vN`uzhZ-Rx2) z?Ha2!7Qw;NkyZT@3o{;1H4`ZN1QBn>OE_`n#s;0JQYus~|IdXr=UZno^8^sO-;C%b zxTFiA%g4?H$=z|PD*93uMi}k3s8@w2nKF|heF&(U&`bBAFU;B?>QrUiWhm-${E>qJ zZ2l|7U}eW)*S*X3Mv!9xmp)Y&emRPM(vQHw0`@tfdLLfC4-7dbun|zR+pf?D0)32+ zE|Bcq@JIu8^}b?XIP?d85E5nLTXKoBm99i0=`Pe4Cbi^$)pvZv;6OaOrO+PQ*1R`i@Qx zK{uv)LRcpg`#4;a!eR7M2tuF=&nyI3Q-7_V2yz?2J`0pixHd)K9toj~BOir&Y+78v z={DD)Pzb+LmdUGwV9O+d;kqOSa{xmS2Iai4_%IB7xL!XPast8Tf^v#l2(Y)!^+^Oq zhO5p5zBG99x^b504JZtbOjaETn-}WIVetazPu;Lz9KkzM8dfU8^m7~q6gM(ZOhUQ< zw0DBMfqJ76byD-CDn^4%4&@U?N+qks-SBW_6#6A5e<15%B+O4dq7IdbeI2mh*nxdE z5In#~$gqYj=^wrMBUb`S!^(&>@izefO&s{=0`XJK=VMpO#KgV?*l%iJm#Q3~=f-Md zzna5aCKlAF1JOvk)~O$hW;WI#Ek(3LJRG-22~@M6*e=r(Y<7(pF_|nELu5ps#OhBj~gtH*k7ATAYZF zMJ~+^Z)8Z{#craNR;#u#T!uM*W$3JgaqH}i3Ee&p*~p8X>9siStV*?xhco>}_&x0) za`(8j)olSh2v&7!uT@FcM{39X&#Z~a7P+0Zrp_X!d|C^^D**MdxSEX5D&6DtM!nYB z5Vxrjq-S%!syE={3>b_+(VStAS-}jGZ1I z2KL!G-9(E?1wviL&Gu9cL?RVwLlTVJFzEK9dUTxO!PbI-JULuR34no2V;WZ`sUGh` z9$d$qu*A{v!76kwOHN!=13O{J6o!p@gA6;dK}pVeiBAONOI!omQ9ujKL@53}EPt5Y z4fqBGE^i6Z{!|9jh!gv?J#Q{KAw2|5|EviPNt)e=*ccJ#SgY49BbGq5-2O<&)TI;!SwVn)}_KGp}?4foQhCzkT& zb_#H%jhY{M2PZxd8Qkhz*8Wj-)*-*3V$ z?(!fM;j+Sak*dHddyqIn1i8dIzsR$9lX%foJP1=8TQNJtx!)XUv5aJvN7;6;W@gw5~B~B~vWAYGLm<5iuAyakb)gs8FxN z@SG|({)&1&yDD2V!VpW^GEVh+d+Nu&6OzC?VtZRZ= z*CsEFzhC}!O;YQ^@c6}44i6IR+T>*wDu+|lx;A-Xd^uE?jCsB=Vs^HFUH|i|@@vCG z#Hu!VS%pgBG_|fxUKm>niK*=~u65yb!m+r8L05c@^q~^PM4XGt=Ai7$Sz~?B1~GR8hi1>^lOlcqJ0bhRhiow@`|P6bVuCl=n=Td7NBi9o4?d7=5su_h_H+rNzK7h34AH1~ZLvp1lf84x9 zU%c%t!P=K?-pDwyeO$g!8si=WE-6$Rssb@l9dT-IOh%HBj?MU>NQ@=76*>>6alwso zCRe|loJ+`aniSWT`M)e0yw(V6Je_4 zGcS@slq6o=M+3Gb9Y&fpnxV(l+Ej)~oxq8NQ%*VpP(|UR zWbix!-)3N=dVC&rVZ%-d`A3_Bdr}(ZD4DA6mOlYGRL?i_opcW9!UlyA^CtR`!y=I? zv(!Y29rxD=q`f4uID}Le>L!7ZlEvcCQPy)zpvxN*9D(HO8){n1x`A@0)`Xd2@;!#8 zJ!llnE|Q^H@mEoj3Kt<}5oj;;5N$}jFwZy9t=3!80mH~Nw(T%Ig`0h{DQ{8BgS9_r zkq(Qyztk(B?LozL7I86Aoa#6+orsHti)x3*N*x1z*L9=fI#}2;RT_5J1*`z3V-}`X zR9iYwU#oY7mnDBXE63|?^rH+}mX@f(o-9GJPVLxm?Si@yxGFEDY}*M)ZgV=JQP4U~m8phZkEb>`)F0OPVSmP4~8_CmI1G6QXyx-j^FvOvK^OrM$2aZTIL- z%x}Gq7m>@PwF*1gNT{6T`}&Y12QAqj%#kaIFBF>lInf+!Ha74{G6>#KS)=#2(4<~m zsJ7hSjL{vQtC0u06dvl*VlK$uBQo9}&>NxUkr@O&b@a?P5mu=@(1#B-r;B&8crPhZ zwM^!qSO@hEeh`H+&%c3Tr1biO4UBV}sMjfL`5`7M)nXuDgp|ZQ^a6i94U~J=P`&jY zYGSH2x{y{8<}RKN&Brb zS{)ja#n{?sMcQvvzw}2rP|hF2iBlSw69meYF>12XqfMOkc734{CH9m!Zql^E?3Ph@ zg&lWDChOM=#x|FK)dOME)J4|T?)$c*rpQPEXub5%1=uv?`cICQYw-z zp<|z((1lJ@;G9pp1IfP9tM1pYW?Ju!cE9iU-4E_|HIn+@)zwwi6=UUtWN|((yfW1lnJ0YN)uo)C z=iAkx>ifxcjaFySYbK=pG2XC6hi|{8o(Q%lKg!EB3v#s+OBKnV@Jj}l=~S0oH`v++ z7*XFi7b2QFq}b6544Q?czqr}#9osCa3+am2mEi_y^_M%V(`8tI)!NvaL*u9o zS{?4$a;qOK(BIVW_!WG0c+8NkxmHAiG8vl^hF69#gb3lO)>ug{`7~aoF<^#wui0oX zc8=~KZ*&bTi59oYNJj&;Qn#_-49JFhwYT=dHip<&wz8yGU!zy!ff(CYOT z*YV5!F3ciTWP)EgQi1tXt|>@m7mW3GY4S zEa42_mZG|pY?FPvOH#$k1u3TUc8h7T197!?n51|0J2WOdk>--E4z%ErYfm>#QPlP5 zWLwU&e5k;SaX;|QJM$}_v%N*(1w=qgJq2dlYsqD5Dheph12Xnxq)r*{lCkIxGCNhE zkX~o+_EDgAXX)I`f=(mR=J5O}$MmRF?)`FL{Xus{<#8dbw2WRj@Xrn}NsB!h$vgX= z0!%7U!x0H#q*y#sR{VSOcs%nkqm=VOh4`fWviB)`*8PvrtoB77ak6B|&A=XY(e$a= z0Ii#s@NnGf_p9|c8(Zr6NUv%Bpj8h3G~h6WX4w<9`usdvW$J3ZK73rr2%OfhU@}xp zzcnX#6N^{!rTz-Xo8~Qms=>y& z+588R3nM{}rm1Adevn~Sj$s|6)u{Oe(124j0nO*? z8X}&tGuK(eO$2S7&S;8Nj<0RtBTZ=4%s!jALBD9ev6aj<)C6~RkL%^Ki)z+{aAnPd zAZvb|?Qii?M=`UJ7i3*AMp67cJ*{fJP{KbFgTzW1DSGxBE>Z>(&-^|-Zbsl(h-ElJ zvblaU62~+yU730SX-4I56=Ly(VSp&nXH$?NPF>P|JB;H&Nw)(g;^%|(v{PmL!jc~P z9fl&bbeVN)!YrBJHR~dvH{EJ(w6Wi{rhZ-aPbQr$$OVKb6noOZf|Ez*mb+?X0SSkt z**?C%2lp8o@9Pl$xH$L28BW~uWPY%<0+)SuKs+6WvU*`@l$z=wWj%j{Vd?byovo>3 zH=MAB3jWCW*CzKGhyJ*z^Lq@D$_bk8r{P({!9IbUXGH9MlB&Re-^T*Vh{r|QOJ4N_ z^iF-MR0XaSgZYD4gv3y@r`X}e(o3=(KANF{P0(#B_bNi{WI?B{=p{`fsMjnJL=lOB zinbl3ch?^VQE6qRqLd>5s**~7B;d&=8IJ%eno5#q{9_`j@t&UKf&Sw#F39R^vK%7K zBakch45gUM#|u%p!&=6HOY+NoB8$ii_&L8$nt}1Mq#N+rPXJHxNJCKynH1|(a!~OUEW5H3Z;pd z?jj?Jt4ith`7kaMYaA~wDWB*IAzZpLUO#kI%Bm%sZzjY_bV+i4F^I&SiaQFM$6}JS z%%21?4O%U)c4i#7B){92Lb!x3z2m?qo#mH9_`Ie$W5Hyd$(|&gfh7G(2$NH;F&0EZ zT=7psh;(dGz46AS38jN=yjn;TE z6>$0kAUXdgh-s`868QUX1vJJrHp-zzbCT32?(YOlfhHJZ!6)6nzYpOS%gVHcNiTsiMO6&gW}7qjRjfRfIw}5RF-q;iavM`voStv3 zGNeNPlYbJB=G`@sKzDKS_F3yj+o%iMKNllqE`YHk6nb?1i;OVf7O-foTof6!QfmII zj8(^2i(Yd@j2Vqks@eRvLWJ^Dvj|-D{T61l{(CXD+~afHXKYg=-5Wz47XPCdW$-yV zZluNCMEOf1u8YNfYU*xN) zLK_O*)Bk4}s>}qlhdVA!ShG)S_qMEncKd$DsppY z%EhA#G8m>nxohrli))UUqFj?sk|r)NLx-Z6;JNgpQW;!QHRjg@@f7r!LQf~1AeA%B zO4)|ispRt6!$GXCW#|T&4r7~)2HGZ3FO_*zwa@FOB~C9D1v+c+hSuvP@JinCuXFHT zOzOwT*e;}|K4JQJY6=+3WnW44B)9Nzsay=tWYxl0<(Fv_%SakVs%V=ypV(k;DQ_|Pxaf|{Xo)%VFdb(&5eEto23=1@pLgn(9`8NSF#A7IU z_ft2%Zwz2?7$MWv6>x?WE&QfJBmuReP@Pp-n))KCcR>kWkt*rG*+WK2O*81Pjt&d< zcvOar!?(O3Gy24&j&0wPp;0xlS1K1wsvEu)$GD+eU|iQ}W?o6sbUa-rnnF?y`?vDd z5urrk^lW&pa2bstWmtV1L(oL098>u?-RmV=D`@CQIpq0dz4LP_p)X6=Lw6m<($O@M z`a6Dm5MkMb!e|Ly(92{$B-5=kgz7n~jWx6+Xrby^-Tzr-n(Mc289GTB72m;7G`l_C z?wn_hDE~dox7TxRsQbBZ^6Np=1pTH0`cE@0eYcqWQiI&FRK&u2Vdk0vSWS zw!fdDN!I&&k|iwF7ybal)9FyVp45}4ABLytt4{0lXnIIh5Vv9L>vlnNjcu5u8>jI% z>d8a5=j`;-+zNf69G4s{xX1U!x%^;;b{^ zJC|`j`9{9vOPHNKRm!@N?(+|ec$|txl|WhnngV|s)d>FxLjwbk>bP~cF_UJoJb%j7 zjh?8B)s7PP-nIyfPNemKl;d7WFWxSXLv`TQXEP#UqVKg-Rdjn1(Qxv_o|1&+4tXqD zZ(!vB@D6H>9CODaEW=3>eLhlk`?Yx-rf(hya)U*(oV6cWOa#;|}Ll#)~CI0Pk9aD?K+w zr?Fv^a$xVqv8g_Yj$-h+y6i14@V-<1;N1(5RL?`w+>3kUP`HQW6U1Sf=zAsg6Z_E& z$+^Z_Q#CqqOL5^-XAh*5QCMvF?NaORmu9UaYV_g&JYLJ z4xtcd-%9b+86K09l7{Pem75KE+Psxg^b!0GlOmp6OdIc~N)Q!jZR*XnFNlb22tU0? zIq}r%AOtG*mP;1-{z6P?8jp^*q(D?W!7Z(i<|C=ETkLQbl?%9SmX6 z9tL{o>nm>AWx{bIM$W_O<)!qhpU zPs<~klhzPVD2$vwVseNYW*hBzOGWl1U3dQs+u~-oGuYU)-ecCUVEVZ99E?6)sZ-Sh7@p>~ za(+SW&YeccLHz{|K^0)&$~N|_B)M?JSzmPv4=sOT-|@BrrM`ndwW6sZy|t!7XbKuH zDY9r>vu%VPL9raTSV~FQrWv-5X(u7FHQh%ZK8JMY%^YH}NinM#j%|B4F7{Y7(j=s# z3{4kJwc8tpuZEwb{tk$!^ruSkn)FsYmZ4lamQVnFn8TTw2+>?G*$Bsrkpx}MrEmy3 z??i^<{FSB1$zmjxQ$)()JH>FI=Bv}~O_TTiL_Tquj5&IbEKy&cti1$%zhfH2-ZQB- zW|pJFE81!HMbZaB*pZriMRT(hAHFU_rKi%6kld!WeyQP4#5(0Pk%~sJ-=;QtKFw=k ze_Wp-P~*wfpq4c_8OrvLFR*#;i8W7~HM`nti5C74dv>6OTKeqF@`W-n`NIV_cMhx1 z>D4jsL(4E_(`fr4?SPa~ANictHl4A=@{ zYm}>gCc}orp>BV^({HY$3qB@dOie9JQvy*!DMj7>`DUlppg5`d7Tn}g{X?a<^w|{E zOk<6n&)IB{;!cDx@EuVTeTGsl=L0z+dP6Hj`aw^BH914nDz6kDNi&GfWf+o2qC-^5 z+L_avq#nCuvC?*b!5zC3IAtMSJzaOYyGkx~Z_#MFOYeil{Hn-thkew?{%z360}#7w ziRMeg*3WG}di4U&EltKaA6Px>zC~)J%)3bHM0lZaW$mmkwQfOtkR~|ZkYATwFKuO% zvH$iLWQ5!WYu}h(TStxc8S^d2^LRn*h~~*YD8H`jnZi@A%&o4~EWzD6h=*eB2WM+{ zS}lwjRk?L&cxnD}Ea~fF|D}GjMOm&kt#oeXoaz9n-<;?Ixw@A0mv0^<=pJ9{%_7;O z52*%i2?;61c^=Bp;YF?|A*Y^ul9b8A2u5L4@AkkD)QhprKGEIuiulnoy`JisT0L*V5{*0!-6>#2bh&0MtibNTbryDB? zRppr&1gLO!`UZCN(NvJEmn2)ez1r&Z25KDdOsCUI8toIU#s=j(mVA{-5uo+_`U|SN zKKQ@|QBuPQRoWXFhRc2J;0?`TK`6)+*g3_v+lZaM3CfnM+gLPGh0P;48aRJI5U+dl4jlIa&m5awxaOddu_rh3gd#@hFc#d|qwy7QnGO(lvK++8T6bT)P>AIw6 zVy3mBBnl>!?}r!t#7cuh1=4_eAPzhfHaMyVqBuAp5xG~>NvViMBzXufrie~cSqsE- z3v7ZyM4ir7%A&39v(I$*#ril<;!?IQ3RMCs(iXI+=)X4eU|V3i5a@DIMKPw9j$FIl zXm+P&XQz67xH0ibqt)uHHb~3)A5NiqUVT>?UYER`mv^@#x7k4S?W@&o*|NDJSfLpiATNPI#OKJPXg*kjumxiXUYQ^*jDU4lWh1$ zIlJnq{1GMg;HohLk%-8~7JSe(W^Mc5d*9CdQ`>*|z@gf~gA?}H&DU*kK+;%`PBEw) zUOVN2^c>)4%9t>rf#A@knI-)#I;YAKQPYoRqO0y5voL%SpYP%9*PxV1MwP=p zX-Vp0zXa@$^{{JTy|y4vHy zOt;uo(-B|ZJs=;)cdK#j|utpDWv`d){W z3VfPY9eTH{j$8JW-1_-YBv_9;S?ma+?|H$iXaWd+Y6t?wgk{$+c!iLU|EKfoV?ceH zSy6#^Ou7M24Xp09nwrg#KU(5QRZTw=K+tQp2fYrd>I%-@8WkS*-5a!jT3~H>4N~Hp z)l1!e=^gy^{Q85H<|->VE)oKLqR&9Gkba>6fgqUT*B2=8i@Ei^(Jh7vor@5UK(J7rmqVZ)xbz4qG!kQPq7-d^ zet7*VR~L5wOS#pZV5WW!!93(bAa?gsmf{O?>)Wax@c`mEK;Xd_7Ge+%AsT_C5nhx- za4yLWcd0@a3D(++^B9~#E`jBvX&_}Dyd<|i4Q*lLQ33#BIZ;~wrTO($>3OOP0_w2J z^e|t0OR{K^F&i~~juFn*4^!ZD^kyixPS52+VxtCd2^1Hn zeyd>LQZ34>1M53AA8LKd-&mOLb{jkBx``AllvygjO{vom^wDebsGREEkU0>03#BZu z*A}fGs)Efu-D8nRmf7oaNESQYLC$k{*pjx6=;L8%;iEkCxuR>Qy^`>ugSEoe3qa8RB z-TBkz1WNX#knZE#^EgIHIE>~*sgLzL@)*oCQb{yM^P*rWy)%!%%@xkO(V{6JX@+;@ z*Jp9<;FB$SeMvLCJHNg^U(wpi&a*xC3?vM{>|r=gZAZtz2U1Uz_k=J6PeOI_V4qZ( zBP`Vzyf;H=Q=NJz92DJc7UCqEQ7Hersxo z7nLAl=#$m1+sdZ-nmR{5Cq# zRZp2xGD?!uzm`Lg*V$#Wb`J=(m>deI50wEs;PgOQH;&JZA{%ID>M`$6LpnnRj@`&$kwQWp7$!ScM)xXuT{ z!9{bR)H~s~bNHCzSH_t^$G%)Cwt`pGJWlj?a@YdbLy^w;x&SYh=cPC1@8-~j9@?(( zh+SXetPkhb_jnx-EOU0w2LgGbIjc5l!}zXj>*Dl2lHu!ORG%52qoJsm;LM=eLNwhl z8CuDxuP!!vH=$LC*C0(N*}*Kxtb{r;&gUm5sVbnv-L37nD#_i!6R?BdE42gFnkzL5 zRd%z!5y;GOsJlCzbr^A1z*4KTXSIlv=~t(^k~a=MO{cR-02=kXb_ctbZh~ zp4)h|T#)*(e=N6l51GTOo9JhRLys%Zij}8Qw#^@BxX5lXe!?54y&-=2GNfqgNVO^- zcTw!F3@I9s$p`UihNY#OU)Wl6a7llG${ADv1GP?q_%7#zko^0f z;YijIYMNpXCR<}MRs^5TAzrx(u3rCBt@80-Gc&LxySA2AO*OMw@y<=n69RJ-Hfk<;`4B zpB?XZ(bKaWEhV4bpJsST{O37QX@zp@OBL;ZmaR{tCd?Cp)eNIGCtXYB7}iROKh~o8 zN}81Y=L`#itjC&ob!L`|_W7g@?Z^X^JtcWjc{C=e!^c+{CW`#Re(XP^kd^-z;_4&@%B?L~(|?(*Z5rZe%U-RjUzQ2JqAi%IG8B-s^j|R)33Y!6#(&lD z`4X0^M2El55Fj(inDaR&AX0Z#e;x)RniC}d?B9e@Fr!K3j+Vzzz>TCe|2B-laotse zqBdc};jV-|f%UT3@0>TFQ!KW0)QE|~Wgss@0{I$AV8WW5r36m{*A3aSlyi2G4_zs`r!h$<#LfHX-eC_XJ~e|$@1V&v{ViAFRGwojVt3= z>PV;aA1NL<`nwJ41*Io}r9N_#hEs++tl;;oA*%2E&usn04vq)wJCfZvO-k9Pk}UnN zbp7*o|KN`-VP5$P+|uS~8$hD}f9Kb)oc@v@@qYsA=jR+4%{wL@_}>h}*mywdUh#j~ z`sn!Hq-QVLf;11@ORb2#d1y;u`pvk~`z$#BnmgX&nj=aO)A$p$D!=yDXQJYTxpxvz zs4sCUQf~j(XndVcFVS9idL=<#>c&5~7s?Dv6L;}+OGQS>Zt#*P{%d1UsbC)|#Hp#0 z9-oPDM0F9z_;qnJ+{6{uieEV)Kw*b zG7vF17nZY^ZGdKjIwfZ@+1ftZq#r5gLz8Bye{+WGDB8GnlSz$^`IA{D=LF)9?ih|( zKCRWm5@r+U^DRD>Gi@bKU`i`cXH-Wt77?AA^0KMOuta*pSzUw$nd6Xo!_v+R0+0y#YP|^vwHgWcq}U zT7hQ%@j_xpqn2_=zAZxyu=;-Je}hl5>x5=e!!A-Z57ffE`!Mjpx4WoF?dkiJBV&+? zW8ql)gm*cXIzZk!kJj9<>GlSR8z=F==x-(mkP|xEHH$uZsfOS?9Mn|Nm7Y6|AWtPJ zePS>xE~r?V7QFI~g0A10Lr7R0zSQ62c#L(k^U1SU!Q0RC3JoLUI!xyd97TF z2)k?58h_{O;Vj5?aKrb7QIA2jM$=lDYW)2QHI0FQwM`-Jv*<^B8gon3OIziDr`?Lx z4W#e!8Wppz^Bm0*uV5*j{{tyvf})^m6@4=E4j zHWqgm<~#!A-A1$BpKG)m8%ej0k}S`$5xw?)``Gl}anxP@2eY-Q#u7CP~_p)hoEjt3(4%%v>DJER!YXo6&8ez3KYAcrNrUl*GF%^A#4xC!Qq z2PB#r68GJaBWd>Z69|c)gBDFnBm1)K>e7tYYqQk{>+6uevlnJh9$&~US|#o8l&(pl ztlMd6W(VarF?rPwqLtmf6sx#%ibTzHB85a8;70sP`f8;0?~<;6E&=f|YYgklmmr?^ z3i5ub)7@R!`Zo`dNkm;Msz*?u*{D9?ZYcsK_6kmbxhv!MZ`~=L3?x6@-LuvE$u=Fm zcB3rpY^`*t*N_`Tjb2;IDZ59ywxerOH!|Ig=Ek5i=$Wjtl2{}u@S_QVSDjlj z6`&cwF?V7qP)p4n*EjN0puoM;wY7hk{0gD;0*YwpCjP2CmR##r~?0P8po+%kHLy?T(S~73HfV1dU z*QC|enU-7oP(dx{F|1uLSlbK1Q%d6ODdpSB`grk>d@P?Gv~C$s${hlx$rRIkXH6BA z;6E)@|Au7{mjM_&rWPC92^m992p|@R^n%)#;#gCL61tgn9@S~?^dW@;va6jms5jR1$tO7-Sf4tMt5ZX^ zc;<}shq^f)N&mxYCcYI|I!AEgGaR?B!HF#`j+QvJeU|xM4tyl=9A$Xaqy9vv3sbeZ zPUEJ`oUy!xtISx&pvuZsKemIaDcu4_2Qn;k!kD#O%UB}z7Vj^h?Cr6WV{9j-hc z=I-Rx*vSdDlZDMj7mpFV>EOnt2li4Md|;k43)8a&+c7^N?TCE~-xU9avko=PjK--+ zwB5*_J-gula72;%9Gzr4&$bzS)v5Srr)4UfO%OTvLQ7wMxFbWxp2c#%q(4uYePBWy z^9P;+2s{v)tJ*`zh#0%Jl=DBEtxdkz7EP&x?!3Oty^}iqUuV}FxSK^(e}+PEE!ez$oUYK}HM%QhTKUn$;j+F5bsLekvWL>!A&F1@Z)n>DA` z-CBVas@;4l_k$c0>OG`g7%)z1Q|0Ou3ts0m_iCPF7<5;9rco~mCKFZz4a&U-r5w03 zX8mml#kAj}7T(Iqr*zB$I{oPMh`|aBGu}!%mJd=P?QtH>ygbXWD0!^YG}OvQK2r`J zd82_=siSjA4^at9{jIahA1y(p-k1-}p_H?k(=Lfyc3ajdgCP(9a!CbNN@eiLQy zR!3=}4)?~P7!mo?Iwzy-XfUJYZmkx!Tpt9Ecu+CUlo<_&=4e^HZCabO&H|Tr2SEB@ zjx^;#>D1G2%AdM=diwlrU326sW2_rbZz0?4Awzc&;0s|RV4ecl(elfR4bNt{4`}~T zi&`aVD5I&>QY{aUL7=HN6ibw~(5HkQD$KcImJu)(iKLAmmLlmj`)Gop8SU)QRDG&Za5#U8%&Lm&yd)cDc{FTeLMC~|A8wJ+6!2tihDvnK;oQ_|9iceuJpN%v z7R~cg{HLK;Z^IXqwDEr|u9`_H@dw+^EBJ_BTk5Q~V%Bc1VpXpm6r4KG9$V4tNYqnc3T zkfduH1_`-Plqm1U|E`>GxTgyML&_hfLo2ri)5Bvgi6U zoy!>jRTN1c@smgAmRFbi)WLu^CWw|rQm2GR1o50qI$InNq@e&Pmj9$m#hWrbgVp{r zNHDvF3h^7K$(V6M*0|iW7()S753g#P8&pQ}NxGtCaP^Vltvo}@4+FX=|3i&m$LRBs zJY-wmx~XQi{-CUkuau6Ka&y}e7&wSnzSwUKe9y|f6Yfo?7(+?xM9L7^7FR8^S1D8A zX1=PHTPxGB&9+ur+J;7Ja}ujsQkCI&sef2EfI_|TmC!7LQ01BTID#9vSqd%5i64FA zBu?w|Rd1|vRmpELu&W}taJ1Fv^|JVA>V`hkPJ2HXfjqaOoUo@mHk1l=TP}Y_%1*t= zF?KK=Mm_FSb}Yz5eQ;HZ0loI%H&!h*N}bZ70?+FVtHy9ftEfMIIiM^T9srC;9_U7i&8~&aL}RiERUsR^jyl+c%p>Cr0bQ7-Ak)KNm^ZVM#;U`Chhfbb>}6} z|0mh%NJ}wkLyH|%HC5oV(Uy??Uh1)+TxKlOPI+tyJElFL^sLuOdlZ#Kl=>-#fsSbSS!*WTyHmE}!o_|L*ngT~S036$-CN_#y3Xd*Hk8i$ zF{y9H(W^X*2*{y7kn)9b(>Rv8l7xRMLugqL^`~gqxeEI;bA~nZg6q z;96OsQ;5y*Po~4XnnEXu3ra_ct52V-_mV-)s}MQD(yp>3FKw!hG>y zAbo*Gs@nZB4XNc?+olwC#)YYiHfqmBo}oVNdGl-CHK{Bs_JS zdPyMy{oZ%T9)0FgBZspA(rtpVk7SW{dtzf_J34ha1BO1nPo z`cwlh<*Z$zvMx&1m!$^1M|1L=zRIqQqE5V}(e#*J)oz7I71Wn(yjNQc@bhicYy{=D zt)BNJI=seWIF~fmcIF2C#)_J7%S;21L8ZDlY!EAjq}h3|Ex;gX)DWcTJ9)Sur+Oaz z@j8o%d;m#8ZRd_|Vq!eHff1kJOR8%N$Y8DDTAe!o%&L%)>f%y&hcU!b?67sPc-%V& zbg$2&gS$+13ELgGXxd30j5la>)ai)c#z<6A#;_g*kxQ%PWxugBl3sFe%uvjCx?7mB zz*Re?jZtoMmeI>pY&10vTa(c{t1~#OH)S|d4HwKpO^HNrZ0fYHtuj09tPDQM)A?qD zj|Nt%xc@x5!YJn}iU-UAP9q%}2e*u*XvbuYKCc2TND}!i9LLTo@<}ofI62KdDh`37 z0Y;2y4ECwwoz($gcq_wjI%#k8H>Xg8H@P1I=zqbch_NmW^AAM7#*VQ}t>Xgfb8@teKZFUApzi)=~=%gmlUL_^~ zS@zO*ZZqx<67tI+mEQjEWJpnqJJ3+Ko83k$z2jh2N|KwN=EXMA^pbScyEKYkvpwi_ zs4YrRtfaF<88XcY7k!w#kO}n@2l`=0= z<1bZ5y@#){ojN%Tu+-}*gNYCE-Iu#X~?lXC`%-@hP#e=a8 zE_K)W)ogXtVkHYSRJ%OArAb&H$X2YZ%ZBvg`Zcy5{5(>XWT`Wj>KH!gVoFTS< zN=e?5vZ_B+uxT=0&f4v)XiS0z_7YaZrqHdgmY-BfR0nyd|iqS8$V(>3QKhvzs*;K8rw$1;q+h&-I~j4DM>>84quf= zp-SCWiIaYpt(cz!P?Ej{ug1IJL_eIZOC4b0h(|HC$-stXS!%x$g+G$5+w80i)|YW# zb4MDj^CaH=y>vxA@nCih)7x@O#@W#}bY zY@c+{*(P)bTq&=Dbc;U4S2bA{WHx0)aRLF+93s^oe|qTJX$CS+uON6-xPPk5Nl2CK zpBaiP%STaxTZe1PppvHLe0Bs>seg=U#FJ&C&rOQseJ+5cI*yTirJo z9Yo0PfRlxs#{8}@pSHTQ6L57i0!80jsow1?BjIs`8F^?KoM;X%h;ymQv*sH zr&cs{1VZc$l(Mq^YA6!Ae_2M>#PU23-$zqL^1S@@Ff1(fI+g}f2KwJf>t;Od7{RgY zOBH5+JKFj|)kWN4s^7B#MoMO8XI5XHpJW#<`pl$$m4844Qp-gqbF!=GBpcba`!-4<5#)H2Uy(LfIf0x4Rz>21$Bq#o76kO&#Q%+i;f9&n_ zKQ1?}<23)*L2>-@Hs-CUQBCgSDlIxP)%q$)lBB4T@Ng~AoN8!BCEc>}C~p46O1D}q zY>hk=sQ=GFeF9~AUQ1skZCuc*b#CG9r*~Kx-6Z+BuetUX*Bnvfim2E~kDVmi6^O)W zF00kUecjhUsjnHjc1jYAoxy3Qe}!~$T)OV3Xv^VTjp);z4D;kRFn{fsFsJfTKVHh= z7kZq0U4{4^kkOoR*fNHffqVi~zQyP$^Q_>&iamQN(s0Y6s64hT6={$nF<(Cd5<5b+ zT08e}fv@m-oZ2_YsI*hgxwi^i_9%zXsg#_vhKLklT56zKWjUEqFPP!;F7VU0JE*9+Z7bXgRQ31Ie>A;9 z?PsZlpl^^B8ZkqDGpi%GFXa)dFpNKhWXU z4jr79Y+QtbRnq2f;MuIzL#z&#BHiENU`acY6qu{AD@*m6-#KjMbYe)g8dvIA_gx|; z-BrrCz_~=RHYO^|x%Od47A^H8l;16*ERI2IoFHdaDEAjwFO;45J$aNXlHCU{3h)T;l+S%UG0#{3B-G5vcMdoF7E`X{kyUJzr)zYFacUMowu6`i6D_cLC zF&D(O?Xo;E0ZCBTHCTojFdLc1eNgS5ra{_phL@$y&tESFh-2liWU1W7-IsRj5SyYc zilTbD9fV^`@r+bY{exi?lsKA0F;OdrL#iwMp$HtBCoKo4b|R*bn3E~?tPT~_yBq@# z{%|b1><#FUEEZEOZF1w@i)R4Sk7Ssp+l|&vAFcXZsXGvZw+x5lwxgH284^bU5&MC0 zGOf^*>1C85PbiDM`4>Tj+i@!F%p_GpOP zlbcj?fKah`$xemRoBIxhC<+^aVIW7QL&(HaT?Du&iq`VdoBNIoA$n=0R(hrh(-Y=4 zGQGN)K^%Q@*1_VP+##IYwKAgIdzZTrV{z0Arj>pQIPS!7*s(rGC)ZT(mTb%`dbwlz zB50&KjhX?g*%%;mM=rt$f}~5oFP#cRU&E_oTdSc;n`+bq2jaqhufs~w$fWsxcgc{==*$Ks2F|p# z`!_j>`xPp|ac^MZfI`Pijpw~^9OYx6=v^6Z>JxOL(>;FkKt=Z~VEmmvL5k#2>dU2N z@vfOeX1t;-oRk_Wek<3x!C+u z!?{ii=~x=o(zaBtE~9?CcZ;Xd*_q`FW7(u4UkQ0QS+h@Xcb|9pnrp7PdtjfJx4Y>1 zkJ`C0a>H4g+*ds}U**3k#9q;|qn^4vT)O`i`0F0Kp*wNdSO{k> zC^Q7WjCr!e(5{?DsHIOFmv*lL?S7Qe4n5;6m%16<%FrCz{aV?`jt5w&LF+Rnqr~>& zBq0bhbOtNhRv3B>s%o8U+(ezK^6rILnN%-`W_<|zzULL&H<=EoiEivn`DWUl&}3gY z?({j(Dnv*!#rDdskkX zv2(%@G8|xJh~|NM-KOliboeOfP~&uHubrWS@nmBQts99Gc6oXH`=P0)3_RxRq-cW@ zq3N=MB=u`wOj9m?g|jeMJcZJxKrV&83JTR-3N5u)id-4?WhP4&hd9_jTpwnPh4zsc zfx|d1P1Mj2bO3DgqCe%;t<20jj0^@r^M&FTI8Vq*t$kTAv`x$Py zT5p=NUWt>6{`zs1=iLAD0?4{MI&U>Vr6en40UH=sLp@g27 zFy7a}c%j*a>3>BqPTIz5E{AqO$NMpi=&H71hNywu7T}l{B;uV-vUFT{7VSI(L)np+sijrK!ypSIiu_7H_n6 zAH0KLpmGRfyICxg<0+rqR8CQRgsWRHr;_ON}+3 zUFdgpHV!+2Xg-qawq`s;(`#$zlJ&j8C1tE0Ww_MXx}zOCf52rBN5?Tn$$O+Ij>Vol zNKJ(;rDx*lU!JlFeWznO7KwVkUI}?W8TPZ>j5~6S(_zq~4*qj}nqLn84tx!&+EqER z>1#&gn+F2>vUkrBEYROxaLJ=NtTq|83NK;@$Jq`#?KY*DwKwz?mGv$%wkB#iggnw= zl-9da9nT487rlO(N|R;PfEkL?ZOU}mk-8L~bWrs>129C;=1O^An}zxc>34;)1~TT2 zK6|N-=2SF3VquQ1oV_Y#;mzi;Wli5w!`{BVSSfcjh0?&N7mueY8P|nSWP=A?QZNv) zT1m8v$7JKbiJvvXpZD%u(HxKCf1rj2Ue&8$?%ai+-9+CXD!HI|~adRXSQu#>smPKx&6*qzL|i*k^hZIf-49GN`LC>FaUQ|7C{?!t?{ zF;bq^nLIvy=Aa%1$eReG{u1?qz!f@j?b$5eW^Z)4Ijd4N@L31p#pE*GrOjlmo8Z1-8kkJ^rUCCn_PzuLZxM~SjPTCp$e$#8uBGq?p09xt^_PZqcm{$E0?$$~C5_d@pKLws6Y?srw!|7gxq<(Ph)5<0_r z^*qlbDL9pr?R*P&PAC<#;IV4vp2o+=E!e7fbre>Ol`vB7=tDi~pYE*pF~l~PMKbJC zdoPMqFB<0%yPIw@o?b74dj3Kzb5D&sbL2oOR?J6*{`pbJ9xYEN3o>~2gXyJQZ3L?H8)~v32jho9 z--lmu`i`H`_m{myKZZDU{HPXf-fl~*h zd-qqtn=2l*dPlpRUT@J%E+-#Ds>BfB)$ipkOxn!qe~A__BJy9HM~*$l2KBt})usi0 zB8z_F8@C(HE~cGvMx8fC$fPNHE%X2hnGKsqqSyjUpSNE(XgvmvokxE z^c$;i1LdMQ6m(#(58t23K2nL9iqV-7k>^9^@QAg%Y6opw7?=Nk4trT+dud*5t|e=x zoYvpbFw@JsR?6ndg3+J`zi9dGDUY@D6pG!Y)F~rjyX-dB1Nq6rx98>Y>r@M7QPPo; zV@f?V=`V_%`enc`VL)ol#-1jiKaep`O}(`a)3;4#uJQJDVVD zMXi38h&-D=)cF0R0VJ1qsj_Y(+mG=KHmKs?=Uf2gK1R~hT7mkBJ3j5p~d7pDDY%{i1#Ha`52rRah=rcQH#f|4{r!G`YwwLzW0(mjBcHX4Q zj8~W^U~xenD-|TK>LVm9pDS~Xjnok7y?>!dJx`@k(;#wRINLbW-He$u9L z*Rkl(CAfL?uP-HnV|iq<7SfQ za$E0M_ANQ@81sfr=e=Qp?ncTUGfp~dv9h(&+c!0JO0bv3pP*(jFrBlIWi;4=lU~SCVP|RpeA>#21 zxh%a=@t;}03i?~rgq-=lw3Ed~djnzZ>JKAULQ5HlgAsPnyGqW%nvJfd-^R_p=>zBh3~&}Z!*?*A>kz+-bg-Ly zAD)9K-!-d>4kB`V@U@(t)e) z9-oChJ}SRQrP0f7(ke#kt2_Q3)(dI|zk(e;n(eUJLSv%5?_!wY=EWghd(8XL+AY2> z@_f?mC#r}xP&BgSl?ROI7Afh;3*X=OfM$=WhGr&Qf+7g=QKo53Wa1W4t&)P8cqx!J zvvzODD?y7N^Jy_kWx@0`Dhn`TE|?Om#K15|ElgycUL5i@nNL7e(6YqaN2(ZqtiSJq z=eFLBei5lZ>l;Az)VnN>DsquU%e}*44u3OF^~d?{y1hPJWN4>6g9l#HGpW3K%o~4; zfss!Swzkb1jeY|C?JkoXr#>-M>=SCq%u`j&Z-6%rCx-_Qiw^1Etv}&U?^5TRHhXmW z`W2wS;}`|f!AtS7k<-USV~9R=Dt^iaItjXMc$hl&+$GQG^B=N>lB__@efud<1??Zf3C`lhjDyltvbFxrm~{3*TU6XGz0jj7D*7YZNf1$Zg##(%%g9K4Ya!EQ!Li{MA(<|Hemhf4C{4` zRtHJ0N=5{pRVwbJ%_n0mI@#0On#$7{Uvb`?RGpvp1tVHu<96j#rl_@?U@|CD?iEqD zB|ol(@e$DAsbLzZL&(D5^b)Yq2imMLGjosUs4j1#P-8E8`=xL?)LyGU=^@+ZEezweQ#!Q9J>#e1jGxZ-4Xs)9f?}a( zr9`Q1(ex6DV-q4vFYt(tfvR3mQm2$>jJ7vVOu4=j`p`HDP*;P>E+yx(XBFnqRz?P= za3}th_l4!$V{ppPbSaUnnM1VkLF)_iMYc!_j}rM@E_PL;cT+Fu)_W@$C(m+sqbxmZ zo@DhE%7HVA*jD>+tONBGYjw!MPV=fx4U_a9Q3YmnnJy2~Yvwr&mnsS=l2p!ZPVX-4KQG^l3=qnvaWqXZ zLqNa0ncmObS+Vw5ZGYGvdps!rv%A_M{5&N`oCu8FxX?oZ%@!fPcXtr56EG#_j>D6m8<5CUiMFiN_o#UdHy=s@B zx(i~^N*8i$GN*4%0IAgLz9x9yUcjkfQXp5aMQ! zx`k<=)xq9{)C$`!Ymizm%Hie>KB@y9EwcpfcySK%Y`fQQv?;^!N+J%|8$AqEen}3o zb^~8Qv_c>9m*&vojn&5Joa%{ODKEb)kDI~?C%S0veq}ksn%b@xXBnLF@*HO4E~}0; zc0uECCCdizz9Nrz8`kdC!5g)3CyiC$eq|0f^?0tFHR5Sq3)A|c!2Bv7v+8>Z?*@jR zHo(}bz(oa0xPFv1qSJUS8g1%^_1=s}f%erdS_FGh=9}t21X5JDEgjG^GkVj26nkJq zW0HFEy~f2<JkSbuZX*hz-tEYe(_ z5qjmT+mxRYP(u3Dg#7%sRKMfl8;Iw(f@#K-pa0fMci^y^;=S}H`IkI?7X@8Co`+q$ zt@JJ!+4-mjx=ToDk%m>&*^jDR20i1bTsH2Zuxn-Z2s`KDM;I$7rCC4k9Bm)AL%51L?cPqU)okLa`<{I;DEh7!{~CjKTC^s(E* zuHIAau7+Q~c=VHDuI~W-dn-eq>N}z}zZ>PX-M;WVo*x7L_m#rWh|MIR0$-@fpSyy$0S}9{_I04 zFra^S&rMPXpbxnib#Dq?OQKr5r_G4iiIIR=^{G{I^8psNK4#+Rar2JzDzJi;@4fj6Bzps~o#jiTrnD9*sYcu@736smyo|58ua4Q2e7J>IGf2ToJkB zaTjy=9}dOcxq^DW822B^xVM|#Myn!n$4iugUWEa5-rD0m^W+cBgo?*yB^rHZ|~0MMZZPmX1Hg17VHuZ0!3J-oA^|NK~}i z>?A!Nzj}aNNkoj?`~;_%0rZ)*^Oa}YYeZ&>^|Pam-=p9^_@uu_eH=Y^uZ+hmEAD2u zUC73HY6ur>zOAMMlv{Yd85GIX3g$jpAJbAZuzQ|K`L z>D})!3*|4**QvJ(gwe3=;RKyDkZ4|%W_W(a-^=k{FYw@g z6#My-x1XbvYbt^ASZ8%WgJhA7KYd(PDC(Tl*}nQ1fbkJ-IWdf>V8WkD=dG?Zdd<~k zWKB~!)$#>Ye64Oay33SBjhVK~7j#M~R?1RX@kJ=OedX+}dicc2%kX8FLh6K-nWrNg z*7#3b-=%>Y6lf8d)DP%BG{%5VzncCUCaj}Rw^opgd=A*Z;$l~!7z!?d`wU(u$1eS{ zI#}1XU=U;Vi4=|bQy<~n3QPCLSnXLixaE9Y&PEgB(tpNL^;b6+Lj6J&@2B~Rj_HW% zJHdGNLY?~x{95@dS{Cfv6=-1x?2Tr#)2CYW-SQF+R&C^jl2m-=-!B92zO?u86 z{<^hIR^RUFDOm#l5JtzV$;XAN;MQIbn*C!DDpoN^mv%&G(b8J#BlJ%}Y+hCPh`?fP zx-?1MQR)OJ^{+upPKoZg&d;W3c&KK8 z>fgesl=>e1@M1YCZPV=`nggPL4gG#wJPk{|#fKXN@sn$99r7ox(dw>Z$jC#i+8@0V4vdQgU5$ zr(4)Q_ufMNs9(ptMRx#G{Vz#=5c^)+F1$;O!NKuJ%3Z4i_3SB);ZqUm&4xQ|~K z#+2))F(QLSW0UUVEyCDrkbaC5lXT)O!>D$)Ryyd>Jvyk^yC`A$`XHvoO=_w)PE57* z5$SSbNwIZ^6d@lAaHt9{e7({#f zKkLDW=Y{=Nb-TRq1mO4rtKZvz!tZ36sjsBnk`WVGI@er@jV>Tembq@?&oCWg zExq3^%_|Iv$q6TX;_7N)Ixo!`C*ky z$Q1fcw)$q0^yiX(BY0O>(2L3RT+AyVEI7MCBczN)J%!Zh6XaN!g)Prs*q3UT#hq=u z@aMq>8Pif^oaqe}u+KgXv2@h&#?rSY@b!0j^o2smOQH%CjiTY+Ynwx1$@OB~=kdqXt2uA{mztM!>^A6>*`m))3{h<8zaGK&=(*@C64P*JjmW}&5D zP(7cSAi;0gH+5*-+Fr=4{yvXdOKm>$kdw->J@SUBL!k8M(y1+6dKPTgF+-yz;zZc$ zz7ZVp{mve>VU}hqFx3K0QnU+izh&;Tl!NmFF-Y_2b&TxTNtO!7*qnWfdCvFxaB@_+ zzsc~MZW||Y8-ITYGxw3yw=(D_aD!fKs?i5LnS>pWhcPFLs>&s)hwXf-bNj(O{uy{@ ztP_$Y!CVR4Hpt@IT$HQ$Lm}MlwdwZGSfEE+t2LoB&ku*OpFx{cOtiaN@Y`UnOZzrx z|07}iOYIdXhOxkpmYu?ly=@phO@hUI>E6|WU&zwET>yUvgH$n+M!##T6*jxzeCvvH zIj0BCxqSfppown5>Febx*@Hm;JLLCpbhELwj>~(M(7#w)-7$c@)#+h^Tc_I|(+1-2 zZn5OKHh{l4v6N}7oINl;T^pu8_4d1yfWF#G8&C7Fg+Mc%?i@f(uJhxUZ8wc^uzmav zin-=41v^-{lx*vw1;mN2T1)4yA@sZir&{Ro@?PkHbGJP9V{ne4%;2iv89~b5J%GGP z);#GddT?EFs)w~Kb$hx;0DHHwJ|0$&=NG{;{?P#P!FJ2k^N)pTc7eZG(%dtT--L)7 z-SIq3qUB^kg&g%ey;lJJ<-X39zDl@Wu0W z?E%65J(Ne!M)Fo|M8?xS{OTjW@Zk`0%%i$0bdg{M+$Vs$v*u4Ly2AV}Xr%k*@v9Cl zs=L-$7(SY71nS?f0KMCl>q=8UEZuJg*WbSYziB8n9_R2bBu;T$vH-ox)ftPBk49fFzN@E#=v%F^0FUPyq3g+P0C{t3yEQJ+ z9!+(@w!E$Ydvkr~M5{3#=XN~)@QaG#Xx9huZ*L}DIJa91{l?bzSlB0a2T~33>Cg^Z z&Ax6vg2p~78IeU@z5J$9IcCCroGZXJmd9$P_i;XeoLB)FxK{-?)C8)ZDZoA!;FVJS zYzTQ1P0X&EC_%CB^nn5FJ6l^x|7wXZ3DLcCdF+du#s_)Tye$O$3jzFH%tBem7^5+< z&Mx@A*q6E(!ar!Ei}(KW{cNv;yTzZ7d-T) zwWe2?>X%+HE1*;#t(q{2`v!)LtXSpcC0h_4uYS@+e=mP3y(({D&^!Z0S5oO;rHo4R zi_&6;-LxT(w!rKK_RiuA&f-S47ozj*?k%4U*BHmUs%&7LEG_V50Yq)jc$bsWe0~dY6Y6bN@j|94hFm#miuMbL9`L$`#OnXCpC8#1< z3t5ku$CcrD$T~kgIv>GK9$K&y<3FEm<68Fg>>R_5s_y^#Qj`@YKR=A)!CNb#f>4-w zU26U}T1^=Wmu(x+In`6D4Ay!roJH{=&~Dkon!XEzVl3-G&BR$g%a!B-eFHUEz#qLz zd#dszB?Jz$BF>I@Ne6M7nlOGz^VLohj3}}9wkAwDd3X^`geu5UH>$o*-llrb8qrm% zFL^B;lcF(h(U?B9sM>mj5l$%*%6HZU)HEVzwO>Q8T1KE~ZBU;G*ZjU66k78! z&FlmufH*c6vje~(X?u2zH>fLZ00GeM&!OQ+dqeqbAQ7@bD^pXGibv#|ZaW*;7MEqH zP}?7-!cTw-iBAQJo(1W^>Q(2J3e+3aHSZJiwfWJ8<0zKXi+!h;N=L~I9RY$UD9-0V8r``>U zJ%Uk;jJv?SqTSd5CX9_@%}2VEdZ@UeM97%T_Xi--^Eq?ec#P(q+WxRb{bW$$CXW)4 zM&t19spqVFJ+zqBZ(nXQ#-6Zb6}Lv;*APRo^cK097=L#A!uo#)XSc=oNWOU!%rT;n zajh@y*hkbIC$If$`u&nD^y&A46Sd}G>MnjKX-j8UroS0GX~*p(-vhzfkfXSZt}(X( z_KX7In8jN_fsRjs)LHH;Hg%R~^-wNXho#OBQvK0KUJoC+F=jb|v$k>3H?}~pKNJ+$ zW)#qN)+v;dk!02`JL4ZwJM$EkXU-UFm#9$6Yf>+m;P+Xey*YEW6_8OxV z!Buuw_B5NM>2&diSoA*YVR@WjpS^6Ki;eaM=B1^+BpQgQ-s+YQ(DN3oK!~#~sMF3k zBmLpr;l#^cA(yc~1a+td=w71_TXK{WI2eL{2X7y=mq~@kULy}%UR29=QAUoM$cw7& zX*JN}`MenUr6I^Kc3J}p3S522FAqU}c^jeXs}lLnFyyw;KqUeunpUKHun)*?k>m(WHV8q4XQn zOM`uNdT<%@eb9VD#d~TzXcf!92d$y%MfIq|{u5ku9Ua$8)nDp`S_E`*%CN^+G?k?M zgC`c^FzzFY=+VPOJ^7P<6?oECq#j)+K$j)TAr z{PR>lE-lZKt+J$ABiWpQTHy70eLe}GT5rl>n+E0T)wg0-PY&z~F^l}hhP}I|-F>rsl{&jt}3ft*B8hE>cTb-KD`xL4&94F%9-=&j&0sd$;LFvFD%Sd!|t~ zbc;z0+QBv?0ZYsfsRd1M7s=7Gur?9)y50b4J+%<=D0r2}j_n0#OUNiG2jgcL&dzqH)!EoV3dUs`*vi^U zFHGYS;-sDLC^av*cEBn$_jWxKw0j!El*{n;oz}yo%*@iF{T)`K=5EtihQIdm#ad{2bz8O?+f!MN16?Dyc=KPJ$DpTH z((8HOt0N)!iL7-rzHz(J?BZEY_tWOF%x7s2s(p09ubZURhyAWS0qEi7LDXmqVj}Dh zJCB?2Onz1!v?s>wSwPOY68MWvEnwfmBa-S`n_oEV{OoMX)arZA*+`(V*L6{L z!2T+bp6-wBKC(AiPz6_mEc@A^^c$)k_ME)?v$4`r0cjd5bNuE34NyXA#8C__HY8bT z^qQ;7J)dC25icO6PgO+)elM2F#6or@qd7vV zSbFa0J3BkGd|?Q*Xl+_6))2qIkX=n0;!uHZodTcyBEyW-XBF)otTb13P5`M>RDSC; zhAiB>JU>w@!MFP@3B6a~U4Z9#98b63R8PR|)13}HON4|e_h_z?GS{DRg-qsn{(cC29rrHV+2{Ul@m;y%loOyby4->?p|V zTms$~h42EEr4xAH8j)xF-6@!mjQqLOPIft?Z%Iw)c=+NWD0w3zhEJvA9_?)Jpi>+a z4!FnB!A=Gy1IHzr2>Tl72FE-hRX%vXD{yr`cTb zz%O8*rmx(wO&x!gziUh#*E?8FE|axVuyyrm06hEIu@N9vmZwRX0qf}1>Ha(2-gK|m z+-SpAQ*PJ>3aa6oNv+mWG0}8rsfXb>ZqREu22yQ#1PE8)Nm(OxM$+C5M;$*kEh9zf?>qjGfkvpcN*F8}1TD&_> z;w@}l?uQO99m}T@54_c`%zHmz_i%6H{mgPBQ&{XmN7LYNfn+%i8obRSIhUYCOjAid z$H|VP&@+{j*?zKhx`Vz*&=lr_A*JJ_YO3ql&lD{IXUBRw} zm4DS=nRz@aSlO0!d7O1O(E9`a%8H#$;M`}fXr4YGto&=al_{N|vX!lS#@jR9gGB0+F28TmDy>S90q48%`C0_+s?}88f(pgj@+qEMx|F4B>3+*D=)zi z@ry%Yl9jLlCBM(_=GQJL@hpcSO>Xn$vsSuVMX<TQywb=8`?uVwJ4)KguKU3)S4k7|6{q;{il5H(Bsz^ zc(hXNq&l8EAguoJ!0OcCufo*}c#qzNpUAIn3tDnSuae_FnOk?U(;f7RHywZC z0>^#IS)DRpk8U=$*4>@}WjS8Z^`FkKPfZ&uqr9Z+Ka*d3&vpH0^BDG2*MBaz{_#E5 zw4VU*K*zm!{bul4+w^9ZWe^Ma=PN^W)1 z^KOB*D|DkZjo?If^};Tbg<2D4Wd22pWM*gaQipPibU$^3FGp)=&QVTyUASOBSjTe7{$e#@j>=!A zdpO!^AUR_lt|+p%N!qH0GLB$1dh&VE!io zGc3^6gsMd83T8PK5?+)jsB6mMMxSu47N+rofcKweycDa4mtgNGYeT&*WmkaoUj(EJ zYUB?W)q9871}|O24*}wTl@K%AaL=#`Q*-|ojuth3YK+qi`1iVRr?05SD(F-~XRQJ2-wh z;QFX+3-J!8D?Nqq7QoNCtUu&huq&~PeS{@@8~gb0qJ8+5xUiEU z(97QFf>{{UukL?}&}VL(D~DTn!ToO$?sL8yrZV`u;F_?`+6LYKuL%D_&XrQx2BI~A zP-S<`oo{i?5v83{n^EgAZX8@WO%d{(JEt=E@tkwWrY*U0^TpttuTAmKH8C0R zG97|WxJ;Y5Gpk{~R;wWcH0_@ur0v%Ah$fSIiy9o&qz0K1D#O-E_w_%dBKyfw6(h9? z8)R~Kd;vTDx^%~9vR;Gs{mtiiWG))A{MFx5cEnS}G(;<9<4e=YZ;|2bHu3z>kGJ$} zbVG7}HmYh@dy5EgDGjyHK73vJJqq-@Wr{0v)@2Ti90603x#U!N#2HxVBxRGbCKTz` zN_y@NTBE^>@ZT4E`TBG(X%aoyo{`qNU~JnnJl#vtRq%vvW(4guZ=RrEu zlJ_YJ#wa&wp*9)j4XW7v#@rr^3sevKN)52`TPSl1hYF$*GuXj5u^qJ5Q*Gp(1xllw z{zl}|DdT67YJI*rL)1^U>1}6S+LZN%LJBRJ@R{m~TI$|Q@#$}2IM5>nEgBMfX?l-% zRkdF#AFfsp+d1nvom-_C%+QAPfQ-4|dKAzIfHSxgpIZj(}A^~hNK*@@5u0W76vP5 zm!vC4Qy~ODBZ_`YdHacbHdYG;UrXO-K6*%9=cZ$+0_=jC=0hjt1aalvuquD9Y>_;42 z!HR^DfkmICb?11vP6F3$U0j!Sdyu{1dnt;4I}cZ2c+>dkRiM|d1HEqV<7!c$9IuI8 zku3To1rED|hb$0^85_lF1@qh=2#Vd&!F95mY;?Li=b9@ko%U5C`LA^l=Eo_GOtxrE zDQWLJIjEf8`V%<2yE&Kwl{sTWC0Si}&!KYjh(`t$fAS|SJ*!$gKjl}>p% zKrSZbHD=8yyfId>xTM}=H4oQF4~&ZONu8AH9=@TEjEb>I@rX$mTak*c7?Gr<_qm92 z84^)!7B3@&JGtLQRxGrtrk0?k52Tn1su^8!32vL%8!6eM2VGR6pb#KMpPs~5hdfMI zk06V5x;p%B^B$_J!|!&+MRmpfZf6}ld+2ww$#R}=k~(oe zFvZlPxv$3Wz*7@-W+rkYRl06A22y2FQAVzlpb`7hmwa32GHmrLflcZ{v5;Xq+Syvs z1ISJ#-K}Qd_L{CCokqV`s8?RMhy zfvXyo^e(@#09B{m?+m)Uf44$q`iZkz7e=mFNh3`gHKcCaA2 zaUWoOc!m*mdb8*#bXipBuEL5%a@ZEodG^j6kTfzR3!N^M<@U*LV|$ZorBs2nGwLuk zalqmjr9Iv4Hg={?m8_#m2c&uErh#rnqwCX49|Xd*tYmAYwWHF4mFI@0p_sZZrw)my z?Z!)ig8WKCwCW<7#U%AcdsX-77#||Z;#l(#DT?SUq?)dDtdKGVzFf*bN?aW3$f$G4 z_^6VW!g`!TXx14Tn%mg|`{@5t-xpx2oHmlbZ^NKDi!SS(wdSfSq4Ue>lx=Ls+Nd&B zb<488Ow=uk38ZMguI~#=%`bvpn+D+mP4-@Bwl+J1q~A}B#Zgcbs_eH;i)KZ6LsC^# zHY*8Le|2Y!XNAWkO3YMPC16y+>y%Sf9R3zQ?oUZ1Rv)>bVU?2LwFB1C=4zRzgI-Nb zFIql~;F4Az zHybQ$rXni=sgZEKsyE@ot^%?2j^FYSlg&hJS70uw8O~I+%w}`B1v0y!omD6-bDXNAzjsGf&(u|q(TUbE5G zRp`_66WkFNdrs2Y{oLBB-j@hg$=dr0yr{Rqz6W={X{SC}Y^3+`Se}+k$sD z&8)n{R-JEbCEl`UXO=IN;C(5F@UmLD(Qi1uHS&jO&0r0G!`=mBdt+%KS=u`xL@{8Z zwT6V|ks&l%fsIC*Xf#r9$43>RQD(MzWRnJCma7I!VFcBmM;N{weTvqc^&}PLWIsBD z^gM>o(oF6#QBIoE^B9FESs84c?KUxbthhwKFPc@A?@*4jezE^p6YCMHj;MI8K_~-WL4lrP@9@4qvfT_=pPTR zt*pQ*Rxa?c&;-24Wh>7wots(EuSmTzH3+Uvotiksw;g@z`wM7JQ$rtbR>v`e?D)6# zFu}IrA*0xvS36{NnQP6w2rK@CSrJZ(g~j7%7cdLEy`dl65DY7gUUPLBts3Zy%NNjI zYjv~HT}JDMX1|9o1X^(6i+C4i^LCwIam@P-#{$3L82!BSw2a-a)Y13}cE7mEy~Mcm zXvB5qprMxryVlXG7sxr9aQH-Lb?wkJ#yXjX>XE~ij^-^XCikS`wX0aUz#gNCUq9)r z42ulpRt28t8?AwzEeRGIIzg^_S)hS{Qb>U&G7JRr@j;NxXFL>tQm+E3OWIF+Sk5+dSR@PtuagNUogw-@NOXCsi{<>4q|48ESjJA50v$93etBAk zMMbbK;)&bnLO!xt-I-SdY(w5J{IAUYm3Zdq8J_Fgoqo@1Wa^eOi~qp|m_~!(gMrJc zR?06aWp_LyjKvkyG!5V=W|F`d zNLvhM5@mXpw>XTIY0|8hXJ=>@H@ls|#-;`ZMr~{cF?c<*~ zrNRb!I=%YI#hq=q8a+2CQ%ljOS(~&M&9%I!+xK%S*+c9WVoyQJANg6cgHCtLPN|vh zuHwNqZJXsS^r^>}jsudn%lr{)xZA|bj*&JRSAh*rU1)wTk837*WRrC4ZlFbUXiU1R!cB;QRf)aD@|wbUTF3=XTbP3!5S`o>Ndr|vl*zX>nj+4O-}C~Tg}l_SV(Zg(LwAh3Yh!spiuSzPTYI`cqkRT-tQ~ikT(P{4f+Ipm5GH#qLkXiSnX z>&-(jv2!t;Sq_VoIsXo#Qq#!(y%J$!F*rp94qDHkZkL>=dh84JC|%wXhUiIGWSZ#zS}E3X=X(o4TFq)YsBBM?w9<1f=RaLkJeTjdoA>sMRJ9SfhkglPK`6-1ZSkgAc7nTxcM17Lw~BCl{T{;fR)xeZW2N6U&}D5j6W*1?rc}^-WB-cgSj>Rl=@Oyr(k8Ow*1i0l@%}O+{Y4J zAW{6+eGJ!61PdxcOM?QvMO&fDY;pxRrKx!uKS3KfjFJk%eG&N>m(1&@3EDe-CqBw zeoPR8nNFvbG}u$ZNyv-7>5?V=`x%N^^kV2{4?Xsla-S0M>kGX92jTUJ3Jd^0gxJ%U z?D&te)v0oq7db1|S1Ls(p|VR5O#$gO^@lkOwo;Y?Bn*IKNW@Y>^5y?g4uR^UmQ`1m zmI6}TH2Soq462XkSIrZ<-0Fg~`$T?q zO8>EaXeBY+&UQukWQYyDid_8Bwo*}RUlA(6>-^*}5AUXZF-J|D(H^k9p7EFXXG3T`|_ zV7`zcfSU$SX?`BledM973@AmdEQUh`u_&r;`l5q@%5%eMax@88={xgsE~9a!D!xC- z;VDdy6ClOj7fBa=DUV16b915VXx=zn5E-LBh`PXm~G&Gw+zp$;75q?9z(@BiWMJOJA`jz;YyPVc=Z zPOQW&P6$$>q%;96l4y&?A}Pss5(XrX%@AhtY zZ+C#f9YFmr@BN8Q%HHp0XJ=<-XJ>z!rD`q0J$TsFvC9$%i!Ci`jr}acCEF2lkhp7% zI`8{=h6qb2c_QNSP&4-nE06SvdOUNEd=vIq`ela8RmxfD%5&^t_};*;GDLI|W}D94V*#X7E(sGQQDIwOJ#U@f7;)@-Ur%bz=9h_vtc%4pa0CGMwrVw_v1kExa@6gzzqxoi#^Gu( zRax+Ng(mcm;T0x0|EVrQ8$U5CGnFICa^{Y_(-DyjgXQ~2Ix=3Dl64grjF@NOMrj3` z!V#}x?c_gGidnB3F0Dz9XsHxf-$C7<{+ElQ2hyN2mj8Be$n&Xk*_aBpC8IJ)|4~O5 zwE&=t@gapg8Ow9xOs#|e5@Q!|%I;VPSk?W%%*Y~T6zQM*>>PP<>Z?yW4SPCyHMlvE zR9_?_&m@Yt>yFBnT}B!CsMlOti~Jp^uG4bVIZvag{(F~I2-d5$T6}{b`F!J6fnL7N z)_W?4dpQ?L#GP{=7J1QOu`aA?29! zpdoTi-u#U+F3II@Xj!8s*VzoE?J_{nE7>juhAS!zYaU#Gf!5L9GG@2M%&d7!N9TeK z=-ITo37)yQ5H9?2@ay($I|lnIl&mTz>+nNR^?Se)UAa*ht%j-$&QYbrzo;M+d|#YxTZ2X)b3V6C&%$&$bDg)n;5jHrCoXUNQ8gb=D)mdGybBdAieR*Ut?8wUEosH|eD8gU^ zYz#cAkY5T^o!1RI89(RuQ+b{nrsKmdC;Fd=IA&x#?+vCMjMU)W*5&G|chMVv2eA?D#F$#0!s}QJISFa6l6Ma1b7a zR*q5q3z$u{UA7`5HXqf2MoMb7c9621Zf*>g;-;#rw@ zm3pN}vQmy&?Lu>iY=17GP`{ zYI{GvtQ>v|)?l1wqY9xsUn3F^dg_~4&xlUX5{Yd;cH(f-#49lBjtb|CgXN)o`>HJ9 z)^~-&Afq!JL{ZYnFu8dd{t3Q?b*7UObA&F=JW`|T zu+K7RMuRYfjG$-;ycm^e#perpa+_T{|=Lt{ni5YkgT*lNvPwfvYc6wsIgP zKo`~WmMtzL(>Ui^L5@fsUym#qH2C6;>?#LOHMBut%f)d@SO$_olO(fm^?=8bBg0PH zLne>5V-?RrM=pHQ>Pj8R-mJ<(#^34vT;;@;BAFnI?Z^^12+^KyL7(D`PxJ9>^69g1+`Ysd5GD% zqrrox13dAtHJOmkolKeyXoJJU;m?+IER2}SjEcKQ;SGw(1=On zFDEFm?+))~7_qwI4sz`e)$wtZU@3l%D3URqk6cOMle~<|Biqxci1ZjWgZ@*3wUA9T2Ry+%pub9wT#9eu~$I{n0 z>l~fopx1>9#?}9-)J!7C(zrj%8M7nF}DWNCej&g}>4WJ2Ypogno3Vzo$Gz zlsvESbeFW`t~Y9?4w*E(q$Pi*hzR*`hNX+NC7+1cGmd14n2Ae14PmFx&(QEvmt4J} zZhT%~vUHcaA_$f^Gd^x&gje!1Cvpij-Fxf_53ye?89eM1 zCSQMu{dC!+=_ZNECfe<=&=a6RoWWYd&N()j4n>V>ejjv6#do-!$XGAkh%2~9RDHH$Th zwFeqS&EmSj!cJ=PCF8J=*kPb<&~%*EWK&Qt&IATUSBXtN5fS@rm_*FfCfhm_UdP1- zOE;-aHV=_e2;1x zLsNKylg&$6>Pu+e_G-?jAuQ5ygNB>rWUo2GB5j&1T_ic#JdRxqE2S<~dAdt-7CKk= z%JA^goNU<;QS`mDEM25I*+j(tai1&^GtJ4SAuRTNvoyRkCtq_MHNDs~@qQVa?h>7B z2@$LP{uw5IqO(Yz2bervBs$qVMBd|pCQmnsPQDfqL=Vakb&=}i6A?P};0zHz*;(YA zJtWJ+Pj>PpBrg#ee(Wf${D)eIx=45OnFu|4SeA*O@Z=K_d&k2~B6iA?twqG%`iKlo zHz`jp5!FfkktR{DlqZ*rI_-FrgN%{#EL4*oo#kOBJ=xMBy!*$HH0?u;B3T}5vM`gL zY!+g@Kh9)fCq20|rIN(abDnnt8~u2brr@L}mzAo|c>>AW1C5emGxj#Tw!%+x2Brc{!SqP}n36YSr!vNVOKKDoTq z>GZSP@U~ZUHVt8yo@3H*Q=j}*N7$w3W@x%de)5S3yYxJj2%E0-;a)oBc|1SEvvt|e z7i3wsIQ#j+EYTKbKVOuk*~0ASi!(G^pZ$DEhG}cFpD#6ewmAFwGLvV^v!5@|5N&n# z^A#DQt<8SEGRw2Y+0R#5h_*WW`RXjw7H2VXelVZ!WpYO~NZDscJT_(+zXFuQFj;Q^j;%7aGRrMYl&z5CB-a$mD-4L&Qvd@@WW9|Kk}NUh0#pH`M+1Pnay-B|iD`ApM~;#%HZiD@yX{QY8pRn@N|>-Wa|*Y^BI$;i?k=3htQwTnmqiZXQA`;xeO0K z>B*LlJQOynV(@}1nW_uIc|&=j2PWV2El^(~6E2O34q;&YvXgC{39sY(21_@oPBssbQTRa{p7slhO+)xVKeW)W(wuy~ zA!vSN(s0w9h0^>uL&HpSvT3OM?LW!T6rSc}^HP@jr!;SSHRsb17U^dO4L8lnUUP&+ z`nk!{MUs=ve*14TG~FdS*%BgF`|mPL{6uGwJij-2x=3`gd5FBnA55NZ5}kZ4 zB8dK&A?hO4$tNOo=ua6UezLR3Is0>#ho9`^ONhAN{uc{T7wJwu6QM_c%`)*5o_r!= z@A#WZ#7=p#wTReT|DK`gCgsT`qVBiFODpE|Q;oB0@gbNQwHC`=nmC zyrx0Z*#NoF@@QU)NyiQ^u3n%dTe-^&#I$ z&|F`q=|{Gc@Pclj)AT3TN$}jz;OR@IlbEd=89aT+a}v_Iu}Ra9EGMyh-Xz1*mmDV{ zp_`gS{m5_Lj21{SEn~t@$AI^Diq4V@Nw@G+lx3qHgG_y%)&?zQO zkMo+8H+U<8rT8d3uHKVG*~({GFz=^e&T0>=LKz41LLCQnAe$22ankn1pw6rq0rz z944_-okjDsUr7ASg)~0K4WB!M_TDZ_)7$(d!E(02((~*k!E$?(rRTXzxxDoq0$*Lf zgUPb>nM*=)10-#G_2%X+DLYisS$dzfB<#?j!P1wUrDNa1j@Ls9Pj^YnB4=v5NyAH8 z@@*m!A@9hrbdk2?6A^pHaE6GPxa89iD|}~$hL^hJ>J4=(eV577UFwoAA7Z8NHktUz z%R+hXXz+BAzU1@B?ac5w%3yi!Wbky8z+~$X!E|~$lx3g6!c1neS*Tlb`wbR$I+IO9 z-AbP@Xp}tV3HR#yITULTG>V$Va}5@DQjd>Q?$(hNkcYC!3eD)bliNdo}0N5Eki>LBmaQvez78kq(1kmdAdt-7CKkwXLxvNPPS}_DEfjdOBZQQHW9IZT$m+dra9R(gvGvFmWG$+nn(h*vYzYyo{T>-6exkETo&}Sqi$o`zhsb-})8y$U(aF~$f~cAy>LS(2Cn9uc zF+;>pb{08jOIaR%vXd_%;#T^yg{X^kC!dMXBQMLuPk8c)h`nRQBx0vL*;+*Gt*aTD zZc?6HBI;K9nn{!^<;f+ZZl(JUGDga?P))ih%fn21vZX_K_eV*Z_Mt|REH#sbne=3{ z5bJ&2WML;gxir+R^twq?aMF{@O4UvVBx?^eikQU>lZBo1~w&-+9~P3$~VZHE5wp|NuLypi##iSh*}#S?K7e?-TO+!98-uQ_4? z`^Do`m)0t^T6xd5gEN&dtZtSMO)kzm(y zzI>rwfW@nlqiZJ8;~5MYEKsdb+yH<^6f)iqfu!xR@t{Wv_-z<43yTU6{&4 zyju#{s56}gDL*tN9$l;Y^~QWziOUXEYb}^?VHHg7nV^QLY!7u0{*%LIP<#(F`QU`g zYplhIkN)scHtpdGUr=ubtx%YgV0~jW`rAkWbK6xaT787V0PC_Dgs@8H!fL%yU26Jp zV3it+(fIedti^6eR__4l(<2oYVO%@bCt*l4%EYx~6x*XrwvKhF7&a=desszEr**u6f`v;nEe~EqIAUAeiw1Z^+aW4I0u*sgNr<2yZ}M0KGiuB(-AvfinGeqh+&5o z-X0|Jq?Bi0=r02WZab+mh#zXoz#VCNZy2&wGbQz$y! zkB_*LpXDJcm88IQUvUxS^;DJUnBVXhYsGkqn&ZWtVXB_%X?Eh+PsAw9^tU?jYZs;l z^H_$Rjx&})knqzLUUaYxH(bnfM3`reDf{>gnqXl{Vd#|T{*iq4nuwyFo3czVUetlT24%tB+_czv^p?5-`aFs+vCcwTk}Umc zp2*ITd7A)>^8Azu#SmqnbdmT=aq!u8dt>_|yB*nh?BDc)44D9Nuq{kYRHC_Tf`0&3 zyQBug>^);ZI!XF2HwRoV)VUz6JHIjk15PfA9A2FAP*NfBkIQAqk|5&j6gcve)uUdh z1?*f#Dvo)P!Uze1)u5hnoiK3#KhK-9qH>-uPAO)+)pdxR(2a}&avWD$s3fjajTBp# zs5>GrAxI1k-8``=hNa>dS)SX0=DajR5oIYv!Xg%k>e_QPV%V{o&V;dFMvW~*kY{8n z8~1W`2~_uSt35f@Zb5Ae>E98n&O-JP9=HwJFhj!wVC+ zp!G`rMi*eTM2^8~hcu}08?=A2T!yooYONM5i9D(850o2CINFH*yD|d4<9rJe0J)@2 z+2+@!-*`9->Z|PzS-2Xy(~;%a1@e8p9J{%+Hqu&I@xr0OGRX7TY!ed2>yy=bbyX_n z$XKOvuA+Ku4r%s`{b?9D-jLy-DAdiGOF?D7-WX9F^Q)LOpKW>@sAl03Zjf+lKQ*Nq z3bvk8XKZhBj9kRHl0I z?{tjbXzp#*qttT&tA5x~4Rbbp|GOLnc#3FUer%gF?1;b5g3;eijBXJK@@9WGVP`+%8b8j@T9ut`=O{b-*){~03E+=U*tgHw#-D11 zi;aT5ov?48caB}iR}=I81>4Bc4JLP(!vL)x@zQhX&gUi=@hhK-$ z<=3qQP*>%ec=qU2g!c{G=m)(GNO7#Y7d~6_2_NH|?Ff3T0pGHXKkI=%Tcie16*J$q z68N>Xpyf52QLf7(1hyVf5#D#~3=mxwDFrI4`&}!+1~_|g;F3G5OPzdv&oOc_xvkQG zH2eBF;vrc3-?xu!-kY0RKp$J2fke@6ocAd3tsLrR%MYxSt#AcuZ(T-;Yayr#q8}3D zPe8GC*oaC{i(;Yd<&SJEbD_V!?k)GnUjDcpfyGpCXM?I7`H5xxxy`=cPmBQAHE;ofELd3d*KUgWoZ4KWH47P3% z6o0f+9Je%8%S$0$Mq`a&Ey$l-6t+fcc{vDa{Mo@_X`Yssg0NbDu~K9krWpwQmFmb+ z;~C{&tqgHfvOeu&e{(P>?Y^=bDO(Cu1o?L>fp%C>009+8|HDdd>&~fs(?%tOVI+RJo<#*t@W1;onvUtzIDC_dD`wcux8s>)7_jQ+Z`TMT-Bm zbC~U>3k-W}qs*E^lXuQeMSa za3Tu2f_3yvLnuR4*sGSX99PT`I2z99WT1A^E7=+P7@1$$LD28W{3;HPen#e3bujcV zGQXOgU<)GitJ_KX9GPFkO40Ag{F+t@YjgOVwt~t@U&~I>=lsC6?G$~^4_wDZ(dYcY zbsZf2%@179N>O}%fZEBfZ)L#FBt&w6@6Qlf?i*MM`Wc(w&`QwX*!)IThJME8H?|V= zH#WbCm7zbe`Aw~3_dhnjnVqALvH8ud1pST8Z{cL8n17n`1jEThz z%AhkNlSRGatze|u@Ry>W z+y^)8>Qh0u4tj=dCRd;Tt;B{a6Y4hp?P9L+dNbTqH3FL) z&yV?`_(eKjYD)qxM`^n}X3_Jx_kuNXcFY0EbR4W!^J)j-uPwDcFiS^7XQ5O^qSZ#+ zegTAs|De%xA^EA=Xd#c5m$u6gMgNuuz_;99`4+5t*BeV3Nx$e@Mxh(k4E|Xxr?P$B zY|>OZ`J>Pt0{V!EJGRowieyW+Lar(W4#mU{=?o2Z0{# zpuWi1U{9zOPt4LV^-X4)=l{ek?WDdan>IRezNuu_ybD3|c2VC1X{aR+`vBtN1{>DF zny*yyb?Y^cOkz?N#C46GbGb6`qw-VzUHpf*JRQ`z4|0}G&v*-i}f#R z!WZpve$kY7NfQDQCAbu2~3*~V_8{DbE!Cajjsheu$^cy-( z)^KG=WdiiA>x;6pE$(L`QK7_kO6r8mLA{VXVsgaE63cD5Xp^nr^Gs?AMJ>Lmil%$=W588^*}vF!Fdh zGCW-;x<+|eQ57{%Q!`m$DW>q@GIcKUL^Zpm2{Wkn0@Yp{RaaQtS=#NGrF}rP&q~!5 z7JCkN%Ve7&E$oB7U*i&qr)gmnMH9Tt97dC<*}^>YSQ?ZQsax)4Ae)HD8jWBHT4H+8 z)_xMKu|HYeh}zQPdk@%UUv#B~sVZZ?Rry26V0&fAFJV(#uukyfP;^`^iM&(39_c0f zZgWpyKBp*V>?5l{4=6(BYGrA=o%g=Ldv0;OBVyx}@=qtFGIE|VEkkPBkNY)LkDUYY z*NC?$6E#scmH0Yl*6_opg*ZqNzegrz-QOY@=)4RBZ@s`4c@arriWi z5pc(+&b4N2EMV73iM zanhiw)aL9IX+H2QN>&HYk^k9uu#CzIZCnc`XkLb?{}WS`8kZ4C-T0MFKfhc>^PNkI?1U6Y4;NIJzj^(8>PQUtYlv2H)CjrduyC==1{ zGuV3vmG4?rzvCc2(}B`otJdpYEy zJWA6{*As=p)Uk;LiHsa;b+$1YU{wsi*^76{p=uTq&P z<&zcF!LQy0Po8-z4M_FYZR~jtUBns*t$Wlj^e2=#r-gIws2q4eK=ttDso~r7A+co;m17 zfv)ACGfVB-n0r2xBP!nmOvfBd$@YW(Ou;t3RI>YuOTcr9$pdA&uu1DRpwjnNOQj>1 zYo^{j5GujO2f#wy*GhcI3!C0?b*Ju?G^q&jema|&U_1FSrm8H@l6meWp0)OttYdZ5mQCj?+MDjVMM_@E%#2rI{Z@n{O_ zzM|yZRk_O{A&gZYWF;9t4i7YapNCk9 z_SS;xR>Xmrl!5d?5{=dB^9?tNEfFfxe59SoIQNLm3yK+kp_rXq435kRcGZ5ogF);L$<11rokAwEEK35 z_0?9b8m2ez#S5o;d~K)+mx{_*KG8vCx;UEFR9kyPBxX%hT;F+SJ6mK(2~os)peiZ5ZqZmn#J-_w!5#0h~(9 zyZ*&%Es^_qmWxNN1C3lPZKf16$JSWN&OMvtv8|tc_J!J&o6sT)2oa*))-&F^ZsX~~AmXxva~wiZ#@zZW~WEXkz8>>(8czl7kirAu5; z+&zr4Krgj3MH$G#wTPOnm$^x1*SuQPkgVYKMb+fJ+`)sXHYL)Qy8~ZF4oKqme0ImmN_R=HFgzXKHiY zo!b@?@zrbWL=(_%uv*6=qT=NvFE|>}-km`{ueH-CHxuZE&Rtd19q-q~gC$#46PNKLE5^aR}>)MgKE`tWYgJ3aFVqDG*cWqNI0l$;rVQiXw z84)Y%T@EJMGc&uc4CBYAAv}tA6Ew~l)0P)|z6kw!4?(jR8|~T_yCI$;dEV>dsbQx| zdH<4LU~C>@-+Uj@gJyGmF#xB%?R2qs z2VyV(q@4u|VMqON33_^2P9yTIbwVjpC9lOFOJWXYVT%SDJ4roQXR1|0>0VM{XU9iaUdpFZ(r$ z>Y$hS*6l>aWuorse4S=Wz3h&bAe+pw!{7=0H!Nh+b!>AemS46ftKq|GZVF%H2|wkV z7P47?6`M##mLZ$Y(_q+Aa^!~aX34iqmPohd$-(4es_jTMp;s7cSbP}~JI%Lsu9&BN zwJi6({@TxrQCq4m&^pDB?N>D|WKlgci)0$^g3tQzY*5X+@`OdzL)9V4Pf^3M2p699fKu zO>6UwSt2^^|0EvMU-sfknDZ-9yY@_Tqd36CVf6Dy&)R+ zc%Pku6!~OToS{}K&`vm??HzXL)R`c`pNg*uMr-igAzpAF34)qet?#W>SD_ITOyYvt zwwtRp$o;Odcqw!il&K*U3zZ9QqX!|*0sO@#n&o-WYpvY@ zTeGQ~2*2%=BBx!UR`~BChK-sJ{WiSv$Xw_}54nn135==u$&@g~c{E_B<17O2e*NCU zu(wr@pBw~l!HL1ueo7L5uux1a!}Cu?OTrOxU@goaEezA3jlv?7D&cCuEYzKyKT#}0 zzydp7p>yNNjN(nNKT|9;27kp8n)(+D$9%&JQCn#YuBK95=>BR@1fds-H*upG0e@~k zxFzx@uz1j*ua33kB3G}oa=A(aGTou?n6ofTf77`}gY^w~EVbU8sILTI&&8V#d$!Ha zG{f>C?)fmg|8%oL(VztC7b(>(`d>P!xORT5T7wW zUx6nM;&bD1@hDYWD6Y+-DjWZ2GNB+2fSBM7YhlrQA|>jKd^T@OifHQ ze!M9ilHPF1J&wx&692Y5^Ta#_B*sQpQH?GnC)ROntU4=i z_?dVM?~n&~#i9LLGN*WEk^GRxWbcqvX8H0N&ZN@-Zz}mzQh3H%z{-j%Sg2B2iN-LU zs27`w@(Qn*VN&}K@Px(H``r#%VRz3fS-6Z&1Uxvt)Cj9~_+JFzi`#WU28??yBavBhfoVsg`$aMA`D7`W9R* z!=)OgXa_3Vj#9hz)iXR$=ap71_Ec}EGx2L=x$4W6`et`%Y|WB~a)#}k0?oRnjcgin zZ*acaCpyAfUCTx{Utjdl*BIHvExXa3@Yt)9+Jml><-64PLXk=p(RB}f zFRGU2x^}MAn035mvsVH!o7c-w`E}?xpt#Sy_o%6z`}!F^IPU5%og)`_4%m?!*y!Le zuDfNxo>#(#+%Q81-9J}7wa~xE9uL){??xHAmS0wz51#!B1z^zPHyr8+6;5$M|BgpjfJt%)Xv$(Vn~0I=X8X6Q|Pu9 z66LjL-jZa`BNfe`<{(Q|zhfiI=OXri(;ZyXb-|P3^=xHQJLnlsI<@4JH?3UFr(W4Q z)4_MxZ?26%PVZ=vffof^^^T4P`14Cdf@ft3;jnVd3;kn|l8^5W@dU``qa=7cE8k4D zxum@e*keK}Ry{jQC_IqqQ8{xo^q>Cf0AoICFC?{C5iI&U8UaGa-!}k z3=wqc#?`U%v)QP=W!nk1bl!@cNveWsN0tZblPtYjdu(Z`ylzeTI!KrcLhMDp^XNj5^n)XN2b`yT@T{2{Ns~Ha6yLUkN zk|X%;YVwVFD{%7$7HQWG5}%LA`0mN_)tAH@@^a7WVa8V$0vj5K_nymHu4WZtg|18+ zUt+{ssAR}|tn$F~4{Xyc#nz+z47C8Ph zl4!k>8<86s%W%bo?p<&7?4646&Bsl;zE{^#ap~R+U9Ad*e_fi8BIUL(OO|xx?~fIs zGG6;FbeO3D=d$aKfh#>Kj+n^sL1V6Ee;ta472_cnt@5LS?Fl8rb25yHf7v$~Qk7=s zX87>!1?62)h{4*qaojmguA>@zD%KAlu<|6m^Ls(#uxnTWJ87j!?gh)8w4H$XI~3uq zO<9?8ElM^YrSa2QzKv>Kgrt4bcq;CjvGCP`=<;Fz>>8D=JTFTK7b1iN`{KV*vwP6O z7AG9~HoKI6IcxIag>&e!02gq&O**hw2W8*pY<#g>4)@PtXKH6mxYm|BPnfsS=s`SO z)#&A|2PKuOU0J=J2=7|oGeZ}8t3kMV&|h2(>U}?VC$cY9 z2WcPuMTZr`nz6+!o!{5ot;5D)K5fZl+wX1Wds^*9$<==#d$&~@}Vj#}7qmAA5eMyk%F zX40Kgt-&Jn`(-7l`)BJJwxD09uY^slXQ}*pzhX#Y*AA>)ee@fN^|@hVYmRh%J(O)b zh@JallkEW9MOOELo>d1^x*uk!(l?}fy9-dgXc{)EKKMA)D^5)tQ-@_}Y&|0Ee9OiW zcfan+JuTGEA0z0xm_R<84BHIf8GGbhVq>csFOKq*p3g_j?C}g=RC3lo+e%f`Zf59O z>l^Wu_Ml>lu%wroB+47Dyy;`FOQNdgUKuJ6UY+aLj!jw9duPewcVv4kEyBCGkB#XB z@NVvF#;SV zX7FYQylMRuD_KMKzLr*_;|0oQ zBc%4!EL-#jUsvfqHW_h>_%s(;YBzeOXH>50=~=EzqIV#4-!e_#<~ji;PD`GVAqj)! z)>8cHf>f)HHzU5CAfn`FTG%e?)=cop9GMR6Li#KxS-*S@st?VxZFJaA2)cqyFJ1(H zm9#8rmz3d4jmXM8$K*O#g$!hE3~!cT6?VEPJ2qo{J|b`X+zem(A}uc^!?j^lUH$X2 zRO`VpFL~IfyKtFHNZm(&ewHwOHMc)|0I_qvfTBxPu4nrI745w+%hrG%;ofqNmb~nl zC1m%aEFbiDX?Xpza#WS*i!E%ji$wqO;ndmnOEQH1Vt-aEl@EMrhAL>SuB8j1hqjmS z)LxdMYSsF7ubSX`xt*)4BIzPm<}2)6T`NADjfe+cX|l~m&PR90XKXUUZ+n%CtbcMN za^kPH(FJ|&WN%g z`_@;XUy2e#NhIY*#pd{^4DRc#lsTGr^L=n)4ZOihH($qlxLp}tJ{_?J-e{q#^{eMO z5m~({!{yi3f|l28Mt$S^vbz(R$Tw&Cprt^qxi+^39W<8@`Q9ZGvG|CT$bP)V!nINB z+P&rL8ZoDDwX*pun_y7nqteolB>p&UM%a9m-F;h@4|-CjR#vj5KgnZvDUbH;84}%6 z^vyTSwbk>PH!~PhN?)tf1iZ}p6Bf2Oj4`z{aHHb zVb@PRqh4hCfQ_!pMgiP8rS$)U87@?NxZkRH)N=n(encxBdv~O2Og`iy?921L&+={7 zl!}rbu1+Ga{fP`!lmIdg7dv(wxvQWwAh%AvQs@ZmoS$^CiHFD=Q6{Z_oH7{JIS&Ho zryQJ7=kO#Vk!Cpg0d0i2lBCwgr!$1&rSSgt;US{W&sg}@y=K_uGn?#nOP%R`)?}Mo zi*I)(6WQyTB~EHTm!-ozg3KZG@|{S+s(d~}6+jEG?j23`T98J?s2{?Lg)d~-LTDZr zwiGB7EL*D%1^g$%$>P^cwj4=r?rNaC@ULgMHkLtR zBC(K8XAk{0;&k8}8McNODkySGQhr1mG4`IhQ#wS3jol7b?3*?^On=5=o1bXT18?+` z9JY+o_;1-5awLVgnnmUKzHPD{8s8f`V=|6vdr_CmL-~i_$?$k#(>vZ@|ByH}`EG{K zZ}?(E?7f~-buHhsaJ8yR|ND-YA9psXNb~z9)nR|RsnoS}48^tyR7cAnWT`@LG-%bE zeX*rPb?pyrWZjm!vt>s0a{ZA(wp^)KYn#wmzFJEUljIUef0DzZ?sBZcyMR^yu}K5( zNk=dIrbCl3Y}ruy^b?(C2JTCG%|q2%%abPb(0=G`CVx`!?niZv|EW%P(1SO(z50?D zU28h8GQOvBXwSCUnPyl%Wc;#Ru8crG+{tP)^pB%MTfX3=*fgBPm!4xT_rYBLOlO>} z1$~s=P6?*xQl17}Ki9YtOVub(F5>;`<>V)$LA?oSM{LUjz2S~PRq~!~#~>#JPydRk zPX1uI48N(?;682D_RDgk3DI)&-v#RkwR8MJ`%e6rpaq|Denl`Oe-GMFDlbflAI_ox zCaXvBlNGh#?i@QE*@Qum{x5Z^7L*UAO!#-Gl7i{n90XJ!z+bf^h{sokXRiy&GyAn= z{K@K=w}k!M<3JNMt^yk8T&Ua`p<;R>bu zQ(L;sHJ^fIR~nRwPkm>t!Fu?!N|zQ9o?qEhIlO&x@u1g$&MR}98(=VmX^T%m;jg7B zfqzJPQ_-JLHlzw}T!X5q;ab6A$9gFN`(KcKyxMGSOgEwU1e^hp@k`=UVfiW*h?(=) zIx3^_S3$76>cQA}B_;KE@l6HX3Iq}Us$9SE?(f zh>*QTOT+e6@MNI>hom=N?659_3QTLf3~-4+w7Rhg|D0GM^WtBysVs;++YZiD!mzqo z*0n*dr$Sc?Rgd^j`HfTX#v)`RG~EPsU6`5@m-r3|cR6pAvBR+kZv$!nOOiAy%NMm8 zO+Yyi1kj5P9Vlgrxwc}5oM0pOcv~3r-^rL@(meEF)j`!|!5x*dQ2!C5`p~gDk}A5* zN6xF9KQLIRtQ~hvZVli4UqOJgokou+=81_Z#T0PAd56Ou-3mthpKZjE@u>+_$n4d- z!;x1$1x7sSEZhguZ;TWv=NHOg)X=3{!f=<#j+guUN+mgox^2A&jCa{+yyS(x=rJa% zZNe=lIh3<+$BFUEG5@7R0d(4j0PW=r+UW4qK?@0w)HqhdrYL9?wx@h}`*=dzbu%0;Np!79wbP?<2s$-9N@Zb~f5!>_Qd~k zM+}269b8ErJ>8m_0HoY-#HS|1e1$imt4{SOkGEbscq)7c#z+>B6 z^+Vz8j#ZmgaBkw1l}r!i(U?+68e?74F9f+=CF1ti{W_4(gaNj1fRIF>Ov7dD8hBpt z4GXy8W__{9)WGK?hG-&RW`wHZxN5{&^EVn^dE)TIzVTVHRqY2fc#;5DtXF zA>A+bFAUvLN>h3Kl_a%n*qkBIplhfcheIEhJ$h;I?U&%+Kw4D<@Am-q@DGj*iYao5N)h}%Bk(rUP{R8_Ih2z?SVQDlCoj(KS z{(8!|-a0&e1#cM~9m9U$Fp5zBJX%*unpx}i`sFb4^`nuOY5{b~h|V_}BQ3usUQ&q8 zIX3o%)eSh9hUK3%*kGNbL{#Fj;9diN&0vNsCjzMYR44jp&DC^Lw@}!5Qj8 z6DsK8cGp;x0fBE!&#T73EJr^UQqunUg~P_bVBxUk-vB>^e?`9B9O2L0F#6tQ3_Rm5 z*W!dzVXk^ol|@5jXE1&QFHEWQ-17?p{?5i-U;BO%!=2MG$-)AP8<26U;$_0eG9eM+Dm)S*M$U3~nw7poNz( zt1~08?$X11uFg>zu3M;M&B1_k9=dm!?8!MQl>xtHJgRV ziXq|#-#q@I*g3#WMfC^AWzV(^NHmr4*O3p|-M{QmDfOQn49Ygmp8dg+bP9B7d=#W{ zwl0nF<6X*Q7D5Lp4mev81#2Q0h%@T*GbpI;#Dg(bd$F)rqZEpP{Yx?Li`!ZW5&*`LBwG`9$Yo)C^J(zS1FkVe<>J&p z9u1_vwPYE0uz+u+G7f{TG2?F?$n~|9hdM-#jEC}R-cyC(YS%7@pH_jH-X1gfVA~+K zm{w&#Dq?E?!c@tX!cdd1Y~9=C@Z$CW(T{sOQ`B|=1+t})7=Ny~5O;?V_Zu;wRP#LA9mNrM%0;rb5O#wZBxB!`2A9R4r;2@*%X;$kC zOYtAgV}Yp}`-T2R35mGv*8T@N_6`^I(r{K zk}HEt*objrY&>=ezIq=qb^%{aSf~A&k;#wd>lu-gm~fAs{b=^c zyBsG)qcGR!C?i9P4eCgsv3Yr-DVN(p54;e#zlCS9k>@Obqa~EtOr>Pm=`s4_f$7|c zDQ=e^70hB>D9-CJFa{URsEd~+Rw4vfOMazqjWM+>0TwHFWPbcuJVH`0jJ3s;(E)T_} zUvY`N;;yj2HwoSJ75ZFQq0_m(cXmTm6KBCgcHEI` zT?Dxu%u-Ey4S58O7TJj;#HRe=O6#~^^Q+;eFm}04C8*w$vsvPUp4=vwqU5eODnBt7 zaY0=QWbpSwqh|Do@qgn-E5IdQ&`bQ;qs!tMBU6gCYV+#ocvnP@9wlhCs|DO~sk>E& z)Ny0!K$;5OQ!MVW8=6je0Z>+TO6Zug4&7i9TM=Upbg+Jh zq6NHNUBY~M%7Yo56ua#%W=NynK1GO>&nc5tIwyA9U7|SqsF`lUWrHw)lFSVss*9X- zotPcI3}q@tm*HWMp=TkKp+-)XWbZ1}iS9~TlrSD>AGlw>O}0#dT`smiyV}uo$kO)XWT5lF1pw4 zQIiwSZpOUOhr4_}wrWaNL?67tQb~pox_!Aj*q54xbEXRSZ%l!9yYY3QqtZ##Y0n4R zb(1!PwqjdGN_AYSo1_gNUYZKTxxGCQXt&t;+UZ_Jfk_7^dPRwm&*}CYHw?}&2#!J@ z}5mevzKe>F3oDs%&4JW13BDPx={F#)?!r;Rf zpqfs;Ud$&|nb0yx;+B5>+&8G6p2sYF{hPy5$-WX6U=A;_kc~j|TIhOIl!;^O9MyyL zxP^BsEF5*Cf74*xtob0%!nO>)Jc;fDm+GuzUi@IhRz*#vl0E*s8|do2bXI7tg8reB z*~}T@vG-zv_1*?+<^ZJctoBF{J5b!mV8t`A0>#>ezPfebz9y@g-R`?wWs*_%zp?Al z{W7eO<33?E_WmYq6P~syPz~6t%W)qEBbx{4tb2X9;MUx-{feqde4s%JZ$;qRQa1lg zl83)u2JLIx14%tdA3r|PhIbSfgZdUnu0(eE!B*;`CYY~@?##tQOj3U<{U&?9eg^d5 zp*rV&FI@MVIfEUp9I5k|huQd)RiY<;+J!k>veJNih99nzodZ1wic&=ON~dxtkI*@> zVM4#>S8m%zthgWa?~z%$zRfPxJLyqbu1R>>re^%{I%m=???lO}Ji)@3NYFge zXt!l_p^Nsc3ZCIVQ74;TS!sC9A`YC{l+^q_$w}EyDar(>9zUU%|708A7OrT!myOk} zPti&BL%_bR<#OPcQmy5;wDldgtY zy}o-`j-KTBF32$}B;~U|*G&mZU8JPk@$~a328W!AUU5{V z!t+hOq7%?;?Jo_;qeNZrutx6%1}*duEzEW2Y93Yf_(GjCzF<|9OTs24qM;XMNrSCB zfuiTaD|XeTZbd!10Sda20?RWiMimD?+S znZd9D4;OhwEdjQ)s2%_1CaJhG=P=GanM*2P@rn!`RMKvp9OZ-?xjU>wd8J8;o^OU! z&(K=LsTQ)b=b4h^t2*K{69N`OTPZr5l&S-HwLxlTr+O_zdu8J+%<*g5ko8M?r2+eS z>Krh>*2+jb@s?T6U&&gsd(ig;#@AUHp&z9eHk5ZAa&0N>ov&oy`KrMCdMoeYAUq1) zy_F8fQ{pO?X!Av1$0}qEQNH3EbmF-Hj;S-os7rO_OV*w-z-pB@8l0gY^*q@!RwC|u zQ-*aN-Xs<)3l~t1Et4(L0e8&xW;dmivFMnK>RbL6jc?ihigoFj{ko`7=I&SPbxu~7CMzHBaH|E7qG1tj6 zILTE^)L>{5k8jkd_x52M(Ek0z^?0@qK;4XdyYij*;%96R;%#g~rZ03?AXa1T9;`AE zZ0#A6?w&pmm%u!{BL|;s$Qx|}`X^X+;$i%c@)>+NCoP%v?|3+T$2;xcVcCj7ui)v( z1$;0_RVl-C43-85^*>^=|OKV zX!%9F1e2#PT`74eQkV8qK-%w4sX)TPNNWYIgUYv9qL+B_wUlGfsYgD@2C|kN;-H1H zcw+-z@rl|q-~*q~suFvDj8!TVuvRDVpU%8)gTJ7kK;i3D@&uLsl`n>`d{2+Q^3dqe znEduwbS2S3y33Dd+k>69Pp6tg)NEVZye1(ql_7wY=;9?|o=pR0S&;a|QPbl?MQX-EsU zwL;*ogF#21rni6$KG=l};C|jPplg3OMQr>dT#rf~%9%YOi5%@yS$6^L}4<2R$i!0+2J<&D@(hZ_@plnl4J4tU-CewS*(O8SiKMi%!g~mryLPEJ&_1tFk3#`KSgEhdr$mJW;eFB2KOP0$ zQ>9$*I8dEszaTlp?WZC;%ceae;d?+!qWe`+OY*js%_WBIFcbaACV@ffZ0LJ37M6BGI(IyV+p?CB|@VzUm~ zPXdp^uJJX=3OT1Hym%V7$8cVlE;{0lba?f@F08PZ7j!WjWyN^SOx3k#lx%OPp1g5jI;`;sQfLMtQp z2i8;^2_@?q66+#qp4z-TWQmh!=)j#o^51GpW|C-O2wNX_$sz!QWdvBhEm@}P*wYj; z)W?0eZ@DP~0}pEfgf}jhIX)ckqhnftC9>a8z&rlmkwo}5e05cXi13b=@N#ir`D1Z6 z+1#}?Wh-VTDd~Mzvd&$CuXy%Jdeqx*-;+cQ;gkZDuJ!p|H7yO<=PiJF`+hQVbjh7+ z<{w!aWmg+0K{yhtNCzQO zJz+wUt)*Np4Gt|#3Ef41$yuWGoo1|=)d(t0ge&bQN)66MtG`%B?_e%RIb%nXZf zRifT#>C4R$H_9pD5JjsC`DI`JL;^$Qq2ZmKe>T@QpzcQ7=2|DbYcKUK%>U~sO)cgP|;Yqhp!^^AS zkHR_ZRDkJ3v9;&$nW+5k&!Z97eK@_2Eh~DDP<0d^9M5jR4iz06D+qjv%M_9E)g}1q zUt}3n0&%0S00JU^_e+f+IvH;#0`9z18O&d4;~(%=G1c9664(Te6}JQC;MW>~cwreU zja7((=7G}2-)Iyf3hIeAQgiWJjX}QLo@#?#4D7j(ZilfR@C5948bRXDrw9eDNvg;0 z?<0mrv$C-P9`yL}jrffK;e5rDg7tK|dw{<%Vh-3^ht!aWE2&aR8cf|Iz%EaJa8b@W zZjtn!c2F)dopu#y|LCNRk~x<0D46rEgv__kGNo>Nv;Y%L?gui5Ayfb85rX;AzbJ)H!i zm4{s**xAWsAe=iTnZx}mD&GD_@>QEnc&8~UiGybpp^Ij@JPE3v>m}zBaew2Va^%sm znZfcobI~%BL!+5dSJ^q5-kqcB+TrcqIde0ix9mr6Kjx4W#rrQ7@3|W9xjo`d!e_&d zV`l8%k{83;MtSVW-q^PQdjap#CMDid<&va$E?wL0$kk!rg#Sn);c`b$w=c(wrZE?K zU|oc9Rjrr0tjgtl^$gSt2>+FA*dr(^vjW2i2h#B5hLUV;dK>ZqOIV_5&<3iiJ@dxi zlafTYF_Rb{|4*_V41y*;DJHmtz`zs$AFLI{=&{Ex4arHy}B}TtXyhtFC)J}h|H>YiMdf$Lt@TB@6^Xt z`SK;+AiAt%^Ps{LcM`+hQO)!F{mE^Y* z^_4&q2zS>kB_$O4s(2Q#T|Q-tJyYjFV-i6@^aSN*r4pXV#<@k%usU8{(ZbZ=)YPDQshX`zL{xjFly@emZ6;Pn zYLu zTq`{0PWx@3V+g*3vNn!KwAO~95qlLF8VtER*{;|&oXT=uU89Vv zV-PU0HVIx1yOC^|#%_*P-Ph=x0Jq0nHHQ?(HLV;Mcp;vDtI`0!cK`l-bqiG7eXW$k zHh9jF6Z2%u1jdEejz@;th`FG(v?eMgph_Y-BnBbK2QcxHW^%_Dk`|>ro?dClo+-T; zh^`Y6!6C?=ZNi;^gj8AhxEMrWyd~TtFju13Uj7hvbgv8s4db&ta61t`NTlFOuqinH zX+^o&>ucC?XW?x4g6k$<5LK4RhptD)Cq&_4Y^~CCAba#(j*RVHVf5=Mqr+@A#l{T9 zS3bu5D4nRvP5l*=ZmA9ycpTCG9wBrYeTGblo^>Cguw?kz|4R%nxQkW%j+-%yf{_^XKl zl4)=0&58V>vi&80P@6`!R#Dy9ZM_eD_;|4&Bs`l? zFSx#e3V}6^kbr?>sA#{L5c_ZZ>s%{}vYfYUPa;ftOb1DC4J+5l#xCj37!uk85;{e4 zLH5C~&zF_9^D-#KO(#!T%6qO+Vt zGK#E>r-wom7I)4ls@t`tij8wyVR5OF)-i^xaQ4o&QXL2aC~xWA>`~d2+gpj^)Hv9o z-pm|DbO(}XD`suLN~9cJ$*H51o4@8{P+;iMIiN=+l}O%Xi;^_Jg25~vuB0auhquqI zL4ATaa)tc``efxf4cg_X2E7ZA4Km3vU!lRU<+RJ;>yHA>kV-SvT3^KbRA!1RD=t9F zcWa#TX1Cj!}CA`bpgdpxrw)W+>)~j}=V?$IDaL&k`#PI!ER1dsKb)E+=WK_Whxx;_=;* z5|_Zprm6yR4a6H_WNOWe!nr&whfUoF^L)qlT=BM8JQwB7+)3wBd{Mi&it$A$qC2-G z(gjrzkz?*~KfH^TNNT;VPQ9nOD@oG}*$g_~?tL%FW{*nKKRylz)R0@RdQ0q zEix&}rU14$ty(vKsTSo4{Z}mnQ<&zv4CU6=UqLFhCW5&~%%qiW$>a zH2$r|=jYjrOGEOoTL1bD`1*0`>(eC=8yS~JXt=KJIKLL zqXT*+G>^{3r|aT+Ce&wRaczznr>e&%6gqMGhFwLRfN|=;;8W1p7LmF!caCI}zBYDT zN^68g(8A5=!2t8RFBW53ai3jgGs;Lk;IpnB}b{&s4|noTJ@Y(HCRG`#gi zXsUr-9E4Wjnv?B-@lFKPMnSb3*gCv!E4ypvS&PJrlyjZa0a&eC_aa(B*AA2|N9sMf zc@C*iuwIf`_Hc1(Sk-Q5J=h3)xzKBv_Fem8?pT%;9yR#!&53tK1) ze{|0eUj)BQ=4v2y5^;}?DY+BPrX=pvEf|#1Cg(QUeUK?ZdryNlk{m_bYuOGC z*-y>B2zpvoY2);u3_meel4lpmac8%gV)0^)hz^Z^gB~T$Lk*Sqqgu2eQ_qe+i)rW~ z)ji~D-{7EKLLUbSElLStVG1-%tgWcMe$w206w-lLi0NMSs-Cz7o`gZkr2OhFGx4=Ow-7xjDvwj04p%jDCE2oN7L$i->T zQ62=&WrI^>PVqz~E#`@1Y*D8QlNbMqcq)@w(NcR{4WsH+J%exxUrqoFd0hlDWpJYz z!K8v{DC~WUx_z`_u*lxqo#SvNLq*A}PMS3LP_%SP+wHsheL^x8>f+-PLwuzXO z@?LQhNuNJl;1E`qI$RxjRtAyf@yW00^jqS^6I*p+jpzCbyM3<1oLtoID^gXlrm1>A z2UPCzs6n3;t)v-!;mpc(U{;S5v9c`zMOI76peffP^lwv+4bxOGXndOgg{!{e!X3Pt zq+hWslx*EamXwZW&oYZ6qwG~(l1(pOggu(XL!%8J8^Jtg?xjg&;WiK!`%I1mD7 z33ob~rWbp3G_6qfr-+HY%Q)iPabUaH#s+P-TX^uHXm@mCRRNzy!5UDY*b<{M7NL!5 zrpAV1OG+MR7_(RgNi`HEam!}}I-G1^_hBq|a^Lk)8!oYV99o5UkD6WafQIF(TiIdX zjUEU@t%T^97uKp98_*7D7EVEH)ev>s3>V_?Dtbpeag=3@&~X_~UO#_Wy7|VOR9G4ssD(>%>B;yD0tQEXN13l~4p*A3Kk7cU^<)jpS zW$8Wlet$0wNb%`;AdHfZOZxL#)n&NWgimvgY>Ip8uxjEbb+Btur;$<)DN`LRHYMl5 z>as6vjM54UZ(yn68QacL-4-sDBWFSl^pb^lUzy5fWvOm__i~P2%-GZ$q4#!k`;&S5%sBR$ zU9gJpmr$*FUej&>HCG{rbf`{s_m>=VaO}9|l#XzJ)7?bWxx^D|yX>#0+S>i;IH@GI z9IVt)K9y}3N<-4w&@Ct4^?yL}t=QMb6WflueZ~ROKy(wqNGkSi=cKah4@|xw^taaX zQ$3U(lu&MNc%n_V8XdsGKY;yu(7%gfhJ5oaOAz$=Zozy%SaKYyg18ppMRj~HxY2~a zg=q&fB%E&eJCT>m`*wPsub}MdLz1r$7fc*?QnhG4b0L*&--+1fp#+c2?ZTRruiO%03$1g$`rEX1rftLPfTMJnZZL9#i zpYxcQ+i$FiyvIR!y*^yl(nYW#h;)(oef}MRx`FoClxAXmBM6)2(V=B=$Uhb=wXiEA z)azjE1Uth7w1ruPV<;>d_oK%f)PR(2y{KBb$0=M`MgmT1^n{#%$md1K__#q{ z8Bld?Pm0D5;f%AOmWFwBgv$3mIUyYbq3m0Vy?wo(6LeR^`>)aS3VBGVo9|EQgao>B zqW7NCBjgYk#g7= za!P@UHlATpc$dNr$Brh0ZNn+M_Dm~>O&HpQuog4L# z1`9Mh3!wv(7j}-rmJL-C@f?z;_<5qN>T^vNCWI{&Dp&fvEJ>r8UK0eEycwCD zL>BD%+0pS?ub${CWzAn;=V+@G?Dax;$}hAtBzM?~*9&U(zQ{$BoQd>+N0wX}9vk*B zesP8;s5ihR@!<_)|5#LJZ*2ZO+;d)%p=sdZZs0GsRT=i`rR@DnO^z@Kj>1d#8-57= z5_{l{I3juMas4urrM2iU6>3Q+Nxa-PdgPDv#+ndT?Gv|>b^fCR7-xZqE6o;?gZvP$@>@oQ`pCPQul zsw?JeZ3I2D6qLvQI)h}t7s93Pj+QCgdQ#f?dMiiUCCXPJKtm>NR3q1L~!jSwRgPPGIm@#(9u+IStyVAEpC>!6T;?joLmfpy?U#`G1Uqe z8$FQ*^&;fkY!rA}htCS?i5_v5CnzheEg?QB`MIM@D8>FqlV zg6Wl&hS%&oyk_eQ;q$!H%~HJHIx@T1eg0hrhq&1#L}J50g;tVte9P|ehw(+Dcbg=E zUymloMv{|*kjQ&79BD`Stg_~o2(j0_*FjReJgAJv`!W=lcpZvIOcePTv^bZ&m2fYnQ&aHQDI`}dnH1vZ6O@DRcLk7vLhi%)FhFV!Sge?c^rqqYc z(f!(5(DItiD1&Pgg|S?@Izp|Jk7Q^d!%?U#sEpi4&C#Lk84o5iJAY0IP#NQonGDfO zHO}Q~ps;m=I=TM1okhJk+A}Vy-^wQpt~qagW7H3qpx8=GmhPnG*=s3#_DPc>^w-zD zKw5MT7znNjzng8L&r| z7e|bo`ROq7=iMX2;?zq#xZ_e4i(iPx#cGOdQNB6tP|q!XyurM?~J3eCVWSzA z4~gGT;r;D06wEJsD^-x%!m?L`HiO!4)hcSZ5}39pgU`V+k|J1;vnT_bsf1y5vkbqU zZ$ut2{04q?c>DOpE&L}ALcd@!uwu5k^D;Of9Y0+T?*ch|IU@(nD4gh`U|T6lC%%%A z#l&({j`shdPL!m%)OF(hpc7xs$YB~@j)OD4pkAtktN(wlk&+CWb$NUMN5+TRFyu*>p-3DLv?dYpoAjLh~ z4)}2?`MDWUay6OC^nXVkv#UqGbNaK=1RH)dzSsF(l`Zy~V9MZHTk@2)XzVI{>ciW| zyjnEp%Ji_cj;bE}o`WZC=~^TSr77P}NuULVs97HgmZ8mQ&gf!?H2=nI+!kiz2l|*% zPNtYq9XfC;81;wgsA@A?UFq1fOv&~~>4&X1pbg_zQK!f_CDhNrv%-w}A0}sg8#aD(7D(#18o@Wi;y2 z2v?%iI{9@vCfwR;fGNO+6!3tw<+2KQ+#Q7F`%OBoA1t<37G}MRGs0zC1gbvhx5qfr)09qurPRM>`w`U^mFhzd$sdc0jt6>K2|pT zSs!0?k^-;5M@ujU$w1*9%r~L*rI~fW*t0>sAo~|V5`><=TAzh-eduxyrs&vg6JFC^ zpRCratFcL%smxC=Vvtjd|5#a>9IlO`s;2%L6N#IXYI+~01oLMi;)?SA@bDKj-p|27 zRpk85#gQB{6w5+IRDYK&dwsZPI2-9l{2osH_?u{JyyW4%S9G?bz!Q7#mCNFRrF^wh zj#HsKfiC|eVrzwXsSw+oK&g0ItljpIm#xjzZu3tki%_0Sp;@k|&Jow#5oGc&$xV2T0Zk4t!4|4YV&Yv7~Mn-3q8D&y{+XT)Kda5wffJfA%2?2}L0 zgGZy2-g*#<&U^C*M`q=9-kgyBLer@76i7q8 zDRQ|CO=KgJD_9AY_VWVisQRFjB^^5Aa@SY?+6>;=>xA0fE}x7J1t;Plb#n0>sIbh+ zM)_%xl5tpa>~DC5h%H!;SClnegJ+n=l&yqoF-wDTN$Y1uD=_{Q<@n%{L9&gRf!^d=wO)siBgzby z%j0l|Iag0Ps=DZEHUc~hf|-{9lLi1++o;|9>Nx(s(eVZ7#$2gEKqeZ2imrAOGnFIf zRkSvn?2#Rw%2}Z2*JwMk_Czjw-l=%+n(Elt0d@NfzP}KEaS7H4iUj(Ot}j4gGe{$c zrKPG=uVr#b3V5cXNCv9f>e?p5h>y=J2H-YE5}BBoz>2H!g+=_u!ZB=yhmDSM$b{;D zdL4ylBxu!_<2uX)2Xs;ynynYqYP_y8wyZ*pgb+{1Qe2v<0I=7DBTsb(tcmNXDVL+Q>D?3RlCSj4jABI&~&@1Iy_4kLF67y1#itWz;br zTpSTA#Y!(MfYku7=pLNX4y=G3i_g2QZQ9(hxLt-6vrMBuVhE;zF;x2qP7p^Qq z_Gu>Hd%cwZdTWJfuYX+J*ob=B7a!Vj81pzIfqG9!q=B_cS2juOIQNalhVfTm*5v8(20xSMWo|R7IBJQA4c;N=TDdR7- z;DsE!0N8S;*7$%rwyJ5u#%x4C$?@G({-I=!J+e6|CSb;cQ~6ZsCqMzz6c|?nl60rx z%V1r^pveFo>tNMOzftPFIy_(gL~YEFW&C2lp6aT!T^T)c;>LV9PsgGRse2Rd&4xP< zyB#%t=fFJdP)Yn%A6_*|jGoB)#}8Wc<~>C!D>JN+MJH8KG;xs8*vP#TQd#1i#Q0dp z4dtd;^TgIs%IDjq5^TcNK&C)=j+gj zM-3(-dvJLBQW9V+G~qQ6h%$26NUDzdP700ba;VVep|~5R?rL)Q?Q4&t7rAnCcgLS2-GX#J7l?YsR(#h z8PPcEac=C~FQ)D#%qinX5xCkqKbH0s$Xui z0cS&q1?X~mZPf06PmKlaP*g(;N*U)y96=}73Q>_*)npJJh!Xo!=8&zcR4vz{LNeXL zXM$7PtRy9{@;psSlpslw>z>>$>DU^c(J0ufC51>B%E&v!^E7Ba68|Lf(=u5sMSa;) zqE^zf$)rq`Et{r4s=?J9N;;lNa^(C-KI+L^52Qoc_!Wf-6i8oB%9L>BK;_j|)iFis zZ!+v2&7#}t{pKhfNH12Gjz$4$gav&_#m^R+;&pClYc#JdCAQvDbpgIg zvo{Pb^`L}q<76Ks{5r-|aT_#avjax0f%BL)i+M2|CD|Ag8UwV=5!Il5Qg z4ZiZ?F22&rT@>q}EJN03qz0^^!3s+S<^!#Qu!xq$B93F)r#uT{r*3~XtnAaGjFF^I z{Wk58kGtBao`6jUTgRF%Vt=cKtH0@qW=kWYYs-b8Ev+uT%H|C^l!I++b~{eK_X6uN z$tv5`L|&oAz6cuA3sFoUS1+ha=Sxzmb=Z}Xomd>NWHw>$$dpfdTp!uF@fSJfraoqR zl9=-xk;-RXs*kBA7xT;4QS*wuBJP!r3-2#Qb~I}3ng2YUsyw{6GA`Wr^T4THKL`5p zK$Cyec-TSn-7%-?KJO#PhAK1RMkgUhBx*gao#&*GI}&ZKr*VR;7hhRky2OPS+;$r-W+*gGAgF; zrdLwqBAu3^a%WU@`yfft!Exi{ViD(vRPO4*5zVBy2aY$QqdjkavI#x(ASW;ZQ5QB; z(Yh5vv|TFY(GBXZ(L=f=Qi0fdQz}WvK=)xT0BwD!PK5R0&}RZJjfzrT2+7(C3tfoG zg(}{&$EI1iPG(Bm?cUZVkUmTy9r3ZzVqv7+*+cfN~#6+ckw%$jml7%KG$n7*N-+x7bB(t$WM6uAuTvWc&{Sq zV(Q%I0{3GS?l^0b$z80T%H?j{SR4S-$10@QY_QTC8`^F}<#Ihv<&sr8-4~F0(}J3* zEbk(1(Bl<4aU%^iRK41uQlWEO8gXmk6C`D~9PjLLr@YId?y+7w{T_#3Is;nx#FRAY z_aSyo+`BVlC~Qu8jfRse{^W?bKoM3*nQZo+NY$e}DW%KhUK|YJG~vhfP$HBvd#`^@qn@t<+xp)Qk@6cl**URrR@S zlsEG}{HHv4?G7oK9)C2hFA4U0e3uRJ(VCA6Ahz})oulkIXGM5c+tW!vw zn}+N7R*1DFvKOMdSrlqNp4l^(Pxd@zKc1BmY(nK;6c!w^CjWFupYM7;UVqB3Lv3qVXH-4 zd?@5qo}us9QRoF3#YuTO%nx3aJg7Rr7aAl>z3e@nSszq4tQRRfS>|;&$6p6x>x;wl z!p`R}PI({)ls{GMeT1sheTmAV=c5j2CGB?T@FYm)rIH4v0#6%u;s_h=D+!Ib&_Pd7 zbQfJ{KqgJ>rub!RJ7#MNm|mtbG5n}_VwJ_tpEt+uhFzRqu2Rh>(J|}(3oHI`JzRyH zo52#jqGuv=Dt%-`bwhZiN~GpJ$A`eUJ zb9e+5dwQxx`-N)Ns}-&+7o->S?K6jzMR<)ul(c`wCs(W+t9V~g{Urs8JA2gK(`zNu z^hP6Uwilxb3>Db6`nmC?qY4CjuBaUH*QFFKQA2_Rc+=OT*=Dm)xpuEtSVUGO6NWoG zl=tz5j3t+CI4IdrR_KkA2ZOdKm&DI!M4=QF;!27ldy^zP)P!C!$px4h?d(~hxMb7` z?y@a5$Kw8|`$jPu;3Ra?#V}(9O4~iysGPL-XKeZG()_tXvmgo* zdu35O`3H2SAw)aU0t6#QHVXFYgE~#7OL^%i0^#ZpRSWweNq{+%N$C@6P99)xC%E&+ z&hy|Xjo7mw7C;goRydOGOZAmaIX&V6CjJ}RiGW}9^t*^?&_^tc@(xh-Wb%lR)8-*J zs#^A=3X_p-Y8)>7a!*Sm_RO$zVD3JqFlgo{uSD7!7yD`Y4;ZNo>{U_A)p3qn1=rcj3mAuu09Ybtwebq7$E(6tZ?yE6__}cN!cF>C77$5JfuA-8{ zbM#AR`Q*Qpw#gP^`o7ZyP5qh}dwz0i;@~`-Ii-ruEMr;U2}n_hZ=B#rI*osjd%zNZ z0EJ@kF#2S)l3a9xCp!%n7;BKu(3-~n_3?d=#|)OX$xxmC1ugi3uV-H%*8Y$_fo;7g z+xiXh4V6x-h3&i3M}x3#d}cX)eNlIjAj>n3sVv8do|>^>)0NVKPhG-~1qpvs&;@n8 zxPu-t=TRU=ykbX2wL-PRj!!b&?-+J+m|TwcvOY9@t`wm^U+i%W|r~y z-8z%~TvZyc+zCqmzoUMMxc}-+Jx(9}-=4OU(dX$Mp#R@hzfM^4MM&t0CjcTkV07|p zI-3AfedBM=ey&cC^f~?)ivRfuwuWb zYQij7ZAekDXga>tUYw5uC0CSyLw?3#^HXK{D~6qP)mML_D!tDaZI@w1eXi~gU-X0CzbI&}pC;c! z_!vJ_zvn1~lx>=KTKgEKl07nZ516eVDPITbCEjS6I@zc}tAnKz+u)_|8JHx+&zm~7 zG&>Ew(#SwHb{CZ&46UMC2NbpD?{LI@(Jv&$ zLML`H;hoUrC=+MFO;*|!VfNEh-Aqbi*0aC$V|3q({L+U}iv;@+QZbWXNkVZj(=5a| zIP`HyQWiw}=#xD38+V|)U{(CuV88{DW>v##ATZb(MO8rlMslDLnA&KxH_J1Xv3fm% zh92Nf#BMb3;3epmWI=6V6HqU5{Wd0O!!B|pY{9$3!Ww{wHZy|%QG-_eapi@DF$35! z<#*Y*N5dwBi$t&vJ{5-69%o{YO67C>J{?s_IR+_X1OjJ0xxQ3wiz0ZgMWcFi{Xvo} zRvK{GwFXb6CyC9%m>t1C#z(WT8u#!-tpo28HB(pK%hTN>58cMBVAc8`9aK{gf*CXy zwX6Rr;Tl_6=|GGHR>|@L)F;CdiA^p{p4ST5Bd=KunM{LB{+y1Dgia%6Fg)rA)Sv^X~Ep7gJIwgyg{Rc29R zvp6bhF8^jyO$N094&QdJ_=g3O$!*)vvi+cCe>cfyg65hFO#G}sF2_!ZnXLaXx!__R zv%MT5OMY4)8zHlQnr!%_2kvAxB@;g@kV__n8gq$V1^#7nr7;5sTl(|j_;xww6uqB+ zD}2y^5sSK`W-H!;XO;y_{5l}SFu<@)``9n4PvL(gN4454@7;PZOuCYuEry*NKU*1? zz4tnN5A2HZUwz#9xv^%mvRTHYMd&8KfayAL@f3(_hiN=oh&ns-X9^tKs#E>K{^zD^ zMh&<^gU~m;5}I+Pf9gX-^{(D>+cUQ8-5<2aaGv&swLp{{CX?i)9aL@4MbuGemZwgF zPT~`_Yf&BJXP}s3TC~n3J`$jAy#G&U*;k7yHkRbip_erhDllB_-Q}=)*v0yyI!hvw zOduJAhQQX*645jH49SpofSai-LVEAP5bN{OGX%s9mMblIb`6|ch<21G;R%bC^-6OI zeBH1Ok<&PIpoJGE)9WYY0Lqmgl`(L!ls!oi$S-@)tpgsjS4s`YRG)1% zD8J(}DG~JLEP*J@Gn_E!`#SaHY2ra~Tvp{69V|zBX<_(@`2N@BRF3k#3Y_ub*w~WS zEntOI<>1=D9Wrgl7#UywySz3sILytJWKZ=P(GgiV6Gps3enhe4ea54F%PSh=i51^z z!n4=0sqq@u5iPhCeCL&nalu`J6J~ljgLA4Ttg_eF4#(Ixgt4z|j2#!-8fwHIk&^ON zEF&%iYjHWIF)&wusXhFv#<=sriKy1W_>3lL#ei@}r+VF7%@{o~WAl|T_I%uCj7vei z7JYS-qM-eZItg6E9J3uXiiyBcUwTbr)WvlyaLbtwwq{ei=e5)^m9S$xc2GSFi)Sa+ zgO%gp(!}rk#;0Y~u&{}!`pj#qL{p7cSc~eocO4JfE9-@mP(8M{1tMaMw_f0M$lvoh6Hzy>*UgCHHs1^Q4nVfE&r}*hUBoRR;Hbak!?fLz z#eHwcjJjUNq+I8;$`jkFM#d{d)vyU=cC^xn42kLAcND(w2D$G;kwP#RlCUAUPFzNo!G$`3|DVg^ z7W%7AgM!JnfmG%3nJH(~h7uuReQ=e>j@@(}q7SY3UMT)i7A1njXmOWz1d>~`t5oiO zOjT{%P-l~lNH28qcQG5$i;ue7yHU!wDc+|zguBN+=?^b;&lmEL8&&mnW0k7`yJ}{( z6OU?a$^z#>v(ph9zYVN}t&*kz@!vSJD&9H7Mh1U~=OnLNaVj6+Ch6#nBcRIQ&z6(_ zg@xCyK=YjVruu9zf;64oI2rDR@4snA(1au#v21X6udPR&0N#kOtt%tg$zSm@_0r(M zH>_?Zd73NoU?IXcZFt`&+0xO&XhNHC;cKMfVCbIj_u?w`lGM#psvrp`r99g4j&EON z;8(ZNSn+%dIcA)4gdMEr;+~^zo;-3vA?AxOp}_yz=S6; z+60yU$}&oS?zfVttovJ;Ea;@BaW8!}Y5B-lNT2y31hdyXl{;`7+t*<-j2bJBPZm;>lnM3mJt(T%+Ni`$n6QcQ>2DxZ zR36doBo$PIrlmTf(LkI?C!fE8ETU;W=Lx*!fz`Z>C{#+;5*CEu@*Ruq(LiK*MblKi@pAzBqN{YnH1 z?~kP=22%pRWNeUJ7Ah(*sdkA-Qr(_Dt_qMC46Cmw(CG>+S%I|I!fEHn^}z({M4I9wY`olx&+p;>H(8yi7& zKs3}D@lFcOF+8GHn#Yr4p)hV)fsdDcbLa|Ypxik}fK_kF1!92=J7vJi(AV1m!(J;x zw(Z7J@I31{Zcul^_+?{!&4mFQ;uZr}GpSx~V=f9(5NrZM7seHWbA>hzUl%5<45SG7 zHk{CfNn`v|qYG0m3Q`bUU7#$}K84^sJZ>khFS#7}GvUZI!dE5s8w4U3&cqOZSd5jV zq;bGt8NQvK&Jhe|ZBlpTXBiBGTN~7g?QDa>eoQLPW>e>+g9*zxW~A4H^!gYUjBxyt zeRNh0@p4xw)qQhDj$dhRiWkdaVDM(fgA2o;nYzJonf@%+fO(wPN~3b?(Bqi_tkv0+ zqZLdB%@7*bK)f3d)RVy~T;{>CO&(zpMugXrKaszFB5v-2t!zpnG?%Sv1)6VR9G>ZR zpgyEf&vrJJ!ShPv(MQAf`Z%N}9FJo`(>^muonz*53?d8>pY05YIJTK2)=v-aw0TaR zNfs0&hfV{`D{q{V&b&e*PT!bm2#>GgGuM+Lq$Y`L6}%Y~VUhI0zzEW?wT8-=SgD!r^?g+K@KNXJ z#f(ku631*~&p@}q5I3#NrA#AA%3+hOr;hu-JA%DE*T$g)CbD@*0av2m#3k#0g^+cqlBc-L+;lvvm#lw9s+BLUYY4b%)(E_d%nQ>a|1 zm$sHv63i$=cR^kZ1I1Xc^I!Y~y9btPcX;x}C2HSzW_nbKFB=oquR&LxU$z;RBslD#ziTR<^ioowrPj zoXz8QD(%9VBQh=<^m z(X&1r57d$CGYd1@va#jDwgZ(MLD#m?P1VFJkr4FmV_9%lEmbGb=|b1fcFX0XtjY-o zUmTwwk~iSEgY4r5H_883Sy={E^o55Pt+=P_b<)}Wy zGY}CUxDUZOq_yF=-NXKe_qEX-fWXQS_KZDpa*dNC?zi`|aY4CT{{ai`+MufP?r)iEu%*izjiBWs~!>wddHCzpYs>soa z%kl@hMu|Q25jMUgcW6k_Yhtf@q?2yE7Ofl~!cI!f?W3G@(3AltcqOiNXI+YM_fqOz z+ebV367L9}cN6tR9OdJmZwOoW7#pE^BR{|!3QBs9?ZP&s)j>$_ac;uC++VKlQ4y!d z+t|dhVMub5p+v)v!!F!Uu(H)Eby<-<*!4h=J<&>r?SbGqxD7~^87vvG!#>GQR@)TE zhJpJ_L=5N2RcpXuurk^+Kiy3>j9rzue|?6VtRHunFExVgnKrhh>D^Fflvt_HqS%HbIbyeewu>(6 z?^>O`N(n#xIaapyAZX77@Q7(2Zf22m&vnurt`F(vm6+G(S?NOP@+;fv^R|7ij7~wuRrRwQ-mzXE?DuOdEk{nX} zftcr4TNy718d-1efy$4{ICzbXZE-!hOB#~w2p|2mRz9;XgJF=_+P8XnH%s$6PywOI)S6`)3oNVa^aE>Lgk zg+mn5`BG4o?{C&9Fe)jEf8zHZpnwtTgAd0A%+08Vt`UX>mEvjk%sBS(-C*b6;$ne4 z(n8|JLG?I&tDPfx%GVBKh=r{w)QRnFE*^Lk6p}rY%SMZY3@GkgQ9X~|Ze>b(7W5<{ zHjN{aT?Wm0hn+?#6)RpkR7Kr89Xz=bvF=!WO>*pI6CkH|*}3%6ut6|6;!Kmk^lm#- zGpw$~cQ@i9qB|5bpGDk1DAdAX~vSxMEOVWvopDh z<^~}#!ZN(yMVA)KbqC^V7Pa4gfa2+4>bNYFmVc09Q499Ei-@mLgy;An2UosOuR9o5 zUerGPVF#C9pf>;}c{Dcey|EwrM;uJq8**J;C-&S?eM3I#;L!>NyNig=iQx>{A4pS^75sG9sJ# z({3`oS*^h2UVDLZL~S2z7Z?pB=~hJ zky^afT|`{HqN0r7u<}gTTkT36AEE6*JnR{xtot_|JZ2GD@lvAR4EmOx3Yt_6MnY64 zvu`t5{9-QDN%T8b60@YP_?e>g`MY+etdy=h4qKmh6YKqZ4xX%pt~(HWy*q9<#=%ed zzME%&9tzbD?*~?*gAg^x_W}lCe<+$C+G!wJsK)>>TYIRr{v#)iT{44P>pymqsD+IK z&}Cw6{iGXH4?<$EE#eO0r*>X45!omZm^9zolrhB&+Td#$Ic?9G3C+K=nH}m3`rR%@%dO_`RD% zEBx&)AnvSDEA0<%Dx=_c09=lH9js^kqnj%^)2D^L-ND!zMV&qVWF<2Te7i$&XOY@b z{@jge5V}sx-e0=$4AdS_v-ekmj1w$k&mNJL@;57!QCQqvq4-+v$mbn{_4ju>lUh(b z2qMBu`G=c`4jOq`bSekzpLQZlBJA9O@M4B2ENwej4 zXKdNKKWM}A)?rILSA+SvbFvhE05nv_&_!sPf$nP5>GuDuT=T&il!EpkBECiw=YWek zShCkido+%GDq=r7qbt=QJ%8{s}Q_*K`o+Z3?5iQ$;A zX$TMQ+7t~D!|`4JRPFqAC?;Ezgzgi_)+#Eq`nnXK*&Ja2Y*c3T_3UhVQ-lF9Q4x&m zTbV4)5C*_SWxw3O!39Ms)xqp~l$6f2(=_3Yisb3-o*g^BwmW%s6$V(Q!f19ne5u=krM#t`#^CULo17T`R&xAc4PK3};k)QlC!u&AdoxPKzy>Rd za`0CW5s$dFGXBZr<^&iP*W(Qph6Ym%S~^i_w#BuI;Y4y*&rS!+&0#KXld+%|TWNQi zAdkirG&qR+yC_0nk4wzKZL@L3STl_yL9-p46obye*CzpnL}46RU98nO0D8O8i_Dym(QWlocHA3fk$&*|;sp<@O2ud z+32w%mSe+yq*ChyEwOlGQ({$9F3C<}kGZ2tA~sgV#0xJ^?zhTBuvaZrrE(_=L2l6& zM?&m&ceaoy=M}k9ECMNVM)u|@GP5B(9t!|_MyUQiWs^W|E(6+ase7<77fVh>jJ?;> z7vANt9oXY<+{y&?X3EkLHq|mwTuq^}PbN~1dUapa+<-oM!gQfoJqz8naqw3Ux5FA( zl(F$C;SQhzEhh$UMA?YBTvW_vQsJ5fF1Ygo&lRsN;FZp?cDoraciQo>Az9bbGw`u7 zR1|kgA(F~W0BHv*FD6GE5NoUUNv5z~h0HkEeJXIu?9?jF$#5eO59z{2Fc$C5^TBG| zBGHS+;niYQ%>F#hWC&0DK&QAKao8H0~04H~5c|2}d8ULz}mrI=!XH%{w)I%rUKh#Sm;VZ@6 zjL!hFf!6__oxw3fX!8!GJneS0QJy@uFFC&AU(Q9fO}zdW?r+9ZR)xd%b9OlVy&D0| zoK7>dJP(I4F}fPkW7`rU27OLCVy9ls4u7~=JEkN%08D$#Nokuj{d#Bm21sFEWvW12 z1vPg+v@TU+oakKb-FiYC;mcxfve|+kRBG_-Wd)A+=FiHlHeCHB|9#$;M3(Y@Z+B+$ zpVE#!J9n3M?HWmSC;oxlB6i8YFtWBrfqx;#FIT?#5$f{i&T46!bO?0`wt-+#C)gAo zMytFSH6nb2wtUv&gwU$JiVV%LttLeChpH466UP09oes?$fp0i0zX4Ce>!1O$g@UkG z05l(U)}YSXM+qGA{S{w^fycGaK5A{4aS*lX_eBuDyQ=oJ`_Pp(`MJt(Hclq0nWG@2K3( zAQW4+ReoT&QbQcrbl2Ey>>ux4XrYorR0Qjgo$}r)ZkT?4KIR%x3|8`F2H7OeVvN}5V-W$OG+c%o9nyW8=_W)Of&(t^{lm_qPF zI`#1UPO#KyH_J!F&u615c6i*owHmBe;68Dw8o);c+D{z^mp;Bh9$oZrI04^qocM-J zf`j-B?!H6KM2+~0=_*djX)+Vr?Y`N)U?w(*Z#dL|J62TY(pYotwD}ep9MZMw-taB; zUcO};dOam%RG7Tmat^_3|x)`HVtWdV~0m!>yo+t9>w^!CmQ8kJ0nfuSl}? zz{T8Giz-#5OrGU_jeTzd#>a` z&hU!y9;!|A*l0B2a9OB4iH?OuL>d#R=)oZ4h{V?zx#1d8!G3Hfx{w&^f+!xbYbvMc z0vAcrod%QzVgN@;fdBs@B4cx|Cv4vHX9VVdHILSQuazB&8+r_ zpiK;yfVz>pzea%9=@>nV=4S2 z*p{la&HWV*wtq$XP}S-571D9i=K>=Y53zoQ@_5w1K7(zYM(tlwgRgj~^(#)N)nJcM zoDb{|v$8Kn%}#4@AHZG-R2=x>+84-NMoZ!#MmEhn0*^ZrDh~VzD^os6t_u=dPE?i7 zBNZmBR9ha4p93%4@0H4C_<1`(+aBd$09$Lg5{5g&mIRgA@n{Q$h>{fP?Mi3BkOKNs zk8zD}p3J=bDeC0>Sd$>RZAYmWKNnO@(Bm8=f}>D*P^ad{J17p-=dd?$4?^MYC)7%P zLXKuYK0k3Hdt#meoYev82XX#+l9i))?Vxn@$tD9HKzb4dS1YJ{vZvT6a*xHB8=Mkm zV%tzEKjEna%>ZVH(2%E@B;u)v0We4h(6Hzj|9iU0Ak0Q~D0~0BJdN6>kT9|A`5|J}&(BeqhvDL+LDfFK zz`>E(lwz$Lm2>+-2a6?K#LT-pCb+yg<6;w=Qx_7rCC*x z8cbN2mryi=urLl5e8=gf8jFk)^Iv18N0&PGFQO-nwx+tf+q_1T!sea&cNp$(No8)b+zorwQ( zrc$r0;hVHrI59UishTzRN})WMHz*`f`-){-y_rF(uM(jM`IG$3`G3*tu_YGf`SQ%n918L1hF0z%s@!t?G}dpL9|gY0kMm} z!$JVMxN|#=z_v})dVHsype2uf@CmK1l&0&?T|)NKW97;B=A9n0NShu&7_cCl$&Iw98+e#&Ebi42rQ=m zz;t~zGE$(dzo8bVC=Lk!i)5%o|DuP%7k!BOqI^k&oEr#_Rd zsU?}LpwH;t;443Ber4Edhbu|b8YoamOVJL?y6>bJ$gHN3v#R#Z&MM1_KPRJh+BKm4 z7)e_lt5<59fM2O8+;P%HnrJvMZYtyU<0*Hra(p6!#%$vHE*}MpLkJTK)?#(;yi>V5 zpHPY7RMUQt*t5GwPOh;N;=AFW)R~|Pab|fIdXddl!sbEHOBrO|yEPRM#CYXzU>rtcsONx-nWfK4;?)D=N7N zc8h~OJ5&u81kBi1G#YuAHTdk((ujo8y``hT^i|1JtF&5^L2IQM zHn96yzHolR_zh^pdL%hlp{<0AFpLR%`AH?I?mFjr!1*zfO^A z1@LT4wp}fa$bhac(IX(yZ%~{`O9YYU%*JF(vn01eopK2%ze!Q%&z=2DK3}ROyHxK1 zyx;1HS1Y$4f_!B#RhREwfcM)y@rwJ6A<5Ql1HMDCnq~8YmnvaZzw00s`|{Da=?{2v zLz1Wr-0HJ`cbNU}IXK0h4J}l_u(#Snwgqn5?{}dU#`UzJ{egoP3OJxIWz^Ke^Zlqo zk#_cn1g+W1duWO1`y+z4E|yu^&SdbGVAO82s=)bUiZg198f9z`b|io( z6}~{#r*PT2Fk)Y4i@^L7H*;KiG&Dwe*`v?@JTU&0VjQ9r-JbQ&+=Q)Gu(4c=vj+NE z_8iS6Jw|KwYXvb&}QAe>VM&8%^qVI7AN5a{<1qK+(n%Tq4;`;x@z}Y z@htEwC+~RtYVq(Wi8J7@-IVa^#PBVsTTg%ECWRM-lm44S(^I$i|678y4wny`eyDxe zDfdb6cO>hf6VS&XSQ|R8eHTUF`u8O7;rcQ>Y2$~>-t!5I^#_u+HNJ^XN^8ix#0l$< z1n-4mAYSL|bAj=-cEql=hd~Se)P=Ke!^)>Dxpno=Zc0mzT|X`hUsv58%U|5Ixoo`Q zu(~bmUrAPYVa0Dr^`QlBx&F<~3Xf>4#T|o_%b(%MRThTm{{ITBtG~NhMZarIgqsQ4 zmD(+&TIGG{sX9Mz@ht4Xmu66lh7R5iivMN<={U`%pbBS2?zbIOfIX|>>HIbR} zZ<4Ye)YQ%`!;vWwdH)YZ8z-rWIE#lt;OhV(-T(H;JIo!|e&V_ZtgrvMX=_mnQ!tuv zpXO%}*n94X-QJ!Hyjw=j*s^zj&^Ci<*v$=Eeka|%hv#wGeHn~iU4$Zqo6X@_0y`s~ z4gQZ{+}Mcfryw>=%>6|PTIl50NQ;9;s;}@uzF)QN!^XNj#EVnZt@bInhj+(dEyVITmU(4+cT&5eZ`kYpun=D@&-22yM30}A|o(!7s07pAK zF~s{UBDQrons%l@S9pcP-p2^v;0gq5E39`~QCRI;sD12R_8k{9 zU9L!wV({2gsC-spPq>ns)zcl|TLHJ8UYVebqT@l;(={xT^(t=G&T_aie9xuimg`k% z*7!zlnBEI$?=TEGl2vjOU$+Z!+KN4@ zdAKg@6-Gp2uSXLfj8@yQ*Lvady_b?bR)}{q*Qa@vHu1w|c1MnGKr;{BlX0KD&g?>Z zLC|RXDFa`3-Cp+%XehlAyIC!-dJJVZBk~KjI%y|^ z)u;(2r@qH8w$2iM*G&l8`bwCzdm4^R-4^Sn1ZBGd5e+Zn%9p2o$3>Cic6!|)-O^3ltgL!&SAEFU9XY<0n>2e= z%x`J&c}MJd8Vj(tZtdjtIwJ5Hi5S6cNJd{5fN!bno-5vR-_}hE)vwTp!^imdAyd0e z#dFu~2+{zIYCG2%Z#esv+hZB&f!J?hu{D9XGbr^!>lrfU^SbTUD8-9+F>nO;#4S{R zx4C&YmZK{A1?^L?hY_dn?QY^U$T55gyVu(eH}6SkF;;*=6sf;E3ED z6|ld%DBfBWzYu>4i0z&NW_0c*h}*FjK6L-qZO`^lwD8DX0qV=QXM`VidnfO{%1Zy& z;Cx16PrC!jI0II9HS`T6vTXuU<9kPv*H2m5oP>XSCz5mM{w+azXE$jGS)A?DN2Ytb zkq$SyLEP4syODZ21$<3)`vhZd)=sUKys9x|$-1+m#(SY1wseV_vLSrYL zocTW9Lpe2$+BJ3^yp}09Ycp7jnw#_C@-lSxJvBL8Zf|Q}H||s58@gl3`w80c6vXE3 zemC|jJK!We5HvT!wnthF-`67QKBv2I4l#;Mcvxq-DWh62^-NEtPwJns>8CL(d-|EdK(*8P-pqbKoM) zIc)ErsE|2KlD5aa+kNn@6ycv7anc@y4mHEwP3=)Y?4EIypj-oi4$(dlvOMOd%pM#c z&K^ow`g2LjAxe_i=g%Vvv!)x z;>n*rHzhVDajv^(H%i}dP9Hm{d+i^mX@{tzc1Dbl)o8}?TC{R}$gyX_Qf<(*aoWU+ z$gmrtbnOu+%uK4&yve&Jri7$Vq%?b+rXOrw|Mu2t&c zg~Hs%&*S##BJeF3Q3HR%O%0E6579Fu?ocnF8HJJU+i>xvN!WptH0iYOMJn445cepX zU08iC(fFM9Tq^AFf1#T*3hO>;6MRZ{{ms2-%3rUGD^s`qK5w*HM$vJe7idaz(AW1R6GThuQ z`w0tk&>q;8wC}0!V>P*BK@TE0F9@36*J*q`wcqq&$HoV{S({O{vr<4YSRb-=TdIe2 ztpZr*Tt z*cO3^20g*e8FkkBzjo*E2X;$~oi3i}X6)34-|2=p7d)v8saLsLp|x=&qCBWWY=M$>#N;I^nwCs^@OmtdF~wnRku83ZSE z!X5rembk%qrkfQXCEEuV0&CDq&_5NUk2U7LlYbUXUBFXnY`rDUUeBg!57)6hgxAKv zrX_Z;=TNk@;lEJl)`I7{NyFND)Ct<{_!Ypu4+w0zmh9FG&j-&VXfdYUUSC{?cKOvK zVQ@j;zpmU4fMyL>H^G#7w(jHex@G%9f*0PQ82%nXMCD)P zX5EOQ_ON?*5Q3KFrRR^P^oElpzPUq+Lj zR=4mk@4`CNTXU^=uT@hfnSH zQ{Uhw#YGSOf&7o!MeeI_?8-ZAJLQfNzlmn`1MNc#+-vL21ZfEG`whRZp_b9B=cq>I&NC1nystDizZ3OLRt^ZU!KBIfy-|l7{_9=>}!GA|LO23p5 z{;D6f-wdpQj=r-S>ojEI!?mkuzBg=c;)SGlb>oiT_&Wu+#qP|3ce|OzlS7^=%-0LT zcYTkWb?Diy?mg?hT_}g&Io8ekK8kfCXg4?goB_6-B{Iq0?_^zEPkI{-e`OKc`T>F$ z|PhXB=B*H zcLN@CJFPMvKhYEI5N#~cYv7XvYo`uH=BHI=)~B4b1zf=QvBKQh`k!`_HmV?15gzVa z1^B*;+t2!po3s@)PeCM$=ym&9k`*sp`xa>*v+uT7pCdU7xEkQo65inFowP@SW;-}J zOqsg%^b1`mhkkY;`pkUM%^J2sA2)!#@`-u>5=Ge=>U|3lXZx~~@@QCX4>Pyf-BIi- z1YTj3N+mcvL`MOqYeO|ug&lzHbQ`5p%>cB@)5Y)ikkQ*>eR6HWv5I6Fepku z@nbuM$>2#a>WJun2nc?X5^QWl_4E{+E^m-S`iIKYjEx`JyLGwJ3RjjuvEk0Tyma*N z-mNR(11zB_fLyz!qvci`BJA*sl^Qf26xt7eI9iVXd~6JUazec<+3oo_!cnguN09y*T5K-E$=;! zIo}WC{vkImxO1_y7I&aIXAJ#>y;~RN+RgHjiD)A>K=BXDUOwjN4rzMz-RalB^+%Jd zb#Q*Vz5@Ovl;(+j8+RiE2Td@bZd4k$A3KO)18-EqW_f9LX*S-M;7@8nt3~`Xunoh% zFx%kezhmpx33L&99x2<@#a8ICTkLl17Iy~O{YjNwD{Kel$z%J3sPJ#lT}LxEH+Dq* zV{IzAt7^AnMVEo*&nZoop**uZFS-QCoS!j)GqVi7SC+->v7?oU+RwjaJati`j3JU1 zY^Y*4jpHRCo@0qJ&CiWBo0ZKncst53l*>ZX7UG|QBB?G2S9-e~d%_-&-d}ZEQ2w#{ z8eBZjET4tN9{4KMVQ3#8AO1`igl#EGqxKbfPayeQMv|O^X99Qw0vwT6E7(}BZ3?YQ zq^5ej;@9-`Bb?p%&sEpZXFbU4abrgx1M>qie zB&>Yy*fcE6nKr(p3tK)kebH2lO;IAE9{*zBc;26WglLEj_4BMk}2S{Dbo0F*IW{%W*_T!C=d2w9$Aa%NR1=^UhSy^2?t)=UhVmHyo1?)XP*k@dctsaV z+og%rJ%#(h?EFt>z-J$3mKQLkbh6T}2oiDROiWU`U8i$}u@B=g?)oLwSovsMX}fgE z^zYe(@7Yp1W6R$C(6v6+F*H@2uj%1i@K=iZl06Qe_7Hr?MT{?rH%pZJMkD#U_>an9 zW8&a~vf0L;FIYX|n0p-2v1#D@KL=mZ!Da^BNOY72M7GmGb}Jyes6iHQ4&XIqtIO*I zw%alGUKslfW$b2DgIMxPAgt}o@;>-WAri66i#T)qGqHp`W{HY6U(C&;T3;Iy_+=Rs z*DIrTgo_(Y=!)UCAVUO8v(xBMEKgLLZE<)s1jJ?`qVbooun7OA3F$NG!b&_7|(_pl-Kl0iCb~#S3+d&eSO~;R# zRajK$(rEd%iJyf(_3=EZo$GQcLs*APJ`rbz1ud`t(-c+En7v~nCVdd1|FwlpxDwmI z-$r%vkbs34ONH`LR)Z*A-MYG(<@VGub#`<};PED;-ATe2Wq5D_cg! zueJ`%wx?8d;wt*c;fe^Pq+`dPQ({Wb#+ZrMDPX5#Mcf8ta8-q5E`m50obV74JdtC}k~o6redb^O^QI&}C< z81dTq5&g%L$9BVN;qBgajPc?^70XAmXPB}R*EL4XZ|H`I`J+3|R(P(v9yxkW-0ZP; zICT4_F!uG$vE!v?5jb0`WYE}{<*hL44eX=lK9xHrwG*Fdj@elbSK^B30e|fcb0b4{ z(IUnqEX9p%V|vS%x|hGPG2(%sxe>}Ipj7k3*I&mDe`C<3t=3U70hj{xXs08}eRKHMo4H5M37NfG9r}wICO0?6oeAqRm6Lej zodgd)zSvU5%^f?vWVTqRTNq=faR6fzV2|neUfkJlX&cjD!j7FCz0+G6Bj#U4^^q^7 zQ@3`H?ES@5HpOj>aYaI`II`!4<=YEK#2d3XZflN=027^Dizb%W{f%?yW2(48qSP??{%z%3v1g9aJ8mfx|z%r4h8% zns^9g4s0gwsE$7$T`*mr4_3<4N2m8sEkHzZZ0dj#^59!KLI>`oj=Ufe?kA^)GV-1C zBhO71HZm1K-780iMm<&XL&EPTWRWVYoZ| zE{Bi26V||3o~2O>vxE+}NZ1sPm58}_<2i~&>{SEhpK%Uqv2#OZLQUjI3~S&P2z!O@ za-0!IK_Zhn2gX1G%K=qQxpT7H;TK>o-;_=uvSZS&LHUqzs;Pc4gUBAux@*!xVpBQJlbBz5j)O{xKlUyzwjV>~r_DRa=9*Fb zqV)i|sNAOo2bawgSC<=i)^^J6)`*GU7G`bH#h`iMKBH6Fo`+qdx6Ll>#p*F-0gpJC za$Aln6t=$ZmM*3-CsQydN1X(@u70#w*i=-e;4uf4aW?Uj3UL-Vw}Phrb0AD6;)T^0r7X9Z?hg z*fMZ!))^E)gBsf^6gy5Ol&eeD!u%?}Y8macIa_MRm|J z$l~17T>{#*9%z%C(tdH- z_Mk+(k-XjuZ!YVr4GKz(uu1;e0~qaYIaS(VlI-o*#mP@9GK;)3`Ne;P1NPQ z3UY4sz--Aa849&qJK9X@p=k%vE1+XCFEoOcuu@AeYm`DDh=gbomOYH1>?hyB(EMTA z&(P;yr?lN}P3{jmbE3yDQCe;k{z2?@OWX=y(Di#TG@qn&pYnOJdnkDOda~Phs0MQ& zpTnM&G;pF@ria7VY+5Dl+7;wLa>Tz=~ z_v1RjRs?2 z5%j~GtH2YrJ&A@pPgGRyzIhfbLLCf;bn^|FV4tY_lP;YI;PT<&>eJ@fJ2Y$?6X{a6K9-@B9fWPgx9jG|um%jsaYP-SQZ0NMsx&@Z$H@Nfx5ajLjPRL$6vbRy$D0N9RHF{$bJY@e5^8hNroVc&}4@&u_TDhyj9jyb~D z8lIA4F}I=uIfzyK)EtMgh}{_MxuLQgpJsC41c=*mcm`Is-i8Gf8bI z!|>?_SK4zsNt{W{iO;%`!D(Y9V}H447^7n!ZOE^z)v`3L)S?|w!kuhQb=X&|Z+fPU zAvYs~UN6rwC=N!e_&~6e!$%raF7>kwj+sskTAGVi8-{is4cqJEP!@9>+Cr%6S>)V2 z$DldX3?bDgs18?4pKDNv2#?T9i<^(_+=FK{m=s;2cNCX?~1jXp;z z26shKIomJIQAj)M+{UX^;Wg^m!Y<7$=lu}TVI1cpwp>1Q;yBG<{PVRc z3v8$G45HLY;gpy&BY&tlXl&_F>-TkeA`uKQagxr*;*VqSIA-8wTOIxqc2Il0%9S*L zP4yHOkeU|GT{Tn}rZ>386??zWs8lcYH>#s1eM(|u-m@X~&{#@8-ei!)#vIzHbj3@| zz7LlpK5-EAQ^C93lDX$8ZqHHGJnPua+KspBtR5W$-2ZiJHGD8LpuqF~tV z0%?_V{l2k#}J&?xnY;XM&1Er_Fk3ka2emPOYlq|NJtZtlDsSy#qvIt<;dV! zN~7}NuSaQ)gIwOPva~S0!;FGxnR8jx1|_6rn(03(s_PBK{sE00FVjyicFNvxC);mz?T5@ZlV}5uS<3)owB}Jntn< zis>U7Q}TLR<6so2?~+L(@f2-x84E;e10JrNsP`s5sxje9;AyV8xJZe2dvWl<2X;jZ zoSs<+YF1}_(TNf6Z!GMI289@JQd&jq;+ijt*|Np zu8vY(-=`c5=54Uo_;NcmZWysj{L_x{jay)Kc(xx-#SuSaA0O?N<@Q&_V9$c%w2OUY zK5L~gjfB_e#2WaVb#(n^(Q9nt%Rg@&+uRC$MyEQ8e!)Jv`vyb>Ve27v&;La`iLtS& z!*lhIdfoF&+UPEKy~k~xEf?z5+%M~7$>W~Z(}XG&s!_O(F{fgVzNV9%k~#Xi&M?3^N=v4U-3(vw`9?~C?@$%^q8$-Q|7JF(d`d5Q z$t@0P`Mjh@-5Y-^8@C-a<4YgxzFn(LblQ0HAtIyknVXW_;4@{y z_qW3ErQ?XnU}v=NI%sUpPQg^fo!R#sRLK+$N=^=+1oKzFZzqBdLd_1gE$R)4t4EZV z`U5*r+Cj{%KwJu{`~MFe6ne2?!I~pmKp55*U*G2It=%2fI zFvD{|vqhbce&OJekt=ed__|E!%`fdFixHUQ-UP$d8|nq}UnMlc>zO>Z51ZTJL%MMW zA$U9LpT&2wip#?#DUI5DLDXP9{aPh~_oJIpwX@QmuCGQSt{5K&mZ5R_%2G?b5>j3| z3N`90@HY2SyIBe0(YU3fmZlc)hoj~A&%HVXQZF_9MrX&z%f)jtARlNLGBzc8rDFqk zA;D4gI-jju6vuB#4s1sXJ=+W7pyu*-3dcel-*fL_Iwy_UoU}~lWHEC|^}+wWWDi#7 zEA_RYJdV=|Emh!Q@zlkgls|+wfeuf%_1EgP%WR|w^W{LQ4BSec3dX3~IN3^wAl^&zO6SDcx8$MIj*QH8+0#-#f7 z|2rMC4*u&V-hH;gPh9RmW*k&GR^Z$b_hg?{M9sE6)GN;aF$hGn?3ro6fJ{O0@#@fA zou@|rZ#r_LyQ=7btu#Wrx&zK9g;2QJ;WZgW%P_K zd-n(JNhp+9UyS0|(Ya#~I>KtO_;1BuLL*A8Fzf)lu_t8$8InqHAU*p z2Hbh!~K58Kp+Ajv>Gm8#w$12>*ru-*D zaiU#l2MrJ*946qM3MC>9^$eiu@E@PukMcz>CrQG}8UVoEXLZNw}3CdGBvjM$kaSv#Wu2d{YL*>?mSatUez*YtXzI$?v#gT z!v+n$yaaFjaAT4Rh2uRUR5Wg;KfEVCe-49u~Yb zB1Nz7Kyx60Ynm+Z&>UWBz{^5-vVkbGh(W_2*4y!0uz0J`9!pW>?<t{I_@k7>y z`g_!A@VXYF3`X9Ek~nM$QMI7gv+x{*U=4011ye}(&#*t(AL{xJii|-i_w=v;lq7CI zG2s2DR&(m3u~d!OnGTADU=3^{+~mNdhc3gp05HF5 zKtfM$WFu;5-BdE8;-e>ya}s9p-q_C21dkUdC)SjVLQxd%en9oT-fB`Ld*?D#PS<0n zTW@aTBMrH28Y>t3Rox`zhzrL>(U00XIMop!f1K)5c~gTxl>HP=K-Iq7%pgFQJfA5F z=ROZzpvJ#>ethee;xhwO?bR*v;~QPrAws2pKHE-F^-s6Vk3SREA$1$$P1w;OP99#G z#fK_==D=YUFnV&UJcC{t3KCF*ZCqPGoe6K@=AQY@*+n6KC<; z_B6WBw^OI4+vP`}tG5qEaM6t)tqjYaZHL9hGvY{oRH^6Q<5D^3C1c#_)rHDNFjlV~ zYRH_mbD{4=b_oRgPf1OPXX{;wc!g=ypjnLKr-08LJ0=iP^)te_B8DjIcfqBLw@pag z7rz((JHSe8AA=zO%reCHp)Y3K!ALP2{4XIg@3!^-B`H1!o~w&g&e3)!dvYD9-2n=R z1TP4a)~_JvQEgM?%XLH=2mM5U7}m*-99yarQc3c3i8=_<^w)uNo6qc1d)H2b(otrD zO>ky?URb7T<91mX@*8*#fjwJ0iP++9$*@s53I3D0caDv#=Csg}_oO3-7)&jAw}LR!3H<+{33p(Sw+T6N?M*z5<1HU1{b&~;ysS-+9f>+Q z-y!*qq-mbA!eJZCqIYp;E7b|&&!ZzRp#;*rqfU{%m0m1?BR>>h3cr(1a1`=R_Q8Qz zIgRB-z_rGdA9!bN{H$pO3Lt4I>D4`vx_8>!eQY1?b5vPj2l}!y^7y7KU5xWkZ2G4v zAXoZS6 zzG-)yi!fR;p^`-Py6~lxh6GAj7<-o}+bnl(sGOxqg+geE(wihs;DF9OAe6mZWxhn* zh!3?0S{PS@laK}i|4~$!Jzo^xRChj+kZ`K__L+Pl$P)M8J00Y=zdPT+m6K|w zo#;c5suIO)vB#cZ^t_mMNDbB7d8 zj1)nNP8KqPd*;1c8x^=f0i72QM0-e!T77d0Mcg!5X|t_~=YUsF11frUj>14Uzvb(R zJ1Q`*jJgn3MeZNjeQ;@3Q*5w^Vq%j2K5t6`w}rnNiP*)0L8)xF>3vE(P03OmAfRGS*JaVl)op9N<*gW z=?3WO-Fu=cP^4^n>Yj2*rSEAI;I0yC^n2t+w=ESP`%8IRmE7o{RN0}=!^foZq?c`@ zn*M?M$^7|toT_dG+PY#J-8fb$AI=_|l6TcIb{38G>FY=3D+h8^%)F1EU`vwt=CsQb zu3qnu4xq84-Uy_;njcf#L3@tq@MSPlc`Y3=*eJ(e3B?p~s^Y9yq<}hIt}CNfpqjP0 zxew|(FgZBea#7eKh?Ca%#&^hp_|1|KWAIl!qH*KJYE$P~pUEK?w3jwVHe< z1ES#qaHxrBc4XCfwl&u$#Y;!3ZSL=gyAKPm73c#b0-5rlzh$kR}@hyPIk*tUR* z{MB@VI98II7Ll4{LE*}RdKY6uCrMj~8yX@|%-exjMyin5WR5kBeO2l@nYp^Mdw5JJ z!9|4~Ak#ipwnfFIEA6oRJ&yvqNYZUALnKKo90-i1f1`975}CPsw|45``JG^?(QcMS zgZOEPKm|1{7cB!9T;%TES`Ah!kXW(=e-0%?_Fq$-cb}b#dabyT`!*3?aDHEr9gL{g zq|VQAwS@d&V>4taUQ(XUrbGOi*5Zk)3sk0KQ*za-W7yIH4D8g&ig<=7)lx7lUbzZX z#Y{^XA3BbTH4F?&**~5Q+*f;SS;%9nvDaW8T03LG&8^bz;4NeJbfGE)_P8?8YmAGX z-aFa2;$%M4I07jW!O7V1$gaA5)(llAdqNpsgvVh5%%l;oZ3xF8!_hD_}V z`6>W^KXdricw2E&AxPf9N^+(kKgCc!Ep*JMaQK~gtGt<~h@&*H_zVc+a+|A(RNl*l zc?#j{iCi56!%r5}OWya&Q{c?fq>!EmRQ~S$Y!nVnDTao+2fM$8#?%z0#7S|_ zxtc=d$vi-z2ph2>10Mrivk*PZf357bD6amNc-9R=L4h4#$B#juh5 zf|`)Rl?xTGdXPc`U3;4NBqY#?S6vfxZ@4+!(W&+O;B0h}9#o!Q-5*37Q?**yXa!@< zl~M4bAz??c5 z5&!{3mA;UVo>D93VHSd%5fM>_LP!W7;Ncb$J-{cfl`I%V&d4M36qy+liI)}twl74T z2_9(@$jvTQ3}fH*-2JK7(;nqw$*D-OOw{e;qpeKPw?kPvMQI9ktMeGi(P)LWs4iZG z!U$qquOh4}&I~@^W*p!k^PI9CCvVl%#ruiqDs4{k*Us{ zPfkZ}LytGK1u`fOj*Y79e0_7A-|^*?r^rz;Xe8ZDdviizl2WSx;c_i4f=}hxNy|X> zR6ErP<)s@C3Jt{73hEWHr`dRt_!{xfeJU(ZNjah)_)^HzRkB&Q0;<$nW7VqIOK?h4 z%}q8G*~d;X8T^?ec%I?r(O1%tc%G^9#Ag(@cofflQ1W?}lgA>SqIev;-5BW4vo#)! zpAqVl@}OJbB_V?7IZh%~m{mFBVE$55u2YfY)wwx6G}$GO~zA)SaCo9P91dU+8$ zpQ2C*z6fsH#t%acBDkw-3&}oddzsD4JEgjazCdAbLQlc8r!;5-ia+~%%m5HZ4pY|k zU-eo$lvcdZpgU4&qVt;RMG_K+s+}a+d@lc71$JV^x+C>BvVVhDOtQ31x*~cu*9EZ3 zUX-yngN@EcaviKRi-!P<=zNxBq(jnzNPM=uxFJ3uN&M2YrDd}8HZ^&Yy#tm;r3dB@uh7R;ia&()$_br4>kdC3d(^yAA3H0EDM;Q_-0)S(*n_V~rDovODoNB4 zmod5NSJa+615{qaYm~7?Do_t~sMu{XDY4Srz#_R`+nq~T1-1Tk7+kM2xs}(rP(Y!H7gXuE)ibB0u(1(V)g?*ES z2F-1@?ZkDKu?bc-UbalqWHMvty&2fxH(Mx_O~5PiL{TRjptGC)1(yGj& zq)&CxeVa~@RJr1ecdruF?24iuTsutdl5bb|a@CFl!QnSzw$*#`3_VT*TN?<9_o@_0>X~9W#hE1ynQ04yOF?yLf1g6J03G;6 zT0~aCy`(1IamOtY6~XUU$31Q9mOo%nD(>28RJZ&=gAzlFagD@$CCs|`ron+;i>tZw zP3@H*G8lU*S!1tyir~XG0^5Xo`7Bh7;3GBy%Venl+&Q3f)IO??-@R^G*?zbbyB)hA z-WhyMq3Eq@Iq_gUV{S5dqQ~5-K=kq6h?3BnvO(vIl@nBZONAn(_z9KSrp1_$vUjUe zr#xsWezG^Bp-Zu}O>V;a%r{}?KczC4;c;>iLmH2&aFu|*kQxcbp!OWpz@(wPvi@eL z<>zY2i-(6@+9pfW^e8CKI1FE^SPdQyq6h&@0Chw$ndtn{CSnRr>&nW zF)I7_3%Sv42GeKPrFQ)<=0-1%wOYZ(a&1$4&DCpshyG%e{7d=qbB4}qY+~NOY#&=S zQDA1hMweGrbQ!!y2#?1h2%8rk9FU>4w4Z6o6byb!e0?wRV~{MF{jqQPDP zUvrMW9@OIVfZy1J%)f3Md%7M!rZG^Nf5SGiX{^1zn~H;e(>i(<3{*BJ{#+9)>|5%% z@I-_x$QH_$PPb2w1}b{+?QC@D?u-sp((2jrI5yj&6E^KT`nX!bBIeD2QPI*%*<%x{ z>$^RS?K9g{6!Uv>>_C*K#%V)v+PA!s;}tQYUQznKWC)=XLp!T!5=BKpnt6lmy~UdY zfe11BzM@2R0{DTO2on)RX1elbY0+fzU}@|?*rDr(Zn8}%$f_zCsYO#cGPCys)sLK1 zaGTZa;O$zk)5x}E)NS&Qom}(5iKy1WXLNf7W&>Wb~Q%pZ4n3QG` zahv@D*CAE&_cJ$B5^WMq|BIw?bm5o;E&sVjgQXzxZNqw{UJq)j9>tHmq_K9~Ks-+C z)a}PFdS~+LsYAuze(7L{(hej-X=tJF*S%v$oranEm4nD(ybC6yYMg%UAhQ_nf~lw; zg}<>=S&cW1#kP_}p7U?*RQZQjXe72~?Q-nD_?qAEdS+6W8&@h+N1ET;3G5qM!IIfc z%+??5T=@-ch)jQUF_{}$(M%3s=}w?Qe{wJ>8(Pscj?>;a(EQm>vlxNbHozTC1|tm* z_YOex7Yos3P-|B(7eA4VtY(1sFlI;K|CG&@zuFy97tF8tE0e{Cqm;Ia)FZS{0FJ+9 z9FV&oRI#O&r~=h{J;^6JT0t8x6|mEj(tK<0)*4i1sU|U_Py7k}bSBmH6nj!Q93aUx<#~9o{;tQJT z=7c>e#SMB=^5$CyyvMOy9t8RSCnJ-QG9%9`heyJ3{X*(a>c1&lv(=U@3dSR7PE)C8 zg$!!Anmamm2K=8o`lRR`VJk+2yW_Zh!;wO|IZ-jZE!)o6vUh*b-WP&A5~A40vR6pc zXlz+IJRGcXzlcGGWzvhVh$os+bR5EpjZi$5F0R^9)ni$e%OhaHM^&@SUUk&T^#3eW zM~6ctHv_{PEQ72rYEX&LuU%NNZzzT3u{%+nm(DPF>?pB`k|^f1adnC6(r_`ACw{1A zS^N*)Se)(St)=d$F)Mf*d2!3Ai%|p0VAnP=`&!(W_>Jy}?%-|YCCt&|D+AEgV!oWJ z*tn!R_91wB6#Af)MNO#q8w@CmkB2!|Dn)(ffr@WmN~Kv0nj2xgg4bpO*uzl?RIH!Q>f|@yV{IvJtK%N3K=Y;w1$2$MDzi=qIe> zsVSc|LcO1GWo2B9cqn5kMwKgLssidN*@)1IG?`^MZ>$I4OV*$}X;@#=yDErk!Ety7 zaC{RFDN-@K_~iDdp8*%Znq4*HOBy6;k1QllEQsR?YN9wSWshBmlhQmm-zE5&Usnpp zO&Rv9zna1b-7)SN*AAr3G~`K5tYR6<6QJ6A^C}*9hN+v*tEY6(N~>#+BGKg~JFa-l zD#dgSH!;(xD>`ZQRazpJBtj+0 zhzskL1~hw8OcUD++UfA0Zvk_71BGRJ|I|WxywVC+#9!g&zjs_s2IP?X9C1Y<4J^7jXI*lsW?0(T%-yo3*Hr58NfU&Yl}i!|y z^}sJ0KTb9vu?@jbE43Q-9Ds2O`gf*#-1O1yi!2>y8`C(ST)T0A-ou7ar{di?3cMbW??nmQfw9Gb*^ND}@egHX98N*7HG$fL zZ=WMcpNm$+ApSC4zJ6WTsr%SF^hBd51z#3~wYZ~ubnK4{o0}p6vDw8+;&W#}t(80F zc$C)%lgNR<@?I-cW!;@A3bC;$ilR&n+iEy!XSahL-)lf!s@YCq_%PVb2P+_n#%luSLt18uYz9p4_ueL>n7XeWoK`{#7;@_UP1Jd$u+@ z?CTwfKAs4Az~E{F^$Nnll%f*{$x@fYZse0-v9S94Yso*O2GQ9vqPkj)5L!lyRLo=4vUH}A|FZ_V0Bc>BEC8YF+_|) z;K~4FD&9=M)uJ68_>yqcr|kiXIY|M}xCKgAan&2i;$_<)lIXQ&2$FMD5~Xp_U`R?O z=^*Mc^Sgpf<`a?w^Fbrd5y$8*ydsJ>2$MlO&f7SQZ@sqT24+G+*{|dCCo{_jI~xsn z9RWLpKt7P-)^pc5)fr?#vQ}XE#HKa#lE82BxmQR_E{rHNz9_yZ#|+J!IPu%|=Nyissqlze%-QHjBi2Gok@zLDK zW}UR}=+p`Bm>fN|CDQIwS3%AFxe3Gcq4Cb@DrjC?T=mD5e#*s~kFBD{ zKhH6~IHwdfKA{13NynG>nsJu5{JZkR*x6HRq8NYW5%~vqm80XnjS3))Wb!BgsGiTP zQ&}^2)5dK^)y_(Ly1p98ZF}!lEoH{%AuYDNs2jVxr`+%mHdtv*W}=roo^Zvx@^}j? z-pdAG$Y;lJ_#Jq6xs=jCGIh8LX(K>1R<8@UFHT%lCy*@($}7KzZEVP!(XQvbMyK*u zDz?#amlg`74$I!5r88mdl-M0_*)?jGG_IH__Q=FKU9pWUYaBe+DdpK$<+#vz3Cl3y zL&KHq_6#bx4tP}$P+3`lZR~{xb~X^R>^ZU{a)chys(a)Dzn!XjS~Cc=r)j;;G1RJB zH%7&yTN?HEs)wLP4UJI`V3|)F&nKYR+9Y#V)DA~(>sFZKds;^=U`$8W_)THVZUrX2#Y?xdj(Q{T3e?}-eT4W z=f+hfV2@rJkuItpf5BRwPC9zr&Q|6umM6AVMe~NqXeGG^Iy?sHwrLXLbX)v5{eAGE z71xT*xW{nsBid*u_q>&Zslq+O7*wSzUNDQH2RDWtiEl_cLa#CQ~(m9ZYX zq#iTa5-O$FSNbeIA0%{Ap};zU2`E;CJWwoG)B7(#pmS;V==PcAAo-Q}R?Wyq6=|kh zw@!%asQ6z6BD6mZkpDF`E7fDKD5dE*`u9EyzIW68y~%fpnC_|i)IR3czCvFF-+CeO zt(a}Oa6!f83oDNgX-KR=vv9JEsFNUwaxBIIx$yNjn z{{Q@D8QRsq`33OJ_i=yoLOX1)tV1`Fxa0UKG+lkHXBni>zxajl#rJi8@nRFQO(Dy^ zn!I;;3O|{x*Xbcc|KjJu7vInQ#fR(I|M-+oF#BuM#610LQKRp#er*fxuYxL`o1k?7 zv;l$*dEs~fYCY9#;{J>$Xzeu{`2f18l2#5~@b}<83>VXQ+9Jt4AXNOj_PP|A1$ zu0EG*flO-^cU_p^dNgdW$E_BG)(8cWzoeC%@VOin&u78hK3H-^a~Sn$buH{le6Hz3 z$?t>Tm^Eo*&nod|&_gU_tujc&S#zc>v|b+!aC^wQCdpk?_kG#+uE_W_i|2lGOS5VHPU5j;}N;8)6x=;n*^xPN)yJ(4~0_U1SFL zJ4%lhO@M4hssiZ|k`V)BkoDAn>*>0@FVl|AGs{U3&Fi#GRZcvTV!?`8d4x|+Zbk6y zlCHd=^SeRkAEollRD^UO^CrMTd~q~JOD4+lV-lYz5XJFmnj@~VDTsr*&wh-?p}*ZE za$E9Z;Oi=p`}0_ReDg+7An2Xclx>+!LUnn1oKB)dos*KMSQ5(c-2H>96n?xux`<}Q zJ1d4g@{_K^;C^5$<<**(`Y&@Xm$HXP4s^wm4&Gv0eqjJ;K z*JlXSFLqFaxgeRaWRp|%UGfUN= z0c~|f+bOIxnid6>@DPHJoG735l?mV3^$1&RsJ-sIUjL;!9KXskries%jY!2_UagN9 z=M1MoT`(u`td`F?kMbU0ql}IBagzb|(oVH7j>BWSdW@6WLhtMqPgSqJ)}X=lp$Y;{ zO)7?mM@_}AUuRGl6M|9pLM1|FmA_tPk*~<8>%c1)j_NS}2It6PHF%9n)%U&8Hm)%R zuqLKGySK;`Bf~mloywaeK@_aQk}$@GA_7>CUKY<0kG=_v{pMusgO%j~@5AE%#hkf` zxv^tDqf+bcE#|0E6zBUUh1r4v_Xmi{>KNP;4ppu6R)ZpU8(laJm1p@jgC?;<>P3Cp zc1x{aAMb>3amkxrMyPBm{5f-273|^mE;n1!3>L(K88ER! zn*Ny&59Q~)Tj80UDo-Cx8c2`%b#W$a+k2D|g)?7fvr)Dn5FC8uQmfpon3ny5Q1@>LW`uPb&ZPtg)F&ElFBi)aD=9M~+eZ(AfaUGX%PP}Yga!40@4t!J_bLvE;)~O`@ zQla@%l8vK&FneyPiqemfql@E_*Vt5!#>d@bL#J45s~t4elq3~|I|Gz=_=#RQa^m1p zP?;j1Bq(IlOE{XTLSSnGC52CQ9sf{Wq^NpJfzpXj_dt<4UWPb|oQ=;A9P$MrV|ey# zP<3LT?J|CD68NLb>qEoh-x_Sv=iH+w&*JKe$dx=*WA%CW*w}+EaWjgri&P)0FZ4v= zKMNGY7u^g>T}%NC^7_lrgi+9hFS!|nF-ROcV=1upL0>C5Ui$&~}uqv;#H zQRGCyW^j0nc)RjVH$(0XY(pjZtHP0gc3a^17QvCcDQyhTmV)EVhrNcsO^hB#W{R;* zX$hZUo65lOovsXOIIS2SYK?r?%>xDh8*uMYm_JR-%=dbu@LnU-nf3b`!!ZcjrA>iU z)4*O2)R;fWjhP*ky`o$WTYzWtA8KQsJCyJIQEtq^f9H?2G0!XRJBb*?Pvn@GuRSJu zlR!VO=1Lq6IG|isaP1nk)BaQ)6<%_|L~)_LYNsc(t6tG}>b~}8R)Vb6k~ui{>#3ZR zpQ~d-P`e^Jmg9*lJ$Nj^^%UIKYQ;V!{n>0T5f$hBg+_$0Zi?vuB0PhLy9kv9|4>y8 zwuMO>KN$PX9bvWqvKz~>1G;38#zRl39FJc)Su9fVlnE6v_%+R8O2kVZdmOvo0hpWL zI620#4Y6`x1nbA7@|-6sukyD(TwcXwh3#DCupqI(9Kj6rRzIx)q`U`wwkd>)^dBhL+Z z?6Z@)8~x41C)>f}YrgizBgO|+pb&ea5^`#Kd`iqE_`8n6;*Rc5WCP%JXQD{I<{!P0 zBs-O1(U`*D8_z8UBO3oCu;?;Vgp5#8wtwk3M20F#ZX4$16zBa?^#ARoZ#H=i^}Z<` z`Hz>rOcE=Q3!Yc*vp^dD>q3FCe?NJ)G*Y|pKNtPU_OU4T1K?^VMgNq|r=GHNPXvSO z;ohmFa5{n;$6)AR%1Iwb@D@j(dI9=B2mQqw^v_s_H1c}W4}vWPRHXOPE)?mzv>yal z64C+z>%NEClBcE^pvkf<+M0mQAeclo9CrWT;W?uwfa+y5s$#$q7{SEVnu$7{sM(YW z3Wx(Tx1nSgW^#z)o5@yp=2#o8bl6;u%8)uN%&Li5KzCUUUGf+>F*`LpojQZ(*OK@^ z`iaQy)~Z#~AZVDkvBUF%ik);Wmq&@dhCL21SJvi+#+4}w+2#GnQij3CmW51htR?N( z`SS{XWN9{84wm*N47;fZJJ{BM@@}u_#s*RK;pk%M^nU`Za2Y9rd0-Nk=5BH|pt4atT$vW+Lg3X^QcRt$ zEUBu_G*+NR4J6fKCb}>eM`dmFtoml6y$H$SMrD=tonBa_h5Nb4srcVj74kNmgSF!g zG%33q&1TfqxDpi?y_$_>JnAmDad64>s+tLaY1%NF4Y5LJ+)7aXC)LyH z>N=J*7gNTLW$u*KPfd=AO@9;iy;ci~r&os5fojUzA&|Fg6$syoP6|3w`vqu}N^!YIA-sMb;qGR;K465D)xAL;VLEH_gxJT{N=grI zm`4ZK6A(6`0#1CIf#B{W^-^}D49O9&`4ix0Os}^VQcDE|F@?=erFxg#*u()XJ39>= zE7&a;5n(xR;zLv#f2e%Ro0@1!!$9TZ-z>!d6^h{8!SN;U`*^SxF*nt-8<=T%TAuby zl+5rFRXj*veiGy>QMm`F8yHsi#P!u=cr7qsHO~OHo~k3kx_@Fu+08ZDa^@cRvda+e z=!}A%)LRQtT7VKXpN8rccnb{!_BW}HD<0r*FD#u~#dZo<+oiczTrN<38aHSNWQD3c zol_t(>Jvbnnp1O2je0u$GSvdTD4V@z&(7l{xifrJQ~3{}OjV)Z%0XG}I51i#%Re2J zt8d9VPA5K7(ZwNs5NkdFhf-rH>de3prcNs6lWN*-HLT|}6=%J*h9uc>FCLv8-2JC^ z;Wi0|@p_|`^o~nMFTgJ7C6w%4pz6(To2M%q!&yYRYo$6a+|Eri=~-tHX0MjA?YGZU z&J}j+tS)lZcSvbp*p4wWlFR4QoIn1m3EOg(PI(H>^qF?N)-mEW8fo^LJvxr>XhR0& z`eFmR-}E5QUil6cX0vPz_9v~R^wE{ciWx-#c#;e)uA*5xI`I`|lVarK+)0S!2b?oh zJa?mkO~!CBMgXs?Ckl_vJ1Q2l$-tLBJEhM9gT<+wM)Mwh9_;aE6DJPp&Qkve$VpYg zWFo|Q6V0*Gg)vDb4qzcy9x2Oswt+62{u%&}Z5gSk(;W?b`1}{RwQO{iA;0|H zDTmH1SUpi?X78|ijadB@E-gG! zhYNqsxN$j1-9RP`{OXE{Ps_=lRqyEOns}B{zfsLv%^~2RuFVv_ z{l(zZLtE_qr{Zpt2EL@DIE>d>SWeQ7Al?Ro0V%rR@+S}B;%oW2?Clv4liZ=4DeGhy z1KB*!j}41_C4zo|vAIU&3ruCm;>Pi+npHGDR_k^fv3!NKqLl$WqO$y^O(c*dmg|NF z=A$?)RQ=x!jU`Jbk}+H(t%++BsG5LT8;8*W$z+ECf-fV=&9y!@IC{oh{2<9g6 zA7LgHGc*HPD2{_U9P2Cd?Pl_bmn94+9GajOisO)pLkx)MQ(^cxRB+PT7kte;-^3vv zF|p6PIw(x}z{4SkBLtEQ3?#ebHuf)o>_p+7%L&ka#bxt^nhbhZClVOx3lF`mC3*0J z{ZDEoq@*Bcf@Ibcb;8QWySeaavpZ-gY+Is2be6Pi1tfQOB7saqaqdtIVWUt^nuP+h zP43}Bu@9f)(-h5YQ#5g9WANP5a9Zn}4@254$1kjG97J{ZJzY4AL{QLRkK_X4&#%#Hn}Bp>j5dk z<8YTtLy19s3$`e8VMXjthh2K0K~H7gK*TVt)3;JBi{fcO@j*F?;?5kjLaHy@gEM*~ z&Gy>jXtWeZQB|aFCVH$rb?gm@7E@Et9-^UumbTc{(U{5=Qk_N3!g;7occ#7?O+`!a zk`dRtZJAA>6K6{^#rub4)KwWMEPTAt8m}Km-s6Wd=!h=|`t_Q7YE#pp9-g64rKv6m z`qs0#K<&XJGWrwkF32CCKsLZU@&?*!A^%}}piG{nZ&;c9$c*~_V=cu1edeW@FOCl< zrE%a<83xgpd^Boy>$9i>>L>`cI28XM?V^5QP}CozQEzvqR4JLc`$5*0s)>GVMm;6% z@Te{Jv$aoM(=GYKd03Cr5D2>i9^RsyR^0_#loNg~ZR#EO@rt&{U-MfNFe=1vcY~5bjwk<6LNIqADsq_Ac zDT0;BZd_f#hg;bE$>CA$;A2}qYKr`mQi|~sJf4Hv4Y%7(V8HHX9rLJw`c7g z=vcRC}g^U#t<$Bs$c3;bqrWhEY^I^%t%IGav^{|m7DZ5jee4CC+P{X zIJkLy9*QENkw-4EjxD29{@T+s6rH+gE|%Pu$Lsp6bJ_4N2Zg&I)C`Yjq)57PW1+UN zum-a{*FkAyr|oF*9Gs#tvK%cO5e6iJ&Qwx~^dp3eh~mvN4Me+EJB=m`&4r`;NQ7CD z1tXz44Lr+0vM);J2aBmPaAVUMz?TlsjRmt!p6#Za&n&b+uy?^@xA5e6PCpn#;7Ml# z7Xj5>=eZsPh0RwbR{uPbwqEcXvUaPAWH+>^xIhekBhOqoYoXhxfH+}IuBJ#Duxld4kg@?L0^HI^lsS|tu zN+0#+x>!0U)dcfiJwsZ&$7$}b>OsF;>aX@tFK|MuN8j;$e~p{Eqx@J`J@)yo5-2S0 z4*`PL_C}D~04{y1gW&7j^j)RRIt%#Lhp_jr_h87{vgo9_tEcW^Z}3o-*VAGw4^=nw zMv}g}96E#-RLts4ZY+*c=MV(cZoIiSf^c2%#LO_u{w<1rW3lA8In{ghtr?2NW|WT8 zOua6daG8}gY9TAOR#G_LX5dg$HPp2aUk)HjlGa-qiQIe~p7RkaW!~<@k?Cf!NT^D{ zcQ}z)c`y(`fwC%-Ie3H*4Vh0Zd>(l`7_5qTrxO`E%tB#E7IEz<75ROa7l-b~l5p63 zp=Kn$+lfcWa-Rak=8nhPz$oZ@JZPGjgILB+dETe61N?j4SPZwT;C=E~-YTGZpA(Jl zhLs@41fKUh@#sfk@p!2F*#}ZQ%|<6$Xtx$SRW;^g8j=MUMakU?({`4Jz^Q52#0(()rdlF=(UKZ42@`e=q?G+OO0?}xPVCIsJ^fp4~~QP~S0 z(=a7XtO1WJbpY*C$cr8ILh^ANMF)qUczSCG=^a(s@rewD47{J<260a@>Rtbn8K#M7 zDSh59@MWE%{wa-mIawXl4dv4rb<86Ii5QKrYFitX4NcEs+PmhIvw82S4y(X9`I#Jj z=Q}ox+S35AwUc@&`)r1w9gn~y2Iw>4=+Jw};Q?!q*gPSk2cOds4YXVF=MBm##l*~M zgzG?%b)axFKN`PX@QI< zrI5XDDwpHSI%R#tKu{hR<$Zm{q$o#9z~1Tv*&0PynXhW}nj#MZ&R>u*7{vtEO|&eM z*%3iAHK>^iU(*qY{_-gTSa(Rrbt>ZVb)D)ArmMzL9csgbuMf)}6HE|a0K1I@6}E&? zZ~x!$;F!Z18t@cAL*YsS#gT93kQ|E}ko(XFj6yl`Ef0=zI6~F(eA`AMrYwT(g>E*B zFp?2Q6X6ULAs_ZuiElQBy1>nQdAjI2F0v0Iv;HHcU{Pmr+Y_?Tbx>b1Lez^=iZMI;O}KflRk7+nIQ&!g5YW- zv$JZ@j}EiO38$6YfMF&e&e!+gEdSeS$4dQ5d3o!z-% z0s4>4L9TrR65U7Tm{VgA>S%RiHJN^T;u)V*#O6l^a#uek!BO29uqBzA!~J8EKAzVy z2_1z;KS0@3sn^7xm{f6z(2Fi^%&&p?BIPvc*H9id1}gLQr+EyEDF|#QnDw(P2o3eB z_A?XBBh=&AO&A#gRgvQa(oo7T4hHtpvYg1Eg1@xrmz;kMclOMm7LS_(9G(4j%_ zp;Dm`?WerJU*wRl!XU8*%;iN<(n~kE{G$2?{?ep6-M~I|-DQ~xdY};<6V6XD`$lzT z{gs6S^Kr#-E|>2Hz}6&6c7IJH5ElVy#q^5+cP}U&{KkR6?wJ&BM=2ismPSw%4?I2z zR&xB#LLmAk3Jnl#yK~ED^Piyody}@*fdXk$od1JG8?xk^?WJ{j^Yk7i+|^Uw#~&>O za3efWj~n&*rhKde6)ezn@{y~cm}&D5gUtNNL_XVz;@xX4m?H~Ul9P*?igWzgK)sq? zTSDR-TRhTzbKufnbb2%6!)LLFxjq2Vy6j5Pd-m;py>(8Z4^*H>u zMtdJT+a(LmgeyHMC+m$^{6^5-XNM;?ya%}d9}SBHAYSgGE>yX-uo^DC4k!=UR0T~a z*s@5yw*J?RtJxe`gNEI$B~e~QowDWB zQ+C2U)yy`*?sV%bt5c2n{x^<|$YVdNz@tlPh;SaI-9}W#Af_g3aa>==3P!2wMoL_f zc>d3YM{|qIz(d7|F0JE9jBJic`gM)1RTTA8z0@_91W~7Y6J178kLt0woyh`Ym|P)a zt>QDx{}cDMG+i?Nq?kNTYA)wx3qy$c7RJ*ydmpzGT6Q@Nr`S2wG{G2kx;M|qfOlmo z3VV4PkMP?%GykkTmCE3KpiZybBqYN_4AR@L@uqQu6wU4Z%$x80n4?+PK|R%Ae*Mj^#mxAfmc_UCl%!?xExJyCME2{Ke!IEw$x1Xb2*vtKipG z$Hru+6cW?WR&LZFay@{`7Ukla_xlF{&#rDF6mLYyv?P2E(Oi*K0g@9aJ8uqd9)%$> zqB|Y>G{+$SZ(n`Ay#}eI;!Q>SOOn4IzZl!BEr~4$mGnApuIZt$rfc}ugyLv4ShfM8 z(7=pjuy@r0mc`zwP0GPGSJj18T{B%(V`+IFFp9|k`23!@v6$){+(dB{%h(v)zbA&^ zAMq-y30OtG)25%GN^aaJek;jV!?39pr|;dob^%pfm9u7nXo z@n|njqqu(444a}xr~p!esdePJnRe>{~^SeJb> z8;54Sgddne(9AkpYm|d!d8Jq%b((>o)>?u$AIk|rOKO49)P^l1R5ilsJ~T-iLy1E* z$Ou6}dFnSeP=G(2EkKc+6x2b_hC)h2=(hA{O{E zyTMOg82C`ym5u6(Y5J8*K>L;kZH!9h58Is^j>T+*gIC) zsN5wJcN1`*k;9!@T^Y8_AX^65#%+DAgVz**YZEFJH}-kJeyjemXEJER&b}!MF_HUo zrW0|hs^YGX7sia~89X*{wwzGD$#s`-D*K^r_ za!m@^Izd&}pVb3VDR?~IJVx$@{CIQ|lgw)#1cxdHB5BJ&g7Uj&`E4Pm-BynmiB(D) z6`tmzjQgzaiR-J--Bd(;_CmIWCcJD}p)%(-Daw#CDXiuQmL&{h@ydSnk&u_&A#11V z5jNWhgmRb&Dz$himDvWO@K~rYwzbWJ3X)=1n^6J*HV~s*2RHj!#oj%_>TLI5 znr&g}qfsE?2gKexDssI;A&Qskt;WSd-9{tGGjF0_J_)U2+pYcDcWLgmqj-Uxd_5GB zuT|NsHsAEr?+<%_wqsQSId@6CdW@j;P_^54OxM$ZDlM@|cr?WLQ?!^S8_os!bDo$6 zcGJ9*i~ed_%o#*?hsT@S2Xyaj(2W*Fn&bJkLn0w-FZ5VRbOwL!LcCli*oq5$=6Pwy z6jzt2GvY25Hv6efU^KT#J_BZDZ2`!rsN7CIGI;wt@meA(T3K~tNkvEGfty^?p|EWo z6^W{4SRj)Q6sUH*(Meokxi$4zJ`&-FA-)CW2@l&S(h6|h@f;kUksO}m_MkyG zNzAB?M+8gktr8!K2ffg}q9kUQjYWki3+D*s5ASxN5U!`WD`7HA@nMgRV7wk5Noq<> z48gnM@t*JnW^Wz^)aM;9MT4nI!7(?E;&((jIF@&NHt^yc8-tN!&`UhI=OFb`c&-;o zFNUyD5VEn4MuFa{7J4zB`1xmy=Pq5oQ2j%-u8#o~ULtc9zUFs&ItKkx&ZbJu*EnBMg@N0P zFrpDUX^zi}f8oBP`Kkn2EXpIiAf+v`UNM0YX)i$>OC0;+_M4_QhVZ?zSp^8pIM03I zgu82oVkWMGO%SDdBEL@yiU=W)DX=o(Pj>~-bXYI`wy&UljlXM@5pb;v^=flBwVGos zm=x4n5*Y!xD!91`@o1C$hK!+xoJ*8m-MvplA^S_!QQsp+ABVkxq~*A>M}xrK6Ux%h zWe8MSH)wcQ#GT-SY!GHp)Pj14xo3u828N)*;zz)xRO5R<(ZlQu@d8xWDT{zfk(8+y zh53wfB%cZ3T(9HsFqte3!9A!sgmS}K8NDxV<{MSrx8OyEAw7wyHzclfQax4|6GW53 z^yzV0OuslBoRFFy?hv%CFF1FDKZ+X*&aY;NCoE2bxp#$mejiH3rAo?YO~(y2sac; z_+Uli=h3LM5I0sa-3{ybU~u~Ifdg<*!a_Ta;j(gL0E z6_yG8=6_Q|Ut|z{Tujeff zD(P0+pb|Xt%b-cb)Sb7TV#3Qt)))qqVGDI-2n0sJE@AOOAt-!e%rBV zLOo?;0eb;qCZ(`}VO*wYcU`n)!*Um^**i>m{A+&Nd4}^Td+L#KmgCVJWw0_cQKu6% zn=<@{Cr=pHDGfhn(RUObrIGV$RaY^ttW){B$Nea>q81HD$QegP*Vg;Nkv)VOM^usg zxY)uWNT#Xp@@&NtELmS^YztdDseQSZAICuV<=*|^C}>}(li)rU4x^z$kX5Emg8MqD z<_!~90x4^JKa1wR_F>3-(w|3+<+7!YitXOtqCK$|&x@=Pkhwsro}1Gxz!E>eMK#Z4 z_DZ*UJW>Ao zlBirEF#ziA;^AJp!`Ra>-3uvyAthsC@euSOrwQp*mX07=XDlkIITlGo4bNm*MmU%m)lKif+=zscMcQ=Zy$de9A7FO_HfTqoV> zMsvBn7InLFqC$S@Y@SmY7|-h;gT@9f_o-9$`ECT>SME^Rr)m>kZ~|B~wy<}G;>!zl zED`hz!-BnG82Xmg5kd;sto%$}E45H1caNx7)fc(3v=(vZD~wOiLrT9Y8QF_{$U4L8 z_*~U-L6cts=+Y%SrM41{;sd|V$#YLbX*6r&x^oDg$;Hq zYKcu;hG#KxsYBsUKX!oP+FAhl6HQJ%x=oS53qlhFZ%ZJ($a zt*`GB%Q@{O2Nbp}dtNy(KkN-|B#=^JsFowiLK9>*Z>VgXH~J8DhohFQTJs1@d{G3S zJn!<@C;Uw=RO8WNqi%6Z`^*7}%_%A;?9FaWon>?Hu;Z1LAQQ58)amvXC!)kEif6lY z02kUK@ktR}qE5HBx-bdK2~#WwRR5|7Y?Rf0n+uyvvz857(z8u`=zznu1yr`f+g)g2 zZi;EJ;e!HjP*U^`Cl2vMXBZ{dt6f=={Z2n7^RSt~fX+Bg%TfL)_}r4Kq0#r0O_F!XM>FCtK&C%-5H6>v_X8dUZbnYW28adWMo%mTb zj9x2Qts3Po=2v>XOUOA`*{B|ROw18r{%GHr_4GR%wEkUjsFis$QC|qCKjuQ6=32;` zad8aio~6`H@#8k~eBvF(uL_b@s%z3GOe8Mn3mJ+1bbWBxy1dP!%bQ_OKWX5RZDt+U zRKPSAR4+_r(tOH+LDd6@XQ80Uaa8Z}Pdh1#{Ct@spCiweN^0GoaZ;DruQ|f(U8gdL zKI^0`?rb^2>=k>?G|X!GoRhA~&&-i#ubIju`MiU+NBvAuXQQZWQjfo;4Q&AjzF?q$ zay{|VARi%;=}~&|7935oD6LwBfkf~K=6aZ@$?7$c93e}Wf@WS-+~?L^0sR;IL7${) zNF+T_SE@QxHm9lm{*r+;OCf2uqV&vyH`YU9a+2P_lZ}Gv&+}yu3Vh#jWH1$gW}7Ge zwgdL>D;_kM9=)i|k0^#OR2=!MZX~;!(F%43jn@m_BPx^hYi=xAepeB@;<=+?Ke(^E z=}+U0wrE+UdPID~gG0;rDuNU0DcQ<@!YS$BbR(E)!?PkS5ZqIW$`$!mh5}@<3i;$q zG1QI?*P*t$z6y<+;C4}iQcWmA*ODT*2r3(uqME0~5#YnO4Fp&Uk4X{uZ?g}zbXt?i z5u$EcmdzVC1?}^qZV6QU^E-L$DQH}U)>{elNP~LrQuhD5c^qmka-NYv9wzn5`MnGU zF5W2imAj{AJmw02gKo@}-}!wFlVV8*3~YZ8Hz>{eK}H$VIfo%Ue?(l>MQbAQV+Y&P z1lB~VE39Q4MgUXgC#h{*i`*Y{&x@*n&BFx1Q*D zm~42?qT2(1eyrh|SX-F~V>dpJjno&GGbZiT%pTiYstmbfIp+iQpJ>z@3v11KJYL7a ziHYq`YHGl0h;|Lhk0KL2Ym+U$!tP-e=!m5^xnLuus;+*jAx);UjL#p0VXnJlu+rjy z2-6}$hJTjQ?k-oy>zFKCk4GEeIAJ!2I-Im@X>Gf>rxayje{SNKiH>*o!8Fhq=1$7S z9tSr=(KXB~tpiS7k=Q#yMKFG0pn&#DSywC66A%_5AUP0hPN?JBvMT6T|1!e>>&Ido zYzYdRAtsI>2H9dqO~Vf32K8$6D<6{7eWJl|xso<3vbch^pTAaE;9;hXQ{kXON!4I# zBFUD9EuIL&=|J;04w@papqD1GDQ+b!=5K9^BkgvpGYV@y3ati?7ElF8RrUTZM_m;~ zVg7IsmpyC7o&CL&Y5{!q>@3EP{$SIzCJKeFm-3kYNYE}|)hb+M=@8a3|H-B-Zl%e| zXDyknyXqje=weOlpL2Axs#=RIUz+> zOPQ)1|C>!Ywbsb%kX|^{djIaBT8wm2b=&{&P}3@ z_AiQdu&izJ_}Hgk>XcLdol%CFNCTXaBwny||vT_0&^#?uoi^a3*N&swo4seAI}OhFyi#9?JZ#ccoX;mu1`g)dUb!c#Uh2-~5rO6^E;O>kOLF@v+Wu|# zc<5E&%~kzq_P3Jv*}^5r6X(PFm8?fIHXrVM(iu zlV=|fF70?_%MXR?I$m6=#<)CO)O+rAeYlLmJzIfV1U_%1*d}1Cz|K6MH5e4~?6bCg5z|c=YbhuxB@P;4u20_A*Fpu247j z8@bRxl%j93VWXkWjvG7BjI}!5dJDRLWe<9us~~%q2upnv5=rq|PD$=fU0Ax&iU=0E zq@^Uuy_o|CywV?NM90!GB<9g>1u)pMLftY>bD+sT47u2&JM=}$%;^q9!c|Se>b=Yz z8;j@7Y$sTmoA9R`NMS+`(?;LX>|b?4|OBVmi}_>1eyThDjjlC9Id~^Dz-S zcWVcxtav#&*^5Wy=@yN%SZ?D&%x?d-GqrwR-{*Nz{tkok!s8! zx*dt*5SD{kNJ1F z`k>Qu6ZwmbQNCBGrK$MQWT^zbl$wobhew;nL3TF!5xG+g3P(q2*d_-$%sH+vmT6zwQOJ`Zeb7%7*~sJ1snXm; z(22gozYYMpySUKhA5Tw!iCmOBJl1hPpxo(1 znR_hcFp;eVRA<^MfiG2s0q~I8=^BYK^)3g3QUz{Id7Orw9(JO1KBASp+e#cwj95D~ z;>MUfs|lxAWFSsYcH^o&6g7X02il&>kh+?iNM`}*CqELPlmeGfo5P9XG$5=uo&^x~gzp>a5tUHQL5YCejOv$N7jxvuryvvRxl!2 z;?O$)<&M2vtD`rhI5p_p~=4?p{@f{NCuafd|foB{Ba_3|YorQt+d8%-Zo8 z=|m;}RaFzG6R>Q4j5BVGNz1;$VWet>XB9^5{#^m-!b)V?DNiP08(p!hLJW5SrIKW9CC^( zqq|ycYeHok&eKQ^$@V)%?F;3DF65B3x^cUSK5}v#t7bq&p`rM)=tm>AD2F9TR;VhF z$VP%KE@q%38;mW278|1FO_G6RFh&;{w(NS|4$cPIJ#66-j%RH(I*z*pu@lRwU%92_QZ|FJ`|eG6T-4I{5WuLXm#dugpiX9`$6K} z94=2N@A`-fiFQ?TdiuKhu=zsSxn>VEy|;9Ul@9)5Zj}oax z)ipOvN<8m0*ggFL1Pa=iv3Wtr#{>I7p|d08oTEClJjlWUwJ*(PXB0ZLU>#}Nm{pW` zq+x@=I1?qW54Q2FLO-fWG;OL4Sh4a-P0)LYjYYn=9&E=)pnXlso4JXJ34AJ^s1zHU zB>en`TF4GWaW^`CAcDR+OIq=-wqe>K#I`S#cknP1Nq+n*97Rc2U)V#mxFJZW>Gcmc zk*w~4DFo0^xdzb;Of*=PJgQdptOHpvq_Ig;Gp@E*HYw9*W^h~tHs=vKwpwdZ^u3Kb zyJP5cXFX2?zbw}dnPxrjS1JcLkIzTobHF6W^$z%^GwZ8S=y_Jzs0^05f(ESMk^QZ} z?V4)ntI$Evh23z~*b++d{!wn^W6)&q62yC|3;v_^70Ax%&^lWfYFI@ScidgHLEn-# z>zc~3rJBgUevFB$Us-l-za_eSl*%nMB^NVZAKMQ~FyVRQ-3L&O!bBIhEbqN`7$ zkY)b5CRaf`^7Izj4Lb4Uo=ChLLh%nc =)Q*2afK#VO+LDMa$Xq5Pd2F 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�bz$MM1oDb18AOowkUdNVS)-RKC6QA5FedUwT1tOjk9K& zD|1n+w=B61HNz9|v`)DFV3*qI@2Ty@s~o4tf$nTYEQW!4mxx(7D)Yr#&{OilDtZ9Kx^-4|D_GyntnZT4T|CUmOl_q%a+K z*M=1Ug}=>A_2QxA2^juwz}d$iABQiC;_Md=O$6{M%6~ffwfucn`k%7?ruudZ4j%CE ztDF6&4cYKK3;t92e>Z)>C4TJ`;ARiMyiY=u&-J)${#GaYtzJ`{HbAMYCl5GHs+Z$j z?WWGMk54%|;Ykpmb+{FK!7%&~Z}Ggm@2!rU2<(gc1Vy?$5at9&&hfD^I~_L&`2PBd znbb}kox~~&%`PmUbNgY8;l}I-V_=0veB)pbY7_TX*;6lypQLf6m2a@WcV8}X zCLC8!b~<)Tj8r{2W$Ow*Wumt?j8+Z>_^eYl#Y}@R%2g1m4p<)beV8((Y&u7l=>ed7 zN=7G21`kz$$F|l~pOU9C__ACy96pcnqIjN`^WYv|e!P5m9Clz7=I`kl4;BD{zIG5z z8-h$dhz!jPA33}w9PZ*D9BM{TBny^m@*AK++7R*Q%yGjy0!+_HnevYE(A9l+yO&u4 zPQB z@%CaQt){R8S**R9v7-F!XQxyro9IGLhsUE=n)SPuHsr0`c%# zAB^NBuyqsTTMNQ!zQ$lV6&CBY2E(bbSg#vEQKiMAGGSkzaEt^CLnEubusZ{7L+8Vh z{vtTX($`ZIle>dvp+O_Ejd@gW$Ru||Q8f>UGG z62l@ILL?bHc`^}OGpN~l=RlJ5mPjhC&xlajY45UesFfgbHo!SD%1C>hpQwsm?=C~5 z4UC~I>^Y+HXWui3MvM$pO~*KKGEE|F9CPX#Fm4{BL}~GR2Xkp-q%9}*2vOSeJ{t)< z1lD@+tef_{zYL8wGQQeFMe#l`h{jx3wduorEyF7;uROgNqxdL54EQAJ>M!1IQ(rnpzeQ41en;Lm;5wx`AT7ThS77#6;@aopqK+2CU?9_X_HX~Hrgpac~AY~Xme zduL$!xRq%{wnQ&AL~I&IbYlo;K4GVUj2L-GUuMd`E&L3ZLaKWwXQ~)WL*>!LUFz$F!v8OLPxMD~ksFlf)f4vKKzOOi# z^4AcyLDE!;J$E~Z9pEswy!! zTpH?R@(nl52=r5ejAG8TRY~?uH`(qiF+_G@Sm!bO7D0Bh7xyt#U*4HwkD22}39}@> z?IznFE%snCOAi>^D(rN;A2xUg{h#M_viO6#t%;X)MH7 z#x<`3l5_rX~GU=S5!+hKDNYw?G6uFP~Xu=s0{;QEoB z>tqLBv8%auC5$Sci$6)_l<^l<9%!d1 zdgqpMOW89<{ZCFByW-Th*8l7zfqMk>yh{!bTXTr}mcI;Q zDltfG9>N~})y^Z2yr*b&{msfW5yZ#pT3ysW`gc1`*oEg>B@7{ZZBdr_A5NB2W10Wy z=BZJ4s0jGK>_o{qXtE#Abx+By@4xL_dGdlyl{C}HUiFR>Bi=#($4Vncr!JdFoWuT0 z(bRhXMP=ihHhubOyJo`P2-Fpm?Wi%oLyVDq;d{*%b-uWmn`ERF%^$1p6h^JI|8rBx zu*1A~ylNjS&s&0>j(eSP(Cv%6xso$I zVpk~db?3;A!?*31braD+BQGtdvS2S~C&KCkb?v{7HHSChmv_@7Im$LmsSOU_P7)e) z1vk&0SoDLp@px+xvCgjOChE?tV!fvI*Xk=dm|ClxBrCQxBD~5g(?s?m($uI*nyc7p z;^>01O3kIAGKQ{7)6{CMsnhM%tX$LKl6W*{TW8vY@z*_Z4!F95rGqu%I7-c@BCOFh z22zz!Y&H?)d0*2(w3Z7}u8!{@M(SF2u6_$HAWH|1*&{^}U7H}P@eDxhB-bH`7$Fn> zC=pE8wK63I|LUAM!WLYQWU6(fh!foPom3GkNsqhgsOZ2A>`cpH*qaD@L96^>w@RuT zx~UGfYt8ba&Tu!fQ#Dth4<%;{=8hDV*L;SZskC`wvk>}yV>=5L-J(xQhV#v4B5q7> zLNJxR%;xe?d7n3R@GOUMa~_skFIef+x8|sOzzq(vrf8I4^W}_b*HY$R1D?1az zF=5Xc;lbUSq9KmEzUzOd!-K;rrMID&@|GWwtY~dNLwWN#PAiyMeOro8zQ*LZ1bmGr z=JR%Twi98e$1ziECMtq)mX&FCImrsDcdUq9oQ)1Htm zZLBX3;hOc7#aFgj zsh9^mww_b>ncJ;IhnsO&{_23eGZP-?4wWah-AU~i%QG=iUn%{yx+?hWv@^+z=u9}Q zxyop^Irh4PO&J@MIFoxNYCRhRBu3M^N^e#&NRFqt@6%hEE;5ZrH24-SNV-O`~a0=4u zYoLUBYnm#*e>j&ti;aA-Z2sKLN)+|dN96h8RG9+)_d9ms+rsycC=AVx=oADHdchy9 zMIT!_mH#!WNtY$eP2q>P>~2r&pKW%;AA7AxhefzOjKf$?k`%j@6QQ<7tFH3|)d_sJ zN+OKX#GEunpo~e;ZfUE0bYgq@q|B)HiFL#GPy}hQQ;FbHYss-+?F53o7J>sYws8y> zY8VSbFZPuoscuY&Ss5>qB%2iOlw!|HH!+`Capd@51+2480%0y8UtCd8(d8%QP!yDF zH>u8^=VTnv>TC(5Pvr>*!=7FP^+slYCbzvX32&XnZY^Tb@X#{rM3EGbtE@{6b=NYX z5T$z;1yogCUaGe1WX2>8z0d@;4ef3x=Hww5qfOqPox`)bHR$L8Qo6rvDraCSm8~|{ zg&iI5kk< zf>HfHr!$IPFT!r*SdUXqh+pqNBJwel$%JAdd7}wzLzsNGy1$Q$GNE~8b;XRby`ILF zG>J=pvwach=xj!HGVEfLvhEOeBf|21d$AuYHbsX%S1=#oaTNRL%}zj*7l?xBP42Sh zdD$PU9rKh=;aXzKS2>t|3B=y!mtjuR=Xt11!d;q<`b+SL$E)94V`DnS`dFR^V|gee z2!msLnkU1B)D9*-{NZMAc?9B=$CBokvMg*~xl(vk@)xjEo?mo{vJQs}q|odPhRne^ zNr7?aklNLbs9aq!gaC#HLOBJXGFDpklhUg5QX)~<9-B`N!5|%<>A^IvOa$#<3E!ys z?jlqt)AKWeW@{limdrF;%TYh<_0kk;1US59Vov`1NI(5m^@zf@8&t={%3cWs9 zD(L(=zYzA0UMM<9LeIm4;&+;mjB#`!=mkSVjiK`uqt4c|Z4N*GcJLiXv+rn+griXh zikrZv7!IyBjNIAWMnq)pp-SHqhu9dmO4-!A>L8OHXT^`jev-JY!X|4@wBoX{=YXn2 zKW8CHk5#aiND%I9P$M!=bI46z8xbF^q*nPtkwlBy>OZa~Qk*|^HGJ>csr_nEV@f@sH8dV!>o9dXU9wV52Mf)hRcF^2%C}fnm_*NQ zC?+m_3DYsGOOKl-!ECtF8EwY%tx$lNdR&)jyxt#PYuU$usQ@?FVunZnm3BA zt+Ba=sy;UAW+}?q;9f=ZUhue;G}T3)RC8n4){p8$-YGEb@2_;Et?`pe-Sl2qkcy{T zkt;h6^->{jFh1h@)s*}=lOMvP`LMMJCzB!ZSXSK`>-+w$P5ulfbyX;z@SS^^{3z;% z)9@|_+}Fa@g?h2gwO(5t(l>zT-p{1RBE)v6*~L3$aaNvCHZ-nGYL`Er$`o5z>U-3` z%Wrku+T0w*;Y7;Rf<$q#{w(! zrO)@a%ay4SUQ(&qy^kps_Rel^52V9k+jEO>wr)|keD}3TPun6Gif0yD5$WExZ_OO&c_rb8^uX9_kjP^gzb2&Q+6fe zP3)GBD@r~Q#>8^se>Wlh@uu{dFg{K#=1QeMp&-54?sqHwcE<@5?}naOkS-E@vMdtx z%pi-zJ{vY)QYmM=YY*=o?5Et}f)o}tU0XwIFL$WVgiQwr&2)zNkSsZ7qZ7?d&E5l+<{p0G!c1Oet-~Bw_PGOxH&v|k9FWZ6Q z1%oM+S@Gm>oB;8i!3zg-ByTZP6=lx{p%pL62v%02_TjL(wA{1qU$Aog-T_h3y%*;c zov1CJ0p)i^>ZZZ{b}C=%B@U_Z{45DNcu985J4R7oYLT2)coZf|F-Y7mrm8r-tRQzf zTnuB3vW&MEBe@E*JlfG{Mb3u&Gi@1gsi;h!muFOvPXd=^qnj5n<*_vu&G)f^f;>wJ z=Sfyo_ZS_jvdt?L4meSOJr?g0A+zOpm^e~u5Kqh4OJ< zRfYxqi>Mzb;VT1&t1nb7t5-82v zGaYu!a=+d)<~VO)rI)vJnW`Bjg6JJ(h>}oRby`hz)OcqJB5P(OK4=&ohUrt>LcT^3 z`^LKzGH`Gpppdva%7zYJyG&b6>n14It+2hgJ zoE{qW7iTdryhmY(qZZzmiW(*u=bDW=Q7c+n74fe8!CCf(PdqTh|Ap~*Px*O8ulA^n zt@mbM&{<9K#g*^AW*43wu8M;Y%;&BRN=~Vy9`dP|L*0+QPhmm~%p@|N4{1R|u2xZ* zChu2d%OuH(x$yQvNiid-IOx|9k&IytT$ z{Ml=V`r;2|vf~(E|KcNHSp{DNSbVI8!-8R)>BG8I%w0;V?BsVga6thdt{!LEWA4~* zu*%9uYy^3}Ls2+;)TtT&s9Ufk_dMOQE&}Y%pE?MKBD?CePK+ zoeurPI-Q@?sk(YUwgMX@^+W9f+-hM1#2Q83k$VRlMyo$%u%%_P3RHN}c}TnHWX~KG z1^RT#f$>pz=?=;Kd8kxA9ukpL@sIhPi_eTXGMp!2R6di@fF*@nwpP&Xj;xM_-TAoL z!G$&(bl~l6{#IIyj`%g@mwfc8(MSVBx{ovubf2w;4u%rmJa)lgqpl^dHk1Kn&oA}9 z@N+ry@vwo03U(+Eoz>uy79tHBdF_$ZdGhlG4z(f}_y!|d&A{Y%qHLY)QK2eTe}SaY z$3{Oe)kXBh0#We^yv~&jk=Kk-n)4-rVlaaXbKf+i@7H^FXXBA>3O+d{lo$OigQM`EOh#W)44=_(WVvnu zZT+@EA$wq=&@^Iz$13<&-x(p!a^Eo-l%AI`9r-x;=>!pj`Yuh7Je!uI;-X*kZvy~fO*AJ{3TgQk4=sM}A~P*q9sLk9&u z02O&xxn>%NB`yW^`u0aAMF(CP(2J9=5rl>Ku_<3}u=#G5kAm0wr0Q(_#Li%^kR*6? z@1rgo{!^1n>olR+E>HVO-HrcDqrmsc3(4mz@1yl4;}v(Ms)GMqWx|)1 zD8iTW@KRmGK*W!e2*}j%5ygr(AdKC6{t8^+})ho$^KxFNrp0b39S#8BWGqmXz?En zu5{IuAxnSP=fplTWK!+0U6)TkC4pv?2Z?DVY6mo~XxjMQxYTVr%B!a|)dF?8ant6nm(XOklP zk49!+k~pbYL|e&ZsfdhvkN4kS;5lpSEiz>_M&hG>s(} z>*qhEPPY+%*#|m&v0N&8-1xn4qki77zL?;?SUzci+O{|!i58EGLbj(RUQ_z_v(8BV zYHZ!PZAWAK_ROVDex3#hsY;Lkr*kD$oF{?~#6*^mrJA>h4nGO|Wn8>SaG))lJsoO> zCqOG;rt*hAe&=k*4BZ(xPER=Cb^&FoTY?NbG@!sR+?LM2V^d8M!K;kQ!RN0-zkCjk#Q1VS;4nPIhP6!&|nJA_LaQsssAQ1N0L5X3b2 zjC*?YG}b9rdbxDQfzK`RmI;TxAJ2(*mw2>OhMjMh9P0q9(p@H(EXz3ysdB1S7l}LS z)VI;&u)4*%mBgV zGlEVCbsyWky&+Zv6n}$ikjIk>`cP+lT>cT393SriUAGrQrbSPNwcv-Fy!LM@FYO9B zBP_u^0X*3U#q279NNsrdI_bE1#VTJ{)MP{cm3XtBq{FF`!j?*9Q(Q@vdH^P&6?8y3 z20xhhBBJ7XSI&r_2cdZD2Rz9U@Ng6#gS|4ls)rY_kZe9VDW8yAHwyM>Q5B-DqOuu1 zgs`B5&+4TjQCA%x*UL^ia=Nf;<<%T=WdTXAuO(K()k-`d!mnn>L~wc%T@kx7*q zYceSpk{uWN^WeRvuW^O%J{NSG^UxY0#(?hF!Cf2XmxK5ynkncWIwlk5!KH+*3lS7z zhupWZAuw9q-h$Veh}vDQnbJ4Gb-^m|1C1Bgwo^4OuBAzjMH@0}y;U!dl3F3x&IlG4 z`(1P-v`Vw;rp~qqRGs_lWO6Zm9Cd~$zn@$@RAZ+ozw){TD%kJiq^!Kx98fs`*Rx2) zqH=iVmTo=F3XD=D-X#01V=4#W`ZR^ihR88IeB14g6Aosk+`vJRk3`=5%O~Z(WevgMN|u!CuB)RPkJm zYcx`o)BXCRpmDb`q+=DGKD=E5U+uu$1>lZ|x3W+$u8Ge&>|N2s>mrPc4!0NN`_v(| zpd{N$H8$FL4+8IP4PJOvG>?Z~u~V!oC3Oh0rpg+g2@PIUIhK_@99E`l`X1uL8{)W9)!OsKrk z9STjBGKw2TTDN{=w@mV@FmIIVA+R&!Y4&86DzxBIx-K=bLWD>H*A6>uJ*8^A-#%r) z^x6FNDcH)X;YsC~!<`4}zWEMLnMbBZR0o6qr9`~)j+s;_a4WwQ5=O-Ej%yG2`BKvd zt%g0qdiHlprLHu)=()fsYauV6^E}n#=FXYij<`q4pByWb%Ky^z;>h???5eZNkeH_s z)FpAqkeD}y_mW7==CC0#*`}gLaarg5IQN zmf7D=cn^Crh6Uj!OlTcz(S7Jh_dziqT=~?_v^SSOi#6h*nke{E>Z{=Fa?dLkM)elj zr!X`-qB0UdD81#q{dubX;VQ`L068E4fj?{i{Z(xxV?pS}WEqm`#>8>j$E=kDMUuok;Sv?rDjf`;S#e|vVaD8) zNg&c;;eJ(7P;ux=IXw0)D%bnmltVlM+kHH0^)Z5#E>%%c5~5UEUr}X9*lUG4drfOB zaI@H6>bHVeX`r3pK)Sd6L0XkN%8W@9&*O#TAO!Y^P??>xMFy2X)FSnNl}Fur9yAHM z-Ed{D1(ov#4Fr2G9CrK;uv!ikDWGy51TW>s4?$B15@B;!lhbRDEod_3(LXw9ArwCLSbtOwQ}xeVS!R!=#X{mp`k3>-3v4q zHN(8By>L+NONXl~)Yj^^6TMNAm^`G-F$VjvlpeP_}Ks!6Dxft9omn zB(AUT16tG8GDjjmlrkmAtCN&SBfNB>l?yJV9ycd zU$^ZXs><3IGMj=rpGS5ICCM+>k!n^DWmh@{8hFu@H*!+P#ty?D8^>J|_71twMk7kH z)VHcBzdtVU2zy(0n`Vy*m?_PEK?pKH$NmNlQC{AWJamKc>2HWC@ns3r17?awFOEb9E& zm6pAFs7em2ZjyXPDxM|kW#2^w9!veyoTnlUrm|%2X(P%)+4cCC6wke^JSTf`AM+_{ z;vr50_qOpQ1zw|;ZiTDx?I>-&kBwzIOz#~^4G!B96Srgcwei$vhaG-6-U!{VK+;1e zyp|E6{Hpt#9IJ4D3}Nlk8ztZRIr3xg1f%i*gJvvTjN+B7l%r~kE3!0H{qqMFXnMi? za;Yg|j|$~cJ;>x};>`obr&^l(P{nGdVyF)`IpSckS86!8YnX}yJ;bEwcUnz&A6wSg za#<+9`=JKQ?pBDcZG*VnH3VBrsPo#xiY)l>9k!sld|ry?;U-OH^J}9a?8+ldnv=3c zPU)ekbfz9zpy@!bg7%~mVymD-Xj<#@cEqn_~~PP0$2bCgvS{+JLf zPc&Kjt=1knKedbtbvk;Io29m~q56S5+2Gk5hfB~Ja=JM;7qx4S;89-9Q|w%&Hz>AO zL!6SHT3~6W#nkKYz#V%Nz7l$xL86rcB@1w~$HQ^khgBG!ZlzF%!X6l6GdLp7TY=#j zCIjSp*JSmH`FN&5F%DI=#M|$IK}_~o5ccg^1q%7*JUjp7@u=)aN)VH8Yl*e;Y=h?mnwDdV9NmNfXfx5YRZh@iMuE{4L=Hz)cirRJ?%9nk#Lq(WgZqn$T+6FBhJ_}((USY6I1o5$2 zYzUE~@k)~+TxbS$?J?BAbOACi{`% zu(MZ+T=D&CR0DP(|8H&C%Q4B$<9xyq<|UdWV&!bl8lo zz0^JJI}H}uM>c&ER`Hk+Htt;}2lkRxwWKPGPvkee+s2So2Fypo4pQYBevgf#eAtSw zAH-h#UXvw)di3>J5@KfFXQL?0jNOp1XNKT-zl|fQK~=ei5WdL=Y#iO0)s?wO%dV(M zSBVk%V1cBC**K!|apfaIM0r2t;80CSMfyR^%ZCdTC&IXNpoFcdgfIUQlO&EVD7j$R z_Jg`3`lySerhS+48$V{S6za4OQdsuLQ12=~ZqkHHkiJm8zEZi4pC~Y_M90Ge9c)R1 zxfErtUb$LJY0D=IJbJmwK@-Gfq2l15B3NpeB}WVkU+#UnK+=Uu@}VeWRMQEAuyLO$ z(7+poZdliPcK9avqUEzilB76DjlQ&Fp774~b0$x7u9g)8@^zif~sb@sBNVb7wNr8(K^4{xNpVWB##zs;KgS}FSJ^!}_ie_(qxwf}7 zL=@P>Mbs24qio3^O`a2B zr&3!&Xzia&*{dtiskOHErVd@fs$+jPNM@IlT<@A@gxY!kQec5wcdgz`HEW0RmH%oo zMDSR!^k&Ch#Z+wKZzf3$&r6es^rqXKO_oQ#6_qXW_X10EB*fC2r(|UQQKW(8vmh*O znTvStEK&OMPl81~{?>of`mX{>zYWP>r=Y^XzYUV|A(AR*=07Gy7u2=%QHD)I+&ur+ zAW_#N4(O(06vf7b_j3rE@tpReNKIV_(CI4A zXtV$=V$GJFFg`~PZ`nIKaWoc8UcJ?*uDzE?S-SI4jNM{(V+n}8P;*$=+9Tt`Bj+f? zz@8UM_9ac(dsJfYCR4NWK2;Bo!d?q81 zUqQFH#!o2CaFgcAKbNJ9*oM-UK})gUHHLJw+E_nXxE0I1)16bBn{vXVeN>Y!+&A~|C^Jj zHtcVn0VF;V)&$ zarKY#IxkO?SZZAP(om<9E08q8zmrDDcSKU7Y;?cF)7lkDhMA5TJ@y_C$3BIx`maQ@ zC|9W76pqz|T}G}}%~$^K!hniG#Jf*I zy>ab#>#{pnWzij-lRG6RxW;%tqM2Q#6W!sgr|&o!UeTKiS*UVDd z=)erem5I5q!Vb(k9XG4vFf-Rzd9YzEXzh8!*fej~2Bk4|vYgP7g=LSwHxY=J*;)fo z_8Qxko%wW~z9o>|ASXjtcMdtCdPn@^J2Vw<5^to-pO`bA1)6O?xvPZgIeJDJ*v8v(Gt}%Eq~=DjUytL*gpzc+diE2O4qOlg&kyE^0z z(K*v1UD&=+fRVr8;|o)Fk+&$a*d{`3RT&LlEhtLCo|n|MF!t^;4xL1NGH=yEuJ3;<@A zF{vb6I2)hsiyxZ71kHSMnJCYDyTUY$nJt0@PFi?><|UP~lsoKFkA^2Lg4y#=*@vB( z8WEHXHe!}#nWR2JtQiZte4D#EWhi83tg zIil?59S4&TgTh@MR6gFFN|5+i3u+YZ?36v7K8RbVQP@?2#A_5BF`1oUGlvY>saHO# zcNY_8dw67Wa-#~u*Gi&>#&ANix7805?=HzSQ@xucwRRZJNXqiy>f}xb$9@=*5e`S@ z@(8XB-RBHT?Ixos8)iR{m-E~ap|T*xbg2^|7EFXM7v@SPV)#X_9Qt`Pn2p_vY!Nkr zlX5Emh?f_$-ElU@`O;+2WL0iR{Lv zG$^D;Wqri1{nRBO#U>e+B96G zG?HMkr%Xp&;}#%uHkT*(7hu2ihwtPkixVTCV38 zQ*Zi?43G?4e|k5ATCQwC+&!Plv^%dng%<<$`uKc};YhN|l*#w_c1rGDG`Z(hE0?fn zcg^H3Kwt?N1lyYFtu z#hpZaaus_wVOx$Ga+C1SM36WQ>NNs)oT=}`=G0B+8oo6(jQY1e2flT|{jEyI(5akSu61Z+g}L;veHMIe=>A$bjmdJU z|9^dJddSwl_37}fi|%io=@p9z{Lin~G=QUj=`-L_qx~x}6S=8=Wf%5DxLcF4aM%#h0B3pH;%?0yIroT~H#XW=)(r4n zK=2)in-C=dwqeR`l6D)#00Om!d#iZ7lFuq3PkH=6^6NuGejZxpxELdX8M!MdTQ}+f z#A=DuME2NF^4qC=@W5e4hP&NxWv;a?U*935%hwk2^Oa*K`|GKw+J(COsTki`G><1?MDd7WI$ZQ;pl;h@g<)hhz2SlE zT$z`rOu{+BYCvZWxO@`-m}g8X1H_#lDl*bl*d~HzTc-n?kvy1^BcxtNaVaRP)l(=E z0x1v==c-V)ZcyEp`$efJJb9gP5N;zx+O7bS(=i)$qE@uD>aQH-xK~Qhj<67bn&k->4(gW7)d1?2euSusqmT4q z@){{BtM^eERkx>{*+kNRdO{KT8_DgaUOb9BXIs;Nn6c(>4BGtYlq6h4{|PfEr1z{Q zh_KfWmG%FaOd>w02AP$<^Q3MVA8SgU>4lw&UUXAF>f=ng@}-9e=z+WD5f|9uh#HN+ z%syU|dH~+ex8Mr5%rg@oA}Wvm2`LdqaDW6ti zB=xBVfxIZC#ItR!!@s&INPn6kJ*muv=VNurvA>u)sXm>MsS0I(r$f(g4&V6L6ts5`M2s}y5l zjMrg!!#>xX&sRLo!?>TtqqP4eRt8EudjuS(aEv^^RFy7{O46b1_A~;|)lXUcmstr4 zQ&1zp%bf%=5}Op9@Fbu-+E)x95JyQ*`Bb#^l>_8gsSDKJ`zjj;C7kVhP|{y*lfDnr zIC^#!;7fNz5b#CVYuwUp)6bVpoG4yvlf56BKvg^TX#$&Zro(D&ud@-Tw@{wipB1Av zWZw#8zuqa^Ht<~QOXY;T!6sQ8XJob)-ntkc&YpM5_P)_3eyD zCH%Z3%3pk&jltQ;HIBo2FtwYlHnAdx}m zD*b0FHu_E*1@0R&;nE6Zfz~rF)E()&1`t(0K9n_iw@M=)vWjr6;a$V=LQ#`S?b&;* z1O@NflOTIaV(hMHLEh^mklwYn(|B@FKKA=;90eao2jH$)N*CU5lRu>g*W^*NE?xM5 zji9*O>qAhfHh$1bpaLK4c+q?PvNxaCL#Izy4txg?%^EA=tA)^`QNTMj>C` zP0VQro@7TmB>vFY&iE{ZpYc(GMYY+IMB7%`9Ms+B$5akX57X}$z3eoV_xtfoZW}^S z0kpR{30{n>yQ8JT@xPY1Vfcho>XGp&ui6Gw7mZIUQqwZQNpyb}x&ehP=%J3q~!YCJ$1|8~t=j3Vn`R!zY{FiLkdE;d|d1$YV_s z`wwr~FaH=aYqdfO+%co7Eq_KKk#zur;WlKFig}c&&sj8GD?#irGSnIBvqhenPB;&h zTjrGtCi00vS`k?*{mfov)I5EzNMlwcCQ!KZL{%mHe1T*RmJ}$6G_fL=5vL1PMlL~R zg4koS)6vUfAFQ%3WF%3iD_4({IMLY~2OU{*tk6IKG97wRUpxj^z^WXfFQ!~r+-GkH z6;->v=2F-d#ly24m#JZ}NV5Kqni|a?q0|f+elzxX{*pq{b)$$J<+R5{Mx`k;cYLVZ zsxK#0YPMDSLLZm7`b32-*Kyv(_~2KHaxv31&8I}kURh?;t>{;avgJz{5qt6aV#f-> zsN~n|Qj<41-ec|X#V`Zl>qW_FGF@)V)z4<@JyrAS8=3SAq8N)G=@*mu;a*-?V`Hjj z`rbbaU;fQZw&=kK&(G$U6Pp7*8N3Yvw?mvygryXJEPpObScpe-S)PK2g`(cxeyb!S z6OlcKjjYwEK91+YIDXs4h?!z?wAMC=YamR`t4@e7tiMx+kU4~Gdq=Fn@8*2VX;p7o z$Hid)*rP+;2YxS?4hIo*C|6hJqE@mQN@qd%z4D~u<%?7P%J+2+X9o|7fxfoa3oVB= z#`khR&`9>g5x!u~DgX-i%?iQQ32Ih;=wisK0anMcH8qlkXL1u5iyvhis2W+3IKwb= zznsc-{&6mG6mm^5>aOP;dwF5h`=6iWlE=bUFBsAqdO^ZOPn5w)3QEb9vDd@$Q?aX` z(kw|`0a%RUv6c@7i+uIb^`tD3sb@+pln)UvMp`$tOmfe1`i!=lgLWTC>PlHqjXca!qO)pFe3# zyF1-x3pNZGrDGwUv9NWE$UanBzN#qf@uA+&{8?j>4JiR73oha7OjS5{wNSp(UsBmk z{QAm#CnB%xuerop&ePP_{w%ot$JgRJyua%bvjU^_bqo84BC(Z}Y?;VP z+GqhWz7{*~ZtxY3}uspKPCaCezNoXzTv!WfR0L(!7mtDT>`UzE? zU~Nkdtf%0$`%vEKzg3D}()kH5&>(_5Gwdgq%6$2cO)gG*E?ew86F565m-ut9d#g?9 zqbxn5aRJQ7e-k39g0B0Qs@fiMZ6cL-d)mR%Pun#Uil(ukHul2AUhzgMx_dEQIz~8^ z^1!NC?l)8S%>SoL&Em;;<kM&e zTCR-OCZDcsd;EQuhyYwhXHf2wWdkv}cIuLTSzWsH=#_x1zjW%XcR544Rs_yzJh+-i z*{{nFAW%(*&m1&1rB4R+o%R^)v{x_~px^kRAZ`Y8P&Q0l4}lFg_SvI{jsC(%4_g`x z!w)wN6^fVhwbx;L@gDVxgV?7=fMV^H)2SL6SJGs6vKqF2zB!e7ab;B^*u*6W8g@s5 z_%NgxfYlW1MkMQu&-7w>yYKVu4y%E0)~}+H%ub9%(3=wjQ^s(!=en^e4NB;Gx&kt< zs>mFL%aKwIm)2ENsqDn7DfIiBU5xpoaZ9hiMr6rW{LxgBo{`R$W zxt&$%r9+uSBM(FwdhT~qJI}St%GZkwv1gw;DP2crNN&OuwZrN6x(lN+`LCNwhc@3q z3r}h%gZZ9p2`RE7#44yiSNM7+$DURc)WzZG;-9|zHJ7f!v7qXb+#qFuF4&2M zL~|%N7Z2~BvH&-<%akVAD6Y<3fm%S}qi)(kvbI(5T)Q&=1ewpgE_NWY-GZP9~&q{nczQn-9qQtQRzptYHc+^|5TWTEmLJnBq ze#(3|s9o2w*CIu5D=R_q-Jl-<+L2{lKeB%#Gr&0pQ)6I+AVfuOtLu$W{Be6-S6S^f#gOUN6Eu?Yivme zMUVO27sg~~DnDKd+Rcm7iGuD@-Kc#)`61PGogD+*s3sY(*LPz}n&zN?&;8(gZg2S> zfeyh;);EnA0r)XX{CzA7ziS`FUcYHb*3en3-Fk=Yd$3DdIz9UHQB)7jfUrlD%B8-e zji8e!%J@m%>9~718@~Ncn&hNIoN~4m?Vt3a5K$DRQU~ zsS-aFg#plW^qDJ4V|JS?>2rM;k!m%DI#=u=D5}*H>WsP9Mj<>|eIz_*!x3}BDna{9 zg8VgA_4DC~G2=V0aXW`C{-YggxIO}PYkjuKpq-=!4F{XV;jLg+`#FP1XccAKR%)f} zcd`spRQ8AvdOAT<7~`R-2!AwEx7gT?Zx1swY2j#xV)M1>s&NG8jtEt2XhnKPw#$`&U!8@Yn;;I@x);C|j8KOGooU4(bmxR@1#8t*VNN0dI zY^mOAPYRQ}4Rzb&&K7ldH?2{f4@8=w^?LWP!(mBw!@SIBa)XvqKf<`aK1Un_^t=u) zZE;YceVf&&tn(w2`fyf~TPPnEDw1_DW9g-JxRo+KKJQVe_iKl8sml?(;e^Op=Ci7+ z#>Wez@}Li=@*)2?9tqo6y$I)C8Zp1KD0R1cB$a!jJmMJRFrfa>l9l@j?RfvVK*qXf_4 zW*n9sBrX$m!+ejNDGi_`p||S$`B9cAD01kLw-PNXx#L3d%oTYIyI8~cq>rf#{~Ei# z&99Y5+p@SLMBO;n+Jrr*9vZYvIPveZQT_`RFz$5N-TkoQS8R-;1nYvV!D<4ke^v#X!!sQRwzicG z%bI0JCY>((u?39ndF>>~A~#vq8!~wmF0UW5mXTGE?Qq0n@Q%LYB-HGG}FzrZcVa*zz;0LQsr8^pOvHEf{j(%@2AC& zWG^xc5`V1`>;L|4Ce<^7g5#13e^><*RWtqpHYOM%*U=^W6AKu7{v2;|?+AO!107r~ z<20oeiuBNFYK1+>$|8Iqec4y$7g4A52U}^dr8T_mX+gcJm5xpY7o_+@NR_Q(&K&1V zjPE_fN`|rv20&<7MOJ|DOdjeaVhj=A1`uQPFdNbAa^fp#`PJxXp9nj>3TBP+l^$*- ziXo+K5#A)52a!U#Xpc2VoxUGoqdM4yIFwDnXQN1JzFo=rGw6ui-Wjy~kt$ht$l}%F zCMSB>?AkCh9LK@x5Of{#7$>UN=c9BQ+jcexhdnw}Rfk9CJdlfxx6a+H7M92bFftRg z&_0`ly8C}jP7-4&1-1JV^kGsi-?{r1RY~fxiqzru!dMepHezn5LV&Nir-lJReR9cW za9BOe`+Ho@V1FlZcnj7z3Po*0z3CHs2B2t)lL7V2g5f# z&G}91@ybZ~D7N-eZ+xDfeibAc^`KS7zNiHy%3A%TNTBcQBF98+&nW@~Yo!i)vJy@Lx?#O%LbhwyC&2iDDzhjs&?!HE4#Xif%BX*LJXhCLv%3NU= z3|k+l8{}sfXw` zx?ir$LGffPBbc<7)FwtT7~JuoJoD$I@1+uao)H)2cR#a)Z^`AnL{5i)_qMtJOp&AADQPjgQ&~d(9TUoEI)0 z^jakhBzr`uIexK~PV{#?fQ1Fp2By^#QMXhtu@Y%HR{9dq+)wU$r0(usI+(;TmYSJj z&l5E&FSD?WLnWG^J+D-Z)~uAWeQ-yG%0GHJNix_Va9OCkgICyCpmcaGmYaz1ywbuk z5p)hU!xP#I|LQCbH7c*N(3D-u+}WY_vsb$~$}28=4NV>GTgIWEG{5XQBQ*JS1tQnuEzQ<=ctq4|!`ByRl%lY$TpQKoUus z2Y}l*#KmUPPUOn74d~{h?^nL(9b)$0n3J|c;V}w}1OKdU72;`^FbLAA^oc4`r@=Rs zqB%NQDNpJ=8rkIN>%TeW!C7m-u^8iPkfjO%*B(v^guTT~Wl6mFL!VvLQML3=!19*C zEYi2oV3cLXwnNm8@m7MyGA>@DLd8GdHi*L*6F+~8@^ap;l59G)y7G>}EXKGPCZFR$h-h287e_6OQ72~Q=;V0OvM7t0yMt3Fh4;8=#Pk$c zw1Huh5YhYh8YJ|~4PTw5@-g0LBT4$DqFiwMj#NBD#JIfQprK!q)HyC6u#wa|E{;7B zD|vs=pxGD3D@|yhQ`nb>hSV!SzIAiFp2U7?A2JxwLsA-137=DO?mX5obrbpF5)?^^ zg1kuxdmJc&j~E2#Dv6n>)T&ax|3?i1@uD`l<+EsnkDo^Pj32X6bhUOvqTZHTM#{&Z zrbguB1(v+wGg@^G#Lub|xdNXkL6O&v(lFQ>N>yn4q`{Ho)gF%GV-POSE{KmzLia^D zf@y=}Yf~v7_ES1nF%24&SDtwA7=k2<>as_O@&rCzf&^OMhzs-t#lxOa`|oE|lBA7H zc2kZ+P*~9TL$7s2+~t3kqOq{}kx*9lb2&*DDuZ^Xu@vQyCE}t!D(35m!@gnn@Xu!i ztstiBy?f{ebxZz*oItcqm07Yfzgq4%KI?-z<9$()yce$J)55h<_IMjoPP+1TCf#-* z`A30a`^tpx^QD|+6$-$>DI#m0ofr#Y>&<1s)&;66(U)}wQ2<8a(3vN8-gPH$DtQk7_3YLZD{13bW%|b0{_b$9{E|Y3ci{VG{NI+9uJdmpNf-zmx`#i zA!W;_H1TVu{6rNE`J!eAWKnomqoHaie?4P~pjcK{zU)PP#2&h>%nj7iFuB@5bvOJ* zE?+c?8*+AxQ`Lb@;5g4?^}%oE1lYb4La1>BwX~Dl+3I?GQaRS&${A2JHWnRD(h?@d zAwL!hr%0WIxWafFeZ4n|;X44EOJX6aMy&OGnF2^pm2WIR$ zG#B__bx~2>slQvKf`p&;{Bjg$8O|d1klgoIGfvb?x$oJDmin;yD>qJqrjBjwL}weU^; z&qylRvW_#en!=kQ12anVI>K=;h_+c{+5z0ey`7N?l^Pog}M1< zIq5=IvJFd7Bn6AFGmhOH?`eN!<;p7@Rjn&j4$H6YEN$pIw3<9g8HB?h8^=inE4lo} z#iLhTs-B6kg}-$%9c<6##S;c0arbhnR?6>OL`L*PtF#u!;ESc&z|E}jsI zfXFHUgVdNmW5oFU#mW;m7nWq<^D@fJUF+06z+bIA)_Q8ykCP*cfUkc4X5-Sbc_8Rb zI`iC7qI~DSlQcDWs)R=W!^*N8tUv+i+BDj6qlMLo{z>tqf#}M0hw@PW=p0u?lHsg zv_q$#wreKrDPci;2ne;=lEdjB81|e|QJ#xAXd-#`t5%;0U-kd&94jkPyUzWcn5T=| zi6Aqz(}a4vb!alNYn|?-DQgJ1I!x8{xP*hIjXK<366PnXfKs_WQ?ap2I>^l4&{Uv> zJ5tpCeklh}FY1>%UT|5c-Rsf>%V2fpl29)`E<=!H&)cfk8)84bEJ>8)unZm~wr!@) zV3)HqK|aD5l!!VGISI!h5;bU;*z-p0r!Mp0eUP6aiIMxuUu z0TjFJqG!(>ac^)fCzb42RCTI>S!Xo)_guV7jV}#SlB~jqM~#wZB2i!?lgn z3Hl~h8pyM&E0=>Tkg) zU+h){NzJ$4#7e(4O;vJnvF$Rkwr=Af!nhPT(Pg(#wiZ!ycUvpd${h5ZKt-z4CK4lc zJ1bRo+^B7&D1Y@VE7M81Jsj?YCu=#|6DP!tPMW=tz)=0ko{CL3oGiuzZ0ZVut<{wG zyva$#S!h+R{mpiw4wRbbAn`|u$g|zzU}{Bi%@&E$rmc1=y<=JRJ|hvs-{vBUp}qMj zvXI*yOfhr_t8*`*{Inf5Ci%F3P{4|>*Tj77ba5E{4+4Dr)jopc$DeHEyd5F=uvz4b61*gR8*yW_D$?G5@ z3qwv8QC|Zcvg)l4af>-j^VC5@WR{H(G}-&>LCc(Pm5G%#>LfY@fvHI3b_|*@_R1o5 zi7_irzt!3!En#h|ofxCtHm1E{v{Iv&L3K~wW95m%r6^v74pwtfyXGtgLc{kGZ1tU` zsT1NpJ5jT~J4EW0#<-0}7NRP1SY@w$>XpXXZkEAERW=C~8$ZWN0_`m7iU$!p#(o>i z_yUAounTgDbW--nQ2H`qrOB$TR=-yeYkShp(%ZI$VF=lyL%p6m;2^4%hlp-YIe2)9 zq}=_KI{lq%B}z+}lg~r=Gezv7(>9K@M|MrCoLW~iR-UYHcJ=FuIEBvIS+Z``gJ8J& zLhY6Zoh-F^UWCOtCFNqjsLCQ$y5(vU&HgaZCj9v3hKt<8B12*93MF?4(*tWQiJ;IX6oU z^F!^~^H!1&+I+N=ICj-mhSJ&vJ53kbcn-qgTVKMf3SAsEjR`eFi%yohyejIgFu=fB$YnoQ3yfk6eEYwQ6z{VmfM-H4O{z@YD z#bYiG#xa{aM>`!nXTvVga`7x=KuQ&%%4f_Gy~ZxSD^@bpO@YW!5ag6WC*{rgf4Uv$*Xah<5|NfIXo?__7*#tk|=(+qhzql5TvD}{L1nz(%gG^( z;9!_aZSuXHGCO_jQugHc!O8=KY*Bd*WcFgbMSuhOm0~ zchY3dZw5^jpNANo2iSR9__nX6h`Pi3!Z_ChDVlX&-w(30oCxFcVRiQYP3%t(w(`W$ zLVvy|n<0bZRQtw|*qS+`w@|jTJZ_XQzVd zLEW%EyGX@+9`7JZYj@OkFW{Iv^g5nkWopjV7U?1Uu_szt`mGw@rxP?!a?_N13{mNt zf3lmVtlCt{^AsD;jI3)=^0JN3L#+0vQatsIkmJT;2v*k9TtrcQRYNGR?CEwMEZrK) zXR4$F&7Un|Y@T6ffm%=XoiC`GpwG1OEQeulPYe7+s63P1GKsyl;*lb5E}rEin+SVB ztE8FcdW_VI-e)_B4z}^ZV>#o&bfdgB>*X z_zt8U%9dMUGZAC-0y|T?->uIgQE}%NT6t<( zTg14$$jZ?P=EM3Tz0}Rpi|stiVI0C^x zU_6L#$iyL^$vp-!yk&TwYV{@E#nrQeY2Ak|A-A8XCbMD`q ziEDFhEoe7;9V6%~+uR)Bw`Ad&U5zT*`o|+Ky7AU*OlaBOLt`#)DdMVD-minBJLC?1oc8%p z7NXvp2Ki%g+!*^H$scxO>BHT{Ss5S6M$&8t8~s4yc)e3C%11Mi)LJz()h_-laXk5$ z14$>EK38l6g?n>-JPXP3+EQB>QrK7G!@G=p04#!8tmWa1!q z!ttFG3pj}paQQdz27(iK|MrDytF}1blD}naZvXa0$iaLY9<6q(JXUVC@tDMqRqLoF zt0L(MpADDg$J!EoC1H=liraw|e%@N)v1+{<(a7T=7>Lx>J?;;~E5SF7I*pkahvX z7cw!7i2)7;qi|@lVW9Y8CJOkBSP0&G;jkKa1HqRv5lr^L2yYb3ZhSctg_L8K75*~8 z$mLfuQOtGQ4u1^Hetb0(!!e}&!L7lv!goJ*IrQRb=jq9kO^&w>M44slFj-em8@rG%xZ{-0gUM-x?IZ zXH&G1nr-7rJYZfcRTFTn}PPv4cZgcF&g4S7D&YJTw05{6@Sr< zu1?rnrn$=3UndfSYr`(lBUjmbt+dw^lLYod2$ls1=<~u_ik~w z+ix^Pn`N~vrYFHL82za;x7%+G>KRT9gS78X>~`30C7jFO#gvytqbXQX0ktV3!)c<_cDA}6j8t1>sNV9^jue=Lr> zkGgpNA;!{1dQPx3Rc*F$a7AEhv?E5Y;-l(X6b(L{hbNr2{G-BATUjLlQ+Df#9JbCb z$L&N`<)19NV0JXIPgj$^^0&t!`8z@J&pO3;nOHI&PVKPMJlepA)LeoDGRLZSGxy?O zTo|%RdrKTQ$KbDucGwmbovn!~64l_I8(j?+Pl6A#t3pdJokiUn#D_ux1L)~U3UlcO z&a3vf7@L?kE}d%F85|0Ikjeby{G=`(-u&6^c)<@rKK`DK0bEM@UD^P=<##4AdlX^V)~~ckOu2d$Z(M%5}H!W2#R~*U4Xb`+5Vvoa`CNJ-QGcZUMY3+w`ReJ9Rqqnym zzsR=zGeLVgOk_ewpzTF247(k##xuc%3w6OD_rW0Vy$jq+`TrvNGu0KkqeC&hNHQUp zKHTLv1>|eGut9UC9jqrA`Z`hHz2^LT7l~=E1v7ZH1z2s;B+R&e{Q2v!P06o#(G0rD z(7t?hxk)S+%b+`RI#_SdU^cke-%3Tq#{@IvwOGz!n{d|g#k*pWp*kH(F0JF(52}s7 zgpMVuAr@l+RihW>b%n*ON{n-1ao7cv*>OpQ1tDn?X<(iQ%WJby&`=_9R*vN1}Rcgua#^=eMGaB(vG^g~!QibsN<->`GJW#XkdMpSh{S6GpeCy8$7;c8*(E6v z*yy<-BHY${OA}#Vt;+H2awbBd#=G%M>Us^3H_y17?8}>orqO94*~R#pfuAUbDSkvh zSl)Q{Fm~q^VmvEibbS0MjErt^zT?DNJ@nwaPh95K6}zA~Q6V*xz{qzmaaP}zTu8)0 zPq^JwuCoJI8t-~zGsE&g>ZF~ev2BlQ$RG*t|};s zYhb2E<@WTwjLzy8fj1(D4k_avxCW0%TI&VlDeX|sCi7l84|uK_p z8UNN#gb27v4~O{As9HUjCTHHzH#QK#s26u~T$vi(LU_`myGc}=r9tu4=bUAG6B~)F zZX^Wq5nl*`I7QmZ;kpe@(oJ;)qxG=fqwkL)9Nz19SSjRTznM;*6h~`1Sg+QE z>4qfcpxQtipp_!xJ*DC7H}f)3++0T?O}Fwn6|(C0Z=ar$cA9$XNbMFSglk_)>5ZPz zGxSR)s$xc3F5YoA_${2M4at?R2Mv%W%HHRkyZvsN#$y19j#aMPm-AlUsyF&L?%B-n zm1{1Jed`!YtpT&JMYq$jT1yl?5g#VBP4K15*=M&=bm6eAEK%yOzF4AseCGl3=C;bY z_JQ*Yo~dCN2^s`~7%pENxlRLT5e&st>3tO&<{%#bw`rsc{ThEK4lar{l2l6Xv%#S& zFC0V;rJ=xC{;>9UyPM2G>z%S4{K#puB&3ax>{KA9^VZ6nWoZoSxb3MA8hS?(W54 zaJ&Ji?st!gLA#4x?9fEgTNb!{k-cstDR$(+z-irmI);O_dN4!1`J_1Jdy8`s(%b4( zjWYo0Gs${$oLlYfIJ&v+7;c|$&rn4*pfFs=$5RD=-Ye(w7Vn^A5OXrqYA-+UIXmx; z8iEt$iRx^y(LPc`T2{3#9(OWvE@w8kcCtYqCog?5y>A zEomAko$xY~&kPCp-i2H&v|=C`L*=iG_}XYhpe9Mq$rB58#7L;4$cTYPB#M#!Y4AlL zF>=T>Fy7~c@SszVBeSiDFw1s1ck>qO2QjFZp9ioWpp zOe`Gftm&iOBM+E(yeEl=xo$QN&A5d|c)eO8v7#lFN-P&bwMFpq?2RMRNxAuF*=Q&S zDFQGe1Dt69-ZIMh63*5UjD$;3JA<`#svwb875IP_2Lgxqfqb!N#?C#Wu#iE=T{jeg zCBL^GGD)w2Tf*t^`6pvg=uGW$xGTgfnbmB~9OAfin$od};+BbpLqWI}zuW~ldB8eRdSGy)Z&W}d9KrBD_9ielkdqN|<@*12RJH<)SjE*LbZ}lWa(NE`- zBF_Gwjj;ubcxcv<3oV)q*mq~1tX!^YWlp2tsaKn|_9jl4;a$zA0UcS$l#72XrY&t3 zx&Fv>l1JNAGdbjg>$!5=!VyV^c}ZuPP~N(Zv*b>6L80YE(Y=(17mP>noUSOSoG*F) zXfV9F!Np_ltm9Dkbex{6%ZSs!EfmjPGVyGzqqQrgLa0P#=-A@$aIwz2CV1c~pkap{ z5wvfH;Ym1e*J&t$JVLoNheO9xFNM1)1W_CNVxtqKA@w*mv!Ct+%6CsFH`{RuxDXPd zuc5$>K%-Qf@pqA(K%#4AEhMIax3uhGPSJdVP$bht8fCg#i#Fx>PO8YGcARZE-1Wm; zD2{trIAj?hg=Y@zm&FJ8L}vVykMGPE6X7&$RmU+}tuK-lx4E_u#_SLv#5YDL&7T|0 zeY2pUh)zo&I*W-ggYaIUlMx~yt%xO>a`UwuxwE?%;|SKvV;%T((BKSOgleYEM1fpq z*HQUJeH4~-1R}8#9&V{JMA3XMx+-navJ0tqL}1Z1wN;9=_wI8YSe7l?^mcJB+MhDB z>J^Rh$fi-}rE*zyhI`ZIyzHwQZDG*hkq+ zHxTc0=kRq?wa>XW4k}DX)Kfom#UbJHgidNmWTq)q{?!`ocw{?mk8XH-^MUb!)HUiN zybNU=3jq<9j$C{sgU^Pe%v&||aBDFyIdB=iQyM``Pk&!eLe@U%X;x9EJ@aKsmr;$2cbrMX}xeH_d#VpVS!uOQyb>x`Pw2?=; zaAYSBn3!_aQgYh;QE60K?n{)zki{Br4aS|7M|VL{;7#XvM^SCqW3o}iy@-ml(@Hy3 z(8!G3tOLbkZ4~f$M;oADyWB}YRv%NYxrwNvop zpNcb@;#(m)joRV!max*03OAWtodZ`NZ=ymBqaHR^lxL5|;bn9kl|DYHr}V#(D;CB1 z-=CmR(iub{B6Y5I7BhMChns`@L`7LdoTRsJ1|1QIzMTak`YFrMl>URcxWZL8KPdxS zPdQFe^ekWOaF^pvbpuG%lOtrajy+ys@o~kHLR5u_D z!UdmPo%#BhbM;P7GwG@@6s~Kt397H|;-ZdE*J#3(OJnr327Ka5jy}%gPvc@y&tMVA zdr(2p%xG_2z}471GsaMB406YxWzrqUjz3$YdBN=Xb65n$?Kqc<@mz(0`fG~%8j-9S zxw9v#0hQcPzrAXE7C(|t(>pA%v!H997vsRq*J#TWDpK`_gXUVT9fd;0n?>(rp`}{s z(0nz+K8OF|Ado$uMw;eFHMMlfkFkxW>6Oi5@1$jEow?yL~NtxEL!s} z{juk*K}y5wVl=I-72tYdjB5i;KlFmF$Q@AJa650@T<4G%DcqtJII23S0ln%Bp6s#J zwr~>vb!kW)G*g>T z;heojQ4S~2ZniE{Q1r^A$cbo_S;ANzZ;PK#sw*^MEOJ*~8{?~^CYYw_L~+)nF_8fyM&s+o1Y`sxwxrSo=$ z2Ls2^@13qRNXj0Q6cY89?9W+Z|_Vn ztWh!thL5JV!uyPKmd?8@ih3?jnzXMJaBF?HMwwDG`oGrqSQLw0E7y1Yy^1omks=j} z5<{sIA_qwFi8u}UsxP=3=6xoTjMT#DI2M*y$W>pz-#`;A)v66qrmuyeXgr53NsKs? z=j?G2!Vlx4g_e0l*dxwr0-)2 zLF|~at8h;kP9I;%-|N`_F-YIX742if)MQp~&Be-FhwHGMC|>`G7}N1UjBj*r2|(#j zKwr*qub)pU6x7HT3W0Q!a=qs5j9wa2%Qs);vsnG761vT(YH6+&G|kou6h4Tv1jdZS zfU$yd0XU!8r+dOUH8Q3h8Q;C(Vu+v7=!^Rz$@)E_t zdysd;=WXf(z9YV1(RZi=^NRMj*B!BP>Lc_;g`lr@1lOnVOA0}?-U-TL9u?v~h zWvSckJ4F3W<}S(p5!apK_-WiNJJ1RQcThjCFHp(RWrgGSw=?-I)VrKh`Tz6V`}}gb zbKf!eJyEsV>*$XTZ@S!T@Vh46g^^mL+T1iBg9y`VIpYTmyXrt>LbclHd!Y%V@7d?N zz<$rhh=D6MLc@XN%^|K*|N9p8v9PnC&ut(BIbUWeNBsvDb*P)&J5!54D-Y*YI+pb& zrJgcBOjEaM=c+K_>KT4y(VoEsyT*!5UP$IbwN+c3M-4m88J<6mTVN66w&qdOUTe4T zL5A+ehiJ)^TWTFqX6oVfV++4^+-N9~5;+Nei=eox@FZ-_&+BA<-W&R3@7twgh zhjKZWzT)cAe{CQ*QiJb=(uo#oi_(`C?Y;(vKFM}|CaMjzzKOIFY~_6MQO=+IyWTJy)`i3SWH_QA)P3gn7X7nkB?qR1 z>XM5A-t^V;*Sg1V4f=oRfxfYLe~rjc|D#16-RmhIkkoM*s}%23qven{b?zkmDMekI zsut-)Su2OE6d|Fx>{!Ob|L063F;&7co~!ecdOlkca3&<+FBa{HsyS)QB=5Zo+zI@v zNxy|&ebCc5{(8$R;((S2;C%v^yXkLE`f>9qowWDbxeD&TTeK6Vo9(_R=$77>Jaz(? zKlTqNs)!>9LUse>y+e$I{xiYD)z9hIl=m)i7484B>5G8%L6mZ6MaS440%u&?-Y7~# zN>EKd*9^|-zYP>em*ZUh@y2opgChR3wB-?>o|}PrNvP}}*43CdMy|i#e>99VzWPXY z0omQ=2+ffL$d>6HRovNJBgxObiVkfqx-G;%n|0+btdt8vYjuX{NqY&b zD9vya_#%kiuNDG|-flZ_k!|~Df;PIlCkUwetsvT5j^l-N+H7|_WBJ}P!F9{{zXU^@ zo_z;4s*7#e4Hce3S&isw5AC}T9F7ZHIK=a-UpO3hJ>{5OB*8(mRNV~+C&WKF&IRWQ zxM+kT%5#C4(`hcW;+Ke#a^$-UoHz4g9NiN}3Wc{6a#8GyXVQ({I{8-XxVdhOy}pE- zYR%?26f7(PEVK+eq(%r7?jAA*DoYrES&e} z(kUz%tPrsnF(G@njNzx+f%BF?&d$Az!ctp8n?U!m&-2pcUOShK={J_h8yb&w(9&K- zu}QKkIV^7OEM6`~BAS6v75HQrjifSDX?$gY%Rj$-MD>L1IsyMn)JaS~2;Mu+QM`gl zF}s|oP>NTKDT->s0j%*#CdGo*$lY95RupGy7$KZauFvyM=63Tc5#4hJCO=m-DHg*MZoXkhMD zbS(`_Eb9 zCmZ~cs7Jl^I@|>d@w#p#qF-!rC^*~XdYLGqS^!a@lXk`WN(1L%xqdc%YHd8%z%#u# zE^x2qGqO>%X-Wd7JGqX9w>)q=af3_@JQI9(g1Z537}3A;z;3x4#T1LYbGRE|yQ0{K z8-TNWZ>;DBO*rhTNJ`j1bxoxAbM@2bUfee^sG5_wEVsJ1B=2J0zBhHy6${SgQIFHc zEqzKo3vOmoJshru==&6e9p&4|MVaIC&7G9g@Zq$q*GmG!YPDVuPpc7e0h$psTFY=!KxbDPD>ScF%fS@cu1I{GnQAx7$I`I{8vBP% zjPq~`H2m4?;PFtOKweYiVzGwLy5i*ew7!(O*R^PGwt4LcpQI`y{bIYDBH z)|>L#DMIF>;rb>eNJY|Yc1IIqcCm|aFVTp&%AA-C1PaQ(FHVH+T&h$c^26obd8D1I zAiKT3&S)JM;5xNs`f8)$A(d8Z?&e#tn|Fv;DV9jXY>L=Us;iiu5@9Kr72 zgIJ`-K4D~{^>_5WCJiHYI|mm^aq}>f3$F(k?r3u1tPoX@{(EgKS&p0`;Iw}7&5`*; zodL0a5J==2f2LC$e<03H;$x-Xu(P~3%>eIL!f@<5F*)#aaA3d9fm#EzEzwhb3w*HJ z!OFW#oo)Qd;FDmDY)q)JC!)^rMEfntf0ArX5N_fh6chbZ1m_%+#or9hl}*k;tx+)0 zUwUk=S;v8{IQ|Iv9w2Y|fz@$_{Ku4^WpJTlazVKKytyz`a%uTI=YA;L{yhfNBN6I) zJ$^6YPNkz8xs5UO9BeJgQ!G9^j0AYR?aHXn$HATsR+7J|KaPBLG}nP{v{#h#lS9R# z;-WBPF)BK}E$aPRqXLB}4ufeORAdc<$Y?<4%a_s)m5gM{Tob!@AVxrDh(p@Qu!wGN z*@*}5aWtpQ5H$QC{T}hd7_dwkfLY4JcLlN1wfucFFXNzGL2a2#InDQFvYTAIm^*SyYF~ zlEoW>f8#O#PB6k)B*n90isuwbQK@@tjpPeGJEGV?pE$HwnOHztE2S}#nwrS*VvG-* zMU%4Vj!2pzG?Z-0TY?=ZA8BTJt@ml*Iz3G|shUK6_mk^MIBC-yRx1@k9J@25A*?4Z zd81HITLQSs!8`SW!DfOt0xq^Q?L;t%J{J1I zefOV}{23?Jd^EYrfmuPb2HysZ4e84h{WDs!Z>nfVM>>YQByqDH=_bjcb-0{_Ifdv* zu-xveuZYtK$GlMFkq8dOafM=u zjVkTP7dL{y!4k5fRNQ4w>gI`sK>nO`WJk|LeJNA=t_Zg_-i*~XOu#9ZC*ilA+*#5d zINrOlQyDg<9G#E#+}*LBnVNN!;j@t5_^h=UMws=s5Py_8tYRrU@~mV>_K9d?CG)P8 z{EzJhXJy<&u3>GR>eAY1DA!pN9RlIrr@?f(``%vkk1OupxA;2 z&YpU+gk4)msBc(ZR5LFR@*rf4vJgoG(kC0+N^mJ8?Y_LvOuS9znRys6FUFW_%bV~> zAvTFCuL({$AjiIpDL;x@`0`g&NZX zpWge=S@VHSGpV$($olRex6);ss_Mj)v`f(`4J4lQ#0FO|peb*10xat;3^9)_b+;I<;1N23C7gqdE$83cXM+&s1`C zs)$vQMUQ+>b&Z}`N1ShsyOpVL(WHFRwKUD>t{^p>dr#FBZ&9YzHVn#owQ~Pf8>VR% zxmre+)(y(4Ct1Fg?sDww^}*fq40?3!2p48P-Op%-ra{l_H*_RZVvCJpudKo8^*d$1qo{BSDYpbJgZPCzj`o8U z?PL9+z0dK2I)oj6h@=f8d$e9{wQ9>XWHV74*VqFXg=-NKpjKx`DBFp?r_8YuI_>1p zIm3=Vd2a=-hbFi>?KX9~$v%HuPp;2$`s=yYbob*_P6W*rkdM;%QfE@$!^Q==g%hb9 z2hW zxfYth^N51hqJl!b?oan+`s%P!oWg0Y^1)c;BMV;Tmc`Kf^zC!_vkqfbkBV1?L|F6% zr3w!B@NM|Hgh_T7Z>%WQR#T6@+>TQ`EwAFufa}pQu8G>l+&bw-Ig?yv_~xJ+8-fyF@ZdmbC1p;x4+!^7%NMeVjJ@_T4>V!kOVu_(PuV~Mwnl!mk8 z>s0spxEKX^5}D}T@u0G6TwoR#sa5l!pvS*1PyB43GWV?gJPPcO-x_wqy&?jcIjjJ# z=l&D6giqgU-&0p|)QC-kTThf|kicr#<+xemWO?cxEW^)JyDNvMDncLFKL8JcCXBua zInTzE90;2sCfl`9tdYhRf0Yjvr>&kGqk=hAYoI3$J!QsFKthEPW-|hBZYa~qlI|ne zucs)wqs_3@5=|yR5UNspIY-Z|==nl!!}w>3TcvziEZJEE8^HEdg-vGIOa#?dM~t_L zRQYUeEtrLqGs1UDrsvP8k5yg{F8QPaA@!|ZfRIPxl%ZzvcpU3^npnp=DurYd-gBrv zabiLKL6|D(`c`~`Ayo25^66zR@Av63GSQ-$Ecq4+UR2m%hA_+K&065V`)1%G2hXq( zOf_q3a2Mge>H@>}6qMo--KJ&sGVIVZV-)g5I|4P}a8xmgD&r_jrHaMqprGN=FgAMs zcHCTzHY#>t2?!q;_C84y!;HL?wJNF*Cl-#^nnBciJ%?ltm(^P6bD94UH4+_!;2*4)u0LYT^-{X*2J68zZr#=dz-GcT~E2V1U zrTpSoBzWTub(ky>I$|Dqs|hWRK-^7lcv$f`{zW-%C$2)`l@c=!L66^8SW zocLQ9_T%H@C5)CRp;|@oKmN_RRxy;VQ%+zNujzdimV^%t4FN66O`JfzHOWx6oXi2| zYg0I7egzplu2)4Wju@deQsVt(!ZAtxI8eS$p=?L3QYcR>%3cxfj|{6os1B*l6*H4Q zD|uvhXErzQ4VMb6(4 zuO@QAXC6a=&(#Xda4Kr<38CDrT!6T%Qbvf9HZ%3a%qMSXvzOWN<`Y=?8}qL`<^D#n zc_wJI0%2*2M}xtfL^kQ(E0i_f`eH}6gtx$%H^rP0as6P4c1;{XfjW+`56LwsAK(7% zqJs?b-^BQ8)5T|f8MnvCrJG|XmMPcIz#85x*3gd!1z7;`Nfyn}h>HHaNS$U<^M)XM z?^c}3X|9xVe%=zV^mwhkI*Ta^TRzFET6hTesJL9UEFR28slfk-Z?m?`trrUgiN_|S#t;b z$`>~|>!=v@!=-euA&f|^QzwWv`y zp?7Q4M&w~Cf}*-~EQ-`y{XIIx;QU(9%w(zm9y+2x)W7S!DViCJ^rpzg6yK*)Y}Qa2 zA|C3QOZAl=t`g_{8r{)Vl)Nv(t?4H1zh)KCuy*$5;PMBYw2R<2*SqP1I^___@ar`= zfQq2X?eT|9s;zcX{F7fpI6Q1rv-e?>F14F1M6Px*dg3E#ni=f%mSjenKANU@v>`kg z8HD}i*r6?M3I2X8i*oE`NNue@ZS`2O&d@DD`{M@fu+bWW&z#(+ut#2aOP^~kE$$QVa{IXwb)MGCrL)=$abFqQnDB6{Jh{T3Cz=OmP;g+YC z)gsp@$@A6&J%#e&w+hP`aEy}sB5@;7zn#4Ov$;L^orAW>-ABYWH*JS@BM;f{b=uUH zB=(L`-f?Q#-aO}WvHzgcj-L;bF1B7|+C(2$Ivcfmt=ilassANC&B<3Hxm@f&rVvH> z*K!XMLH%otn+3i<$M~ku&yP+p z+I7NdFE1_0yFp;wqY~GwTWudO{w2m(ZC7VIWG|ef+U?+=V*t$$wIN2&9#M4PQ*YmoJQPNvbnqcZ;JM~*rs@`&}}Lm5}(QVs|LY; z{fw^pyTUL>I(D_Xwj4BLQIRhZJrwItTOA}z3dlSC4}&%ZmXwU93H$P$^Vj~C_s zIi0uw>JINI#UTD=Qg0$_p~!VR{CyNb{I^LN#nKF6|DAStPKH4FKL%w=HOy!|PI^-l zbMGBjD$$ZO>L>eOgT6@DXBS^2?QQ2=WZQmHWOTyD0vbZC^$jdQ))+hXLOSgOr^0%N zy0;X%>kRe(GpKK@V+426)E#%snb`9STh!|zCVLf4pIQAyO#1C+r|{J?TIQlUb*sN# z0W!zjC>PTyM>F6Hl{n@`xwuKYW{ft-t6vzpm(b}N7JC7_W_J0KI^84aUsbGTVOD%8 zoif!{Gh6JfO_>#6TBj?W{{>R!;;fg^DI1v*xo$0oua;`PE~`_V4th}E-kfHvh07V# zjb^uLq4u{H7!7iHow7X}z6jckd|kn&9dbsJH@_LmS2QTEqnfi&dzhhoC6h8GcNKW1 zxEjtYo0N@rYI>u6xy|*VzluS*z;_38qOPjboDE?RFl;XLL|si$J~sGQ-qj7-gL~y& z!=yXlSKc*E>H~b`UCX3Az*pY24a$RmYUe&tp;H|AJMWeT^+CS#ZlzNm@H_9; zHtm7E^KN5M9^^akOq22e-+4nO2nuWjfxK2Z&5 z)25G6(;2l_q2>rt(K!{wPPI`Oi-@agh;0;}XZ?WQUQ+Z*7ZfEG(I*eYRcnsum*(ju zu|tKI%!v0X_{nxAtDX&yB8<2i1hOY<(wHN2`C4Iiyl#=)qBl^WgqNtpNNO40wguxI z*d1TuS|iow@n&@$&0A%UKCLv_--dSN<(I&}-4W$xFdJg$Xo4$>lTXdG z(M)bF=`d%uYV}4?+M!&2<_?|*1bZZcW>tQQa3fO?1Aj#9t82vKK4mS8Sd_CaVr%pL+uSb}1v zpBT7Kj0X}7)Y~>_j$`h5rXW@hAPIH<_JwMzwm4r4Tf%NCkIn7hzK9+X^Q1T0t@4;| zFy}Zm+(g`i67y!ACM(Xg>EEQT4r}DRrG=B$LxNssA?mSSX|;m2g*sY{gQysOAX}`T zt)+GO(|1TK<#io4=}&}9flS(T5ck!S+&qYJjW!0*%NZn?jNXxhB25kmJPCV5ax40# zH)|VY!fNdCMacEQKPy7G*sLuCM;oNg8`Q0Ew7R&8zDf8~5ibj_yVThcf~DFi%<&a@ zC8dR$@A!o z-Rj|JofN8Eo8#yU{^zC6y&aA@sAn8d3x@aEV64aEJ)p>9g*OI=WkPoJ2?vIZgV{$; zoYMn}A~?ZS*xXrSSVZ&FRxhqi-(A?{(2~>-=q{4>I(bt^;mt&YLzLQ7(c2Q2c&-<8 zX{U1Kn%StWp zsI{a?G(S1KL)eW*m-n5}CA)HIli{5z&~C2eTUg26)JjCTMl?eUFhxa~D4L0TehMcJ z<~GiWk41OT5-lnYGE5o~g-QK2B;2&B0WR?%AxH0+3~I*jn(OJWL*(i8=4}}!1lS?ED@_gtO@bYzl*Oq;N^4zc-rmBW%cz~En!|K{MWshbUENGZU z(7+eY3av>%0#X=2m4Z^@;KvqW9-JlRN;GAn3YiFOoR&yL zj6Rx_gW(WK*@%u=wuqE+-8Q8i>TzWvZUgbMhIp*D44+C$A<>*N&6pmaoTLd5^OLgq zf6|j+nC8QT+*L>LJW{~Sr(M;jOwQF%W7sJjqH4R-3=+2u3_&T!@e-s|Cy{Gm=f%o5Nt^KJu6i(= zI9Gu4q>fX}WsRYd^0g6GU<%ODOF))}1#>Ab9IUlw5Gq<+g?n)#Xs;qVNAQhY=PljeOnRNb7M_g(Im=)(OX%hJK~4TFrVco-+~8o15J1;8QwQ;e6-?EWMSn@4aQB zk-e(P$OS(;n44X4TIUCR8rFLf{lF%13dJRx*|5jkYGHCcH#97>)y4`XD;jpb@LTlb z8t`9*Qn{w>MK7Jr^eSSTeALbsWbE5mDVBRPbp6+$T-@dwD9i4AW)10LAeV)HL9XDQ z%nGb5y9;s;O4+vXBKF{3+zOO0nZe$-_Ysrpdv6A_nHR_a_Z1uO`(d9tyq3Lm1YEz5 zzJeyY+Mvd^B`4yFmtLYD6I&(KHBmDKd`~5pIdxwRTdT6PG#!u|g`5mXj;Srz&>%!+ zvFDr;%wf1+CI&@cWP!-Mpqv;uU()?`45Q)N0yU+Donv=6qf|b5bERF&(eCMAN7EQ_ zWp{U&Fy>K!VhV6Q#m%md2s1;0I?QDE>Tg9UC7a5S*^Amo>8$Zwk1BghCl_ zECn0%fDq3O8eXRmKs37{h2ttsTUqQc4X+ zjm%+f;lA2{%SL~AOcQFX8May?94l$&%I{PzLW|ebiS`jOO`4K89jqXjjXbmv?%zW8 zne#zCQqdkk;QnN*(pVa)wva=(wt(A~RJn}v(6~goJ(D=T{bxQxpi0eyUkOI6Ij9U2lZ z%K=y=8Wa4;a@GJ_sX`vzwmN&;ZKlBr~f-8xaY>Oma{rk*lK=>J@UHf+L zE$!Je6!S!WqcqDNhlK3`ubyOKGMuW?-%Bywth9M=-f&v}$!Q$XL<@vF3PZrXUY?>O zh#qX2?o!btTR!MS=xQ`v-=r#tNRSK;O8a`Ux)w}SPg3T;@TNkEZ9TroWx_sHuA#BG z+6-|u$iNoquLBzxn@UYvka?K%rsU{9%|-uUJ**Z*KbCUy#-wh`PnYyt?O-h`4#7sk zQ-HIN;uveZv%mbR0JNnIsoU~163Qc+?cgZo1C`I3oe_0Os)!9B4ro_}@6E-%m!3)V zXR9l7joNt~ke|cbU>W8L+N)P*gN?R)`~yI)3{1{;e3nE&?%~Bvj5Mz;;?33`2{&Lg zk_B6X*O=ygl2`K3=)^pXxA{f+WBLGa?_J?yTF*`}k;_`VYUz#yjDn-6Ts_HiGEfMK z+)t`RyLb@T)f9t2Cz$-~=ejV&e9&-cAbe$%)A7&iiXr2&^X9^C=9E6)L~sB@ti?tY z8RO?FSzch$KiaGzmJ%%W1;;*h{af1VAhgvBO&o6Pw(CLhl@G3x;YAuMxDVISghE7` z5@Qzn3ui`Hy@s>H7X{ZB-u}%qkSsD>6_;^`(906&D$LWdQ~#xylbbi$X!>fE~r%I=t(ZoLD@x9&os5~ zYvGi3C>3jZL_(aGx0%RhL*WP*-7zNVMt_#?axw&?3JK{hpip}zSmD+(99x=ZM}h902|5Xn9xC$8lPOzU>4cqD93&IQM)dV8Ds*u^#hS{wd@#G2 zc;>q#zKyni5DxN-jC_RT7Bn1U{<7m!XbRP=)Kt>H$`6~8FONgn1pABJ^)us`b@%({lv6=JUIB-ZtzUe-k(kl3sy zPIuW1ReT%Cgmzs0wU~l#S==-?_QMhO=F%9PHHV^k(9vC2qc#fCE2rMD`EHWE%9OE? z&};D{y&#GG*+ux0<0EOIp?-NEHP8qTTTh_{Z(Yjxvp<%`kQLl8+x+TA z-@cfQ;b;RdZAylT9FRr&iyiMa>dN>f7n;~c=>r}|1pAJ_^W_vCt2DYR9N)d-ylh`d zp=s3`omMC^Y*N_;hRArIUQXYC)yB~b!;>gD=~b2Z@`TeQU$e1v7HW%yY7&kUU(cd0 z?UDXy6Gp3j!;K~D7#rf@yCaJh3*TNgY&HvhR7B%pAED}dG8sArA*ySznz7m zZ>NOQ9N)2!941$=sA2D_z`QjFCmY|*#L-ne{V_Q180sJTJqrUR*!Of;y-{!;sqbf? zD4q{ozs4VAAm9$Dua4mK<_|My&jc%~u|ij&^u@yENBzi+rR#=x<8b7*Qzy|MTR23P zLr;R~je_wX|0D}V;e6m^u?xWU}1KXYR#oG0AN?dKK_VVp;)U0n~0x8CAz zl3&uH^Xy#6AEA5#l2;JYoX9HvU*Y-{ybqM^mh!J!OF((EhIAmtl>gsV=wcD{)0`u zR$mP}K|7jqki`nxtQ>w@?wtHFg$6I^LV3Y?eEwupUx&&P!3-;ekdp(PNAk}$hA3{Y zrFs_H>y68W{Yy5Mj0EQ5Z#hnr{MEwc=;mIaSB$&;|Hh!nIzGnX@z-gLb@+D^$x@|J zt#7t#i`6=JgnV^8_ip-!g&_9ta{&B#z`W}J86!Xk%w}zoB9vB{4|NDrp~<`_nRzk% zD?-v5Un-BCI4H^&TD7*SP8Y4R@%iCuV~N_*Yso;qJHnlne@hh2u(PtNGXLpK9lhtE z-BA1|d#}Vb!}%nX>z?o*fu^<=AknA|10Zao0t{VFr$$h0o)kcOO9WS||6f6Kt-1j@ zux$=Fob&;H{&XD{#*Oh}+;(TGzf02<7fL>=j@+-Cg6M@L)f)8`9^`8O&p|i;YPku4 z7Zy~*v7nCRJFIv%To3Ev%BD$LDqzV?$ zMqcOC$e1S9zVx}dco&uQQB6t>9VQm2pjc3ase%lid>tBcVCE`PFD5asi`_xbie?RN z&e|!7TQrHq{>iP&p$)m$;Ke0^cC}vVEJf2avy(Ng;ga5(f%ENMB1RGAu8J-y&9F{6 znRFwO@Z{hCcVaHd(aueoyNNCpQy%M}o#!GYVy6n|uyS!b#}P%14c>dOi_y-Pj?qj9 z)HqIclL8BJR>PNes>BpC>+9ao&XORlQI64TZGvhix7es&n8(_A5al-*U<-6jF66uE9%TZ97g3c1>+ z%L(k%HZbZTCnra0%>hKRcGBizsxmj;J`{vV5!TeBw@mJH#6HF$9hZ+WEu;T<86!lJ zo>tVOLGVaNggr+$s99Qt_#3&9abEZ9v?O%dTW0Ci*HKSiSJ0bjOr%_n#Lw?9Hk-U95K)26!>%v69{Y zOsGpE>%!bB@uY|?NsVd#_#7Ve!{FL=QuyNe+un@x-AzuzUpE66`uD*+lTR#faNb-i z#qGz;^GLH2u9rgB##45o(+;3VX-qYBf$%+$-UzvDnCqtyMnfPouEt!l<;WkWzIA8V z7?O1QzVn8&RBw>N6;xGA>;CT=*ZbjyX=DvJ!Z)Lm7;kXCCz5*s-zW>)$R_pr>C=tK z+4S49(4c`Zx^zW7bc?%nxSRRLSvZd3O%50JpmvSBe{Pb6Z?3U`%XzEt{PmQ25?NJ8 z)UWZTS@`-SPn<@-Sr(3#6z|r+IJJ+tsc)V}NbQEUWa9Yhh0>4$X5NL<*uh(5<4RqC zenyTjLQbpRGKDY<>p@i&pY~m=a-R5GrLa+_>XhAVxNUrS!}*kNok_n%J=u8>-dcdO z_HL6#VU~II-CigSsbHun;j@tNGgIg~wfYjeb_Z>QX{+_Est4bFSTae&_D)T`8pXu!Sx)vayrwZ0xP%z4aScIl7xe*e7d;>+iECg(s}n(W?wD z)YQI`{>X4oXpcr;spC4t?@c2^x<}md$@f5b?_z04rEHt`z%iVNeHqvq#I?qXNJt&U z)h*^)cnh012bKHYvZyHzbK4A*aS7@G_@|(m{G-Ui?HtHbB9|#}#reZHt$ce2Ms0>} z-*>lE%I4wSz{5K@@aZE=EP#FH<{n2RgsRl;n1xnP6-dit4!y!w1* zDc(^|qp*v9(;VTI?-n$ia@m
C~NOng(-_F~c{*pja9*Mm1g?xir2Mo7bx{zqN;LM7~A z27+4DC5Y(uU;ax&D)q-ap~tazhZuAzY19$#z3)r^7NwX zv;W2KjZL|rEt`#|)6J%^HLJ^VZ0Z1zaWCe%6tXBmI>+_o%Q>!R>@gdkoKE9y#(85> z&n#;GxFu%bb`N3u>jg${ok*dn2h=X3+T3#Dfj2(Rzk3dYZ*ZEGI|Fyl zKzK^DW!}2#_mwve-(6DpV%ulS4D`jvMU3v6!bkJ{D(k_9WxmzZV zWX@o5IVD^b_1!a3OojE$m9WvBA>LIhyO2hRYHB^u^**PIc=&Ml%5#nJ7eME8TY)ia@&p{pCZsq%9=WK{9kB zN#QUqVt*8l7vb%IV%>qFUlu55pPZM8MVq0T_W5a><14G2E!<2Yi7WdC=0oG&$t?!o z;Iv<9NC}o{51a$JYTF18HG>tjpqQ>LEQF1L$C(|O3sl3|aUk9Lv&UN!nfv!t8eMIm zUS^dJZfP*8C_8Oq(;5P&k1B6a`SOf=iEU)#N*SPjK~rCZoGrSULWnf=foT>_2cMrt zwid1gjR2$Ax?hmKJmTzydooD)3(^+_SBr5k8^w5|)vh)M^4{WZsC%bz$p)%hPfhf< zD;WRUeNy-^wXQ#Y28Xpr^AGQ9qd2QthalGmch)%Bx?c)iIJoGm!!o73()*`zp;~{7 zju~&M;$F%R$V4_ci-+@RJunMfn`+~?s@T2tm`dER?&^cs(+An;j-V2DOU27YqIz%& z6}pl+{N6>|4~ki)?Mgiu9+HJ>(AEQ^)gGFKPj7thYkYa1Q7&)oVGO#QO!UUd-JlQ8 z!dcDLXu}^Lv&)Z2;S)Vcw*D;fM#uS}ADKoMuBXg0I?BbK+kNWTX6U#_*_bA(&65Ms zal0M+cqX*TqtmF+K|+K(;cZeSawR>~;=NPco%xs){a~qvlGs7r=bRKhHjS;dno<&d zkRrzVd0ZBzfpLr5$;W3R8=UpS_2hX%3R}1oAeAb^p~@=+Z`tSkWKYaMB_dK{EXv^e z%P0qrX=_lYxF@9$cIxdK&7epfgG^OgT&)z>Vg1QA%4Doe-)#-f=6;F;*VZ4wzSb=# zeNW9qH}HK`PFm~6q$;JSWg;9wug;TOeY6yMexII+PllDzP869HDQ78Y`GU? zV92mX`k-mKP6jW`M5X3K`6@AAesOmBi!zZVfdb10&eURBAlv(X;TA@4%aS?DXnRE(TYYnYwDA-g{Oj}>p@hhvBkt@UyPhr>&s8$9!v5Hjapa4xG^7&d%rkldXY?%`x&iBA zP8Q#q#@Ry8u3(9(LI>xWQH-=K5z0J#TNwQ149P>eT-?YRli1jcialk9je?I~&=S11XXR?~8-;GrZS7!TXWGVs+0*9F{hD#lBKWMWY^_$Sl&*4FF6*3E5iS>x>OPi3O3EpHyISGU~$#~UHnx$M&^ zg!Qo9ub$S72lq269BX0N9$a@TE_U|W6t;osF-Gz~mqN9U2xk(Z@(vbw%N2Krem;$E zHE0gD7r1ijn*I*z9r=Y!beIcVpJ`Xu)(3V^aZ>liG)9p@WLHl*Ya;r&B2#4 zP&KOqiT^NX;mg^m1{eY2Dg(cgMpv)32lc|^yc%E4L^iM&0<)uEOCh6!iAv*volTB! zU(ZChRcPM!8pF45q>sA_cU4=i6fOZ~lqURd z#QT0>qs!%5^*tRN-+q}w*22Na^Pu)cx5uHCsY}?eY;;BwDpj-TNC)@Uz+7DI*V#x! zz{tjtGp?qtcFQKBm;7(iD4OkHBXth@aCb76+HY-C$I(fCtLd-)yXNqOQ&+X$S;&%# zThaPEGu)?{v))*^oVDNESXyer6XUuqI&$pr=F(i~KUnyvXXqvaEf1f#+_^tmIM!wN zcx+-`YW>~B zH-(DPAdUrjhZB5}aoIZmFp*7Du1>ECrAQqAv~esWrh&#`yGFw~6Ncrkot6Uy8D;{#`O3s6LAsdCfIgTz*s9p;4%oT};i{$^G zg@^oya=R*M28KKh@W#5-x*N{klFoH`ys(Kd-a#c)8R)b_=c^_6I;_76ZvBfGNGdJ) z&_W{1LF$&>ufEKC9X_uSAiAi5XaqjOlP%yG4I4CDS)aaJDNw$--MUydqFB1i;u}HU}oo4!=@1 zrkQn_Nkp2}4aEEGayG%0ok);96bDbbu*Mq^m%D!z6A{cxil4VQJ-Lg&jL6O7;bYpJwTseF^)n?QAjfhtaJJg2h1a(w5+ z!gSEWbw0bfjv_n(D9X&F?}E{AeUlChz8aZJc}avs&P<}+&O}x88BPn1H-EXe?+xOW zAS({VK1-vk@U%f^HxDA`K<3GjCqR34dei6PBR7ocQ<*vvxua7P1#Z%Ehv7X5i2bGbuzI5MN7&%R;%ig+mz;;+4#S@YX+Euh3ga6m}L}mL@-J zz9&eWhlYfDjxWP}yT;8vyJdt^W?9GAbQVr=g~VD(!W=pb^dgcQ|5Z)Qy*bKh^jk$p ztBsXOXe@U$oDsV`F8C?F(}mqv#yBjuc485sfa0+*IqA32vBdf) zlQr{`Me&Bgb0&w!WKUsub~`M+o8gWd((z0O)IkX{Be#egALE+9`pO9RUM}f$Ta~4A zJ1r7cTw9H5Ga;`TK;G0Hv8r2R)x!q$(XfNG5DE-oSZ`!~K`W1I#J$hzE=PXMO+kG} zCiQ61Iq7md-|3_qt0!yO-aKbKB)bgiWAIUs|E!}VZ+`6^!GaGCkxL-nOXTh2W9(+=b_KV_ik&@p9$I%)eT4;8tkOi z%%L1wuCz;uo%v#Y0Jwj<1Ve{jdI&wA3N}VDxenfobEGR||Mr>0b)kjR%6NjMJgfQ- z&o*nYe8^{l3X_#4imXPe&ErTlq<2BCyWOEVDawC)Yemz6Yzqj(TA)>bmIl7Q*BXnx zwdo$^6tG@tJAmj85u!D)wFReujB=bK?XK7t1aPIY{)F|$z`Ydj7-NvR1+=-s?$ha$ zt`nIDWE@UT9?tbD<9g5TS7<0|D+^VKBy_vL6i@sB25nQkwJoD{%1#7`#nDFOqpS{` zB@JZHRMM1{^F24+U5;~cBhVeu=|=ku!nn{Tb4eX%yN&8J<6V{8oR`O(Ct^&eI~CS9 zRaaq#z;&}zH$?nV$4ONt>N^4Tk)BYK1r_Tcavdeta)fY4E`*#_FcBjZEQQmr zxhR5(GG3uLORjbuRUQrLxS_~$o3znL%fJti_YQI$QjXe);wVTYLCA>inN{A%xXjV1 z6f$Mk`9Sj3om^j_JEak|YK=}Stl=f$1In9ETqXW=3RQBs_`&k#6(i|0HY$0|_yO|f z6PMXDYa==$Qn4QsbXYT^xLwXkqmu6eKSueVI)?{}I&0oJg~^!V(eu^fE8|?o_g!pE2;;^R0s71< z4%c06T<|W%JtX>!i>p((TZ~KQvo#lcc(iy+(Qe0WLa~Corx1W0`fEivb2OvPpSnYa z$$va=Qjcvm4uG$`a&hZ>nDkQ?TA?@`cf)S*p=#j>8v#6hvIn7#A@Z$h+u_X($8Ovf zC>ByETJ0obM@dEUiTSYiM&ig7-UTF!DI`?W)R(j2h-6Wv_fjT`qR$6cQ59q&INBi3 zGhVG~OT_h?99t_G3m5xXv9V}x1^u+xs>@qK9ToxkH&!hS%`iME-1EI0 zT5lc7$wb)OFe`#cpw9-f--#`x=;*W%}a@tNLT{gxW#cqe~c`Mvx9TSCC ztzAs4jf-xdGHLf3sPJFELmtk=`cG$ID4PFV&gzCq-}J(8=8QKNxTw*li9#5d%Il(o z$ZutGH^uoD3YxN|IN_wT=*Nvo$!th4H@Z)|aiWy3|>-{SAM&fuMQ}@98 zTS#j56dheg(u$){DS3d61<|AaafS2gJZKD}@C(#+ol~2ay`#hvqluGf&D09A~nKS-S z6O-<(9WsKI**GmKHNaDRB2S}n{66x)B6$kK4Dz$!y`;QvER6NIKJnM^8h_k zL!3F#}DsYKRimqLg}Ei70*@ zM#(oilmyKc^wPo%z7=%s6De22@?XVC(D1J8EW4kAFHbP|A}tOYUY_KNqd}dhK@~zB z6|Y|qoJkkci}W;VcpdF$l(2?3@M5rr)-=|^^OkPz_5Vc4x6P4oX%ijN)43>=Bqk^n#4T|qs z=R5&V5lE=x!bsQ*nyTp}?7%3w;&50GWJiV^2)MiAsWF1F4&D*y6#}DKHX}--Q9ly2 z@?&zmE~xM1(=sukvtWM_F*n51V?-0xN}~}Xr59|HMeu5~s=<8nRc`)pQI}_EXhx~S zO9%PJy}|H5(+%lC}4Vv*T$`H0VOvUaG z7ax4KjwX5`T%cd%U%bLwa{&pPrN8S*WZB8_@$VQZZ9VR06b9`SlFvcYUsg zL9Y{5+*m+53NFLGo}1?x7#gj1vsx4V?4`P04An>TZP5frQ@Q@SHk zC7!61x=dDhW8i#9FGyoZbjU&=yz{Be@J7Jh!Y@oCINFG9{levf*^d|LNR$zo0EmXR z<>v;sA1~H1C?{2dA^UK6?+4dY=_NLTe#ir3A->e6KSSNZ)pXF& za$l+8kQMj|fa2~kF2~_j8UoY)a|wm_P4390r3pc=)~Fu|mua?I-{gVIb$N}3W1`v= zE#H!z$QDR%J}`L=uhr=%Eyw$DDwuoYbvg<)U_r-F)Gf+jc)f-}kh6udAPn5y^#&aS z1=LLgGg}<|btPlrzR^aYW}n5^MmC5dW%5l1hWch4#OZc0{5iqM!<(~E^g||@)A<$) zL;rLXcW1rTLa`DyOu2Ml7~VU=>B_fhI8=9!L?p70r?*@(cgEW_`ZHo+P9JS52FE+n zI4+R8>zz7|qy&O}dim}Lr!C*5)9)JxxJ;~frx8Rh*hDT1(w9uG%X@Sb!qo4T8Q#0$ zh=Wso(0g?hn`;XcPbiuToOXDhMt^oS8pB=mt;OuZ`*r$e$y$t}ARWcrS|5l}SmX0z z44IB_e?9GZe^M>Z2V)E~`na=#5tN42#c4+VR)F9`3c=D$J6NBpcWBy@)jE@c5tEau z?=^mGtcs3Yh{H{a7?bhSmCi=3Uc;kN{7gv??Rs;Li(-73aL=hOfYEJFJ$z~_ov_oA z2oXiCRP>gBzNeU*?D`RbU=q=-YMoSBwcTk(5qN4~5gQ<8)=$E=s6%%y4so?j9~Jnf z(Oi^z*Kr&IT57IYLL^>uKl<{jR8q#WDP?zslzmKKId&virk;&bV5X{w1CDfR^(9JG zfQ&`4?&w-L41p_&sH$R=_=DCLL<`OPV%_b~3pWSWj|;4g(Ry`lUDU6}`P(8WKc8>9 z)-K1ra1&7eL?&g?k!l{?$g^Ih04)E;EkX5@HdVc)O|)c50{j%ndjoY;QePL{e=LN32E|XWF>b?y-4R;B&ik1nh?KgB4}6ZBlyK} z%OA=Gpo50vuAL&8xR7q657nOhQu-@NA3P&r$6Fq_9xk7i{8+AS(D2{#gqQ&grl1qJ zI0j@e9~zyQN8?2JK1i3M{k8Z}U5ys04L&Cktpx2dIB;sF4RdL20u66hB1n3e-kTR( z_R{AiWlYb-R3REDB--po6U;?k73ERs2_E9w(RR{M8Q$39AsUv>xH07R3lX-kfg^*Y zJjj(esSvJGIoI?-j_Mb6s1ic_W?bSd=IlM_JpZ*M_22}DxC&+Xq{M_*a}BRO*iKWdHUk$<6#ZfbHg zPmbJBu2e8wb-af8LvrNiO5Zf-PGM39+6*GAuwVoZ-yZdN`IZAg zaqH)L$9~(OJ{X$cNoa}-9kj3mZf`^-h9zbk3BItz=<+Cp#?@1mgEpnT5J>f&l3 z{#1k|#7(69UW}n7KtRwBRhtVi!St-28H|ak95YdA%6SXAA^Ucmf$V)>B07!6-O+P3 zqjz*k+S>4BB?-5t^-Zw>T`_oLxrQzuatbYJXeAJLxaA- z1zcd%dE7J=eQ!@LV*aBZXrFDE$G+6LGxKAcI(4;))@xu`>>fnK$W2FSKpjPZKQhVAOcrF!K88LdD6Y zG1|$@RLIBv?Q7NbGX6C)MRtMp1Mr#e-@ab0HBl{qtrQlFvVGit2gZ(ZNvq$nefua8 z?3q$@w=2_0SOLAEbyLBC7|b$yfRL;YOUz*xayG=z61g~tapjYpIt{f;X$te8dh19o zL-gkff+JzL4(eo}X6wm3GcP!Q<1b7M(U>D?nhQ%`C+xlYeGY%)ByO=^CX|mZP=&-O z{QC89rL5}5CZMNL4mFG9=8-CmV#Din6d?j=llc4^7l^OcC^5aFf5oGl4>rnWZ-CzX z<7RgMIzcE?)1+&=9S0xgC#{EYu9M`*il=TLztND$h~^09BP~%ZM-wFD!t>??H`U;` z34J;|A+-yON$0#-I2-48PP$R9pt#oZt(%j8->2zUmX=^0D|sO9M^HqBBiC8xtgSz2 zNJL1NDAWF{0B_EUBJQOAQKOAAmHGs3?WmejAL=a%D z@>f!f<4+nwO0!46t5eq!8Ob=y<453X;r=XX(|{kt!9@M#h4*W`u|&EICSu+>VvL6P ziwi|F1hO`UjpwId+Mz<-rVV%+JMh<-zR*lyg=on{q9Sx8y<|ym3OT?vBE}M^55?sJ z{~&P)?jn2)rz!j`^ulud?L3k+?6{TQjCK6YT*rZpEv{syGA=CKsOWWWiVWGUF@>(> zK5&7HzW-ftp%sWdT(6&v>a^^>4`SP0Nk#{kfwEf(gJl8G zOvMWcEY*5P4st}*ozc~V##?JwhVMRdl>g75oa$qnmojtbUpPfsX5J(#hfz2=na?J5 zye}!w@giO6BkM8m`c-m;wda({b5Wf>^(IXgo+j)q`P`mgOs6~*+MVw5u9=bZi|drr zUDWzW`4R?YnsdXk+xLX+RHmi1&)3C1Us9)htV*dOkgGg5FZbrWlt#_fv&mw!zig?n zq_x`HV6~Ul>1X$7vdH`Ln(Kvh89`lyJC9QGhv}_DAWB3yv<^VHNGBGgNksRmrewXj z!u6E6Y)`n9Y32sy%d5Q({Y&#zE|*|3T99P(Ea4IEqEa3{aq;5IOH56q6&b+hfKjFxMvvcVx<_5g7fuTJTZoqhqdl29f%te`dAznqJe1y_bva;vMGk7@H zJX^=t>-hBFA~8>8966 z5Xr}*@)gSNa9|C^^NQ2ZyByrv4lLIcSZb}>LY>|P$D;qr9S!B=;#v{SPPI<{t=56H zb-3TC$IlApjW^BG|GxQ8uI)sT>NJ!OgCm27D$B3qz%W*WI|n{Kh86xNRXwyVi;Cv* zT-Skvo($}U6pG?{4iwYXrCPOKBri%kRB@K+b!dVY*H2K;@>_7wVmSMX4AScK861bB0(!SclW*=yIiA zTWK6!mtBy~A)hM}#*;gc4xpiWy?2!B5Oo7WBAtWkDMVgU-R?OIyGO8UbW!pu=S{%q z-5WA!ge_Ds7ETA=D8X`e7%VT>7Hi~TLz`cPGwl-ZWkGGr`-?E*z@sl8e%kDWNE zW@+L-RS8UgUy?VD=@0jf{w{|^Tn}gcCNX_*KFX&zAI`q#GH#`5Y1!+8+D&z8$BFBL zyNC1GzMNO!W(mF730S@b>?LDq-&|6wqZE+jI8vLWA(7xDY??gl(c zt9GmwoCXSDkvbZdey&@bo3(gLH~nnl-a2-dx~?p#x*??MR+2g@FQGh8i#Zum8)Tc| zzH-d@&)r4a^}x$foi*-=5naWqekezW!@@a9=* zNGU$^jz12b-KMt{L?L_;4WS!eA}tsv_l7)E;%vhVX^}Bf9zQ;Q=)erNw{ifN)iH#H zYm4#|ja9yT&1I+!2?V0G_Q-5Apx99hfgq$BgS^U;LS948I%kJJm-;A{ECkAs=mw%l zE(}{3#+O7mtY9v1I&O!BU@~07Ddz$Ba)7fbcUtu0vaM333EP+l;(d0x4u-p2NOk}T zt{NVSH&2*X({2}%ZXo>m!JLmh779vhrZdtFgf}M~b`*7p+nZoO)p)Bl7EqnSx(E(a z0)nP1^5`ih(zZ`S5URPM@Ux1WuqgVD2n_YHxUEK?8c80P(RE%J{pG;n$)V{mw@VNR z7waiBWgunU7fr$7xP2OjP?v+oafbxQ#6qR9xEku&{`|&xpABVdSoRt9Yrdn2!*+4; zK>Q_yIi>qeByvhm;Tn_k2b3)YQA=;~d&YT^JVShUhO3dO7--~-_JXN9>~@;ZIAT#R zT3>Tk-Kask)e6=Y>WG2yi}#mB$LeY3+L(m^wUcnNNpBPn!&d@0`}%-^BCZYU!U%5! zj(dw{vmeYvfMDsVnh4Pp%L~rJJftHTk$E!RM8caBj2&~>ppRmUK4}C_9>xs}h>}Il z+G2Ns%auOMM9@Hf#b!?+{7)xyXPs@MIEws0cnAB0gs~=$*huCYqF0RUO;GT@Fz0l_ z#?eAiA+9Z&g+m=LibcZd?nx8L`L&=)4hkNCw{&p%KS$FD3a7t^k%=jTKH|#FO|6cT zXN0%LV(zdz892gFt1j)b9o{IISLn2j0@dj~38lA8aPf>814XljqY=COirtaD4j=R# zaGTB=D5$dm|ay}L<$ zZ7po{?`~ywV&25jz@VZU)zo%jh5z|rUL^NOVdzb5`tAoeA-8H_XplrUR)o5UhJO^# z6UOFQu<+ z;&LXAzTKJ#LOS z5=LvBYa$VMdw+B(mydqZM4_TY#h(}EO;9&6kXxlUMtGlEPJ68xDA3SVMCy38QKURH zO!{GX60Hb&y5NgM5gI7Wy2QcpVm*aI`d+&`b-a1O*gEGqP;@&Sz8JV_*`@8}qemah^U>Qlnebelm6?%UYM`N)@U6=}%O~&SVfJwiNcD_9ch4)@Cx7z~^ z49Ph6g1Nw)jR%vih0)9VHAjfAUgc$ki)7qiQoI(Lsg+@|ilI?VKQ@AJv{p&wyjKyGUrm0I1^ zls6wZ`}2_|1~rtrf9Hd_!yc85r9XPp@jj!xsYg3-^yWQZSg*Fg|l`ZZy?z~w6*u`DN;_JVB%P>HpHW?UmS7d zs^1Q0=ZO}QI=a<*FvVMDxj4p?Oe`3nA-K{X`-73!CtEn8Cv~58hr4s1q9c*Li;K2B z7`^&boB9N_$Z`!PVK0w*@AJY~#!s`6^kkHJBVgVKPqz{D%l=?6Jj1{sYr}gv97Xcs znHd=RA`MIg=~)>VdOMunoM5bJnD z@Oe4{nuORldnX=|XJwaBm!anyD4M}a*xa11Ei8nMzQ$acyuTM@AnKP@%Dv)VXyT|X z^$5cE+;UR-A|1owpt)9ScMs=y+c{j$^o!F7iubQDx9Lj^`e8lj&wb9kq+hBd5EX%B zS9DEo^WO!BT?#>eEGq6r{CXQhn`T(|q^`X+1b1V-LC0|<>KIw@so?0p(WJj326+#V{+kT? zXui@9i-FNeZ?@?de@`({p|{x7driXemO;+8f2)lm>QL5wD|~MU&VqfL14XwD@y5X1 zlyA2&RQ2YN{IT%H!Q55vFmQy+?fylMnX~au69wkh6>Y7f!qH7R`R)Q2Z~Rz_`UuUY@5zAo#=)G=kEd|RP>#$cDV!q? zuM5Q+KaoNbxd3}>o=~Vo`AHqi(dFe<(C)GH)_ZT5sLrPxCvK8^5j^PEj&OCSpEpnh%~ser+FYqNYUhhof$_$2 z*iBfy&n>6ZzhL0$rOeU$yfVJ)FQ%vqrBOHsxHIyl6o&C09oXKSV0?66PNV3-UFwT~ zvvR&-AP8%X9zr?3^tmd+ucqktNfsD+{8|b_)G(QM1bClFPFsFGg`qXGiKMfxj}U(p z%uV_Y6GeTqU$;4PCcc@8p__8>mPv*S-?9<(>-J=1^4m5BdW3a3|9sk7L78mK=%7|Hy829jQk2XAgL=ko^^hI4yT z*xm@3{rF)ff_~X`-0Sp5I+E02onj@F^P&FOz!9th#H7FW|C&4N__(dC z4QH8|xw}obTaMG%Np`!NnX%o(t?jrm#OZG9${xj@W@a?iXp*?g%*@Qp%*@Qp%*@#D zTxq0{F1({-NuIBNY*RbGJ&(>k_uO;eOU_X{y3#z=RGr^TfO7KlBu&e5twzd$jL!k) z9=|Yg>U&|L7h2?D3H+IR7Roy$R;^SgxgPQ9?xa)9CYDnptrsJZ5fKd21pf-zT$ zKgKC$gX`K-Ee7!^K$iHEj-n`UfOUvJ%N$No=ZjGl{+A?$xJ0l2Rge7k0Dkyul7MdS zNV|Q3n<mLXsKE5T5!uQ?st^Mbo``xRYmUl>Mpu}1UnB!|#(ytTPCx*DqLKc$xY z>OSbb|B+Z)^|U1y+asXj`maJkFNRYGPIn-jO#=M!za&Ys(=IBTpl%PmccyX#6a}nBptiW0LNFs1BsR4zuAXFAvt7F&^%vbya1DimF0H7~dRC*AYEb3&5Y#`f znPe$oFA!3;t|eJ7@p}=Wt#upTtneM zBiRl#wStHYxm&#DatjSna5HXU`a4(|xn+XJze+J(sIXfJ)fTr(P!zP=0_4qGtM-e! z69Z7(Mxtogo}2EGG;Y>Hx1ZiNPBFLEJ*`+&Y@(>RU6Mp-HvOa{epDJN9C26j?GyH$ zRw4WULEpTCWZ$VftLteX*j_++{DdS0T_BXQ;N??*od$PIP`Gr-RQGz%G%^LB1hmSX zk|eghV7WDm?sd#PHc>AaC>@olwIX*zx0 zFEOzD0*ZmVMjYj8b$Whs(qDM9-I=k{#KDQZ$= zWY^Hno!g@7F!)875Yf1Fx1RO(Xw6-rNzK%9*`-qDg39<}xx8>ptWne5W~B`F4OvO- z!8|8sf4p9=wc05F*#1W=pLbUY#P{d!1vIz2M~q=oEHK%t1knAHxcM422iqIF44HY1 z+G9_T#Pi%yhfGf`i+cd7Q5h|MNCLkIiu__^*bpNp6H_T-8r?rXJy&Yj4ZlxNbPkou zm5EqrvVDPCrQEYiL)4rz>yaSUDAD&H4gNS~HLX?O59RU2naWr;5}-g-dL;Eu+wYHu z%JJS=t}Kj(zuB9~A7RpvZU)^>?Jz1+RGcb3b8@IWF;iYV--N&(tqqYNvc;H85FEhK z5M=SeutAkNi3r9cf-(PTk3^21&SG_!VGdIwtLH{m$D5vbv>`q+% zR)Qg;bm2^SVSARWjaEMQMm%(FFg<8YTX#epEOz4{_QR!2J{dNw`E4bi?28!o)}3XV z&V)WGXB;h~QS_2)Z`pFi6(90-N&6BLsd zNGucd`q}ZwBNOD2cp<>`0-7rvh%C>pQyI`GO?l#;aN2f4$GQBPh*I#af|! z`r@H&7fSP!>FL5U{annhVl~6JB2UkUHfbwKg${K#$D373y7Qzb2TCqwy~V`f#%<@J zv%^v=InFex*DVO^i*pD3_0L1)J>`Qtrj|*xY92jcxBdQCqTzl*{D1^i$DMJV7X4ve z98wp*@cgVW=B#vr$ph5_6ZCghUAx_;Xcez;IoalPLuZGBRg&Hz`hx*|u)CRZ)+XTd z`?sOmK8^Sn1^*#Z2v^CC9V)A-;m2bSX3?x?IyUIAZKK)fAz^!olz?)(Z8w(d{_3v) z2>)9&@c7cSKljNH`-tJfkipl(XddcILn7d*#-Cc=pSVvb3xi=EDip$d#u>_VIj2iL zfFN0NF7n2`e3HsEy)8*a=G1bu(m788iD-wZY;tEwb z;)UFB_h;_DLARFFWFq+y=}o1%RH9LylFpyTpn2Sb`qUALX1s1oPjU9cVaE=jsmnCV z6PUTvgi{MigBE+K8#0aj3}!zVe)a>tX(}}GW0?JL*ewNAVn?M>9>mO@2Y9EY@OaW= zlzG#jk>N6lru*PsVcBoDA&U1>6uT13Uh^|+{anP4=TLT#de=F`)z)*R9wNBWjA|TD zVTnDsmb+IFNk<|%k50y>b_u(EPz)cHNG6ldR^;v#RJk9MSf=duTI3a)HY`rd#jrms zjs3N9=QtJhmb>n+VhqJ6&wQ^L*6emuR3AsAx%&*Y-?~H+T-zOaEq@k64iYO$mrE=& z!Bxb$GoX8A?kzDKu-U~0KT<%&ejk~~zZ5I&5W&rUXtsG@i6p!bswfr|wfEDo#I9q? zJz@~c{biO`;+lX>WqE+YQllgC#r5W)p>QIe)KM;?-uys?C2>&QoR5Qph+XJhGjb^U zNOgv|qvt`GHvd)(9Vf926U&2Tmb6_g*Ua+}i6W`O^>YY2^BBejF~fRjoMa!!nN2ok3sf){kvhpKU7LL@#ju`ZQ^`jJ)jfh=b@9YBe z;72P&T(K)pn#agAY_W??W5~WjKYpxC!xy{w(F1rMr|=YCk)qgnyu!m3yz*?VC#W=B z!HeHohCN#1u7@XTc-WE`p9sXylT;$E=*8y&qr{VC9;WQYjh@|xJh+cq>nSoz2HA_t zh1MdTYTzoal1FEwo~F_;gfFgN5XsYJ5|;4AB|)>{XUHT>;fu?Hyz)$$C8lW6o=~0e z6yfYoT@9)J5OIneJ z>k-tSUm{cRL>hh%GSpIHkHAY4EE|z&^7PEhR2q&ap~)cIu*r}%`c@NJYT<_4YN$GVn}&uV+^{`^ zSl+I%u%sI{3sTqKp|CK;8#WE{%sUktmVCozL7sV+!on1A*fi+Y&vz>{DJ2{>1=^|p z9))59A`YJj)XVQxiC8iYH^-oi_dbP%E90N%fLcm}KId{*HZte*2Zm1m%O z&gWI0f$BM5(C`dW&-tQ`XP|n{mo!9!)N{V9@C;JV`HDjG|3%OFs)=kMdd}B0G=tJ} zzOJwgPS5#*a1^XR0rm7}ah8q9Py7}F=Wu_n&~W4@wk`+OhJT@wu*D}f4_J-(rOLyS zp13S%E$vq-3rBcjM+`dk^=pM?Bf=BcJ7}%uHwqC~cFL3Hw=xY|bYjyO_7sRyU%!)S z_>vPpdH~Pw6&|+a#ODF40e?_g!J$9=71bCj#;F zXO)O6IPrPFDDfAWhbcI5qX(V(`m4;6L2lx5p_=q>2Cmd%6T8LGsiMEDGz_tc>lZ}w z51E7|HE~JM`s_bt5~k3^WkFu~m(0SFnYbj#EB}^Bm?9IG1)ci(kIa%%V&XEOQ(ym; z88#p=ad}Wx{6B?kF3d(U;$ShoWiBAJYr7ahWyUn{d=!!X}pkc^M+*U%p=t>F; zPh85A<;p4xOIqTx3=M@7-lVd0G1XRAQCV^pmbkR26|dTdwx6uTWkF}uuBNc06_wZ& zVCT-&WeT38#P9E57ITdR%SJ?{JUw$wm4+iK@jU}(FxS$suw^Aa5zzmxts!CwOMDtI zQd~zv!;zM_af8mNT~}q z&697furQ@2HVyL3Z4?@o*u-W*p1G~U!jzlXH0X@l?G&1nf)kqpol(2JLa_nKiBAOT z_geUKIJ5@XpE^~rG4jd+9N z6q%)1?Fr2NPE|;XSDsL{bDF|atnP$X`A%0@3RRteLU@KuvN@U)7`x7tiHcX80L@tv z%_itgKUX^K~wfOm!!o}x7- zG%}1xG=(cnhA0*JrYY{+83BTlq81D zP`*&bUY2=^)xAI_u1F+Bt6nJUjA~el)VvVOn9NeJ;)Pho6_%p)uCOa5cUp-T+s;*3 z2Ca4>&+Wyu`RC-&ankC(c(HAt%u<-rg)+~CL{YHLWf&L4i*4t{Nj4&Mu@yk%m-7`G zj?~50|G?~OQYB$aU2GmOLQJVVEUAmjg8K8c%EFPl*b#%y?(J7tHX?R$y@S@UW)vc> z*p(;E1u_j=>|)axvadM1cR;4$i(UNa0X(w`4_ofy^8gKHPT}DSUU|0Gyh_6ry!hS$ zy7q#Ghb?*Wi9q}uREfBv7oP`=5{ohqQ}*IU4?4SdNM^|(dvUqY+Utb|uGF#@yTwq& zb5E6qA$)QDf=KQqldyy@E(ux(zDOov3SV3nv>VLsH_l63Q*M z!ori3@?<%zvalqjJXuy%mV~6#Po&|ppwn<`3QJm%hD`y~`7;94pZSFXvG=Q4$dE!gmRfDY;^JbcN99YcVpt@5xX8#WJ!A5Z1sN;bK+S4YFc z6>Zr50jJ@P>Uh|q4Vwr=&@l}WSF~aCfbrtE!ow78*zto-!>ubc8Dtwa8Pe4+H<6_l zZn&+6W_$P6&@hA>wr3E_eH0d!bi-yrGtc`fEKKo+O@lmhKZS-R->_MbXYQ}CFa;bo z4LS|?0EH%{gu|vlr{Nx`P;5ZN;S+&+`9UfXOUB{m7?klItgvup95xLYryioEVaPcA zb^|SRr`2}{+XzZ&r~ibYks$Q;g&8@ z`0uk$=>9V_ynAHV(9WIPqW%;7B20TI=RDd#<~l9g_0}b(=%*QIMhvruGs#bn@oCPj zHLJ7sYQt`N;%z&p={X(u?6Fdb_8splm#4RtHtvfNLo1z5)*oxOo{R0BQTrr!_=)|x zkL%iY+a6tdN1t7GSHvg#I&4`4#DaeTp7>ep^VvOS#ITe4EMj=PhCwCBW`C!l^>>l| zC&>1x>FwE;NUSP`pM81^^gwX3;vaN7mt$)_4wlGD)~X$;vy?BaZyN<$*Je_N*I)sEU~ zD@omjoj1_>&C^wycCFcIJGH6`k_Z=W3!%B+GZYHfagNy4lnI3G5tKomsjzgGYb!Zs zkZ@?1c06yV*m#!K_H0X3ggFuzG*YqW`q?Uhd(8`}MDWcx)r+uhPSMKkbY!LV|>lUQC1So$_;c}p;c(u$iYf~@HFDj6J{~E=1 zt-j`TY|pa|u_1L*c61<5yjG>53Ohefpt<1d6x%Ht#!ICOD&vdO<_Qn``_aDK*DDNx zsF5li@Xy3K!}SL1+(i%^GtPY*b~K|?Y;Ot4{N@=F z&szlH91o%~%d>h|-H?hSaiba$y){9!eUpgLd6Ks!hzQ8{K*i~hR$h%n7s^3?t94c4 z{mtk%Svp`>#vDp7jw)h>|7CIrRR?iT@JL3d1R*eK}y$|O1Y;B2O&rdB+cy^<^ z&M`k!Dz!Zlhq8DGWy5!ceEVwEs@+Y;-3VH++g@!o7`}qg2`c|AEV~W0)Fk=p-66}Q zwQSe>bgVt)gFB{{g94+{snx4<>n%df!iK7f_e5-BK%ZJZXxCQPyeW&)B`rt!eJfiO zgVmPzMz$TN)s5m_08fe^X8LaQ7S;PSw&&~ySq$@uupj&+i|IPw*F+ug;=$S>hvNx<;OMxbqfFr=Aw z+=f-JU2a!tos>pxDz8d=R{|k(CRm9iK!0T}u^hvuLACFPLYn={mr`xl&A4-Gu5D5N z3Zo@Rx3NW0HUr9LAC4H@8m)=i)$uCjFNzAkj|c{l^7qGonP?tvoq&4IM?#iv3#>0# z^^RStI8HtMNl@X<^zFO(5HzOIiSxzfEgZ{3^?Mj#$2AWC&;)EY4$-*KgiLmn~7)`$taL^pFvMwYV%2?%g+ion^X%8Ch`|@XMVhUL*ogYJB z#|X-qU-cO#t+po&bt$HtD$Uvi!ueU^0iA6BT41-rZt=%onul&rq?PL9P95g`kf_%kFb17k(#ceRR!g9Jbv^#OrqqozDVt z;ddn#&LL-R|3harzNc^$wF40Cc>BIg!9Hfq_K9H^$6YB(e<0Jaj##rv3>B`})$>D{ zgmb)_AFqZyEoPuUl1PM(nPQ=s&kzd1n%!nU)-d=rW_M+9tmXzwV%c^M7}`VZTK-8N zf}q|`m}h$cjov@i*q2u+`lT^$d?K~zXA%$hczd2KKToo-kG8W}4829{nf*nQWt@GC zoEhw3cZ@bqlE29$tV64860lGG z?-EJc1FLKX5Uc-4GVl(o<{GR2lt>ERNoLR{#Gdhg#Yo0T9+AfXX^`ak9szCh?-<8C zaga1;ODs9=gG4td{6{9)oEx=3UHxB~h&$n6w-nmT@IQ%$J>_82peptXiH2F`=gP8W z>8e}yECrh?L<}>_Ha?3XW`uHaC5a_v;l^gzW!UvE?xw%8#IPARk)RpjRb(DkvBDmS z4EqbksiCV%B(uB{f!#_-3%#1ml6Gk3Q-GT3>JkNSaAvm+Ah|}Iggq2-NzjhrYbqog z8H(6MhAK|nntCmTh&vSJNpo$PhCLLqX$&*vbI3Q>k!hIepUZ;wkX=`1Ntyn+3`n)T zp3Ja;^v~r%mBRHE9#;BiXFH@I-#}(z4ghSw0Da|#3QO7nfK35L#2d*Jya9mSI;c*# zah%1!8k#d1;d%v)05?%c20a4YR3RGV2yio*W}qX$&1ISajsUliSq3ly+)`#3@Ca}# zg=cUhz^!GL0geE-QCJ2s0^C-nD0l=w`h7>4h&wRmN^>WPhCMLyX~4YkM2Tknc+vNRqnW@~iDrK$+(qVLl?>du0E*gEBogKVC*Lbz zBsf)O8K8c7n#_{6c;R~lsD`IY6ujbv9VdX~j5x_aXRl`}BpVr|xxPVrC(crcxPx?_ zG}~kv_8`rs0s7^TOv4PIb60n$O(cB@Lf>xGI`66iJeCd4T*_)^TcvvZ4t?DKbfTkO(Up zLgO&U!(@*)Xh+Lvk8^*Ma=UWmGr<;1-)stW?sQD0h#&im7&4xl0(HQ+L;#L{Ml@M( z*hE^&xiXJXvX#dtOeow60M)O1btL`zS}ahwEa(pNeJYE37}ewycEqB7GodFbB5K1l zD4ll{dyLN0Q|wotq%b*!+d60#c)p1yxjEUd40GcFeR@*iF+NLS!ePe@+MzI|yBBzB8|_?cFSOsiCR(V!Q1_A4~HhdUyY#9+Yo3Rs<*Q8)~Tsw0%FBLUh& zet}G5dPpQU8g$3k0hvb3vXaNV^W!kIk=V06tK%pl`-a7`lzqpDVNS;Y4qKZX!;M{} ze9h~a(iaZ=d>2m2CI6JjKMOKT+Bt59>H&>U2PGaPA?MXrXkC6$;(>3Qn}GN&W;o$g zrWQLS6QT2x`4NE_x=bTh~H-6L{sWzqts^iQi;$D=lL;# zIJ!t-0(%(pBLbDq#S&3U6)Qs~LN9PGNi^xMIWRej9p9j4zC=&PDNy;*9SUYrbc>S$ zmcpZd=R7app!#%KV(DFC**jdH8;2p5#XD9jT8{q34?9jkyHq6x(=`;i`NJ?qh#Poq ziA8n**N5oD1hcy%hw27f67Q5MJz{AZ4sH(V3Cec24rw?m3 zhoNQ{w_CU}hwj-qJqEjXp={ljILy}vMKD=g2gHd7X!;Lh-1b50aYrH9$mE<&1nP~W zGLdPMm8qflaRX*9$0QoD=a(+IkJ56AG{@%wS@*bxM}NtZ$vy100^V7dY0S?Dns9RU z&gC+XaaXhnhwB}*ns;x7MtV}v1d*#x?xRw;R-F^TT%Vvkd|!pebRm|>Ib0faFXa7X zns5c0-j7)HFNTjOx_i%}|IRu(goU6~S&94V?{A_?k!-l$L3R2A^c;oNGw7wf2kKeq z*%R}FlzFukT1R`3#FcJ0POSXQwXZ)|rZHY?mX~kPZj*-qn*6IAw442*J_(K8PO~~j z&t7PDB=w#wi8T(lH=#G$AEsl7yFqh_GuJ+v={{Vs@89sI?EcX@f*qwJhB<+_jsFok zlD21l=$CCB&5RzYv###<(Yk@%9)?Oz=&g@3apW8mD0@BH#Gu*hqjdyXuOS_T$+(6YaTvx|X6(An*wcJ>qoo z!!xu5`LzMcjnCBD)~=Rmn}Qt=U>5l-#L}lh^5l57!eLlr2|SV*xY!(q9WCOX^yerX z{u+TYo7FZ9J3`TJ$LFdfT6JFI5IzUU7|+vi)WvQ(dX3wc8e(-;qFLA!XbgY8hT@p* znma7p`c6Z(5UczzP_64$C^SvMjZIXCzfhqtuTSI@pP;9_2-0k3Y`j<@nYF39$uTyN zR{0Xux>?`WHVZdKP(OL8hNjtR=jaQhpu9}A-jdYpTs(kwA-`N@2=v;5a`<+ zZPjkixmL3sd1R(MKeg=tl~R@#LOtsBA<2~0syNkkYM$UvHM>1ic3q1e@1^CUU@uko zuOwjj{o7Ell&EFi5Yj~VCKzHRSiZ_7Qc(T$#*k;ma;-*tn>l!C2ij6RT)6Ry?oE4B zk^({$6|rv)83LcHZ=;vTWA(IHKFkuas7>AiNc?^qB4s zcgiHp3-s7tG2~G(?|YZR5-$D67TI&jo=7ixcbp=)Db93Mws4mB$SebFo%bp%MYRqZ zRo)j+tkqoBqA@>v80O;Q<=^*f7}OM+NFHqaXtneMvVF#)F}J^t82YPFbw8-#KugA4 z+eokbkj8e(1!I0&fYI#329CVr0cEU@7#Pwn7<1bN=`$ZSvZP%uX44p2N1T)Sm`cN2 zEau0Gp}&an?c*v<%Ee-?Pmp5s359~SRLu4W${?RqSnyIY+Yg4yN1WpNl-72?g<>`V z(E2~E5~N!u=C=XR6+WY-$UA=_Me4I!f|Sd|Y+no;d~q_x#OGv^O;U0{FWXJIOw5f& zG$;Ckj-gK&u3>7Ji-<3T2NU$yChcU@i;Nl75U> z^3EQHnffVIf&IkiSg`6HvDQk077~)!(K-Sw|1@bCPeA#WfsXhy)pEb-C)@t8VdU6K zKL2^bzPZ%q#v9s+`-_B4G0n-*E@6zyH^`#C8v`0Kls*HZEwnM`0qkc zV~^H5?Xb~jXkW_4@2wd&sCc(0%YXDOQ=rA{RmS%gWbIGBHRtf*gtdt*)(l=ffxhj} zzO|MY_a$v=p<~b+_vH@5s`PEi-d~okwq=jCveI#_71~l`d26N8_UZDLZlr|&KI??; zKSRU2M|KVE+_^2vyTLEQ<{CEimJ`X=U&Xgs)uaAKrkb0s#n|WZvr@#8 zypQP)Wb<#n&3fHgDK+c{nH1;8X#-a#dj?fWsEL2~{n=ct+vBZvt?o2Sdsk*$dxai* zp+A#rE1^rb@V^tp%|8^oIkcrAZthIq`OnDS0ee+^n(;HaIcFHO=D%dC>PND@y4#Rp zC+REy?b~a&ZP#0w)g}w}q)?+WqCtI`vw0qFJ?%({i-f9#}5>o8si(#kW22y#~nso06;;i0tfn*2-EbnVgrz zmGyL`Dm_s_TTV-*#P&gUoKd>*1y0q3u$kHB(+XxbgY|S-p<$1+q%>I?rlR-lyI5 zG!Xb>L#Y&VJ~!(jZSuOp>WW1JO^rt6ezi?7*BoQvH{qmC#!YH!!j#eG?{<=%*QY&?tUGpT}-iQ$$S2l%uB%qqw-c;Gx!Oqzc==2*6fudf7Mq5iB-EOl_%`dd)r@A~;>c14Yv_FB zO+#xgg#wxv&1Yj6G0Yl;E_X8nL)5yo#*)oCS}nhM+&Y{ZN)YTZeFUs?+`>d*b^_Zz zQVMR_vmX~F<6+0Phi+cERb+40vphTAw?wob+(GB}{2$XKbK^=LI+uKFiJ&`Xn~k%- zAI;ou6IgfJiBHcOVZ(+9JBfPPZ6k{nDpKMO&urDOpZ88=^>#_Cp{6LA6TuH%Dply>&nWoVeC4AzV!~G?j&W{rGsOEp464wCNflKAgM`5pOAGSJmI@an6g_ahfJ&P77>1smX_BOhFRQXjYgNBRLsXK|5 zHu0zwvt@!CiWlN+@1XU(69ez8PT8JYTOl=Dnt5bVvPTS2C)Sm=243hYK>o~67ND|Z zP=op;cB7uuwQPI##NqKqizXmRA#5?5@`b+o&e(P(xn#l4FW{?B?pfa79IZr&gJ#f;#8djkMF=b#$q^dKI38g#$z&$d1c#`ZG;-_Zhgqt>*VUOth5ES@P_%q2e1 z+WA?5#iKPUCuXhH9HJ3b5ZiofPG`Apcb5sXM?bPRv=2sX%;^rnw|Y=dLGJ!W@uYjk|f8lS?X200vgK0y+y}Z zAx3j83ZmYxO6AIYcaDF+CbilY_vDo5uWZk_Qn}lp$xjpG?mQS1g%vj=AQk|V>n$x7&BnfB43 zeT8FPYvj+^Fp+)ciYoVUz0HhO7Mew#+q2khze=WAw>~Hb3VWqDSnZ+!tD(vKx-O9vP<=6&*CwZf-OhOmywiHCg?tdE=(KSchW!1XZ%< zrDqGbyONtY)rtNjKHP*D{WOcGP%S%|h9~qhEiTs-qJ6h2o^jfe{XJz(#T;u8_V@#O^ZqUqsVOu7jXE9bUST?+N+P~} zf*qGAL(hbE?ZzY>6)>1&e<@gzx}a;Z-jv)uF;mVs`WY%FaiZ%$*J?kbd4B1v&~;Yp z&azdflY4YHf5mQ4yonnGx6_hDhV>D&#yIDHspC`=?v;jIb_}60cRsQh4Y`?A6cn!u zzE9ou+MH82FSW2+(Xi5T0!7Y2VR5atdVn_0*lxL*iy8RVh%!-Y#){E3GUY zSA&NAjRu8Itk)jW+LXhTYZ1lFg(7BZ&009Eppt1ZK0RaGGI&(XmhS0)UGwP7#P&$Z z?R7q`CDixs<$qN>SQU%q(nNP)_dT@keUbldBmzy=8Z|E%Hm{mPRibBm7tIZ=o<;ovORm3XIzFm;XtBPH<5!q1#vZ^hz0iCHkQW|v{4W~KP@x*B(G3Cxu zMS?Fp?0=ziEZ4CLeBrA9g>BnDk|;JL8H4T9Q1q5EfH?cICcdz-)a=yr%sHsysQDIY zpUcEpcl6jsx46%DYP8JTORd~*M1B9#&|qubjc)8K4Q=o?TF48%; zzp$CKsU{kJW&{1u@vZq=kjgop zQU<%wT0M(nHti3|r42#9zpQ639QpI`siD>oJF?xL#c7*5gKCZ&FQ}So_pJ2}LaQeE zQ3FN`uV*vAeFug*T+G2czP(y=Gc(Q6uFd>14a(fdy4JdxCWrPzyT^}<(U&&iBsLRI z2>Ca85;e9!!;c@(C)a)ZPBmd)R5$U79N!{{*vqvRv#oyZFlbe`p@_YAWG_-S;tOs3 zZ!@Tp;;w-EbZybGc{{1ykVg{>sO-FB_(HKi{Juu3SrsF6&)5C@enK0HYu*~|*{<5v zDG^3Nf1!HoWvM68Jys9u*$YQ%qd35hWKfYjxM$bbLh|@dG>d&m&tC4c zSd{f2+Ow-_pfoetYda0`i`e9ZAHiT0f0%Dy%)h2QVXN$}c7&{*RV_q&_Z}YFT(Vr! zUxQolTsnf_Tiv_TW7znNY?mQRj#8F z$Rty6Nf7NI*bFXo;^aEJ%k#Ez`A4JvgIN#exZTE29ITrUd zRiFDZwJ}oHA1}VNC0=d=lU)9Hf(rKukwt%!o0AfYzVV5Y#hlbwP*XoCvKS86>a>MB zPNRD3$&o$7w3^?7px$~)WDQNLx!;Fwgm`LXv0quhwhF4Br^!~;M&$by*n7Ij04-(5 zMz+2W)R<@Z-&d0__J}PsalMIR^qIc3=t8<2e2I2RKg;(eZFF2pdBxHq?Z4FAVz%)U z)4L4yUWIb~vk_giGbmWr2)KGb+&B>$XP*-gd72$0mEv}gJhy9A-9eHx%8f&GU;6We zO))|4ZOrlC<@D#wq|cMivpvg6c{CC|KeSz0r!#J$MJh1k+p_pM1?+)(LEK_HZjv@M zX5AYE?7lFxOP8OI?1=388!5}>rSr1rXXqA=7ln5BFJEe}cyYs2u+BwCv;0FEF$ivi zp|?9;oMZ^Es12K&e2_qimCjrWG?#u!NHHsdt=IHqKJ%_Hq*c5$v^%eF`tvh*9qr$L znHXQ(;7U++aN&9u-LmrX;0wi;b~@Z@hnx6GQp{@)KYG9F6@hg+J4+9R_unl{FNVh) z&dx1pe)vi=NpH9kOY<2S&`R>Fx(r?^b}WlbmdjagWkzH7s{@N($8F9zQ~)2XIh}UT zcPxqt=|)mO;pdV(>dQ!+RxM;v)e0ft>rm1 zz;4^y6O`C?(Ycz}3A^p_<1KHzY0<&oYH9rViro@d=lW83XKlCfbaH-0Y`~_x&`p3e&P*-S(ZmT)oSN9U7LIPge`C+~ zcy+a}X?}|vv;=W)#+!P!&!wHf{XUy-8~FUqJ=^}h$Nh}6+uJaI5chn&C1$((E^^*2 zZkRWU9fNNb`KC?R@9Xx}XWge{_-e7%{kG6j?NvwKeA5i z4Pr1AYYozIA@0|rsQ-X$FS|9c%J9LMwf%05))?)oKWxjN$c}F0(+|b0MlW+`^{F9> zPoOCNaAYr5j+4E(%))L%u+QWpzP*-hR}&Xm1p;1Txsq)awB<*QRV2iT`0d)?;DsDGkWhtG)iq}fj^x1C7n z^!jK0FLRW$>-30Hcyzs7jtgM?EDJQ(&jr?}q_gWZRMHXX%2*~H2<-*`yzgVDPFKB& z3viN?-wdlss80TZ|E(_F9v570>%Xt$OHIjrn(>cj_Zh?NPn_=iqWI=ccPukr8p^f^ z_9T4Cw@BwD57Qkp>SYJ-rZx7Z@&_J zskkao%<89UA0h4i@c%`JeC1mNgP&_}5Db15zdbO6@`QT8R|7xgel7|1fUgBMh2mY9 z4vm!8uZvbXns*OCcMN>Pw`jSfCW>1o+uAE8XUnc@t(WGzf5(60wM{pMWKh4*`0`Dk zsn+gTb^WD=?4E(*>|3JEkBQDOf1onEKysfDRQ)<(dS6WDXXhPI5q(>4Q#=rr%_hio z-|=l)HBX4Cv7#jFdrWBE>bs&Pk5LswVrg*lR~pG;hkm*KMK#0s{BIRcmPV=v%~WU< z3!gES7o_By6^$r$dF&ndHJh|676GA4YbAmC5j9 zssAjNE7{hhM+|m{XgvE-WVgF09XD)RGGu=_x|Q$85<$2mod$#*>p(gFNyO1zl#T(= zQ@x3eCVATZX=Jy%C>^sanm%5Vv+aVG|5;?$Phy@^t+kJoN{7ngHYs{O$-mCZFI{8F zo89tg#Q3=&plhZ#bHwP@`W9{1p~l?jG@MntY13|(JTe`q zN&lv|nmuNL2Kx7yO_OW6l>u7y`G;ap{yeU=9fq&FIkn@zF1gm0CHg)|cYBOkmk{=WO-?@#<~i`x*T#m}2J@1gj))221T8c!~G|S8SAzP^Tjq|M$bu+`^G8$H4SO!uz_FlBMu( zh}FGaEE-=PgU4g2uH#gM1<^u{T5B^kf+=fADe5H?WFNE%{jxq{ww_{97Uw7Czo|(S zUEfAZ9AX@Wik1t1+6ZEvwq8+s{1UOM`};-N-_{b-ik4InGfh7#!ubJWjlY>B>b8v} zW^BEpgin)^eN?5Kn@vX0KXFbN(#{d{XDkwB_j-xBddG>Pi)Z~s3*6Z4dxn777VS5RQg&7oPV`G&O_^8>kekB!q1)=ww z6n`;=VA(DC!rt0NEYMpe^iMS4Txr00(xCAMAMZS6(m@Kd3R0%vMc6aGh+&8@Yn7-D z&LfunT1Xgg9Uzg}Nk%qzw8;L~SYmq5aglwrjhJzUb^)6Ul>JGn&{th1mfd?q7-I*C zWgnd-^d1}j04~i>whH467k=MGp4&$YEpUZI*)-C?Rd-o5e3weh^Yfr^4DBXn)_#m4 zGnDNu5f$D`#EKG9h2h~|5|8gA1Ju1cNrWD95_44N3QtE1vA|Ih=5srV>F-PyhVTjHziubfh81LbmE$-vf5`%Hj?AYmWIp4AQ)K>npN35qnZMeCCypQ{leuq* z^a~CM!{hnb>wF3)Sd{Yd6lQU_yx6_CI#}TaWzITWYM|SuxbQP8{YKQJtgTOZ9VfQPLd+s9dt(NE0NRR$F}jSz|DwnUrU|Q*tmUYnpeSE7 zj@nGDZhF4Z&znZ#fo-G*L(+KRd}k7|Q1VFO3>OgdZ1FOU!nY3>#$XdM{k%5i!CcaV zAqfQqIR5HV_6;Kk=o4|Ng5hC{u>JD_G5`D{qV%&0VxHH#h2yChVx~D>Q8d#@MKd*Q zrkX_AeKjQRJ%bwS3~8^95oL3)5YsM{H-evZv`3d4ND z6@-Fpbe1rc;A#Ln+61EGG}$$5!fA69OCQQ}7>{3KMJQGw^!^A@lN&-Y9U|+Avg(mOJVHGCB# zybo^03vR{vmMWH7lkrJts2-of$0l%AJ_U&@BE}((BW=nS<+fzfATa%;5*wlbSWylz z*Jq1_W8Vf8P#dT$>8e=vLOe0;o(|#YK0>UhZWtA5*qHlfK`WU*cR)X?MpTz)=K*>( z-=La2mx)*dqez=$75a4S37fwmA_g&r43EW%@Wg$@0*_7=j(b-TEBmxcnS(+M(EdZ= z0@{f~oW--aCqv03sU@6}wxBE!i9p74PCP+dWQ{ zP97j;bdJ_|L$($DLO&6Q0u1*Iiqh#DNIcd~rQq2)ODt=kf1{upd$}nkm>K-B@7SDBj))+$=#Ji%rNncsJRiXZVj7;&yNeo1T@7U=Z`%? z{{;#WsJUT{Xy~&NbBym2IfsXc>A%J`4mG1!iUu4WmUFmC=+EG1pl0+a(a?w218?dY zd|KGpH@7NpjU_vnEzK~7^eI)!BUece20Ui&3saTvHm(%!F^P;JCysx}i9e3ZjwSa9Lkg-+a1We~a3VVKvuB0=;duPvc>GbE?Xc|i zOTsaF5ix7dJW)C=3x!S=+3F67&~LU7vlW~ZR-JZ$tU8oTx1Sc;r?|Xe*+!?(qH2hh zPF5QJZ-|)Z^f(2@vWJ~+jzIhVG@w3VWe*+~xo3uv_^O3+*_Wdf?aL6pfUoca;O@65 zpJ$RAiY7NJ6UR{zgg#v_st+9_=J7-;=Lg6G)u%5jc=2keU3E-2+R#scs+ZOZtsQ-T zR_k3r%rquTnVLi;#!u8rt=&=ByGNO(y2FDGfWw#2!SOk(HMzv++H4&rD&D z+E1*$IGt%!zw}Uq4iRj}loEW+p2711zwiwI(r!^S=`txHL^fQRh);}7#$y(Z=h&_I zdEr2GQ0Olg0R6wvt_o4~K?W(^h}~3UP89V~Q%OwdpnCBBeYi0GZ5N61v1A0@llzq? zl1ay&%o6VVM-Z#&=!%$!IFCxww@`_OP3UO8D0wuVnCpN~)Y#HVyog_BFDWKBK1x=H z!^B+M72kd`v5JV`RU9Iqh$ugqn8B)8(@8U7V|<`Xc`%cVU`pSnB=wVt&5u_e#<6Be zIJPV{Jz^K)F0zk5BkJ*nSm2S3qJCjCiOf}0&z`~|6ti*spPEhei?&9@A;Q8fqG-%$ z5;v}(A~t#`!ixwuT#X1I!l*$}KXyGSv52u$V@ea=+-+reNysT3?GgFv-haF_~4bt*T+P=VNtxS8_y zr$lT;gbxmj!s_!RY6_^3RR=`wHwgq9<(@^ZmYs1$oI-@Jt;*SaJP%*@33q=lN=Wzu zA3ubSj}fLZIP0NwrdODz;v|OZuPT%;*P%pQN439REF5jUC=oTJTNidIixSAh9$yfF z?>`RdDN5oZb4H-2I)dyIlSSQ?L}DJ#TIDiWdZ}A#DUPD!@Q-m>f1c^s64j{Bq_+k{9;&B{V6+U|A?@;3V@O>y967Kva zQbr+;qIzALsQbS?#5|59;dN9IGp4i(-6HHc%bcey+)6s8Et-WVAhSb+jvhf>aD?0m z8$`+cIAS$#CqWsUTQ&o z(n6Yjf{mDIMqI=OM7RcH0xasHD4o%OkGugD2R?FU-J@jeCKG$aqRiikx&o3-%EM<+ zXTW{90@zdYl&Tnf(lHu7W_*L$BhVf+G4~dw4u70wBq_-_N5J?{hLVT~)u+)Jf%==r zDNpB8JzIQO$?BzgVL*hZj|0m8`o(BeK+!lfvDnQm5vY7_hOwx41o~@Z;ii=l7#F$; z#^C3{t1l4Mo7bXpS&N$Vu*gZDMdI<xCQ^u4(Lv8mUiP++-- zMA0qSLohvgNJ%|SCiWDr3NYf#WhTc|MZ>XX)vJ`@c#ch4EWAxSi5d4|90_t-$0;qF zG|Fk&L^&_DMnoY(%N*f;aXE?Aqsa}P4;n=EB9!Uu*HOZovYo^{6hh$n{($lWN=i0z zo$#JaCvnXnd7*lfmWE9ld0Uc2HJVwhbS#nwV1K|M%DzI^nSH%Z`8I*d!0|_P#h{|5 zO*pq(iP?WMT3Opkc5rN-B?5URBn%fR0HxYZ$Eg&GoOaS0kH-1&Ja*pm%KE6sh3?n7t$ROpgPuhk1O`P29^|*+3#CDC`Z=@(2 zcaR$zz8o&9pUNeH<6aHkN85y_CzF`>kpseWVJnG?=z_89o0M-uYSfLK6|&K^MNS8Td2OP8)kqS{QDd^_P^dsKf0n3U zwHnXE@-F4MK0Mz?){K+N{Ig`$c#}~qLXS)#)^PttQS;43V%kRJeZY~DCQ9@ti23$g zl@EHz$KEz59ecHSR_X+P{1 ze(fx=+FQ2^pEr$|ab99XH)1!L=AIRyM>i3x?9COy9WBI+Ymlg7hpq~PYcwehh~OR} z3T_`k1-EOw@hj7a3Lx*eF5&P;B`4*-~7a%(zyupy*}Srzz2`WMWI&65vYypWoLSz!7Q2*aCipoA15KI2Bidas@*$5vZ zdc&XX7iBZ25VIXQp?us$HYmHNLwK|)#44_wP+I0u1%!sIqA1QqBKsf}f&V5A&7zxN ztV_83n5h7vKVK9s{{S8jP-rJ|1YoMcVF4rBa?FasX{P_%K9SRYm=p`5g&dQ2iQx3r z#5@hlMZkzWGT5|_7hw;eH(q#(4a6$`yI9y8&yr~BBRd3o(4_{?(o-Vv@Hk?j+$0hB zYJgbKGD?I@=p8Y);{AFKF%PO?rg?rsrV6|lb49iD1hK$vn?*&-c6@}}$zx6z6|M2a zTqhLMNVNM{eu@%}6A(f}Xvl(5zg<{w+eWPBNhCF(;oluhLxuyX7Pd4+7`xC5f}E|F zly{tz!?rFL`p+AP1s}U0YxP*(01t(Xi03a794n2a8DKO>pxm;kg4%EXZEJN$G2*Y|y987TJ4N z5Yyj+G5~DXUK08Yd_rJ&Y@NtnjWR=En$>EIuo#dNhT`u>i-vP9V#NciG~Q6~E+tfi zj}ZKiHHe~-XnwO924xyLpHOvAk0^?bClQZfT&S8eAfD+MPpoLbC!X2qBC#iio&o=u z(?WYi+eF$c7!WvKEIe(g#B2x8h?;^9V%qpa!ZaNv7}!t6i16*0s>5tqO5PO;vl|yH z+3}c^1miGfz?P&dH=vee+Nj+WW--ai9os0(#$d34-GtFgcF#yMsolzyF3MrIp>|_aFoMEvOH-zzN(Ohy|A*XB36vs# zRH~uPmXo3A(By`ik1mPwR+J0i3g9{iBYL$09Z@5jpCU}xrV}ekO%^84Y7+HR$;7;< z-dR1WbB5NCNYSDG=gop-C8du{?9R)|EC-pO`lD0ATYrq0@m(~{*r}@`X9j9aHl37M zl%8S@BE&Q+(^uemn1LP^=({$O#v6C}nTP^JD8t7i@DY_E#r7AMsL$DlPYQBVMeXwe zq-_F7t#pd&(`arpOg>?zRO|p-a6oyqgiP#7G-TMLr%?YtdJ3J!u}T1~A7rzh4r96hwZC3cgI%U-;Dr7IjikyrQXsW?{^zB*t24z0F_Ta8Xl0%>m zSPM1yUQMfpOh>nHy}625`IleSU4b6535)}P@!xu*LljNGFM{d*gUVvGWLN@PHmqzw7#GE&?T6#&XjQZ$ zIF9hEnF=N_n!p>LCvw(YCehk}tOu;|E3Fri|KZKc=q8I0Vu;)?tx;aQjO@o{WIQ7F zqt^=6i%u%PZbkN^6}@6ir2x<0k1H3Dgk%>nO^01SMtD!|LG}ZN`9$@mT^Jq5#@mAY z3{=0kLX_S-o0$E99#M8?EPB9j7R_&PbYh?yDr#eebNeY|Jx(Fxfk`Utw<)4@>>gxM z_8_r=35`%Y-70GOlZiFh4`{rhhN^SIRCyj*30yik3xw%9JZ8@%M{GshicV;TGHnn^ z3CKis4LP4BDF2#6IqZ0baCeL)@w)+XgX2#a4TZ9=HB7zy8or4Ujz68i6DQDr-y!Os zMcu$Kw8DbMBvEq?!|mcKB2ug$>=z9QAz~&B*^4RYGk_5@s#)y|Hc^fFCPG`i64{NF z7>kbiFV0kf9FDh{yl=%vv?I2Y=|)r>?6w|Z zG^09(Wm}>|dE;ne_S0>``G)~wp;s}&3iTLvXMtZ5MCg$<$hK%}$hBvR@>ld|*lv^Z zzKdLJcaQS^NjyGDp^wnjV*l_fJ*Xksp|zs%S|2f2UzxC@cO@?4&9Pf930vE6^#6GC z`e>!CnQZL!Cglwxo2WsoAz$`1QDXL!C>Ta1V10g-s4}f2VIEFZVEgSfQTxIPoOCD1 z)HFs3Zy*y}J_X~y*tjkhC_gVC7aN+V{Ja~FVW?fy{FFcn`f)#Pqx$hZqI7y3rs+V; zF5%dHm{|Gx7~y&aZ9#ChUljJJEX)DOB8R<4;|)0$?hqBbHW4%K+abc47;b%Wm$KSG zI`(3QviblXA0SU?t_bA!5UbBV9I*&-5f05kkABhvD#`PE%gkctC zVj1cUY-*cZh2!R(#HzBd2-kNGVz$>-i7LZ368Sc&VtI!mW+6iUS*2_fRYAk2tA*aW`H| z2T9Duy&q8G!eka`9FZ(sA6bdjPexq*R-TZR-JbAF0X{!^`mmi-=gseto% zw6GmYBUbrDs|fyPBe4)(ROH}ma2-LZ2-?+5QPX*fnE!c;FwI2!5RA##A@Bru30=-us4Tpt)(J>MW3$RB8*qf=!J5#VnD69KQMCkKQV*dO@vFvx9#GJ>xVp)4C z_AnKDH%!<*!Y~e0J~3Pbe=`m{gq-1q&6xj*O(3!;TW={VNxaa_lu3$Y91KGDK=-a+Eq^W=m2ai^7ucmr(G zap8OqjS#kXk?_=9AQn0sP_%DRBMCGnzD1+5A3am>|7D7@b1nG=vSK@lwAV2qk+t=U zqVJXyE50yHJoEc#VkOQRQEM6^W_n<@aE`e`EO-G4O3?fUTt<+0gCpVuB8)bPg7-F( z*fW+21d7c)t(0GNT=8U3KC3DdmKkSB%*-bXLl%^U??=Mn7}7W3UX8II_9Ef>e-YK^ zuhHXy>ctqx2hYeTQ622W{au|nW+ftU1JdSfv`)F<03IKp&|@7+8nT;WF5+AYW^{|d zKjspvzxk>%F@(belQgvJC&8_m%ES?PVg!zvPeEeY$+{9m&F7eo3$-y`;lH$z#Gi3^ z#0ta})ZoMr9#p*;C$u(9wPUZLynv$bkeq~K?fX?oiLRn&+S^5mlW^RKYF;T3q12hg zYFAbXYhN!hQ;k;%tV6~GDg#Obl2PD9?vMq)UoQOmlf-O`(Ut(?hIv9W9_8zCJLQcU z5mAH4u84x&^GIn&g!g8Pe0L2gXAs$NVTo-bEyfjo?y zK+)ndVOn|?BW(EjPqb6moFaELrrUsV?q#8EKxzz3OMSvI;xe(|WDGihI~wgi2u>+c z>M`U3p@K`IXw)i9M8u)}<5dw1pzjF&Z#PBY^8R)THE4w?)Bq(DE(_CxD=~c*0|O2W zuR}c*2wQj(F<;hZ;Y>eDEHooa=uPR%Spi=^suqQm^z3#U4s-3bYlP-ced2crra@e z6{O*FMK@wMx%aq~_nOHqv_X6^NL4M!U_$8pu?Q<7S}X%5rsT4d&%ZX}?^Rc~{gdnaWLRXdYEYdX)(7xC~0d^7(1jhGHZbYvrUs;G&E(GW7 zR%VSy!*M)0ccb+Ju3ut>XQGK%%_YqH0MFWPQB^*Tn0NDTVH$mjglQC+Kr@tBr0|$p~IO(fkkVD?cL+VJX@oM>L^X*jtwAd#zreQt)^V2-82SY zz?cof-PJ=1hF-d0FM3BJ7ZLWH6jd+9;yw-}F1|k!VM2uO@R$u8SH8={V;IO4t{+a5 zxR6gS&|E**@D2DrQ@PNOCt%=+@_jG95C4Y}kNtE&xqO&h>=N=r>@t3zT}oAcLWI&d zzjAjOm9jY8`vo3MYyjl{s!=Sm_b)1c9Znvx3K6=_E8PL|u=mT9eIv-joAkDx4vNhd_j zrClh~(Wf`33f;!z#4IPyi0W0C;RcSQ7sN8xXq13}$!5$tVYToKqE!tQUt#_L1n$G2 zHgCeV!@_juc$9SL%LUNy2g^5UqViEM3CshigkTFUQ`U+C3Mx}^Z8J2zS@0#k_TgpB zd;rVQxx)DmTt)1|<0ACrNsRwIiB@UVMNyxQR0&JX5SEJuVwJ_{xw2v`Hymj*lLe9E?apOrl_0f^bEn>;UIK%Y@~820n6p zQXdxx`%BUIsNwpt!nFe{Vz5q>FP%q_|Jyu5g#K1T%-)x;{2S9O*l`>J2wcYPw%|hY zh-EKJSKI}Z&6b@|s$wadxppX3LwF3HV~V$evRPdNXxP+TQT$Dm&3vSUqbQp#AE`97 zQ#NZv4~B(IiiXt_SUvs@3!GP~aSy2&L>wfuA1iCrmyzk*RaimhM|5P?l95(9H^3Pi-?BAKiY+VUpuk-TaHGU z5KZLWkR=?C%psAyi5x8PSVSh;e~Fox)rxOG!~a!?k{biWYG-4HGx$C}BZ}jW5VJL* zeE_wdeZtm)kq+=Zjr&BP=vNnnl@c(^71OjgY!m*clZaKl*e0rf)Y_@~$9D4h`-N-Q zAc?MVDC3bLOWh(&4{RbaSfzE1a5Y}U^JvDI zQ9`o91mV;$IOv4nAcn@kZ|D(5lxE`7WHLhDYz)VM?Bx0#9Ng0|@F@iolbDcs!_){}+?6;gVv$r>BVUTnFVG zi;38Q2%n;nD$)?)_&()dn=szHiS%c2FEO~=OO)RuFUZ=jipl~%vHE8ZD$Uv%jO)UR z#me&z+`s3**^2df*h4nq$Fw?TeHGOy_@CY`>c?kc1dPw}!=kDcD_{uBhHb@lK5vR> zsO%@<=%EJY#EH(dN&}uS;2c0+4uU`9BLaV3x-h<;LE_VHoCn>MH)l@7Mnt$bRuuH( zHazx4RK$A3^_2e`M?^m&thp*I3%ZGE7EGfN3l`xUi%5UTqL?wG6{>!V+oz!XFHX^9 zxlAlH8$)ZLuZ|T}>(&!1-{lfbRw5R<#~=)k9wO$s7DK&I-Zh44)U*VhLI}+{E%Xjd zGJ~phQKI}W19;&9YAW)J&}_7B!7zKS@C?%tE5~94tjW4ugziBL81(ldyAFm2UBWXA z?SClWi@7FywN-fW(IPYl?R)TC+b_y7&c~W;=)pqh-ZD|T>mVs9h$&<~j$xax2F0>b z!-!RtZxjv3hZCzZ4vB`8J;drKBngw&)Pylv2xDv(T&*a0!1zXi@)_oPL0^p9WJ;`#cu{8 zh9hF0SI%e=NI^vfq4sW3dv_+WU<$7PV9FWXLUU3C<3|yzotZ0aQ!9uCUGC>q(t|ze&!>*zQ5ohx+=Y*|G7LtuPL4BCI>&M~RTrA4EkR4&~BNfB;9Z=ry!DHygnlS9It%|mf ze+as*qTY^Nj=-3&9gr>)jt;`pdpI#ys zd>3~|fm4?v>K?dCtnoIR&@FAj>$Fh$DU`0KR%89YP)o!RB4{B|fO|T`oJmw5)*-H= zyfb^0uj43>omngj*Wq>?_DYPhE*+24Dfb6m#6CozGRc8bPp&tN{!8FFR}iqM@tV$RSuQTN7dVvWydirkBHh*eb16?t!LBC%;K z<%t$VoY*;Is8Hig4sMATgNVpxk3^eKbRfcYhlOR&3Q{nvvWFox54kT7DQldR3x=`V zg!KVrMWN)v3E}O^C+7ZSQ$#%4|DRw}RVxkFyaE*ZMgqm zyR8>PujhsD31qvW^wyBDJ%d~z6b)sGl50Bgi=CuR#h5?Tds{`|iHpQSPP7r25q$>; zF2r~d1oCyF{z@V-XGWC>X)`fs1kTV*QMYj$vBqYs`3Jc;cC5m&Qsiw!(J3%ww-Mf$ zD+;qaNa&)eP;5ZlK*kX#mFqCx!$!D-<-J&9n&rJU_yTtC5ax?>v6L>3@x?95kI|G1 zhFFZ-fc2qhQG$wxc@N?KKyZH)GJTd}nfE1%; zy!NcH&cyUP@S5ieUsMcN5&}A$C3{! zj|W6>_e^3=-2qX#Zxu1mG|cgYx_PL&q4BnpLibD(vGOnDg)Z4kOph6J%o;aaXyxx# z5i5DFUwCtHH#E5K!$<~H9@#8{ohOMopY{ru7uit=b?y^&b5n^mPFyc^kIW%f{_j{} zu%l374lLOuwjyrD_+K<87_iCcxrjEzHp=@1EnapM$DVz%LKHrGh{TFCDrC>*3f;ql z#BvrPYXbTYai17i=VFOAD903kReZsX*X++9r%{8quN= zM-U+&!|7ldoeH6}Jq~+1j=anF3Ab;E znDg3mB8c^-G~PIuGzeGCRP5@U%ChQP~3yVqFj|e|3 z5(OJGNa;d^Hx`Keyt5>*XhA;9bD{jtbCCtpB*hKbV>T*XZG*COJQWC>yi40CfAZLfW<-cyCkp>jMhXh9 ze}HyqfC@qP$p(?z+D*)wfH}QTjyj)}Tu2h$y@!aodvW_bI1?|4(5+R(oV8BjsvS=( zv}2brj7>zFAd%``>J*K2?ZmX)moX{@6~<_hSBEB*K&zz={If-2Tp}sk5#jD6k$-Xo zDIrAoVzscme38VO3RL0nGUfq6Zfl0nC1Hn?HfUtMYpp2Xg?>JiJcqg&ygJ;J0Pg$B zMCB0-Fha0%v2ZRvMa<oxI<2XZJ@A3!~O1*5fC|s^1r3Vqh$AvB_274Go*{#QgapG!X zt~1TT`tUj2|BsF5(b>Y(iUbJ;%sA2T9`DefZi$rol`&Nao+ILq!!u zru|4R?O#u(W=tmh>afsnpGM3&6E`P=FA8e`u;>cq@pSUBdvK!?Xuf$}sF7f5mU3q< z`B;3n^2iV>B$(bID!(5=EO=zQaGG`yt9uIlM`(P&D|BZT5UaSxE6mrNA@i^@()Cy( zjvylcukXOkp+GU3i^CM60n3PbfZqVk&%vEWBoZULNzUQxH;2(iYw zi-hi@6k_F9j|js%xbsPzLe#L?RxAn%PLU|oQGqy$c$D%dSR+t0Pk`|kMB#_qNF1C> zh3vy*q5E&K?paZ%rlUbw!g`!^UmKlsINuEgE0DOw9M?a#8u+ z0I}dd*9oTydkb|>VJ%o_Ot>I)U!n$t3hEJty=kQEK!h%o0Z`z;j{Af=aCG z1^F|kMzkP8Y=IPj^$6?z*i$I^ev|O-U4TMs0SYYi zs@TV!LaRjo9~+cl$bv&?(pllGIZdqYC1fg~v2K>ged!Qh=nz%pa3U$}uje_So3_c6w5Uzt829@C$%4NUWFDygR#2i}KL=Ffh z`Go_E&9WdCmSy%Pi*PirA{I;<6pp<}v$H*IB4|PG7;Jm7_NMx}8Vu}X4 zf;J=jKN6b#q?1^%7)7htgot@PpKTZZX~&3Jr=l|nmcik|JL4)b`|I(d_^w050*f(< z4z~C3M!^0Grec8O>2?JPDhDU3c3|z z&flMjC_ya2zix;TrrBx4YHzC$MYMxh`P%cMeqb1}^53FLhx!4`7aq`7(z3V4i_oi> zD9+aJ5SD(_SWxlrb`iJ>Gu^@4hl&Zx>(Yfi6Q3ZI*I=n8uw&se=6ca7>c7CP^6M)Uf^r|4&R~CXv8e8{5-WG1Sq=8oG*Mnn26~9u_w5jY0rXy>q90u| z2*h;>yA8*lSvy2|O)EZuR_r|{96|lbbP*_uAy)AfvLaCZ{&`XE!|}%kdv`)O-abjp z{zbY7Vi217r)7!o(yf@1g?j++#ywzQId@3d?;jx1-$ix^+O7)!?Y+btDDPR5r9#*@ zj3h-{i!auaZ8s*ALcoksO(-36GQxrAAj_$(!u$DbVvb3;X^BlxnlLqkEqQIHsEIy6 z;?@ePf$HW&kz<@iA}^70n3mU$FCc$w#B4+`V5AG&eoQ7|frxr^R>1faQm|nE(-Gka zU=a$a%&riD(?i692}i_|udF0aBR>e$-%b@;^|zSE2etXzM2>kqDKUs(S{E@65sY{Y z#>FJu!4bstJ#j+!Ir@F9uTvNw^AZc3#-ud}Cg?=;N%;)*7L0K>H1h zMTsKk%aJ5bt)(1rjK|0~c#xbHZ(`aNR6n;**b76r{;{!}9LjVb*_myZaQqe{p-`EF zQEhN+z?l#Bq7BM@XUGm7pH(sM#2PU>$-eaeOTpJEc(bu0I4z%8AZv;UKIXvuzsDR@ zb=?JF%)fx!WG_&~cHCGD_V@5+An^5K;rd_}iG9)Jg3#e&5xgIh3BfTrRRm+!6I*s; zy9jv_iTQ?(iJaLTBq|JT69q5NB?WT=UIyG%&+;>el!cfR09t+~dZ$qM zGn!3oXpZu8Djuhjc^XzKxC7Vy6S)yRh*+lkz#L({*MOvh0r&Zw6yER--1WW#i3Y!L z4m(0D_$4MdfYXW&E4Y0qH+-1?>pFc^)D^T5YfMiPISZ1BRg6bk1q|N>A_fp)0PT1v zc+C-U5)s-;M7|y)paMInhulrVavdhNv5}ZtHd4dpI;Y6_J<4O&fpr<#?-hL??rH+- zed9#Q!HdMaYkGwnk@@b57mfffJqTh|Wic;}g5bIFqNt<>V#Ob0kW#~@s2(j97DyA0 zUy`w&Pcllijly{gdb$w2gp&uHrQ=1Ny?|I_4*r7zrn}9ft_44{Q@9S63199yl%B|K5Z9fBuad3kq6<=eyHb(e^Zk&})R7u3!o!RxMr9 zBb;gLh=ryjvjNU}6wy%kTC`|<76TNJ``IK>QF?}09#+Lwnh@dTk)rT%EuRV>XGkit zboAvR{|Y(_?7xZ1l`K5YBFonE$~#uFu&p&B`zrn)6dRna{BI^@gTARlSZ7p_xC;Xo zP*Q(hcr{#BUT{wyC(1rrM9ep#S-CY1iDY5AY2)nOANuEkR z7E>cC4-6+3{9T-IW+Dd&THTCu#2Rl*6xsjlz&r1t@_+OQLsmX17&gm-Y?CnmGKrKH zL`*uMXpy(;Fo}0FD39$jh{6g7DW?&k{365pzu!5Pfs3SL-<=owKMoPI-h&%Upkxc~ zkO8lIw{REW)(xnl2-bomAjXk3jy`4ONRp{#`3b;GU3tcGoCT*oPjoz%XAQ z{JTk*@$+Uaj>x>&2K{7yAM3!pf0#^L&yeXI{M)TxohI{$63R_HtUQE<1L%KQC9Lu2 zmO;sXaH}PFx3voQ0d%q1-_Xs5`uTH(^PbtnLN_994^9`xe|S?D#t0@f+EI`|?n79R z3@S3LA}PGEfyg$I-jMG0?DfSCJait@!Fazn$QUpVKZf`QOXvU&LP&=j3HXc{T|csp~9}6#^yBTwe>5nFQz9?feJlQ%5m>c2lm9tmt{j7IGVI^8f)-j7ABz)+1#m(`?3>_gm#@;~+%i5bY* zi-U+5)9oJ>;YWne*Negzk+5bh7Ue~p(O`Za|D@)5EM|0IJ4VlDlIibQEvKiLa!=qs zBCyUv3CQO4N2DUAYE<&SS;G6FgP6MygHPa_)T&IuJv{7*QOaBl(Xu3$atFRQKphSeq4^U2kE5;&1?MVH%YuNm5gK$mnAy$)6BN|Tmh}rdRBCvlaDU%Q< zk@Im3$3bX1nr`3>VEhOSx1Sbuuc1K$jnCqr7D4Wbm7=1og;*Yz`pMIH^YSHSO@Q*C z5DiO#A(ujN5b+@8UrmTWTJ9?Rw@X;ISx7mG2=8F*2y{cUg?{H6V%9t7DRbh`xJ8|U z`^7Zg_$K3HW9cK6d-{+Zh6h8+?ZeTk1uRS^?k^@EOS!1TE+8LN{)Br0AowXxP;eGv zYzg#xux1=slU>5xy_?K`ok!;G1^EAeH=)$K=_pdX zv3s+WL_Ei)VNcmzhn0j8IKkl2(@HEN)YTmojZ3x=%lT!qs7OjDmh;aVE*sIg&}t`zqFG0(aYb;hc;nG_#Ktwa8>LM+bU4?Cr54_%v>_5@>Ee4Vc_6 z8geNJMVr?vyYs0C>M%=Q<4vRStjPK26}-bMRDskv%e#9{#8yO@oh%CejZrSvH#K5A z;&#e!#rTgHM1+^n*#OIYv=>os+9YfyVGbY$5g^cq zTk0S<@1O`|BoOo5bXnB-(d33k8ycFBd(9D1k#q^i7?*MCX63O-lqcE{@k#xV7O@i% zE+&h7?RPzt&)QRz-x=^2XSq2eVmBg~jtH~YN@nj#GFyhoT(q3b)o6lO<9?tCZ9>0d z3$Z$+=$Y#)EM5riAJ9pG%D?A|V27WW=6TCStS)YpXuKJF2RhtJ$;v;ODk}ee62FK! z0B<9Q1EyX1fH9nj_&GLdrZN$41oY3Lkpk9-I)r~Xrg=lrtc?*^oPHMie`P1;D*qls zWl-V6-AS-)VzcmH+f8CPmev4g|8Nm9casu>2qsK20sqXk;+YTD5p%_y6aF~70eEKr z2~l_S4AmXg&QQ@$_ki{c@ zvO?goal(E*vg8oje?ho5`iTWF=pE3osi>JMLLc@MbL~Xp7((?&#j;7rh(qXU+#v(r zQ)`946!!~2{ZB|RgXfMFBJ@E4vFd$T$`?HQvFZg_AH@!`v=ySV2l-##1mDKM5>(V~ z5k(JRIaTmqmn=;0?!mFeaRq%d)^LET&F6%6_7t(2H?UGF_-ip)6}*`kA7Ga-j0b_G z=R`@vK4O(0jbzCGY@H*V3sHT-(mm0l=&mFZm7AzY;9oCP;vWjetrCql_v1wEC*7?$ z@u2*Z1Hxdkka8Rm%oyH-g6ok15m?0FdKiVF-cect_Zz63cWQE7ZAa7 zRpe|>Bc|`~6?MI6JwqvGwKHq-fGE1_ICkJTH5sM~M-byZP}jadG`3u%MhzS1c#PXa z@TZl+godMOcPIX>E@mrT2?@Uux7dKA4$CS*@V@23@vl?FJUlvCry{EKttku!*xF(RLC;Qr4gI&Qbg=r>8sEr%t;1Q-MSI0{@J z@MlqVk_b0)>IR#P+lGlr*(4c;cSp(?iSB12gQ|Z~WsRKr{wBR;jkt#M{BIe~E0tI+ z9HF@gjsM$dBFp`JOSyrJAk>D`|MhZRA>@w8v%K<=e#?$ltnF@L1~tN!K69>^M|(-S zIl_2EGOwFsitciJWU77Q`+Bg6ZdxI#>bPX zQbaX&TWNyP6{5;Ej5NVDdqkDbPBEoUJ-eMDek|3L)?eWF7x?vYQ}+Zn3^dhtGU>DU zY7xlTX7bidq5-8IUrO%NUrHYLg50OuO75STCmdjAePFBP{gv24XMWwZUh@ zhYztffRi(Php5!qLtT$;kW@q2)nj8N~*H}>r_$GHt~ zvKA_3yGf)T6&FvjRKJvl2Ai@`TSP^^U-0{mfWU*13ae zK5|~nr?h6N!$Vz1ynLJ;)d@JL*cS&{ksvYBTxX9<5;a#XcC{TK zHHg*pG|9c4#he)}NjNWeG@iQAqB5^1ZiIr%%_eZHy{OQj3nptTzQ8I_st2 z!}a{!nX94fkGvibN@1%+1ZJ@KL}T+#ljt!)RKbUXOvN3OL=}8;)C9ic&%={KQ#%Sr ziV1we$I+a1*d%W2O)WuhX?8wYBF$)EhN2QZNsS@>-GpoR0!c?s2}LKUI<&`xUgWDn z*#>?bsP~Z*MD_d>le3*?h&stb2>;&aXu*!RlP51QVHp$I(j+J{aAg+-OP+(93vIuTU@^$ zCKbrpK+%yp>NjaO><||d(i_l?dSF!irb8>5kAK5!XCr@Jea07yP?vG0v8F{Fy);(4 zBKL7?M6zj~sq5ZFRLxJ1n8Mdbii;J~>-ghk*OD1hsPZS6(BJAs1!s`lMA=G8Jdk|& zvZ?FQUR2GEBRT)$#*q@be!FR`;n4~2ZRsYo`IxBS+_5IDPj@lB39qP|$%d-lDXyD` z@cSWBvpZ($nJuYgH@_+ULl;g`qr{>Yl-DrQQK zXd>O@Y^o9SIe!UaA73%Gzdt1^Hk~*UN(Q$zvBO72)jrAIaftaYnnGvtEGa~?k#CJ^ z;0}nopPYAfJt%dEm7H~bHb-KpeTqsalnk0<>K`P{uBfCjZ#ehWz_Fhn4DMW-&03Vo7kb4sM`5FD-iS1*?^M4bj~35 z&wNw6aGa=ENz@chN#W?76iGUBl*3nDY3jPo6!Q}wU%8K5O}UHvcMP>pZ8ar#bP!el zAU|6*p};kMH(&8?@ed?}hRXLBo5HjCqRP^^$*IXaw$+p)aJW34>H1w~zEaGg+yXhT zR=SSGBnS0l&YQw-j`1x#CZ2Z(nTCbyMO8dhU?QVgio?I7qse*Y7~eU37BuzuSBfe; zyH^SwE}nN+nue!{>7(MeyoV7P?egzf#9u_o&8CYk`bBf#d9R(R$mGu>_-ZfHICdMG zS8fykJDdZ8oD-c*!B@2GBXS=ZFvR{fnD{T3qN^8~!f$#1qVnDTrfenO2_y~rI*IZhpxOoT1d~}fHykuEir7^3zV;^A(q2^KuV%9U%dcii!@$L+ zJcBtJB6qSDM4}DPYebd|F?nYwMnJ*0sitz#N-@9Z>;Th_k-*XqI2Q(GL%k+)nkgD` zkDZp>&pncReyrrKrJ8!J!*sYk+itW!CV4%GNcSMcwtcnh?V*y6%pMm_-CoxD@ZQR1 zRI1-*ljyrkRAef(pYZ-{sq5AQ{5kZeqZ)x#Oh%CC)5!!@Uyy*Kmlv35sJ||8tr;N! zlx*H=3h!AZX4FtAG~9YeZI$V3#dtUKkEquvU_#(8l=36dXOZ#0MJpEKmHkbTpP&^2 zE&1Z;CGb}8gKs^z5=kOQ^Akdm~#FR)$p6sChwKyl3GEdkqXi= zg^WcTO!#jc+XLS`R$GYXv*yg-?MUQ%6MA4R2?fZmtxS8r*M6zZfc|Ul9MvY^H2RJv_b@K_hDT z^Y1+E8bX1Ixx#owvc785D#?gb6ck|UpUCr?BqNr%rv18`m~(Apn(9&H`mx9~hf4v| zXjRekv#segf(AA+8xuB$sr5Dh2#nbHnd#Efn)rHHR-YrMVA zvgSKWR}?*cXc{-kq)j@^Cw5o@#D`Vpcf(t)WyJXr^#7h8A^Fcym2OBRJckv04B5F9 zr7NQ0CYR_q3a+CZQ+4X+x{g$<`foeq*>YG^*)!`*V$ytylIKg=pIP1^@e#9ZG%Vx& z->{5IhbP*a_^ExOJfvS$g|C+>bJ~as9HX=biQD&?zzIsRkQhQmECOHcHVImHRN&+o zlemkjHv~@mOychTq5{szL;Qh5{70HZ5jf3_0tt$TROk*CBWQfIjR|gGimhJhWXj(l zCaAVjGlbB{6|PYPv(UJBfT=I?ipm?bTk`IhAZhEj8Yhi8zsfjDdJN%Dwj19r(z1vy z%r(_N;ZO-A7O<5XlDD^*n$M4ls^9D}!4e`K>L&|L!-X}XO0Va52KCT*6MvQ3;8(|T z{@2axOkKH0R8x9SleU4+5`h$!&B*%c0#^?b(`G*7e90#%VRmIpzM+j{*D=XXo_93` zBwHmDreJ7KaV=nUxZJhEO#AMzn6sB<8uF$rmAurRlD2J=>n%PZbAgeV-sk&Gn3b>c z?V(Ts(ZxMYb+;B#iACimIi#DYnlJmA`mOA$?QjW}o^kzjwgk~|X}KxwN>p3@Hr>Rj z>sNsn$D6X(vqdHT)ysH$M@7|Db~H^{^bjL$`(_ikeW9qVTlTs7O%x}KOIE*wCV&56 zakVhwL*9nS{#{C|<&2o!VhToi#C3!b_mFtTv?uzz=8lnRNPB9YYf&3XQ%^CCM@?Bz z=Kp0qrKYr#$w}M7xi4EJ_1+#XQgW$g86)mlW2!E&dPVq$m8K@JOialvsev!V>DcgG z-@z1K9w#a?eZ6TsIb2kEnzMq-3jWY+)9~~5Vg`~ER6nPV5An!6lVAm`8rh~v5xj80_5E-O!f7nc78M#z1YF%t zs058O&Y8eEZb<6+W|Qd6cL2dNu8f~=5m!G(Dy4?aHMKWf6cs?fu?KiRc%w@fjF=~oie z`Y}OjQ_l%4ZTE6Z1jYsxM9Aq#C8w?Zwec*#pZ>;L}_pl(|GF$C~PuoQ93Y=lhwA`v_91dzZUL^D|fX4ltRc zsC+@y*+VABIm!u_N2Pq|Y*Y5=I8l++Y($CbcPBb}k%sJ9c3On~PGTCB{n-Bzp@Y0zP&t&K0vi9!y+OS&*!5?= zdusI>Q~47bnheD)^o|6aI-YK5T-m|(WG`t%^=vk#L$v!W6Z!WH`n6{8^Cen_(l^hv zVk@mT^QGdkT-U>uQlTPiP4(Y5h^l<)mAXVy2kYgZN4YN#Mf3R%;pY^*Ob1*{M>A0#ES_NKgV$kC|$S6l)PFbs&xG!*UJS`s@Aa|7~mu)XENI{A@Z)}_ET0f7rhGBc zb$qK#LC((!PNDpJdIZ(CRJN!cV@*T-I6eX1UUAwm;F;XRG)`G0s%+2_lSpU5is(2t zG11GEy{B7MGU9!Y$?2C6mHyvTuFDrCUHvy~qI=kK1vwRSP5Nb?cIyAun#}$kc{_4^ zYx+LZoD&mO-)*UB&W_UmM;~YN;a$e_UW|wa;g$*_7Vt#(nDqY}#$SkWt6cX}c4OKw z;(<%8CNg5|GS?VBQDnB6Y{EQ{T%#Cqt22`e`j2+?Wd>}zGj^AVZ>Ma+~?I@?5igZacb>$eXnch$Sdb@M67K<1Fnrs`xnQnduJW5Z3u z<3~hQ+|K<0PDAb?QRT02A3$R*=~slh>@f|`aC=6@+#x1jHA+-rCfn&^%2r;S$f%%8 z0?{5TO~W&^k5!n9D=_2*-E;GS$-h!$hZ$mV;U2aNm*dr!$pp+uMJ;elGJjOKqn3QcHYKp|E zj>g+(gs8eW3mP;{8)nkm@uEdwfz$(}*WyI9gCa-pkQ^I7(G;W!p@3>K0MO4|R5RJSxTT-MrA$Pfdz)YX8ld zoN8mgR+|}#Y*Ln`A{$+?y;MM`M^?GsD3X}^`&?6dcV|&W4^n6bUpvkKL*+Z9T2MaS zYeLtOSw-2sn-%@PE0>z!Q`|P;|7^3V{zV&6fkkUfMKf_f)im2x$8A-`&Y9{H6GT<6 zVw-k&wh^LN+kGbd_*PMox7wKKaEe)mI~yc;d5!U(BYKTEmF=qh{kbO8D^pZsgV#7U zo5a-n#DUjy&Xj#sDXRQXs_Wx}QjSpSfL6;G@z{FTY(DOA3A{nW38pRUXChOmdO@Nq z;aF5p9qP(oC)KFCkK7QNZu6Ve=P18K>9frSS5)HHVj{O5=JZqIfEUR1qCtxq;OR?x9jjQwLad4uDysKno61i)DHF;6^P8HRlA>bQ3^lcBoS=oGpC(P(f7Xa9 z_|7@R<&XsTo-hUP%@tKPk6I9fuIXwDPD~V4Nky}HBr26?_AYiEJP36qlVw@S{rBXe-#WcQ3=mnuH*7ho+mn(g_gizUYx5>Gg1f}XZ z(G(mcDvn4`B3!8G{-tS{RV1ox!8}vZ=@L;7HXz-#*Er>G3?hJxE$fY^KO4%T;4B5_ zXq-#%NIl-sB(7R3sv^CkX;|zRRdAliD}v`))gUx5-ITxIp1Wv!iF9o-c~uuAuc{GU zB=vgEf8rvkoesFJqn=lF47jc<;`ivpmRzbs(sk__Ni|_cbUg0rJX}%@l@y(Et+;+f z0kav+hz|VY$aut|;pCBA2`@ite2o`HMSmSI2~Vad=VV7y^9;pFsIO)Q1mBoSm#0O1 zD13LPDf(=ksK|ze#=E*eRNbL9CU!FbHx)lD5*2&& znDMvG6cz793I@(=R70UQk0Y4S{O1X-`&n73ozxkr6X~wNPHBcyazD#wICp$$roXdI zRJf|hlxH3nRs6u0rtIWFQQq4Rnc{0si1OW-G_lQ{MAZ(SVVu7l5f!VPXPh?N$PmtJ zRE~r_*{1Zlu$blNq!i^|DxFdJ=SwEIV3Mfnp@&Ui0hv=&RChO(A5i6nNNKLAUENVs z^662g_WT}Eu|4~Z_x)MoeV++t@`;$KSw!Iog8w(uRKL56;1mz0qb(-V_mZfJSB9FZ z%QPXX-s4>Cj#;Jpr@Go7kt+4Sm9BG?U8!5xL09#GqnCc0Tzx35Rk!jn)t6tZTlnuP zg&q#occ|;;tx{!1FrxQH*RA|n^)v4Asy}~DQB`NIro&FPyTtRKOVu^B(Wq;ex~^-M zD)l4&A=UYmD}~Qq{cMZt8Xx~3r5eAYM9;J^wn#%)5*UcRy34hj{lrjni_7yWty+kF zv6%et7mFq4y<}=<^%fPF&72Rl^VrJ=CEdfOf@r5|Sar~pQtz$eiwM1eeHZ{FZiK_3u-qkZ9^(buaXTrn7;=06m zNs7;&cb(z{4R~%jXv*IiA?5;&X9%TrH$}(jD}r}lcawDHwUZ>gt1pZkxO~6+y-n%IY{&QUUh@C!^J7Y>-!*KPT1s2}4O?8@>Dw{X1$194S?~91#ar!t z>|JA~HlGmla3u$h#y5v9 zB}B7ln?%_aQO z{h+r=&RfLmjEMOiEhhe_Rid1V1EWPXymHETZp{@{oN>xjZmi)%9K6oV7v*;{A3#Mn z)`1AjU2cMx#)~TI*l0X2a!*lzyX0!WTRf;6>Nia{?iMir%cW2VrAJ8&BD0=bu35&2 zc#6rtjbyZ83x?Y;fEG$*9rLx~_InJUoiS4vu$70Qn|cBMaLDAoJ6Upf?~~N(k#M*~ zT^Wn7hzh^h)g)ivF3LB5kcs{uuLUGRdrY)|!X-HW=B9<3we_ZcCP(<7_QEk!_&x^` zq3C$7DZYVy!r&XXTYL@|?+f!yc;<{W z6`#<4i-y-{o6Sx^1RNvmN2VznP@8`o@$EbkB^sP%>Z!D4NXnZJa;`eS9GiIH{)%~>VsWHP$ z(ceJq&Mz`_Sr~j|?B#A*}KFWUGW;G*b4K(?~XdX3enK}%= z$TV5s$+C|Nng6t5?+jQVS{ zO?dDiQAL%DP01QwGwN?MOz|d;?ozLHHQsF#M3oGr6dTi=0+ad(lUu`z`w`5f{#P%l zGwJzKGYAwS$UsoYPP2IVE6R`^X`qp=pJQJJlU18tEVn z(Zx*v7-kdr}Ezs)A~QJ#t@{fbsRWd443EB!U*Cr$q79b)d|@obp7-M3Y; z{wLW2@eb_@dHvoVqaMtmPMLrRuj6U6WWsn~sc_xW@Gx zR=w(f15Nl9zoY~q03_-kr@RNgr<+Zvj!G+4Oso(wXF)<@@L$IX zC5Zow!=>PCXUT<aefUB$2PcOYCp&m^CjEoLo2 z4%EKP{1D##$BmPm&tw9T02S=0IJVR@uqsl;6WSYJFHXuq;B_*X2#;9l`qdB#qxh=} zrv9d|sNlLUO>D~;QQ@frU0#teJSoHgQt~cR@+nZB4KS9lAy%{QK7^F&P> zKEZfC@5axzn|Rmp6`PCA_)6a+jAoWlArt}+L`N&+{uhk+({IQqG|8-bgCm1n-I&oyT8Qf|GBYv>iOkJNZ zF7}mLkC#}mI9zhS>?ye?mxJ!5(LG!vdr6uaHN$n!DM>SIPbHzl?N|hPGh%g^lRUD^it}vpS z8xX>K3QXeB0#V5ux0#yvDepx6UufAuw27J#_+A;JsQ>*iVUqLD@2wv-cnIb_p`Sk(nilTfgg7j zm3e9%>;F@1tM^s!R;>5FS|<66IaWjcv16;bjB_PBbz-YcjNs0w3a%zGY3TdC8f}-G zyq|59yq{48(f^>N)l$B#qWoG_d*JAWx0Z_LyJ~!rrXr_J#(NYCqG~2nX@vK8G}YG= zx<}&i0VX zD;$L4t6bAaCL^|Dt?_qZJ%{+uNm#?-5F^#Rv6E}!GHF)tH@m(WAk8Q#T4ic4ZzTWE z#e1NWN&cZ(%yTJ{MCJaoraW)2sOl3(jIR&x2sk6oo9Q34h>GoHzNfaOnA&@&0Y;1) zzAE=oDTm6{{JjVlOd|eYFiFDGd9kU6T_$ozs+gOWO9a)?q$y`DU_RL*<*41v(TxZ! z?qI^jgm4h-(8e@;MRO8LALwS{FLe=BH*J$?ddLY#(?bD%-uq49rj??yq+cucX%qaQ zlgVE@N?cUOuf>Z4O?JsrG5!vctx9+Xpr8l+1?t8&t{(mQJq^kuVra|$-%Z;kO*M{f zwSy5(W~)PtXxQya?v*svz<)=^UP`Y}HK&KGEfW~kF5qfEj3flE?d0lsi1{Eok(7W_ z)j6NoKDrR+HwPIHO8CP?COWr=sKnfD#`h*GUnFlGZE8N=DXRX(v!?K4w(CLB*JRXD z+^4(oO}WI2ncAWquE_n=mE6z#FVxV%HJyS}L-GGbJAK2XIExl8xyf_BY-xl)J^9EB{WT>C5{F=m{y^n*D=@4WV=-_{aCrAy|T`=VHZ6^RLp~zSG!>nMq+et<6V1ERBY1;6F$t% z4ZdB(5fNQ@+$5glnSrD;Xo4hBbDZEI>Nj6D!EPy{8vada7NrjzG4WRq(Kba*Xah9@ z%GcjShAa?62W13Nq5J|x-)u3}JkV6}Z_{rRKKw3`U6 zplBMAEY$QlAx~=ihTxQnK5DywUGmV4454-4~c-cNvpGLs~nttFj&aeB5ik%um z|F>Rxb~AP5!~xKhzQUxvGJ?0&2npOqCnK_M?AVI^U~g2HO#VOe#kGqOyC<0JX7>Fx zv~V_~>6|IJn<^V~nf-t7zKmWbFRe!M(%R5=w@9Xi`1Y!LGFmFW!xdX0(^O))tFB(A zDXLvvXBbgG+|}TdX(;Nq-_+f_UCe=QQg^`VCb3oIY~cS1&BTZgT4EgHxvIoVs14pr zGfhd7eH{#YJ0#K6snvW&=J&}HCi+YANAMpzXzC02F(8t6!X_*q;0{{&b-dH+-e2Se=a;`&$`yNky^EUwdfN)*;4y^^&IE?BUvS zR??91xyOW$To6^=t*=Qubxf4+-2)~`h+fs4)R31 z45!35Qj{~QpPBwqFHzOCxu*6Ckxqo0_)1ZCk_|}^`;LDc-ZgDpA2Az3^2y<@m0Nj^ zY~{{4*F<`eoKiui^r-ysxFY_Oztfa`9Tnw2PwE8m`7z`FoE=OM_!ITvh;!G+^ zFMSWVZXuz7{C#A;)dxdbZDiai*^jMr&F)Y8EgoafSQLyr?Yf6rQZ=&1o0t}lp+q;bjkDq*T#+An53>jCu=5l@6aC)S6=l7Yte0ckhXu&tV^e}|hC zlEd4Y!cW_aD(<)4_^60f(PuoSdS_QrA&2}w7YX^rMA&*;#ab>H{|%I6BR-fD&*8i~ z$~3=D*`@mJR@eTO(yY!+FeUz7qMUnKBVhW$p2j<*o2c-^9ZV$TER#rRnN%L|n3zlw zm3)4;sol;^2i}84Ci&zZUfp}7I=0)yK47J*w(|+2_Wr{rTo@GN8zEtoPu<}vS;o|T znUwy8o)T0#e;p*12rT6>i{Q=cO`{AERhqif#J6r2`sRiJ5V8PUN zd6yvT?vbsij^Ox>tYJe;K0j`Ag4Y(Dd|nVLdr7#}aYp>{iYZ_tX)~E>W%b*9ljkF} z;u|Gt-?Vd`nJ;O`_)l|FdqKsXV~WxA3kZ z2!+V2V@=?h0iwzawwtmu#BmY)?_pD#!Le(IzeRHqDyWb&8LOoNDDLm5 z-CtvhpPeYmLsm@1N0R+i_hg&8+Hq3naA_*!5DKJyQ)2?d&Wp+`XWzzhpX8NYl(dWN z*Pt%3o4NWAKW}7wL#-FWUu-mqrR0DSeRhUP{%Vn^nr}y$`hU>u1mA~SjZ=4Okf>0S z8ZG6gMhmeu9gY8|)OaI)Co>~BdnTLa4+n`kx0kmZ`}_qun5qp&#k`U!Rcd3o>lNY( zh{;}4`_2#^&BT9`t0o)g-pjmKFU$0gyPNO~rcx;WZ9h}?O;S{G=WG+(UoPhD84^Qr ze`Yi&`u2$N-O84pDmUBZOA#N+{S-T+^6$OjaM?NE1YbTYs%|H)3h{A#M^L@zOH;b~n5fD%lT2V)cTt{S@|H#M|M3YTe1EfREOA7Ki|3AdQ~n;o zWOcEtDe4{*6`Q!oL@IlUs!ktmBAL96kmyd>6~R8Nn9$gU22&(wGfzbDA2hk3{MwDK zYmQ4fg1dZRo5Y%{GFyVIa}Q#;qoZKW9X?N*r356_D74m@mPA5hp=X4*?TMV0((jq4-!2}50M5Bq<{_DE9^L1Ls`+F}C3Spy<#WM-=!jJRjK z$v=Hc%-1|E%~3{T2G1p1on=&=OhM*ZFv;K=e8G_CRvvfmS0KNAlKUhdk8prj;Kx=fb9bH?@TDebi{`PA%u%#+2=Q zzH=yaK4YgU6rE#*g5saCT1BXoHNI-1nE;Vt`KF@7Dp9dDEL7p|(ZR%r92DieN0bZA zTl%^tGyhWuhPb}t?TV7J0mg~#=q#%Cfq|y-qg+wtg>1!zva`hhQQY}}Dfe&U?XyY3 zk1!`d?M|QZe$3(o$%PkKY? zuOsr~6jSicBsQF#Bn@qsxvpmB3uoDU(>Q9jsKR#ClcIqwTvT2SGo-wlR7t;y_G#6t zzSU($+|+C`E*=zBwT)<)Avd@UZ~0C5U*|+s_grZb%a@5t-q+jI{QH8a`n}8*5q*BE ziTz+Q`@jBx@C%y`spRT_DLh8QIEua_yn^b#rW=Fy?)dD#&9uH_Di$+$Qj24=Z~3^3hJ`p+(#}S(?8+Wi}1|N#y7P^Oi3z{ z&{XmMjVdU0h~OfUPt7s4yT-A=FY>9pLSE zK+21F`=RVxw&#HNE>bioCgG%F{QOnjSF=s*!T~Wm$ZDc?T%(CaSkA+_ibacR*=@o_ z+~nYdOR`19A1pVq4m_h#UP>?;m49zB)n9fL6}&=!7%Kl(X-Z$XC@T2JqsG^tt{C_~ zr=S7VzhZTaz_X-i)LWBHMFT&wh9%U0f4|to7ce(7JsEpS<04w;72z_KNZV&ZH`5b{ z#>RkgYN>=!abo=NPUEqSU`lt>*nXj?(yS9E{x+**M6%X``j5m!E>?W!5oLnEHRgR>v7}x!h#G$f;Or-8k2a`Mm$R z6ioNFqU~xryk|_FGfMIrw@Jb7g{JZDzG8;ailv6^GQnlTX|Key6d}UDJKMx-+R#ne zMm&A}CRQjkHBy4rX1)o%eo7p@gx=U@Dz5J-s^Q@gtw6Hz_ryh&_aA}ad!1e44qLXO%W z^)U4(30%W>akD9OPEs0&$g6uz-M94jBK9AWMF>4Y09pkJLL>4k=76Z^)ZRqG+yxPP zb(Hb5!=Z}*VvBM14;9t?4&PaovC#EN7xta*B2M$g6{1RF3FACK)DzP`-EO=?iF~5+ zV7e(g&-t$?pEk)=s`m=+8SiX1)rpa!Vh7Hfz}rDl{tN3&eBl96)$i3SM}q(BWW0w+ zL?gMVpKHy2Ng~$vlBwOZPE^dxj93-(51{n6mWB$GvqeZlI{@LY`WvXg}FR{@0sb4SI{Sg1H`QiwM=C=s`|FL37J_*92Z{YR@^>=_S&v zKI_E(FQ0Xi=3ujFTGm!nNm@JCVj4`~aPv^p_fqkOn#g41oqAqW{ZxvJ;6GPm;!hkE z<-KE!aXut=gxU@Hu2*JBt=iyFp|An(oiWK}%S4suQqhj$33FW?`89kucQs|FNYSF` z#B5W11CM1y%Ep`Os!gJz?O2MVI+f=>ir=C#M(vwrra8~dm1!tl$qW~jtA?1^&ZDAg zhnAb_y`4psy*5dFzv6MK^WZ}@-2+YRxQO~!fqxTpa41G8m@1W*dYz&KVW|G1I~Xd|G_?!_>}#A5!u#kqQf~02IU{K6@xl>%!H=U6>UZ^f(WRY zwL?PcCRR;oOz@V4Q$r$B)txY2Pnhi*=nK1hJ{&GXPq$*r^`T=VG^~T`?g9y^p-jP1 z(Su!&5&hQ=Q$37{DeCTH%SAM$kU2x@vl~t6x7lpRkSzrtA2pRT*_>EC+|Sj#koX^$ z##j9<neWN~c*&r7_DzWlsVMC^*mD05Q)QS5bhy0+{t2n29ki{?XhQ&$sSPJK6gjV|W|cR18M!;&2JQBF}pbo`elQS30OEZ!%+`;MFH zf+38QPf z%$8b3astUkn@#Zpe%iPFQg$-ecyIU7zU>poccaj^EwobeK1D^86D?5Qi$Ik6d8P^e zfn5?%`WAcppyH1Grs%VQbh;0e;vdg3(J#qtp?YYlY3Rx>nuzqBX=)~H5mk7xlcN8( z4UJHuqA?-r9m zJBQiCxJmM_n(t~WwC<`T9JX9~ozS>h#)!?^&D7ulF`=>a>LTbfxi7Al+!xtIU?^?5 z>h6?QbWq%lp|s*6O4&I^*bxD5zOUt1jvj6HLXP%c2@y9Bx9nG1_oJZKtD`*keqw;J-TG z#Bc6IJ8mahamSgGvGjJr;XE%jedh=h4*NwF6|OY3Ej+B$3*>!~{9V*k45p3$7M`092h{<%Yv@$;CJJ+as%-a9BN)P9jEzj=tL zz>_{xJdO|@d^cV(k>8AkVfR zm56BS*4d;E8%}Tca9U$&Ku2atd8@;W@N>Ey^1G$CqWz*9ZlL4>S?_Ij?PX4>-qVjw zkf}{mOpCfZCzz)0D@CP_+iXe? zCq!ii*)u?e*a^`rU|b;iH&M{6oL+4u-^^j0BUuNIx;`TEtqugt)L9$EJT_3Ks#&8< z?mNwr`%XJay?LUm7oUI`$cURZnT*H$q6+q~RzUdAd?(=enhuGIK19J75-hEhbE>_m zSx$}@^|3*w_^vCWD*i$I1`RF52v9n3n29f@oe!a^{jS`$+-%zNV;W{+kFn7a{A~{? z-v7Nwjlp?st0}qvq9|u@rm1~_Oh!<;zs6)v9WAc4jKqI3dETr1j@??#WW;stP1f5>#O&xOS?X=xR+w5lxz$!i z)EqInuhDS+8t){!UtKgBW+8LF$@s%?dd`PS!TU?t|A|YuWuo!Tp_&2FEW#^DlvNt% zyK+%AEqhJ`PO6UmDW;o9TaF$Gc{ogrmfKP@a`<%Dzk&6}{@1 zsrX4k%$=R20_8U|)kok-&ICa1-xAdS{Efbyeg{o)#u%E;$8dV#8Q1IV>7tl$sPa1r z)1ad3DHE96hURllv$*!W@vLnYLvYfg+LL}o-Ccz8(RAY&lbT&b|9_E`e(D_HazHX; zEv`CxeasrhHIh#Qfl1-@VMw2+Y>}*^r(K_UX*S2vu$elaeoyrW&JR`db4@Neq1=D$ zm-N~}uIe?CjsoWrnJ9Sf;%0-gUd+)D`Vl!G)$XG4lNVHtPt>>`pTTxaGo;~%mrMoG zP8GeKyf0FJ$9vzf82Q~s$ynwy1s@Y{L=_Jo3 z#nCgSVcQ~6k%3}je<%{=??})Q@&1(9!+C3rX>K8mr^d5GmU`Fg`igkKS6v(_ncUHY zW2foOJT1wkHKzF5BlLtH5&xC>rZKunROo7AH}D_rYeH?Af}^o=jtP7~3Y?a^}&Q?Cy!<02;iweHkY(h(To1iR7-6w+dcdF2maVGFD0!b*& z?qOhQ!g>)sl6Y zJwnZSM%CU-U9(Kg-@3_EwI;>nJ=0F|ma=!uab9$vACy#Ii>ri=NkwIhDIuzo$|cp$ zY*zV(nT)46*#HF}3^Y}gRhbckrAqy3v}?o$e!oG&Yp8aC?+McFi01M7LB+?s@6oVn zqlrDvToeBGolU&YcJBXuwzKoaCDXi;pphC&9gq5ZPuGe4>{_*7O2(HP=Po7|n7(VH zsR`$bD(_uyl8d*q`voc7>fWZf135p`-r3JIM#u14VyWNO33CZcMfG758ZuOre;2Q2 zgy{WK@hB4t1fO7cGn6b@Y(m#h5W@l{q_&?ik+H;qP?Q{Mf=`U({{!z7731Ebyw1}* z#EZILQ00s!-uo)Gbh0V^{HUl*=Zq^^B$?(oBgut3NI05CMhs>?fvhjtCB@LZ@+D64 zI>OY&yiUw)#@XUbeR{d5f`3vZ&w7{qJ7G>Jof`TKb5zB@uW{H5IS5L|i5gcdL5TTaPDQ7=yNjwWAhw54b$8d)@oc}r&-=Ee zrh4*vF(LL3Lu{_c_^;_MD&BXuakjS6?Kj#;?bMm3`S~#0aNvPSu3i1uh65)Ly2^8< z86^`&7-wh)w&Um^)BnM{05#zQ#ye=DsM;63Ci%zxqQXSMRQ25+Q+p@5K)n<{NN_-P zIBI-15feahx8h8w&kz^RCHVBx|59v5gM}3RIY3m zRr(xZdlcV2j{N`KltF#6$W(liE~;Vk7305jT2$%aBPPC-7ZM_q*i;gU>(86Oj=rKw zz6+Y^TWMB+_ZT~;sLwc<7SrG8Vj3s-L=`^I!=Uha^gr+FGoM;7JWq52@q5{=0)hGD z{80Unn8LBth{4nGqAAWK`T@^16HN3Iz8ffcjddV`?;kOZznL#8{@=Ey*vTTmjOH;D zP2DehuuDY`Y3e=Gq!Q^?r6(4e%t!W$nK?!>%??J^e?u?2?hZ)4Im38HvcA6HI^$<^ z3Vc1oOnv6EC}--jpqOW7@qt+^lRc*3i`}9sAMNOxL3tjUy`5d3Ug6BwE9`bT*p;!8 zjVf14vuCNPX~(JwY2Tl4U7)xP1>X@7MR2cAQUCw%sEPcE6gkS$IHw8zkC==ierlx2 zt6VI3mAxe|v`$iAt8{Im_up{G+{6lJ`!s$?TS=rc5;9*LH~V{po;-mO7&) zT1pRpU5cqVIZ{-^&J(7z4KZBAo;qm!oheR6{I=1?*+KXf&1)989_k^@>b*;@uUD|G zGugFta>@wDTTJrV9-`l zOX{Mi>kowA)WRLE1zjXnJsEZ_SkLdVu&?W>bCPOu8FL-Uc$NZWc>Y=AI@XSUe;lP1 zOMS|NM*Z`G>&P2iRC=4;(*9)=Jh&k|ZlxNs=TfT|tgYud~uDniCj+$o(+oSl3 zd{g_|UQ+9DaJEl0MGy1#3l!hss(yq0AB19UjsLl=qN3Z08KL$f`@E?1PcVrmE)Z_J zz&=n16TWPZsIqIA;HcY~#v||kgz@!i$NHax|L;9a=&tEvMs%gOLs$Aa6q;C1w%CyW zTSBl1>?9o*!FBn@xpNZDC??6&52+JF_?nKUa9w~F4FL&V%2|*6&yJe{Hm6no!He{N zI(QNEZ$M!WGF?#DkxC}yuRCv?`k0-zL$zA&C?6xGDLDJ0{W= zvW!#Dv#c{L^PXpxecyT4c=msD#w%{0$dE0rOK2*uWW1TO?zosYa%GBIS7EY_ZIG;E zBP8p~osxX_FxRlTlC18czE=(Rx$aye$%Z#z-3fle$&McGB6V7gSY}eU*U-a(_2BVk zuCIqnzB<9*T79*r6=(ieIGN%4x|!dHlc&tIigV&34Yva2i(EnOjH--}O%Kz`w!4BU z{EHL`eZVFId>a;-a6^B3HgJcWL39*}YpGmBWH&`ih^>s6;3b3T4=_mTj_@K9l;m@GBRW|`t&vrUiMo9O?>fm`Sq(@6TQD$4C|LUCp+aPC@crhc@UZuk@${f(Ut zGl7u`%9ob8X7d0;xWjH!Rmq$YWxcpO)v&?F{}x;Di0*J!a3C>UjvCLCR4*fR_g2@) z;}S9x8Mz1C88WeJ2GXj5U5}HErlNkXsQRaBOxbkyfe?Iy88rgT{Z;s+ZXpy)fU?Th5(2}7I?HIZm z-(=^Q_$D6{&we%`5d3A0sXH-GRLR(xrgk0Ae3TdXT(yK|P&w$3saZ}04aHr0nA!pR zX-cx6b_`+~Nj5MOS&79Hd|HyVhVj15(e7~W21VVLVO(2`y z4>%(l%~WcIRHSmJ@qSHPPvl+Cb}p*#p_m;df9_){Ru$5Mp-`NvFz#fB%J@3$u%Hmt^?Hv|7uEsie#y_nM)tmNTL|nX|~gYaV(?a&)09)=QENHQ_PT zj&DV6crBv6P3mr5db!(SV))om*G#WWGu#?y;&D14t64ow=#ws@d~dTRM0n0T6Q4`{ zHxhlh2_f?7U=w>iO;qr5;s&TYImDE7Y9r$^`WSqMXikiB2 zxrv0?ltkdhS*}|KN&r>iGt~ctsdk}%mkM7_$_#vcxa%R$#{v{(u`E+HoGcjBT}5R) z!YA0YK(vq<5gH~2O>&&JF7Wpv@f7K8s3lOB`dYCvx|9{t8%`Gvis`Xh-`QmNgW{qR z%8wG}2FSfG*y<=FdY&~U^EZjA{V)58nD#p7lvL4Ta-v8`ps<+M+w_ zkU-*3n_Z7H!-4mkVJ7cE;=AzO(AVU3$`|E-d$fuEV>W%CX7i$QqUG@BvpI#>uc-%9 zUBa$510;sRWu$T=dOdrGjvhW?T?fza>RsbHiU+Y>E% zNmTJ~>Rl(Bq!_h7vKmG#lRZLs`%s3ZZt$Ceu3N=ixlIb-j3@CNHD9eF|NpC1;#|v} z4AD~NWN1jHPbZSsQ#K2K|KTRR`(SaArqLbO^f5U*W8yl&i1z{}!?9VM#zcVkR&eUSJ6aqv7f%!KY;N&6Ly9%5oWdWpI2 zl*EvK3u^)dn#qboEH`K>le*DRpd0TB+Gy(Er3MV1d238)C|MQij>)F#Aa6uN;h!gq zqlcRDn@oP3@G9y$hfVml&Z6>OUucT&8c8F8ku(vAn7mqIgeuOnLpfO{bRFfjDEwbn z(;#%#L-OAbwwla1SyGRjV*P*Ql;r-GA3vroVx5lAp$g;sU^r#;BojQ(1O@Tu(oEG= z3wRmz0!a+sZ6e2L+m6^?u_lvyH;V|v`Ej=rlk)mtQMo@+6#@SjeNB9jUsUelwx(Q$h`EH|GXiU-oBDF9 zBT<)}0f%Vh;n^k-suvd_ln~VoWxVeXr6V_)>~vnAo_1Wa|1w;%*KC&LLqs%` z=SToc$A{<;w)3rO~gFhO-fMv7F)3>FPi1z@+?Os=^Lu71M@#U)U4)? zhT^MTwS$LKZale)zp8|R2q>m$$|*5y`oClK2>>>db)na1Qou% zlx4xcjnX`XJ26i{RWd&<_(twGfquJqlHwLNGZ0+loaDd;!@D$>Rzqrkm)t;lJ);aN>ZZdg&j*BY0GhoVx5d}wCpP=y{_KJ$!THzYRjSBIB6HM4S zxn07j8bNA3DpOccBHrz^iB=94)sS`8B!5o1J^Z(=FzJJt-KyJ&?Hew=+fGW(zX@uY zql`G>HyI0uh-*3Ha&a=ABjSbJ2WGaqz^J;K$SriXM|K{s=k(J3Pu%?_3cDRa4ibYlLc-r;uSvumJ zWeUyXRn3}g$`i!%RB^D$3GWBT_zq(#j;^Qn+NZ7AX*7C#xzoTw7QnJ8=JF?=*g>^k$SGsOnbY%xCmm9)H3bu3nugxa}M}E zBb`eR;Xf12KzvzOQ`M7QZX`xGn#d11)bHj{yUPj&z9|HwP;+*Ysq*d@75pu4&O&|r zwx*;zFDX#zgyT{B?nG1G*qicQ_^JQpAeF=RnzGBe)=~2pZXhV`L0c}=-tINdhX<%` zCsi{brg6(Es@(DLc-Ov-RJr3!nkzD!2NDeq0uxO2d&|VthOrHGr%p!>9i{e}xhC<- zVXD;Ga+yJfD*~RaChwsYVt$=Q`E44FCDxhJ(~Y9?eoL!0RI*8_s#3O^!Xfi$IKyh^ zcR6P2f%#O!&zDHm2$Sa&HA^16Eff=>qdc!~JqDjA>$)(xAhzX-i&W|l(&hvybXBerLTbtWz zFC$3nP`L#sU4?~I**Y8w*nmcM{W!_4+bJoLfvr|EqH2IEyirn+MwWz{_WnB8u6{C2 zeK63qYb9T=ltj}O6W++;1#vQ@RALg-JXBwLM5-MQv4cFh;5kZtK5DqsREfuDYCmAH zfarhO?Lzq@Y`d!fnb4@b%N58Ye2dbSuBQ5Zrd=o;%B~um-?Ni~ssEy=9e#SmIC?0* zl+ z8;`W>R+`XHvqbsM^)lh@Td4n_y;6K*XHzxEM;Wb85}EvnP~C~>7h=a~Foo#1!%fYP z5vpS&5*$r<74^OMn38@Zm#E?%rgk5*V3a=^aFs2Va#XU@rpm5xW^Rjv~xG>a)Q3WwGkXDo9wO#OU}i7*LKl_5%L5E#I02L5H|Ox}mgiV?n& zmlC0>mZu+re;i{n!n)REpz{!jT`tkV$wmXsM( zjp9Adh`!L?G(18sH&Q;FZ~Rk-Qa(FW(r0r&Fx(4fV-EKNOt~l4iYkYDFe+{`zH1{c zHmJTs3yOhzAf-yBz7Q~> z*XQ&8D3Z;d-eC%7?GqKwZ82pB{i5o&(5A!TQ1c|$Jc>JTa-jBxgT{Hglc>g(RL81u zY(m2np} zR}bRP8ALOZcE)pAt|J_z?y+VQnjaGt9nsS?{AMQa>X|7?HDgT4e~$91o};{$XNl`^ zn#h~QjEi|KPm^o-7|Aj0DGlEx>8E$Nz8xv)>hvTtWtr0~Q%x9f)kAwy)nfZ?pZ5##dPTt8vF%?{DEm z#8(xjXd>NG5vpcy0M+Xjo5YLU(&3Pprlx){)RZse{ofoUkm)t0C&&dw-uSb|`(=fw zyeD_Mwl;Bs;v-H}_5E(wr!|s?@MZioQT6Z!Q#ORK{03g3TGC@?R!m3g>Zd*C;x}YD#S$?E1eY z3^`%F|*&8}+d2N>UgBck$O zI%#U}A-xvPBVr3UbDiHLG5ER@15n*6TvxXfpIOYf zSaOf_HKjdxj;r41P3W1kqVfl{nB4D=@_zZF)Q*sRjKKO+rugJ2wr)ld|GRM?^LL(3 zT@RZ4TiNSTcP=!spEip5VXws0x$~~`efdWDCVn_?{2%QU^9d76#BUtp>PKb`if^A` z@^9m#K<*d)l_UPMw%)8*90-2G2KG`&$I^pK?Aq94hYUZpuHUvIP}G$pcbt zsQXt__@Af_tO=EmZFiL4>o@r$`-{0}pyVU>#CTIUlg&o>|Fy-$2e2Q2zy}*$XJ(5N z;81$_f~)gNDOJCwbO!kYIgt?gh2PX)AyOPFYRL2cvpC8U{X1b^javq`aZJvZGDaZBC-Sij3_v8qx8iErjs_1MA zo!e(gA&OoYYNmd+K$Q3Uz9!t6Y$lZTJW{sJteKLB`m-xd!3cK6kW+&WQ-_qQ6~w>%3_IEOCA2ydZm z7x8r@FraEcCzD9pC#w1v7fkH%MNytF6DIl%KM~ZNBZo*2!4aKIeGf|LP;x`AtH3KI zsQoC%luzerrvgh&IE~<7Iku^AbNK!-NyA-ylz4ZV2 z)Jt!#bEYUv6Dm~iV$%qT)iX`?1mXY)J)UEn-&355sfUPWp#Et-dqk=>nZQjsqRQG1 zHB~W6c97Rht+4th8`=nWahQ*HVY!@2tq#0DOg4c$;(jP?AsY(87peb4$&N#=HNB+- z6>HlV-#r0QWp{DoLiwG?Oxb`5qDtmgn9v_s8zB5mv#E0K=^<4pt7lON|4Xdr)%Lcg zGQ%e-e$6Hm{xl%Ud4iy#n%&3b%{wZp;*xEyDPEo#Y&;E~>AJ0pL{RYCex|U?Hc?I@ z%E^UFZ_vaC)4LIQhq}1V&6hm&BR@9O{hESP_)lyy`FD_5gglZLmG?6m%ps3I(>&2j z@{rgt)D+c@5!Vq$;(z&<4>n$BBNNPx5`BhACK_r7nUoifa;Y2@e|8s>9;dp^)G(@x zX38y8wwc+C7}V8d962E7i%F89jue{Q1v@2o0STJ9&y8{|Na7#h)HoCP6Pw1UoVdx9 z_F;;J+_M1_Jbpw}y^j?w%6H5+iLZ$9!I3qtOQi5Ma`_;*k?X03G_>U9= zs~3sitEFMrlAe-c4l-hKek-1Qizy`S!b%vSABP)XOGiMcTS!_48!xnPG;HmU6kq^cEPc^=K zz?Bk~M)m1Q*ALuOP*m01^#UgqLXVRZ3}gPJ$@8b7%oDs=xy<1NV$l~}jQI1yiqsOaUBP4(=_qVfw4y9%dEJ_0qX zP2Qo!+!yd;AJ_l5))Bk6hpFol7v;Rz-T1m57FB&~ACrh35f#6Ulkzs+|5NF`V2VH4 zEUw;+y(NDm^L7I6o|5YlUrc#Sz6swNLIVM_Q^Rgf)vlrJai0Y&juCXdLt z@_showa+WweU4Z1=ERI=>;P)|uw|3$<-OuT-cS2XeBXRg(PuWBhFbapBju%`#-GhJ z8|ihCR%aPeKhsPZq&Fqp+Nl+lx6ZB0B;#mWt9^|4g53(_E@U2Wni%oi4%hzuk_-O_ zJSWudHLi~@h+pj^SwS73JEm%xXDa6Ah$v$9hz?%(^#RX^a-vk zDi{&(m*nc-R^llAoXtoi-#pQzZmkeib$QH$rIV<}^GjV1@aL%A(_NSM5b*Uq2M9 zC3)able+Z~-zXcJgHFOBe0H9jjrwu4DY%v?lFHic>X(oLb>se4gBiIAT{ghfWK!3s zGV)zH?3k!27fg0<=KeqKGHEx`{|6~QPBxX!CEKMEMVBl#;Sbn;L;U|JwL`rYK4yDuiYaG>$W_tEj1m zn@nWxbTN73C8A~yG?i;B#5^#H1Vemu#MDhVB<4!m>#A$`8i95pllaRCQQqKklc?z- zD*w)|iu{k4neUOoD7%tHoV;o;{PekK!RNsReU|2)=)cqJoTR(oV{GT2tBdLIDc3m zYU<&!CNh_*CFR}Wq8nZWm49h*P1qup>Z3waH-XGXbtQFS>KcBHz-1>*$=2nf5`W3! zp25Kz>|`o)c+{fs2bSW*%hs5NS-V7~oOAih`|?rsmGnRDb**4~#T;Nf zAXC!WSuxufkwNDqWOj@=dcbMd<>w_6xm)>s)Z5&O)mEN_2t0Do)ZLvBGjgQVp`v<^ zsehC5JNW0+oBE$ei}L*-#`DJ-Q7Jpw=7;Zp4tsR}+aZDJ3ryK1i$qmEx51RA^b=LS z`m_nWyG2yP3+K(l3%sIIv)Y�cTY?r>ge^htx<1Q@LxcsQT6YP3$iIrfRs?l=n!8D!pv4 zspz#^RPhaBO6Ku0BUDg`s_I`|rf?Hx_i`cFz-5Kh%zh?z*FOG7JnOG*XG+@< zm_o@s?*Az7I?q(p9v4-=;i##c&7uRrMk?u%n#tcB^{?^oQTZ%C@n;>T(tjp_45g3I z{|CWsT}^f~FOF{BCfW4&&fe9;I8m}UiDdsRAz6=amFz>CC2MJ0ut!|ZUZA1VX?c#M zK0m}vySJmL&|0$P;Crfr2~Qy+intTpEOC?_pz|H-HuJPYc@EPu6=oKL%A5Epq4omZ z(vTnyq>9@wGPQjs(NtuTIB!iijX(14H#K&T>%A3p(!qZSOrq%i4aNzvgRPv~pEgt9 zC!~VN6CGSr$p2K+n@lD7Am%;-6RMfJ9O~{H@4EaXk85<}YXsUHGl>@_(1c`yc>OC) zbT~8V;qx83T+sCfNfqNw$(JWYrA^OjMaPTj+Vz4f0y($Sx6_O?#->g#LO`ly4E)D+o{J zGeo?q$&`J;9uVrb?=$83%#>Ahx2YUR1{7+4W>*!7mZOqzI23oHC;+wnhZ*NxFD;ab ziZLlM_s^3?)f{x4>_Pi%w&jwDmLXKjj0nzfQnWC2_b3yY-A+s{ADw!XY-&`lY3CZx z{lAh!<%exe-S~-Ou4s`ubv0ija4DZ5LJyMJfW%UkUhtB(W%7DcY}}i_QK2dB6yYxv zkZ+lCj!F6>%_vcFdWA`QfoYRk+_@F||BIJM z&IB@>)dSvEq+dR8N-{28;c820R$V&4Okq`QHt+yaZ#wMvznLak#}`P}SKTG)DS9}X z-i(+>XAAZ8fL2QxF^_H*NPUU6>XFmL;*#U#Ex!F7Iu)@qz5sO`7ZIPWm$N8^uN)9U`Nt`AO2qxzaQ zEGVLwNrj>{#u>Ix)YN9Wsl$mpL4Z)@PINswmXl;G*Z6GLeTTWu(LBr4-Pgo*j*i@@ zR5yN&0Hu|xuEPma`U5qj2tC-qZzH<&0-O4UGKze;+hhbcM5EC*@N4R1vsi080yn8_JO2b6J+9_am` z?&HRS^q(iR+RBKF%xW;@^-wE%LA;K&=S|j8Hl@F074*|c*M;qpjMUSvX@3id3ehJ* z`Sx*sAiRvcK!-zo$zBue!^RWp+nh7yC0;QNgLwxObA&ti7!bd#w@G|=NL2APvrX-g zEKyD~pD`N$NA|0tn9Y2$o>#WvyA)I8C%H`zp+~uaz!_a(rhZD6Dk5{ZfhZ3*4>f}( zQ>a{X(RDvtw(3JdI7qDAW4wV=qU!i}<}%tjsqTD@KwBoFh(;$d|Bp_R25ufI`KJxW zf7e1$>Guw5wVV;72b!F3$n#OBN4MI^xKlD#WVC8##Gj6s+)TD(R2K6{mC05OvO`XQ z!*NMT+uzE|NQ<@0rkiOyM~Vuya3;fdY`qDuFBTPlb+ifI)>~BluX$Mw%1dc3ryl8I zD(|EfJK~ouF^MyT;85)Jq}K~-?_j+S=c9yaywFZe5?du|f3E9$c1loGp__;sB{XxQ zamMiM$JB#8Ok@trIOSytryip>04o1d?CodFyr)r-)1?zT+u};w$@@+M5rHa^B

UE>hMFGu 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

VEk}cs1rf0s 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

`fDf!~H+Pj-BFqTBM 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˭uEo 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 diff --git a/C3d/Lib/x64/Debug/c3d.lib b/C3d/Lib/x64/Debug/c3d.lib index 69909cd5d0d0fa308bf229df8cbdc000ce651f06..396c4d369beb3b475b8bd7e6e959d3ba4504e990 100644 GIT binary patch delta 1091807 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¬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�)JqK&PCNr#w&cGWChkXxE@$S$F+r! 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

QV@mPI}-(MkeWL`7O}vK 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

~q6uvj)( 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

{%3 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

_1aX2Er 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 GBm5t0WHdVf delta 925619 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

    Af# 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

    olb2yxRWqZpWmn{^|7yc&vUHFG^f$+L;KyF)3 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

  1. Z$suI?FzynwHIp-JH(yX&gVz zn4=r^CdY{1ar(q>8H!$06w3BY)KBj4@_e)6r{i-{J%L9A2IROvwY)|F#jvD z!#~(RAyMi3NOn(_E+&tcg`_oU4~&WEro^u7zkz$v)&a6)F!yqzNpyL#in@wSCwpTA z^M37cZH~MWRZUl2=eC}={q{Wetv5dkTeWY&~`PT*c4%WNRBL;BFj>_w%Bw-mh$y&PQ~Qw>uEQx#JKQxn4_)F7h?pE6w#-y@9( zLsR!DDcarSg+#;X3H$~81&#%3PmL+iij1-yd6n{jvIkLE=ukXHwU?d1%)&VBB}t%T zuv2h(a79p6t}dTmKBGJk3 z?=txuNFuVd3NbW`$@Iq$#&?(XkPX*S z($6c_6R%?Tm2X#jwL`Ua2@?=Y)PIrsa(DTJa%Z`#{DT~4w4-0ks>*rt|KwxkJLD~7 zA=z)}rGARPj~|C0kIyl)OrLg`wsE8w zsTrvqsTXP#!i6^(N}(R1k&&j6>XE9EfuZJ+(Gf@DbEsdaTc|^5NTOw=5NQ``6KWXw zCsIGsCDbR>CUTj2n7SU(4c!afD%8qQLaF)})P?E98AJ~?&D@&rkgvzB&pk(zDkdw& zDSm|Oqz6ml^du9ja4WkKJ7pemswoGnf;kTDS;h762-QecA4~}YVvv}=m;o3V28DTu zI*#&aUy+AsKa;;QUm9K*o}-KOrP+m=MH)1D7j=|z7iKI*L{rd)_$HMocnVB|hT4!j zkPqsq=^cm}X^s&z-XI8aop|@@Ld7V>5#nhgn#3mA_?yz3(_k_fr^MCq@3DP64g-td zg8LZ3@Ngwjt{?2H ztcI4MUnHKYO^Ma+Ny>iF$@=N)6$CvAE!s!9%sL&N8qFp~i`KDkQnVg(ut`FLZGhe2 zxhX2CeyD!3Ppf8UtB8AYy2@7&k8mQiZHNn6SB`Hul$bbrtNuPhHe_nzdTS`hL`y{U=Tj9Uqs^Bh^Wu*0K zk?88gk?ev%mhxY;L*TfUE!oRGnW!DzOUM(~B>!NCpjkl{u?sGO8-lB)t|l|c#-WcWhg1AM8MErxJE3HcwuQ!U_PvSIG{2k$a!ET~DAB=>_gp8pORu>rCoGYEx)k z$SGC=?ZKJpacMdQ529$F;S|(B$pa3U5g>AC0rnTxg0`WpXbc*QMx+0bC!qcfipyi9 zC}|(38)*&dNoEM9o`8>e&p0FLV0o`=BLJCK6Qy57o~ga5qv;y;6k~+$G3q3$Gh(Ch zgyFiWNP@qjp&BBPrOIGOFTjCj;?P4kxJ7^gbrwqZh>xzZfkTDoCOY* zPNhE-TNoQs6T~?2GFIySh{P!YJX|}DG>16aW8Q|K|}}%)yLo68oMH&@Ko z+==8O7#&Av)Nz5@+UAQimZrKNp~&Yuee%qt>Sux$8-mABXuz5f>jBTDILkKurpkT znG#9Z3&4e7Q(S8tGh)G7u@meELkB`kA^$DI&~*w6hh{B3f}vNKS#EKF<3`omh?Mzk!m@$Dz=MaN9XC5P47$hFkPba!*# zbl3A-^YrmP@`im!ebxMH{Xz4Iz-$Z4I>7qVTEj-NEVl{lgYBp6RUES%mmN0eG8fC; z-F?el-*es5*ZbHT@g4J3_pkGpn@cAHgCcVQJQTBDvwe49eV={b11<0%1cLx5UHn@> zKyZi+K{%IUhLWg!1wIJ90X4j}v~%JBH!a!8-O1MS*3r(3gWQZ{7k3V@D^(*$@{m36 zr0=Dz&09jpLJuS`Pcih?^E^2rG$u4EbY5~(iu9nQXz8X<>r(lEh&voJ!#&$=LFakj zi{y>K_UMr4dP~9C*S9S?IBGXtM;MJqX=UI=U6jxPJx-cH$I*N=5B&{(pR+bn)Xve~ zu6SMG6nT>Zhm3?YBu>4 zW-?R)iupoCApr5o{9>&Fuec+!J#sLzH#a}mhc}q+=O5uw7&LxUelx~R{#}_tu#C|{ zvRbmnYG%&k$K=-pgP3?Zn>S0K7v5p^6>A=2r>}e! zXTBK59bC3t-kN*6tcFx7naZsvbChH+xTEuHSPoc%_nQ{y{i0349|C8nJCnoE4b(nV zAD_Qm8m<#MU*R`0jK2-*j2h#B^bX@+BO`Y*{12}KovP@sAd~jMjQn2-Z~RagAD72J z$9D1HjMovk{;LkgXCgWw`&g>+Ycrtu62)^%J&IT`fiY9ET=xW3I*nRjt!_PSl`wU- zo~Sc|Ud#*T4@q(=lWLU`W{J^$wimYghT-T3@h{OHhRMPAhS`R!y@h?1{fvgMt*`lR zw>wIXukmdTX8D@v#Byh>B1U%J)nqg+w0g|~avzOL(=0Z?dDdxk4RT3^=a~A`JJctX zTG^lS%D^lojY(qSrK?4}QMR2>##$@v%KDy7yBh>MDG$J(ByOvL*?uPWLr(IC0Y`zuot_{PWg$ET)xm+dyJiBFI1H?h>SeG`&M z|7)?!KPiEW#gaXemy##TBGOv4aNxEv7+s#&ot+oBA8iw$N``WeCaOhOB%fgWqVd5B z^V{M~QAJrT6SpABMR>qj|pmBi_Vage$#)kUt`gg|aMx*Aj?u&ju{6Gmg&R7Rq z4_g~s7aK)Cu@n4l{0DUi#J==Fq*)PH)={ldPB*r6JP9RacZ2tWWa<6jM5$A;T6s5e zFS3<yMr*ddQl`ek*#( z&T~e~_i|Q?zj7v(ZIgq6M`g35O{GT3YHk}@Og4tuO!~G&M}-GTKLoo(ZY2cL=Tfhc zZv17aUYJ*CSy)wAUO1vzq*!0sC>+h0EICd(&22*)lzN@b*zK`Tnz$$e`9`OR(jIJtMJd| z9m_|9Gr?JjcI92l@0C9(zn7I|4N{ru7~+BQu~HqF2W-Uf$Rk513ug*U@-10dE|p{C z7v#OrrPyr$fRbPRG~1);e`aa8K+&=8N`^_BKJ0JC^Ze z7xP}w3yP_V<3tfD!KY=>$w-_P*T;XyKJc;(GJYoxjEec%SZSk0L%d|^#~;L~6^APw zxRtn{{Die7<&&(V0LdICc_VqM+hv_*724`CTib4s_VHWD{|eSKwQ?tKAFY}NSq@K? zrRt=5rd9|>rPNtL^oFh6p24>6kZJdQI-K8(1Pc!iUt zMRIz>vE*%WnmZ(Z&#=O4jaJJJOZ<~0`5O4J#Ch>c@%gFkWtTIj4O`-S<#S@AeSrBv z#-J`&-^_gV%}y%(NJ%IEM?WjJo>WKFGJ1eeg;-y=A;}4{vs_|#TpTwX*H7J9mX?+2^LneE zsDH2h20fuar9Y!@W@$+u02EXPhyVfb8TtX)0a@`KFgI}n{0M!5`h%0fv&s?RG;l0< zr{W9r6&fZh9WERxRL56WM1%3*J#NtinLZ;vA-)C1S>V>L*4NgI?U#9)4R7yd-)F}< zl8*6?;|{s=wX?cwjtl2*<38ukdX9S9crSTvzAe6t|DJ!5Uu)hTm|#I#yIbE{bGAR` z88)K5kNtoh??^i)I!-#2&bQ8*u76zwce|4Ng1f?V+|$l`#q02G^X2?Y{Ce}wz+?-? z+ROUETCjnZ**1#3zx}YCT#TcD{GkcFlK@+#TGP z+y&1`PY3TcugkZiQhWZuzszqm?+#40;H-VDpR7e2+%nfjvk$NzwNo5<$27+|htB!Q zS?0xMA?F2{0G0Abtp>n=+)^g2r5#88o>5Pwe@Z{& zs~IL|mS+yAC3X z;l1ZQ;B%V~2PjsoWs!|zujRSq>FK@iJ?Qh8j|65|NY-y9E84Qa#agJjS znX~lDSj`_FnW_eM8GO z``U2R{)PUzc#Zi^SuN?dfLt<-TjC9;-{C)ySp+K?|47zJ)?01Nh5V$PB)`S&Nt-J` z%1S~9>zaI@a5!rc`?=@=J6;Jtt}SsEi;>*nWvk@vxcAEHNR^TqTpO84eo390S(YJ@ z@nizIr@%tL!##vPh^ATnKAZWAHQ=+G2k=JF@A4nYtb$dH){^y-4OTmI5kDm-%Wrdg z(f$>nWCMgw)^+)Q;Rx1b_6yNNc7iiNzK*j*jN*&Y2gG|sHJ}HOGi6Qh5N{DNQ`Gbf@f4v2 z%T13ENz+S2%Jd4+9k>ZJHKCDV(|-si00CyOCQu8w1-t#&>k29kU+ob zJ|a;v#Z4~|&k=QjIzVmUKj1O&2&e$OU^(~z2!X?ara%+G4L$=d0~u4=^ak-75eMo6 z*MKWP5=a0!zys#Ni@-OqD$omP3A6x=U~`}uFc7E?)BqX+-GCGjGCf9QfHZI&s0Y>u z>w-1FnqY0P4j2G~riX}<4X6h^0j>hA025dMEZ}`0VtRrIm>wXyVQ!!w;+Noyz%XDa zkOzEV1E31v2cH9%fWbgl;5!%u*I+hadIC3q5;y=H4E6>4fdj!o;7ed7W-VqFW*ufV zW<3T3V8AKN8O%A%Y0O#7dCUdOMa(_SeavOdRm`Ol<_hKx<}T(I<~HUA<|gJE<~n9e zYHMm!YICX_5kx#yUQyOE)i(V?{6^F;iA@sI55ytFVZ?lB9`qm73Tg=*fSCCS>2^dq z9;O3zcpXtk(g}3KWpA`%ok-`>HGt|vb)l<;D}`MMx6wz972g$V3J$8BYNPImZI9I{)-Kj4?#&1?2Q$<1 zQ}e^}L-IrOgYz5m>+?JF>+-wvVBvTER{l}GQND4$Vg5n>ZXQv1k$;o_p8t?3h;DYh3OG?I-ZBhg4Oel>hENNMe$w$NedtFZ%Vg>j{Exv?p! z8L0`WF)3e3m6cQFK*w~4w906p8mUl4v7&9RO2w+&%G`?FuH5*{xXf+pE$SVr#b`E8 z1ttSCf$6|RU_9^~8kQTH8yayU&ke|xa{Y6ibDeS>b6wJ% z)4LIqWm9Bdwg1Y#YyWCZnGTBfihmTX6qd{-Av#Zi8(<{=e)Aa1;#HR!%IXC=ocuv?Bx5lIK0pU`(f4E=x zVE8~75r@aw@w(wU;o4zgTo5$ySmJ%Af2Dt+f1`h* z=NKR9G&~GX!SBJX!R^Amr@W=Sqr9QKh&+!xi#(0MqCa%sbuV=!q;#*o{HCw{}XN%ZW*4dn5RG}|5e1&YMch=#7)Ib z!>z(S&z#~P7MBp>Mo%!qIRIR zqh?uWT6bG>)~t29^^o8SeSGODt9t|D|9ts`~qRQVSUnXBAUndbMdXjNT7$q1f zSSc8gDy90TMyG56Yk*hEYO)8?wKKIcxM<}EKok=dM!VQL+dA1g*gD#7+V0!#+CugK zK8VlaYa7}cY8hT--)CQB8E__?4yVDX@K@oN;XCo$@z?Qpxcj&_xDU~ep-v%Ktc#(u zVX$FzaC~rFa7=J)aDR|ZU=i#D8^KCgVp?okWV)QblrCLNUr8IYm401(J^Xd;HSJZc z3h%+sFsw49>`8mV9=FHrwd@V-_3ZWSbM5yC4+!rF_X#xBV*5h-0{bHS3;PL84{~>M zH!@4h*4EP0)_kzPw<{e6ht8pObawP`ba$+De2jmH!xPnslSmUu8yssL>m0uvlyY)8 zwNy?j?_TaqJJQRdOQK7oyP~D?{^ifh`;~tvH)n69?b%l-bIcNp#?rA=tac3PL^$Ej z%7Z2+%n3RHXJ@AqaZ}Tu+>hLs9M!}$VNFC6(v)i&Yny0kND7jUq#_weGV)*Y9P(W9 zZ1OB}cTEqC9ce*2kXED(S#l!H$mOV|sAZ@|v4*ksvF^?u&YsTw&c9Nsj45kPXhUdA z7)UtcJnSrUDP0Oz57&Fm2ThUOlH7vaklcXW$93C<65@ndAx4N65`=gmQHT`Q6>ekh zWbR__U~XsbX6|8btz3(!QtDJN)dk)e-WA>n-VfOq*&o@bgsed15JL&W2$KmirCeE1 z-ou^4-NBv3?ZutOb*J>8jG|Q5ISn3z%iuOR4G~ofRaEs_`BM2x`9k>@{{#OM{|o;I z{~J#uPzfXgg+L~}3zL$>WLa`icyV}PSd~;Jy~!rw#^FX`Yyy)IChoc(xbC~|xzz54 z@lu0$hxo6+hvkRlFXfAb{|e^`=L;7I7YgqQdtjQeny?zM8nPO(8neElo3ggC>a#v& zKW6c{pf2n#cZb|TcfdW<-8|?C;z}Vx2p{sLr6F0UEVLxkF4H#CAk$jeR@p{5NI4xo z1wIWv6+Rh054ixj2)PhBANeoxe&TN8ZQ=#x8Ra?Unwp}yuAUW|7Lq0833pZR&&h%tz%brR-$M*gIGUqWhzl zqoblDqjRI7M0uiWqK~MzXtSs{yC=IlyBoU~y9fIK`!wYYtl7}C3 z1xJ~1CakHssaeTXD^WYqHUSxnMiLAM8%dl?97Q|_e}y>%e~EdGIS)SzKMg;PjAJX+ z;MgcOf<1$bVNW61nmL{so|&Fmp6Q<1mGO{io|B>zBDT1ls=ca%YMN@LYLhBKh!dWu zo~fRzVCtu;;Buc#!3<$$q)g0CK zn$Bjj*=+S}=R}u8X?WsSxMs3OvUPGrcx8Bbc(Zq-caxXw+aA6V-xgkpSb=z(*_zps z*_>hc=)Q)&Uj!WSH(_shPk48DbbM5Nemoscg?$lkWKVooe0ThE{0sF1^*!}7^%M0Y z^&J%%nv|N58lPH_@)5j*Ug4hM9^o!!oyt0w?I=5)Ia$hF$!s@lGi)`SF>DO44{r!B zjW3CBjn9xzm+zBT3s((S2~UYlj?IlV^ELG~_qFhi@ohEjFzqyLH|;X*Hf=M3<~x~0 zCZ2hiA*rcqvYMi%sf}ufI;4iM zWNA+FukVlVm+!Yv>3=MKBp%FvAbuzw#79ZYev{wm@9h6ClF#+__ws-8clY=7_wbKR zV5oR1nu?>Ms7UIV1eS`RqGHGxJ2ss(gEW)0ffP!XC!u7=WQXM7IZsdduDrN$7KE4hv`S@P}UpuL`#_{0RUhKI-CxpgY?CT zMTu>RE!mCPP1(oUujw!8Zo%Hce!;%M!ND1UX@Tj1DS@eh-vKQ}N4Xij5q%x~s{~Yk zl>KE*b**#_0}}#`0*wPM@)50*jFVs`7)e!0Uv58cf9_H4p~Sw#{>0wIfy9jj6fHzs zMb{HHmIwz48wgfnRbobZcGZ;0JqzeTa?0!mtqR z8tgjk2J9B>ChRKg3JeiM2a!QkkQ>~Y-jUv(-c>1!BM^B+BC!^(Hm(QGjq~6VxZb!C zxIVa;I;Bpk)9Qq}zdEkY%W|?VmHs1{LY9%erT#~KOubKiK)px31KtC>8oL?0P&-pQ zQE7U*o}#DfrFxg%t#|4RdWT-2m+Ni%o;s3#ZN<8Z^%ZL>K38y+93?@CSJqIll`JJi z`Jm#nd{)KGicj*{6(8l_LY<*b&@o606T?I>1xya(z}T>+)W*~%)T+it z)QPe^2$$-)vQ@EVu~~5kxC`6~9zcwNMng9$7m8ls6>uJazyeqVtAbU)mf$~NEAU@% zE;t*k0CQk>up8I|>;?7&$AJ?{;CL{ExT>sU`hzGl9YOpHwSeULiRt#ljk4Xc5i*Ic z9(1kH6zeg*DZDQTp_gD5Dnehuw#CJ{dimc4y>Vx(R#BLlo*$mynBS9!75?OJ=O5>r zYOt3AbnWVh}W7 zYuYLEvEbVzA(c!uPmK`_OnoLDOxMYj@X@Zed$w|X9mC7)-S`9C$7ol>grI}4)O0gl zz~9iS@iPr+dqev}0$sJlep18H*3o>l>m5BEpW-!$Xw!NJt=yYl9vx8rvHVW@HOd;x z#4yel#9NxUrm424#)VvgY8>n31Y|5(JHktq`%n+b)iBl5+<<}6~aTo-YBgTzoE zEf1~Aw9h=wv{Ne4KT-D*H`H@Nio|^PiB$LS2qj0=B|1nCiN>gB5auGrBc>o62r22L z8U=eV+)UZUDjkfDj)oI`MSa-kC<2clh&Qz`)k#z_l3{q*Xkr4((aiOn5_MFqQoT@} zVDDB<%!i-?msF4~9;3E2c2PM_1pb7*xPWql>i zup>EC{DRXxe!+k-Re0smT()bXR&rf<^EL7sIK@-uPE4Dl{?G zwd_=8hhbTKrhICwrEiZZnW3wlY6Q-and%#r1pNPrE&f8TkAGYO9h*f8Cp#s_q0S~7 z;eX*WxP2*-Sx7!u=95;TeIdcgmyJl%b7MuIclL4G7X@KR`jW)vZ1-UQU}5@@F9iHxoO_SalKUFaDggh3cfja>fxD4n9b%b;<2}}v&!kSSh$=YGv zsusoF;6cPFXaL{?E&$7c^}u#O18xMi0tv)3Wlhsh#6oBR)EfE+V&EG=4WYY*a%0dq z3IM5hulip$FL z)C`n{MnWT?$2|FrCX=}Nv8|rfU!Uc>JPO?zbetr!~P3D4$lvd z2{#BQ!fmAEF^rj1(e8$fe}PjR8gj|Bzde zN5f|z8zJ5*OK<|6ur%B}JURR<+%?!0Pr~fZ?8+EIhvA2juPHM^(?hpXUBg2WlMu5J zb_5wfBkm&W6V_?wn8M_(=++nvYO!ifcxPruW_xB^CKL8YVuU{7E#aEsPo$5eCy|-q zd8nhQOdu9`VI%?!Iy&<$?Is`M*4CX1_kn#cVSi$ORD!u*U|(S+^lR!P>V2>~b!!Dr zSx>=KevyL;KvBI|t=OO1kJ`-Gmx>2!P?N@lv1zeMu?|!lTIZb$&4GqMgCRDpJ!vor z3ydTcjCrGu+FoCSRF6akyFy(c8B7Bc!dg&$s@BDSibue4&{$}bX};-ZyyODzX|HP< zYP)KlYgTF<8m(rMCZKtweX8B8xvbr*J*C~Qy$yUetT%~5b4{`A3}a{GQez{154{tZ zmAMELBkpLgYU*n{Yo2O~n&n!XMy;8k@o64t|I@xl)kM!yZ&C|P!%aGDBkbtRMeQ!_ zWb8rpA`}=rq1~eWTf!a(?*LZSuUxeVI~E0BAWPmte{2a=m0pOTHpRpjsFTFAQO zrsPTRQ}EG<%Q8oL;m z85`?+>fONX%q5rvaaVgy(?Hur^Gs7kvqEdvXfzWwe$7Me6YU37E%a>lX0^~X!lcJG z#*WEc((cwy!5&gCM!|w7wOh3S?gV%b*kW2}+8Kt(eaI)t@5zJ6&&Vd^YVr?qZDc)i zvl1DgB+2`=<>Z~(&87vWTX7F?UwcE-NZU>GLbFQi)aW#mH9^f|?KABb%@yrF?P=`} z?Onj43aiRg7}YQ3SLJWzH{~bb3-AOw2;DA#;1+0d{yuaTIs(~LN$47M2igZ+fX2g8 z@KdygC_Y?*Tt+S+$HQkM1FBLtydC=xeFT04IS?^NZA7RM+u1A=5^)>(M|D%R8I3}b zRn=hkUv8A9fv%sn-C0KlbZlb!yBQ5@CooaNC);2dN9I- z&>(iOQHVRpzp7g*G^#r60j!zgx4e$Noj#37A+9OCrn?Ah377?&4NJh2@aL#=sP34X zXf0BQtb(kHY?Nu7*@W4Qd4ykzZwPAy`v&_C6M#`*A)rER%Wu!~5j~Jq;7Yg#4k4!? zry&o*%g{n>E$laJeOQ0=OW1qZ2Uuf86U9u}G}sJS5e~uY!t-!8T!qviJ0n4qS;;BV)(}as>P?^h^d=z{t z@)hhUbO^drfP+$CEA#+52OWj%suXk`x(n@xEOe01b7do{ zh@6gW0Q(600~-sg0nZ@Ez-J;4!Heia$hxq%uq0w>dO>e~_nSr)B*!Jrvy)rOYQ~wrHiTf!0CGpdF;! z;FiM1!uG!n(pL=o{fXA)CwO`eyoNdS>Jl1!XyJ z8SexAJ^dZMtD>`_o1%-NlVY}FrlK@QF-tK+afW!7I0}a+8AwJ_hM(ql$>5WOWFnr7 z^WyxtIc|xAaajB-?+foU4`LJ;d4?RX!1M5YJTi)k%J@>gF=mQw{hy+93Xh~|1L#aL z8QVsWm9f?7F?uFhe{0*`EM{%nwr$%+*0zl-{`|LnRX0^nJ=O0y$Fj|0CRs=~EY~fA z1Oo-bnZua%qYa`s6-I?uVO40=82nf~m%t;igiK+|nzVM$Q+rUq%YVpI{3M^lU^7^Z zG1Ae}QPR)SPtuQ4i~(&ZG90oVv>veTx5{i%TN74eRwGtNTL+tr3Q&&;jtbf++A2_@ zLQ#Qe8*3|T3(Ke|R=mJI$3DS6#{OriW2tQ+g$N;H2%Eqqsv3uhCW)qreu@5xb;7m6 zaf$r+gq+$VH8dp+%ffeUcWhxhWba7mMCfenVvJ{EnGeyAQ7J)27z_>phZ{#2@gZyo z7wVbnOz1-J5&VR$#%;!W_PX|k_67D|#7d%3{lWg;uA-``hjfQ^+qFBiT8GBb!_nQb z&au{kj$vZ$NbO1c$p0LF9lb++^P%!kpV0pBf$$9DG~;w*A&3HdrDBnIq(P)%q=&P+ zv+9A5dPH|rw^O@I+a=O9a>RMq$&fSU3i%M?P@=*mca^*PxR^SYZjy1Lv5}~usDX&Z zX0jP2<9UJ}d8^a><=$x8Kd^(yrW^)mH#^)_{prs_=R z7IL4cAE}?JAFCg#H>EeGK}kpwlvufW3)jrmdNp3Uj-lJF9Vr|k94`EiQHN2RF-H@BNKe%eP}bvj4>CP3(bR5LsB-4MPt`kHD*m% z18D}P2Bj)}zkCbg3*!dA(SJm6SkS}Y-T&SH&2OL!BErZ4@~@kd&yhKC&eek8`A63eNu2Z8{UK18`~Fq znSPNT9vl%I9UK`P6+{I81*%g&gg=H2>XY&l@+pDIfm+g<(o?*XygRYGvG(B(;cvw6 z#I~f?q&B1``lkBE`fc&;@$ytBeVbsbVC!J_RB@hUB2B_i#*f2~$4|je#ShaA(G1rN z)eP1glOLBKl^>QLkv~S38LAtq85Zg_S#|aq+`gz+nVH_FY*{Hgxv6w*=??O4@*c8< zZlsUIZ6$9bE3>MsA*;`7v%2g$Q-VR&5~8MP1WuMfJhs5lawOT99m@ozGp!8B)DX{Y8o!O32jwFPSmRu`-)SXr>5V0l4@!uEyj3fmU8DQsQXs<36@>r@TOh{Az| z`NAnhV~WP+i$)hE3OW?DFKAcLwxCNv=YmcJ9Shc6~2Gd29)^rZlOCmxx6ux3!CtV})$trSf)G7UG{aV9*cuPv7-mKrM-=e>6 zIBzhTE}5%5P2c$rv|7&YIWUwUCNTQ zXvG=M5$-W?6Sx#;E_}n(iJ!ZF7hf;FR{XAXl_?f%qG_s`q)!B!X|@&aFKUZ9r97=X zs@zJsOsRvKmpzm1n;jbRC!f|^VElwMx9#MTh~>0L@9Ad9Wsa90XRaIwMdPwSl3P0N4J!? zjQCgI)700r1C}I4tMhepm-$wvoOw`rK&jFxbqbwaXVB?&I-OS6O50l7Mb}x^N!L-= zOV?A^L)Tq5Ogmf~5WQ!$7B>)2#-cet*m5nPHA*DhdD^+!!%8%}gLaJehvvIxzIKT3 zgz}_vEM**JkD(sAnr@yBr|B;*%zlFDSzLBv@ucF(`Qm!n<-jb`e$83s8D(Q(1K~^N zbLLg)HR*Nf4XHRSNxxuLp!cIcrQl>XxiEV!+abFkdp`TNRHd#+;OQcV*pbV1$~_Mdcb)g#RW)i}c_@lZ~=`m6d1=Y(1+!EqOHZ5gKV2ls?Yr1>D3Wj-g10)o`R z^e7%BSVN;!`c$Rr*O~>Y&+d30BgB|ZS5cM9!<1K)2b9m0BI+#d>tqOF4>ihMNR*|5 zspq7z?r-p4#aupKOVHx9L@ic}(XtVI#1HBc^+oLC zzr(wO)k8HxC@PVfPI^nbBJ9W&8C8H8_~!oZ{^9N|=+Ar^$?pZ1fQvy++3Luu$ePH? z$VE+g@o%tmSr_aDOxC!Uc3Zk$+l4y~QZY7VDiyyJdm>v(o1hx0hodGF7QqYQ#qbgs z>+Pt&se;5|ak1FSIZ2#Zwa(h4a*~a7s%jr^z)MJaJVfh zjwLP4ll~Fsked)b;nVo7L{k>8Wv zkUo%Jk&4JQO+QgT;UDk@@N?mp!cT=2yvg(+lg-)U-0Ix!+~wTu+~M5g+~(Zr-0SQY z*{|BG;xmqBk7W-;Yz4&yX=rds5k!FUc&HE}aW^~|Y3FF~z?GSxWymI(>Y36^gN$BnlkLgv&Be>^;r=Oht9GjPD{q}>If8f26Cx!)=mx$he88O~eo8y*`T7@9bnD5{GJ*&qEM{LP&$oo$?Lovoei zoGqNKob8=xJwyLer_}rGGXLw))sm-%$yQ$8*4j4BKGD4x_!ydKW!qZV&T}`(c)%in z9LQp76c^h`0Z6dPIW{nj--8iT8ki0%B%H?VC*32}u)gxRj24zNwrlohj%}f==s9bQ zNXtGY>do%!{x7&)B@o|X|MFlZO(b==i@AFwKe->hWxnyUE2f9OHRg}94nY2TI-gvY zT$rrl0bNBdtPAHtyYMcQ3*#cV+Ga}mH%y;OI?L{t+}1yn_Dc3i-qcTKJQIY3JDDp4 zpLoOQCq;|dV(|m^Pw{4L4eoaCMu~ve3YcM^&gUtIE7vNW))L`#<~LqvdP~b$+sxo~ z`*R0Bv^}&VWL7m(jm?X6?9-w??8ll)4^Glo{Y`z9b5h+@Qk%PkyG8QJTg~^|RA~}x z9{JXqZ=|Wgni{H~=BN8dqse}XpO@h)M<^|VbxK$`Uz+CEwag8MLOVk{To%>ebRX3d zO+WQ_^(nPIbIK&q%r?h>0qMi(;Q@NEmS!}60^>P9A7TcDo0(muyQNQsQ&=B)L+KgO zQ`T{jP<)U5L)=VKjk`g@=5=s(barxj>?MNf#bH4gK~H8&;TC2G;a-8sP7Iv!%S&o$ zCs`sMuNM7r26j;*Eb0(Y~F4=hb}7gy(4p&sHIoU{IMfu7=; zoOAx^_6cs0zrAIbYp3~wwUuS3YnypQzz9vUa&4__TVw*@dHSK@kzon&(_QJ_9N8<4 zTChOGqOubLN;^LA%iYM?T#>IMBC{oaslS!$qx+M)LVri!G}$EC9{3J^BS^4PtQ-&E z!%P83!~Igy6!4c8VkFRc}$sFwa=eNY8lB zC{G$y-Siz*Chj6G<(&5qLtlxIScABK3`pa($ad(rfxyD&Sp&IMd=cwDd{zHFdqMw5 ze_p@T5*2or?vrk}j<6Qk8rUj%fMt~Zs=dDBo+BLcI|Z(C_XGEQaZ}Gx$q4UzZ_3xu zU*G&z)&+1$ms$2pcUXV(cCd%xFpMYShz9o#@JrSlYMNsCjdn)hI$zCvC7j6$i@vZb z*=C7?`Ju&7GwaU0VZ&TI|gXSZD?p< z=x$g6e9Lw<=ael}f3IYn!K~ytyQ}Z1hXoQTf`HT5|1p}F_gxi=$LqxU& zs0zX+TL@1uPck2u^h`#WdUr|iJM}lUm&amH8dr%=b59nYEW)8a7I8R#l1^}4wlCBV zdRY`P#*Hy!)EGAQE53z`fWg?Q!c#?f)Tg3ayl3Y)@J2YF zRZKw%XW3`lIkFqBr06%Rk*uls54)$Qy`&>oEHeZ3eLC3>=})QHKTLp^C1o678Wkby zB#+A_GA#Blr=z4TcbwI1Z|-usYsgx-YRcAT>jqA7t?rAK)0Q#TCN_t=kH-S+_fK-a za`(=AR(MbOYi4R>&TH1mP8XgoBA`AO{Z00OdO}dP7Um4Mi|IOTA8it?2JJYl8aZej zrHO+fsH!fAL)MP?&i7Y-|io6eq4N(Yb`hu`X0zx@IHr(Ds;;BIsL(9 z#V>*{gTsLlKr8$r)?b#f_)+9>qnN<~0(|vq7LqOGUV={) zhzq5K0UQPQ8aD_7K&wra+JDLC;3MSvY>V(j2vugr7nL=|jMm&{hVtqjWj)LGY4&Q4 z7tnCCva7Qzvs<&Lva7ONvSmq^M9Vdx$sCJpVB`ttEAf{!R{nzNDlppuEky%p8Cs5x zDz~TA>Jn1%rXD7ko>YN!Xn1L7IdbLD-gj z8yrU^sPXFSk-yAK(5l1|`i{uT#8UbOS=hT0Tm?o^k5w+yZIrOMs91$)hAB-xKp#i1 zH@&8Op;V_dN>x(c&^Sm4QHpAVDnqqJL3#aU$gDQ07v>sc*7G;;H}W^}e=+_rDj9zn z{}{g+S?d?!ci}hTPT^kRKH(nW0pV`pe&H_R64o+STUJL_CsqemdsaKva>r~ZMZ^%% zMKlpr#1b(@Y|#PMVb&4WA=W|GQPwdQDLn`LB>F7+BKj)Y%ihPvr+I#XKhO8`scYPOq4?Pd!LijibVne*BDT;_zfIt=%wivC(0lp5@s5m0N z3>qVhBt2O#dIWAy<;#zNt3fvt!^v~OlGETB@J;F$SP4Fcwki*@pGw7Ykz82SZ$u|B z2}Pn|qH#XaAkiq%H_^%XK8lJAGp;kPH+~f^Wxch(v2T+V#RA~Ia0|V|0_#QUN7_=Ok+rGLkbIj9ms^0)xvLz0*@)*tE; z707qT_QVRnkFlD_cF4KNzR1bQ+Mq(2pP<~N^r~KIhN>^AFPoH_h8m{okH#Cjn5|ST z2x~ZYu|sST+r&<>No*Ed#cpvi$HdWdj2tsZ%h7QR91F)KUJ~K3RMw`JJ+{&IJN71y z7mkLZ5@&z+MDI^;bzd|8QuFoj!f?%4AoiU4jQW<*rTDw(09z84=S#<$4ne&&onjqh z@9Y=I=gH^6F4_rNR+5?IX9UWT%Js@Wyj^swDxv}I(OjJ!W^jH5-pH@3EJmYDT zRKqmq(?`-ZkZqAKrKOhBagxUoc7{jAL{R(qXzt=@wRrV72J*+RNAHmDl6mB!I2dI{ zbJP(zqo$D?Tg6jKJ}M@%!<3cx&uM_*$%4 zcthL@?u-^ECqd?H3+gN2CpbUOgcwkVy#Fxw6tx}o33QUmkMJVH;#7zNJ&iw$capWF zXToZ0Gs|Av82ep&Q^!k3qY&gA;GX3D<*ngs?q6m$y1Rz%g&u}>#g-%i->JjQgYCcJCLN7;7 zLYk<>R2wxzwNtHBnA$HngEE~^b%yrJB&{7DSAu)XvpuRr~qXe@gT=acvu zXS;;Y8zZZfn5FZ01L&({Yh|ltdAi8TvuvyJ2;c_;~dmed)dS81Jz6-vb`EuY=R#T$2Y;?6TSK~`cgeAO^bZ(dJt*4i7 zzFBE)Y}sLZ>ev$6;(F)#;Jx8nXs!rM08fJBL3N2Wxfv`7@k^fa2Kz?(cH1ub7MO1Z z^!$zVOOg;TE#1r82m;n&R)npdt$}5$?UtSGXynK*a6EGGT!Y+?-OoK^yx+XA@0zcP z|DS)3`FcQMZDiSQd+GV%oo8Nbkz0pbk+%A_hL&x%5%$}5j-#<-q2sZ`=j6KvyPvpU zc*c6ad$YdlzNUUeV6OQ_;2ZEAC}P)^y#sC(Z?GJaMG|{f6A~!e@(wlf821wuv7ota9w{x4_W}@r6@=p znJA$?mp(TfjU0<4EYA$PKo%<%Y^`af(Hkb~Gr^Xc*M^>wk)aWxF`<7@mFUwLQqECL zQiIC5s)_13v2_M($QGKFTB_0dZw8kTS22uCgrO62F4L1|jb~kDdDxTK&)M@hZ#hl4 zySZfEYF@eNh~cQ=nBkv(t#ggDTd>UXLR!r-id$Fsj`^SPEpvKom;SV2xBiUbq~Az< zn0{$^VOZ~6=e(}Ip{}i4tm{~eFH@KR(@E5ER01QyL6eP9bBDVU0j`9U0v;6 z9bMgA`Hu1_R)MXKxGu-b=`H?-!D9zozFOK?A66~-t$CE~udS1PtNoun;27rE=ioRe zIUhMwu9L3Gu79p3?xXIYXP2j;_qbQ-Tj2x!7yUE+YXW@(LoMGe?W~Wh(wXngV{8a} z7yEWQ(h+iua2#;(oKu`noEg_?*HjlW?{4Nk?gl-3JdM34y=vboALPI6pY2~4=pPti z`DN*7eP)f=KAFec3hmwPyX+_j<~JqJD)pqu5+##E|j~a`;%8+ z*7~CUtNwZZje&uIk(NqJC+l--)b`mt!G^N;ur0)qm>Ek7+CtWT|B+eh;_TY8?U|3-?JkOJk%`qua&{_Fm^{tbbAV3g&zrL*;gHD>!_ zo@gtw_q6Y^qaBgFW31z-L+qUCeCaH6U2x5Eq1~S|3?K+dK1U+do@p`!+km5p)c9?00aTlbw&9Y1b*&6c@tX)P2ky^6d6B z@}BUjd@HN2PM7?%{A&aK0>dmnEbXmNtdQ-4d8{puw0E`duopN$$4JLP2j4l>`P2!! z&bX$z3f#@zC)_2Ty`CoCQ(ld4wJ+?y;-BMR9~clAZTVyAVtr|i+rFA7+0gc0_PutD zBkCCEIOdQzXE|RvtGO<^X1g%%Htw_TnCFnErT2W*9RCJi!hg-b(7z=xB#^|2F{Zrf z3hI3Fx8`ixq1>SwQ9P>nT}o~YQHK-{D^3PmXwtz^q0ynSAyxXD#$vjLvMV>K0?MFr zwraB45}KK^ny#aInfjOxLO(V2gddo7gzuR)^q8!qA_fm z_$hmqcsl2c_$jBcq?9{Uv5&h`g5!;oZB!8ae)5E4)i$wxsQsM1hGVYdy2IgI<>I<~yYIRid2V?Ic%OMAzEi&1{yY9n z{u1-qz(1Ua!~hCExFud5zw< z*Wk@F$_UsAz(`Dg3{^&x(Pb3b9o22sE!8;w6vhia$OMJEn9qb$S)X~s={eC0)-X|s zeM7W}EfU{nujTv_|Hs`VG4VPAnMK3Tp{$KGASr6cF!O#MK zp{$6LIic;y@5%@b^hs2Y_v&6f^`^59a z!^D@w-^Am@n|$JJ;%5SxL?r(u9wnY8ek6V+{v_i3zjUxPREpA5^?=@2d?3XyE-aod zUnrO7VmVvRn>$UUWsap>>DK)A463rfl0`mPpchcnKVr@(CMu17jO^s2nMH(6_z%!u zzAVzqA|)NLlnH7vGts3=1OAHOnWYZ(vOF&kj%7BKPL;0F&$E_UPg)f$wQahKh+KpWwfSdm8oWGQxagQ~ML*3j0|tUsqrI z)oyn5b$p9%ajXsx30cyPNG3vX-qpg|7P@k6vq(Q@C*o6esSJP;NbX$>%eNwST)Q$Zu9d*;e&ZrFM|iAZHW4A- zPCdsy8Xgx8#|Db?oJ&-xr+#c4X|3mixU2e|`U2;GdTKf)32-|rmXYRjJ@ip2LxybJ z6CW-4#_b)wV$6B-s&F>lGgddg!^`y{NaLeNqEiz+W#>|RjH{w^6$ecBQY=kHYNl^& z9O<|D2l;W4x#V!XTl`|YDd7t~gFhw<05#}U6ZYX1v7PC;fxF>;^40;8bR_Rsta^AG z(Mih7m&LtxkN4vT;4_+1xmF%7P8Tc4YxCeya1B_N`&>LW*DNn(J1iA#yhUP%$pzbcBXSsKoY+T3~DlPd^>|{Pio`nYUM>7I~<9r&E zA(&-pE@;8LFJM?7%k9Fod1gE5Ch2DD0P9bygH^+}RFG6u2#2t!iU+)MI>E9)xXi}4 z54NAGVrWVoGaZ*5W+z7e-Py>s%vCJ9&tkd^Ik z=Z*L=r?w={9i-UEy(L*8iSsbLa_=KA=o>9tr#R~C#H;Sluk-uO4`uZM3gtv#Xh18S z&2!3U+P!WOAO;oyj?$~S%Q-*C#|dzPoDJ;r5wAGMSts7ksepPxkHG&h3m_Xf6FLpK zl3T&I;0y3+@JTS3bb+Dd>)-%r95@DCFWw;DDBdJK$7@fIshg^X8Q!W%MymNCZ>+3A z9{3=56XBL_NI9TXS=G5%md{?&%vY(^?XqVymm@@0r6Ev)F#a=KjckQBLtCI<1{@2^ zqOs^Kamjs^oY>zq!1S}^x~5g8b*4?Gd8S2Xt#~+Y1a1p$7j3bojqr-}veZXiFH=A( zXzOUX(5=voP>tXh(??TBO)`{Egsz3&n@)$|(3Q|z)8){m(Ctt~=&R|IDQ&zOdS^Nl zIvF|@dTlxzdSxnrkkD0ECUh}$J~Wo=Fx^BIBY4PSrBP{6hE1DV= zwKp?A(}zEZ;T0U=lbKXO6G2mEh2V}{FI>iKE?q5MV=ZRQ6F`be7VTXzO2|S8qBUCuETM1ri#aL9*ggAYDge%xndP}z68M=BwMa%&ATP50ZlK=)-4|)r*8^}vJPYxWoA}2R0N1>;;!N{&ISKUq{)30 zIZIO~yA5&^FIXBo1#k=&!$MdDS7yFvSg;h9z)Cm@+h7Z9hOO|Aj2x~~gu`i=3n$<> zd^eMXzh=H=?64l@!v^?QRUYC?h6D3pHmrbuXMScPaK0Ee!A$s1=3gcUJK%en2bs^A zXBi62fD_qR)(Jzf3l76n_;DI%{T=a5>x;9uAL#d%+#yK5%c?ll5jN!&Bhaa5Ost4rGJb@$f{rA6yq60Jnmt!~NlU z@PBZA7(5it!vo<#a4p!C?FqMmYr=KlNpKH%3_JvG0yl+6!K2}kaAUY7JOge7*M>i3 zK4v~-e&x>O&gO=L+$=9UILppTvw~t&F(+G~EL0k^m2i7y2jv!c9$Y(j3E3Dw7%x;U zMJSL}GIcx)&%?9v96T4##7FYf6e?%>j4~tMQ+`nXQA|0O38y}hd?QFO&Jn}38oUZR zQCQvi9`hT+lZ_HgLo-spk{43{QtDEB=`>&}&^Sb*9z+fmUS_hflan_{6{JncRLYV~ zgPV-=y@k>>g7u7B%-Qtowi3=tj+wWG_d&6$aC^~Ni@Y+x6Pw0>9#hJp4G*gs$&Ra#FZkR1z&RH#S@C?%BJh8GMPK$nl23nd@ z%Y;j$b*-1JX4_TrN|657c#>iZHtXLdwcsvyT=iTFrCAkSDbd2H8GSb zh}+}reC&g3izw$d$JV(AhYwQEg=dE|v9aRYRK2HJ?6&xv8jytP(*^F9Ec#pYMTF;ulVvVpYwrgZE|EBq8oSi63bV{(opKMEvrS?sB zuVYL2s?*^b>9Qpps$tZ$8deMAjyNaw(bXc|T~w33RkVg(>h7<4p|Ql)y9b1)8nEI+ z)NAbX;pt&GHcq^ebBAj1G>_d84^&sGe{;^O=ceTn7WX(eLKhlm6WVEp=noR^ z5VjI-m8>D>*O7@Ql<|1zM5sf$eR`dKkN$^Y zt^S(foME4SuYSG0t)nT3Gye(BlI4BBWmBk&oonUWTsj2(mp79> zUUWvZl>L@nDEZE5E@{QxD>3pu`U<3V&EI5g0W8BW-Oj7&+(_F_+e}+e+d$h%+e+I; z6M{vhywVo9?zpx%6k#L&F}@e^GX4;LDfu(L4=#`Egd2jh>dEr9@+q{*v{|ULn*0=g zA88T4uDC#QQu0;S2JlIJ8KPdKf3I`vkEDL+5c+#Wb>>vcmtM|)!zfeEP@X19$q4~7 zjf+R3+-Pz1U*roPX3_|I@r7ZTz!2GL(UY!O@`9nvy3rz)3%?fsN06|#p?;Hh6=GN; zr5~g(_4}+dtrA;9R{Ol|Hu;dCwE`*H%rYq4{6q9w7EGupAy3p#lqc2*$0l@Xarm~a z#GWB^Fn&tEi@wJfMur*>1x@J&1TSH&@jv@~`$ytyyV}vsvBrUl{c-dP?G3*QInt3x z-AFfQr^q2ET|SryxO%(py6TJQ>`-DVu~c;re-1x}ns22B;=Z^zrge9Yc1^cZ%@NgQ zzfn98?PL=(v)wI&LsTDPG_5Db@SIYO4Bs-WCuYPas0z-t@ceL%*f#ND&Qt0Xk2}~Q zHlIZEawRj=Th)b{0`3F#x^zHd;;OxUB@?+*6(dPINjJD~m36<^cq)ERlI9JMJ~Xb$ zd+p&`>5;K^ajLJek3d=!y%t@Z*e$!BI&a(&J*b!;8Rsi7KTer6C7L^_pT2oV9J_?Z?ILNzJtDF4{z{O* zBfu%$!Mm!d3lYnxWwf%E;Kitv=AaVH5cQhrx~WR`My)E)Qma6DY76i?)Q~tNagQ3K z*MssB+MRf3(uEk_+#pQD4nTJdeG4f;2z}Lb0eKO54taskV?JU&7fxgG_&e!d(My&@ z{E1yDw($ypm~Or|Z8Kn|Y$CLm@ne9yj%<{RS$(x_yV#!8hWjseGk6TnHJ zOrO)^DGg)|W$Q?GJHyl!EkT#i<`zT?`=UeWCI!C=TIcLpTNYE?5M;_+xKEhZm_L{y zksiF^c|kYcfJi>F(!!Euofi`i$r=DA9!lt;5EukO&=0P#z|N=SC*<1M#i56$MjEwY zygn8B9vXzMpe+xw)Ca6iOc*u<--ezNB-C;Gm4(}iT4Tbcua%FLNy-!DQ)Q%- zt!Al3>Z4*#if_p4Uy?^fX3&ok@Wd~*(Q)VQ) zW(Q>B%GyQ=F{8R?k`fPPSBQ;PiRFmxqhpHqp6{5yj`^6yU=>>q+deoz=Vb3)-%)>U z^Zh`uBvhhME7d}^K&?|-gO*@(aK3B?b+cfDV5eZ4paW_Up(E;`ylZZ@e1`m&ZWQ{1 zzBTQg-i^w8QRbv2d4lqka+30la-8ynV#(=qw{ws9dd4aGLF^Q)v9v!zo{cd!6z(ay zl-yXjx5%zPV;K|~9GOMn38+{lA-s_z`(Kb{b(kbwg%IW>jWErbni4reh|Y zWfU+ACQ>ofNfc_fW-?Ao(n!Jr;qjyqX~-i_Xeg#Nr0yn7YD1C*>cNreri!&Jg(5|% zNvSEqvQb2uX^c8T`IcHq_EBQU|0s1Ric)21-{cRd5pihZJ~d9i8^0K97(Pk3NZ6z| zm6}Tn`3EJd&C;AK7glahqLnjL?I|W@MD?F~wMwr9RW;SiRXs_b^;>IZ2~sPhw|bivm@FD)$L&LUtTG6C0N)16_m92=@tpfpLWTGN0)_3Mgxc z?25deC7|Ey*J?-MrlaYpG5m>)HiEXyP0TLRUD7YSP4un76QTv|Wt{I~jfBCi&fUh{ zEaCB{$$m;c%Ub4vfOG+`6Iz7sj25H2pr6p|D2Vb`K|Wu=KZ7}osb{HgS?gNoy6<`5 zfz4@i#(W-gEiL4W_~$SmqB7~%~WC`j3(w&`}>>=pGd`%u}ex4kiY(>+hCRPzB4b)sv17dL)Tq!P-Pq|Z* zsshLj)o5H1E{FSBh{LV1=&i+;L$>>No};DXtt0Ae?fK{#@BQn&<7?^P>+fW~7wC^( zjVVFTq0K-GkXI9D6RQl}OtT4d2+IwNy%2H{N)dREsH6_cHd3;vm8qY}@r>4jHq4F8 z&eEMy7wZFWOK^lJz@E>}FXem_f90qpbnaH}dI^^|RrW*jN!9`gN*D63B{m1=F{aUL zIb-Z4>~Ea<+#OsEZxK&e&=0)|1EOcsekZRa{KU%S3d2f+)&9=^CGc1JIdDjPSUgid zQGZx9MYRUe3Aq(94aro$3yuV?z*h3eY{Q&BRYaUm)R5=oO>0T`qHUT>DL7R-#qANu+O?+8BUtJ$sCZIb@m!mdl4r)5{%6U~EjCX^>2tx@Y30+Mo zMv}3bx`w(2$v`rZE08)!3u*F@8X6|QLc7V=G8-x03Ky`P0k_VgbLfWX`soJhQldYs zy0T{Czw8c@PTbk1Bea@~HFSsNlc%?@m-#K7h3$wvjl`KC|}s2Vg7Yz~cv?6EIk zKd?L45gZb031y-7U|X;u)EcS}bp^}8w$No7T8GgcaMhC?rd^`xmk zJ3%=!Asi^}B|RpMFdhr7d_+Q=sFUcK=tLEV+#+8Bt8}TlkvgXqiQkcllIp6#YMmsI zxhxUN=0zvU82)hrgtSPu%UmhT_X0?%jI_RH8_c7BsM3JxDO369`RxTsVNdB1=@Vfw z-^e%cJt9U`;I5_Qie!@Pr>rLcOBZ@asAZwu=Jwjr+CJF+*ngNp>~vE(b^sQEMPX-{ zZu14q=gND^hv*Y%SD-hg55=i7>1?{Uls4M7+B{_-Wst61Hz2S3Mj4?UsYMfCVH8@W zwv<@PSeCX(xLlv?H)WxAkv2#yNnt7ble=kYg9?|zsc{`;08WbY8WgzQ*j?DM*fH20 z*iqP>*wNT|vgy>Nvcn0;dd-INe)fF~97)_)Rw0zSgRUj%knFwogLavAxps5}Oiiav z;4fz6UkUP35}(LlNMAs|DO$m-v%Cmj z7vEF=6u;Em%zE4Eu{}5cu#I!x$vY#ipRRiD{cg8sruUz>v+s$2vR`8MSzB7~S^c(G z=1SXrXWaGM)zE#&?ei4)YWcePp8KBrr}+W%vw+vy!g|;0v%NI`vfXpWT$Qc{?t^Zx z2kGnTd**xQpX!&Hp9X$1@zge{LN!Xgkuu2=Y9q;^cDD>s4!_-l2eQ*t{@6Bo%#0}LggssQPLR_n#?9!1&DYcN{OnYKO+12 z943~q75@ps3Ph+rML=YLMM*kh`A^V|Sv|TzX~8eWUlY8tG^SpY=Y)5dO{9yYORaUR z7p)MhIB&a1o+a=pE(#~H3KZAPzk*-m^aPw}mzXM~hX>f++hT;q#?hE3(dYP|;d0~N zU~R%}ot?1I*w+4>xZZwIE7LX8{WQemj@siW(8tzhxaBNdzEYYUQU&kNA52hy7+_)vqjJe zVk0uJyMC~*syYV1yo^256vx)MC#m{{CmW_~Ruc7Ow0J-D3j0iWYB&`eEnd&LN!5Bx z!6vbZBn_?sZlmX>c!2t+`WNSndUm=@(u>7+s(^W0Gm+`liIa{-OT)F>+)XIUX;MUyrvU ze8-pJFUpcYeR@fFP3&lTVIW0)67Co{p<_$;@lM6+g!d6)(%SeR+z<>a$Rc&cm*9us zYip{@4e~L`BiZ~dBA5Ix7K)Oi+UTzcp0FMN5mH3dq?%D1emTAo^?RI|$R#=?XyF8* zsqtC#CB8D;&-fp~NmyWfN&FpcLE0DwBh@0UBjrR(Vmz?|e*!;<%8%RPj99zq>zFat zH@up-k9s;hC7g^^P#ec4kyevH`qB8H=nbPGA07~E7RQh#M~_BlCk_}lN2kR}krm`* zykEQ`-iGiKUmahM9uBXIJr19V)eax1B8k_>Ii&9RFn$=`S_)I@>Nm;v$ZdKe@|AKT zB~1CJy{ydYVBIX;OkGMhO?O`TM|(+`)@5|lbu)CcC|Bs;1n&hHAzJuP@J*WkD#bDW zNxusw^A|9}))H$>c$3*%isH|ri!A4D?;Q!}f8uv+q~wI8nD@bV#9zz&Mb;Yl5b#R3 z@v8Bz(yQ~X(U%KX2$u=FkUQi$=1Rb+5Ls9#>=as~4WgG4mJn)%eud~%rU{SyM=zqU zBHzdo(d!F$7v1HHnD6q!*{rzeAFICjGrP5<5qBkZ74?8dQ2K{56%CQsVH#q67%HYd z){miK8ejvMiP`StErN}LU4rd`L-KC9Ir5qEO5KlC*VOma8|5?Q5G)^iR@%{Wh1s8b zky*}tt9LL5V|m!~(x7D@b^>;wP|M)@+X@k^ZK_VDyY&2N=56k0`e6NC^nLUZ^f7cZ z@GLD$y=D57S`m6?x}3U{FhF-nr)j0SoNkV8wl0!uj)8KnTsY^>k?0&6g+`>)X#_fz z#-!TP_-+yQl8%`*XpII2gw^rHGeJroW#eAOLy{qs(z^^XRjx2Br+wv zA`QrJN$*HQvYV!&uq+M0jYdIeX((r`!64JeMMSi?SxQ+>b&;*0Zdw^k)VfrP(;gR9`r9lN>&fiMk zM%Obzeuy7n_!;Fw8$-zr@_mdxLMx*?zaPUTILIe5NrHxgM$8*>m2e?*xpak0L z$MI^)i{*O>ow1iND!!7xoxX$4%B@1$t<5c`Z4d3u9E%*E9A(bku3_%Bo}Zo>UX-t< z?}4wCf2)7H`C(v()R=oKpa{1LHVJkMb_l2`TIyN)C6aEcn!k4zEHNxL)G^dHv@yt1 ze}nUZb%cs64qcXR6S{-Ghdzuxif(C`56`1b%H9bMm7mi%)i0G3C=-k|v|dVu>#z2r zGO0`Grs}5XqPmH?bIRY^3(A--uA8KrtgER#>^kC_Ns(&*`EV!P$lQaN(lhtUqa_F-8v_Uk1b%^z^^*e!dqKibm{zO#vQuUD`nV$LQ<6oKUTP|Cy)|;jZ>nH0l z#}$XuIokQrS;e)%1-Uo5TYKkuhxw}b7x}yRuZzv*tljDaS=O<@X*G8Ao zz1iK?JKsCpSJl7R-_?IZY%yiD|gCri1B-ICs13?s}f3UcPCipUVH@Uxfcn8fqGBS}Q~8Xgae_ zuIpIZE|14=$2F3_z+J~3$Nj+dl8y40>vFnzC3TVYkqzWKWMLg2wU}HT6%%}6_CX9q z$*>Zv0_)>?x$Yv}Meo5k(mh-!y*nSG3suL}-DBN23y?}=*AhES=RYOIS+__l&^z!8 zgERF43ZF6wd(XHu_|Ui}ur| z?7bpA=c}ZxG{%$KYB~PHBqTGaG07p!R7_Ozop?dtgVcrBlflK-K-b35(M)tzbS(@O zeN)ku*Nef!)RE<>b*AxTqfHK9!e6ccrKHm&I8<+*SFb<3xW5YNxc8nEcMgyL?z#Jfm@nb?5 zFD8KTVLX_Y*?C#F#b&OwKDKpm|8YO|ZZtjeThKPN6>Uea0~%u*Vj5v~6OQMOM!K7^ zj$`yH%$BU<^s3BOtP}KV%+{=}^h)|kdUa+S)+u@oX4@j`G`%LX9qSCe7PCF;EWI|f z1FIv;L8U@8=!fB_p_HnI98mZ44rn{nF5N2K6=)BfCJ0I!bDMHoaGPoYr?rQ*m-T`5q4kmVu{DN|;`Im} z;t9?{SZ=I_uZmBR$J<^Io)DheE^97mE^6v&J88J~j`rpDCz^+vN1B8trAcd&n&nz1 ziiHAE<1}8B2jxb&P+QSk(47g#9Y>t|oIC+vAP{f`906MZ3QjVMrx+y;cKqw=MKWrF2`6@rz5@4ye>C(wcgFP-Gl05cIPKn=`5EJgiB z|3WW8J$H32hP#9pVHRWN5ERI7g71P~f**q4f}euJtRt)e!r|aBa40Br3)~zd+c=(h z0dW!W4s#iC3Go*59`goMiTI59g87R1iTQ!~hWU;;he~48*cA3GYMy(cdx3kYd%k<6 zd$D_odxd+Md%1g&`?T1AOyHv%>SxbI{H4&G27vd0Y;6EOnrmO8fnO z6d6fI(*gRm>@0sXe{+8eznlDpww>T2o)0JBkFat0E%~$Hg&@ zv*3k~lzN1^rLY3uCk*iHP4e{}Zsb(gG1eb-W2gyKuXKqA@JfA=045X?AL0~!Cw-kL z->KiIL@I&0)YAyo5SD@^VLgR4z&}ZTsz$0?%3Vy&i4XPurv9RiPc(68eRI)2s6VN- z{TMIg`Xe~X>X!e2d5ryt`Gl!%(;3epA>#$4JWpmO1I^T{97BVZ<|xuw(ps1V+XzqT zj6uKt689ok!H_ea^PlmjGp8{_f}kKE_`rP6e8*hJYA9?V{K5LplCs;1+KAe-i|yF6 zB)=tdBugDj94E{GtHN6$1^gNt+K=^XZ5V%Txlm4&Eq5$)yw$JMUs7FGHAPp~F3_Sh z1JiZWwsZ@0Yji7gOLQAF+A|ibMeEQr>Hjf&%sI>s(G+Vo>m%5dE$38WvBR_IhZ%n6 zTxQ2;nl*>@32eqza1It@ZPA;6(O7f%UsoV(3fsc=aE}-{ObHL>j-`iqC%7yIgV&PR zmhqVPNap3AW^58~wz!x}c^Ual{wO9@zJPBO++z+CWLP)kM+9S8Jwviij`2N~G>{s_n>h7lMW@VFILlc|lOjT>7yx2%p6P%d zQ-J@4*TB}q_J>%KA>sqm<5n*sj>t*r~GlvLX3u`FVM1zC1rY-y+{DKR&-I zAI~q(Tk`HarL-ltEuF0Br=a3UBrWf2!X8J*Me%pB?c5T^tH^JzIM&0w&s@%{USwqB zixd}lkIgkHl`;;06r;X)l6bjpj-}jk%pznety6hU`AL2c<{#N<)A7K&Br%mrwMb3i z6QaGXPpowfcjKR;-3=21vkZ-G%WS7K9Bp0AC!5jU$No9K$xaWhj1CN1W7!zaaZA%o z+gsBl*4J^up>g(i3I%7FMQuu{>`kF0HA#Hpsk3psUbeMB#5yKlCFsQZoQ=A?fey<1 zi5qHVVxg;Bbg=%fU@PS`>%ZujXe3b-_5;sTL~cQ#Zeq3jtgy4{rRog0S2a1C7CG4+ z8{c1yPfm50>@uu~&zA2qGSt(( zqmxZJ7GH%A6N@CfB+n)PllY`nX};*P#J22g|E*|CKS5l}vrJg9f&YCD31>O_h0F@#FyQh2^ zd$#B|d$IhRu?6R==&Gb#DiDw7bW+!pnPl4-%L2-vJOJVyxi{pmruqH87#$d(E3kyO&>+%TgWzj%*vQcU6QqBR#r z!9~JNAYIf|)SZ1*6yqSI51?0pwVLVD0m$kEldiqYOnFUME$QxVDS0HONGD5AgnC17 z0}CackZqB5Gvv^B3YGTfpET+(bs*Ffe#T+3X~++}J+Q9u0> zU5QSk52vdleX5hGmdKXI1wae~Lx0SWsEg3A(nqLEfdVFvDaYh6Wth*fzOfCm^)ilb zmu!@5jBK=QoNT;otZahJsM{yoj@}3?L>C9i%5)=Lqg;p8HFS`q6RDKMro0Od;ya8t zkcN^vWG?KYx{YyoY%*y)sW~Y_N)u9q3)!PYmzoIEmwWw2Nn3pz{gJZP)HNlYU>ZLI z^D+NIRxStS_v9nwTjWh;ewj3r%Xl&;(r?Pjn2!Jgh81dE(zfIotOwmxWXxoYp-p8` z*rPbQbnjSKT~A%u&RgrV#59Fid+vHVs&1FGZEAtooFxP!o zXE)3<$Aj{=@f8pHhMU%zgO=9jOV+RU;jRa+E^fF7?XBfKZ@f0Pdl)`_*cM=*X z=9dvv^AT%Mw-Ap|3lM8jw-Jv~glwAZZ4nu2Pq{tD4{~3M8#0 zbnXe@qJFfAMjuao&$o+q`s6g9rKRPr#czFSx)}In9q+)(?m1G<-_C}vLoT0tmIvXj z>Fwry=6&Xy=9_|-ndV5J`Wtd8XA`HwDg$te%&-m#>a+_hB5JmVM#n0{t(;Ag)`9jO}HPCzuXP*Vh4mG*2^p*9xzwo)nt^#+lN;u z47i!NOT4G%+LVhjK7Tx;k$9STo~4TAghj?wTc`8-pxyFQ{9eow`B~HHz|SN*RW;Q) zHHi;K`&yq_BlxBU6XI$7G45BizhQRZlc5TJsiCRuF=2)6lBTYmIuachEyfZ9g@eHh6p6bN{`k=Z3{->In z&5GRY&hll%>1+?pP`VjFqETp?VNY_j=o7nl{Dz_2V~FOnJri}4>%u!cJHrRn950Nx zKRhFz4WEq9PW21-lpHZ^3$KdLmDdXYXXL17ddDV_zLz4KZ;)?lF+q*ZC1sM`lC#5= z$)@o!PnmJJHsDl6VN`LZVT0+dmf1vSekrH=K<4CqyIG z$N6zZT)Da+)5;D)52)jzdQfdeTc|!%lNyJPK505)EGJFphAdX@8S505tZ+z0ssVk!nH=%gPBR~#Fc z#y`e>Mqs=?=0oOOybeXkAIq2_USO$Kw4AbNttUw5O}~6g%3?@Fx2#{wF%X zu*}fhw#xR+X0;En|A}w2dt)R=(E*d4aTuHfosN`V*^fd`>XXz&v-lH0%u2bsD<376 zxrXa^PzDS8gBK|>cgw^DVNcb1)nV0CwwJayIac(R-6wv_P{jj9>m`f3JUs8r_^J5Z zRBy>q!|M2~*kL1A?N*OVB76?t!~{K-O?FS7kc6c*X!oFg*?4qQVpolqrXdGRh zET~^YlU?!P6c@oWgS&w4<0qIl?rhq0!41JO)?3zEkS03Lz9KSnW=sA``bo>hTR7uv z;ovH>)}k}-FIw-|*!E`jSN52rrTd+Gtml{Ky0^J+m#?kqw!bOARjyZVHevR6)8TVYaDH>v zbZvE6-P_$_PY3T}?`U65|8jp%|07e#xzjb?$2HOT4@?2)cGp-R+cerpFusvH)=O%i5fPR5oU(}(euN! zFe`jFb|*&j+>bquJ&9e9u?jb1w_;ngo3%tOSxeI5wFK=tEsG428v`O)3uiyRo!&_D z()G%P3iEk9-bx1mr@#qtcoE)MwKTFM(jn3@GA}YWGAD8=axroP<}LrKsUQ4?A4&e9 z`HG)HzNdVwl+ov5=3wSx@L&SOfnvr23>KV^A%IuuN1d?XkzlQWBhtpa$5|s#J#-rj)Pmq3&Qv5ufCW>(ozkm9e$zHR@IBmFm^% zy}>=frmCgNHo0!<0ip^~e^F6nGPr}|g13zA&27zlo!w3Dh%4fZ;I#cjYF~HtJJxXI z($s9O)vzqHBE2L%Cp|wsH@!GLFFiZ`$9Ms&C3G&a!We~Bsnscd8ccK2>@+vcORtC5 zK=mlXNeGwm7IJ9lnJgz~%J0aB$v4Rx%e=BZgkkWJ$Wh4Qp{k~#k%g)Ss=?CF6gA;% z;1z{JOJqM$UQ)=kYNRsrt)MjfJM<^iB{D8@IWmmjm9eKe8ED<3Phe7v+9L062hR0Z$XR)w;wA*BE8*=qNo> zxSLYRIv$-EO(up42ZC29N_T_ASHv~c^&;w$aIC7E>YeI5cu+MXEATAW*6zF2aTY5 zViN6pBXapBC-AX&vRCp_@{}Yhtxb#g(d64uFuF3aH+w(Y&QB8$;~YyAt4CKQ34xq? zKQsoa0}1spy-|~-wO+)Dx;)lN4qH;FhZcNyEcurWx$zH&pJ3DV)*sr08j zKYt(Ns5s1o6}b~>7LUne_tXup3$73T4LUpv=Ey z0JauwV(%A;IRQ_|6ZXVCI?p3XOpMfz%J1c=6n?oE1QGz$?#?RJ&{LhN0X0XkQ>37H67TOnD8XCcUAy`Up3kG?oxu>`cMq`l819=^J?HMlx zZhi}Jv$z#_ga1k}mf0G7$$8Cr#TiK(MY9Slf~(B-;9$WZK{=}__@CgAU;?W-_*C#r zP#-N4$STB0&`CvYcwrD#P_MB#J=e~2DP?n-rHt#}=$4fuxhKJ+2< zCiFJs5UJRWO_NN|1&zR4k%eZFMQo9q73PiBP1d2dt2TzczP*8cuKlk4p8elD*dcHU zT`YG;_apaX_Xy8O&qvQEPu5%N?c=@Rz36S|`|bPVYiMd_YGk_TzvQ1_g8aparp}S! zk=S z_9)M1&n0goUvtxC|60zvkTR?YuMOE4*BEm8E7mGd#X*MU;ZBk5kt>m_kv-sEa39#$ z-QQi|9_Sw69^~%lE*9N`-De52a9VtAVqc=0c#bd|r^DAF_9J?T7YXxmMtlQecR~em z5h*~tN?44u;+qhM5F^A(gatSgz9F#(VJ=}HaWN@KtR&3E8SwRpT?qq-3rIfVWx_(7 z8Q+LFh!`SXAk4!-`1-_dgd%Yv$xpmMzevAAScJ3Si;anciDBY-!W^6)Uzga0(4RP; z&sf)3g8GY`ij8Q; zYFCw+%v+qDO_!w-Srf@6saV!n64Z{;UXe;=O(j>QGFdZ8P0mx=3tEi(ofZ*q;5;wV zqTFw^uy{S^27NvG40()UGr5wylYEu@ihxpi|80o=;4fXG&_2>GlFBBtLK7G92|k^E zkRhVSS+iIY4jU=XE;F&oM?-iIh0>WkO!bXN_i(Vx!iGSLpeArXa9gZ0uMVyXu5&63 z&x|ciwMtLK_AtiL=9JDYE%xG8&`-fn!ri<>Tq1+a8)$CCYs|RH`()PgmoS=&SBh6z zT3PN{49tLa4(}4b$YgLY0Iy6B0uKYz`6H~qtsQKeZGUV&`w;scd&}UO;I|;l(HJ$s zao>@49&=7~Y6Lf!f1JGqM%GTjKvpByf3B*+nxGw=EF1&+i|#*&--sRV2f~|RRZ*C| zf<0ICmtEg;#3T1sNS1k9a)Q3;zB%~ok^$1HQWAZYzmH!ep2WFKAIdz%x)q*6KLX#& z2r*|fj{)OpI|4fc<(h}gC~F1_4*o*?M7#%Uus5 zBTF1EuAuK`jHN|b(^*993->#4D7!kF&uL*o8<%h{Dg4?IT7hPpVlDX;d6Xff9j)D~ z*hoG{ei0*v3E_*14~kvnYh;dwr>TK=y59PzWD1#1rjgfb*J%G)eJDwx2>g<7p&$Tw zV4nH{yEh%+)#BA>$oR7v3&cw1g(9Z|ZG><(cvUD7;n@q=lSD$!dlB7qPLh%`#bY>T zm&x_B{Bqf)GQRymNo#_GvkK71D&Y;d3=)1Pt|ThtX<{4A zYT^ZRzanoSqh=hgwBc6be(>U!7L<>&j(j9@sQ9(`iEfu=s)cW@%WQ4EM%u?~F8{+{ z&(z56+H=MNvWR@>A zkx-_*i93lKL20HEC0$8gLg{KB@0e|-oFS;mdM>{$*vi6|PIom8R4893NE%0i;y$Vz z8ojDtO-Kt5QzYPp=?UIX` zQ-&?^z4FV0`;g*Jz7IZTY&|KL9GJYHY=i%etA?9wl1Nud zo6zd|M`Z)qRJ6D?@gUnPFfj1b|1#P_H_l%WALd+5w1^%c6o~b78TSVd_|on7zbQFcb=FFcv}uePK?*S~#UVtvpjyt}JN}s|9NZYYMX$;stKGrJyQk3fh9R z5Gm*i>VmuAEqDs&xkAQm{s`u4>w1S*@R}(Weqyy0HDYh}4fVg)AJ84teb>trZe?C| zUUlARpww48Z88$RFf8&=hfO~rs)aUiQ`te{G13~29-jDV+Yvt z*=&P=^Pi``ukDrKN70?7{27>{0 zV6T3U9;U3NU97!N%%?jCZwFO2vVWNI5m9ZU_+z^F`t|yML;USWR_b8)9b%Z=w&P!$LD@vj&4p!!WcG%?`p7&TyL6 z)l3)xJITQO`hWLVK^z$#KNXl2sH2@4T&j(epBjB+o_?zC4{<@n7MdA371$Hb{Ym)ZN5aIoGX6ETmy2S&jllI^bVwcx z*$ov&bVo%H{mnIabs1IS%M}eM68uMC}stf}WfCI1r5O|ijqt+)@y2dK|L`UmU!abA=tP{~m(NtoX za1eNvqH;G(z_11E4b)}fIMsMnchx%8dsQWPNHsGn^sLb2MLzay_FQ(5W-g@~4kjmv zKC%bIuNbO(G||4rM5E+hkI*|YekeXY)lag|us%LBcF4$9Pf7y5w<5Q1N`eqeBzq?> zCr?Xa(mJ%L{|*$2u1f67K8UvW)5XI%#}hT8tCLT$&#--gJ^(S0S08}JLUkdLKCU#GL_&~AB}7S4CLu{IilnlhYQ0h{!AqWFeb^V+3SdpD zTjYG?9c*8IfBtCxSpInaM1Ce3vKY;Wt&i+U#}v<9Zzt0|e@Xs%4#>aGz0dv1oq(@{ zuZ1^6^#13k--jRq2rL|14eLUW$M%BvgqI*ZfD<4AE`SWUfw8$Ux$(J8MRYE-w{(7K zAMPN!mv@*;W>9%ecug7CdADVH{xU{$@oMoJi;+2x7nNV(4`kxxEbdIcPH>CaS72sc zlpu~2A$wk;W+S-@HSXo6lM37uVT*^!8n5?%jK;(Hzn1j67dvH-G6*&c%>Td`juBw?DBdYPGd?mtF}^8={mV-tiANZnqRrV#&Ijp7 z=~|;CvH(6Gz7)O$z8Jm;z7XCV(HPMP(G1ZP(F9S7&Y}$n4vLNPqXMX5h@prfh{1?K zh`xvch<=Fvh)Jl4s0pa?sBx%asFA4Qs1c|?=#`kIMa*){3d~Q`FVt_;AJh+2b9gg& zQ+N}2Yj`VoOLz-75`jR#5ikS>0U*!_6hffn;&?a#PJ;W_N5YA5d|aF|7cQnj`Jld? zv4r}D*A|yy#;Daf*J(KZ8(<a+Y&8=(}w?P zAn8c56Ca!{vfTwWSX%{aSOwPr<#V+qvEEgTPSs4nDJ*D^mF=FuajnwVbVbBQZFHsQU8LLyPl5By_IA3WIAV%z>?P`@XQw?1egFLP(%V~00p#!7$I$Fcvf8gf*WB}=ig_X5>vUmX)S~?aItVR z$PjfC^G`-;pSyE*uGYCS|AaK#>{>JQP!c^SpnfjS|@T2OB*nKdz@B|Ct|3KVdmZ09u zqBB_XTkbn~bIE~cTsUiKm;85dc#3n>*pc5{GZiOsCf6u%~zrn+({dEWR2lQ&{ zF8qSPb-Xw*O7|0@m$v72ptBT%6%3Mr_cP&+KPrOFT#a8Dd$*6qvNCT#311S z@DfGpZk1T?t`zoCeN>$VkEo_)OGPer7x^@{j|P>}4f~Q~MDN&r<2MZjk1<-F?UksX z-09(ZXT?v)=cIZ`jv3a(=gE&4dFol-@kzMvmB{WJ?3Y4@Q- zbaP^NF+0zHH`>Ne77ylJN;HYCNLEo-k?CY_AXv_)9j+}UuR>STRoA&J=dHipkGva9 z5B;UYEb%OUmpYn0gSQ9g2S=z6Dp24Knd1~H08DVhg>h?IBh>> zB4$8Iidm9bThbYJ+I+@bU%o;*k+KF|#86QUx7!IKZ86bShbVYuTG$IU5-J_&vcaoKfhS3xFN_-{TT;72i zQviyLvORgV@}ROOQIPLgI7YQePGDwX95%7PQ=oHTMPOw>6;ubO7lSi`{y-q045$KR z^hez!^sOu^gR^NKlI@1IHV_Ho)UiL;- zP5zy~oXL~pWK{%k))RS0AqPbInbNV+ma?#HCifEkrU1aal~tGXt= zpRj5fQMB98pD5tv8fYT8E|*rx6u_$-2vd z^59wceqjfDeUuC9!F~jGA#lJ8#2dsV)K%1LQf)7nW+U%LEJb2NpsyRU7>5nRpA5_l z%nII8eKq!0KgHj}kHe40mol?VuXdQWaikEb8L1tq7itv3g*O|Dp`M|Uk*1OAk*bk_ zq2`g%5qsiusDG$?sAFhIqGco>X&-7EY8Yu9sUPVY>KkesxkNoo-GJ;4^afgmS{cx) z0eN+PI&lWkO-(bm6>}YP^*Hr8=V(&JWW_kek8qvzU~!zDWMUOAWjA8y%tLlH<01qO~mV;-Q7qutt9O;XmGf^inur&Pms)CDDlJi_(OOBG#q-H4`IDFk`Bl@;z?URFl}oiNrl#`g z(W3R8HHmL*7!5p*Kg0cq_BHGc)WYA=+VKkwZEep8>ur@9skWKsx6NfAZ2uW=M%-<0 z65JHs6%57-F}mY{roGmpQE3KgJenbn-h>Mdt8=(hDZsHzsj&o`@+$5qZZIXI;v_9e zX5y{0O}tsQtDqX|w7j@Uu!;pM&AI9a`YEdcGT=qxsoI!W)GADyh9u3kydlK|m< z$|ct6=+tO7FJy3j~4a@M6 zr2rrM3u^|ffCaz+SO5V2kSCz$1w_FZDN5SU?oL{Zev%o2smJGG-ZRdKJDT6?+VWw{ zD~Vr3uCar$lkqC`6k~+$Q4xI--37S`I$^kGERf*T;p%|usgc<&`u4cGj(x)R_O-koNKEPV?4WEb0tR}87EtsA4u(MR+4;}-?W=%aZ1@CyU1d|dFI`AnkEAJ3l1j-{I6+w@zD z`con|=cLHRS(^JBSVvipUxz26t7IRdPoNdq8tKjYO6MKgC)!=wXWA9TwRBag#Pid) zfI6Hylsbmm0%}N25aYy4SgGfiZy|LAbr^M>Y^jW?+bjF1{iSW4C*XJBUgB=xPT+px zddun|8z6Vee5fw)uJCV6Ki5MqGRqm|%7UUGQY}Ktk%wTNN-VHGB@Wnb{C54^V8c8u z4KH}PREeJ+Kz9;o}|_2-Rg2Q2VI5+(bp6OG#8yiv(eWTO$mP@S4ptc z@YLD(y=1d!6?7hbL(vWx40j@WA_k%UqL*Nv2-5I7@G%ylb96D^I6o&}k)NDjQ+5M= z06j3Aj$|U(2r@#5U?RfENEVv5l+D)Mj+90)I<^kdaY|}yn`@S62Wx5|hamgP#*xO7 zAZgJk9Uh)R7tp1g6^`5Ck}xd%4}%+qhY{iZz5~AJ#x;>uk+qT4k(H4u>28I4Wmn3s zmR&1z8*d}8rw+xO@G2oPr4!j1c7*FNQ$jJg5Vi=`6xRmFjF_<&>;&7P(818z$llPB z(22Ij;rd9`CWt&R@%D zFWxHNVhJ#_^0)k9Oq%=wr!UPUxX&CR2(fO;&k07cD8fgq8N#XHN8ux|p(xKDEZ@W4 zF2Zou%W=M6q9>AuQnPqAr-95R8)y2-U(Iy85<(&P-1oxQwsd*6I0HS)%(N6OKP=U) zMDubh&vw#Qu+Ow#v|AjFoXea{S9jM9S3UPtcVEv#PuP3ZTg|u57ciah&o;9x6_%ft z8dj2dg_Um`Y&&JEVxMKdWVbq&J6WzCuA8pNOXfa@95{<9vvLD8LuHB=qODB zyP%5_Is)UQ2_O#e050$iagV(&Qqa!P-YR>YXXk&HUEy2;_5=HXjRAe2rnCxtxOW=e z%s9jl(qFP<9GK;c3G#0C%1tjUUrk1@!bC1FDdh5Hd4D;N%q!H&bBl$YksXmkk$t5L zO8asL(|x=nTndB6YszcJxWT(4Gw_!)T8P((*IG=>`Mj9?Dt{0YF9*4^_w&KWFOA#cOEC8;5mil=hw$?UQhoYA?p zEIT~G{Y{H=i@#_S@c+SPsJoEE<=4^s(S5zXpfp@3biT|7F`(au^^gXtNbiLHLX6Ur z;nv)abgE*2f=t>Ahj_mdp7?)Zd|V#?9NW!BFkVLx`mZ`TkBRJz>T9mXtIa5nFI7A@ z*Q1E|6BskaD|AoLr_l>7)h(wjVy4d83w@@@@6D_a|1BsGU6I(6 zo$tRFZR@9shjNZ4szp~OpJ4j|_&}L@A2b@O4GHv7J*3C#SC+McV$dOIA%rxRLQ<%q zzK8xDR2_mek91%32M`BQ6v3@48@yjFbEd{#^%b2WTT zRZUIJqDWW%9qwJOk8yy(71kB?W}o5ShM2n9J}m$1^W8Jw?_e z)-%>T@D136y_-$utmcg5F8e3M>%e%#doT0w=P^2pw}`#WWxP7_(M*y2EoUL$Cb+^J zBJi>v$`1?1v;OEg!UwGR;9KEKFvlJ(-^X4f`pTXp*)E6qA4+CPn@S<^8cthTOg4tu zO!}6N4iA!k2y~6yOz@@8r9}@!hkhBV=jZ2J=2z!enQS7LI0279S^_=Cq{^ zO1;izY_`}ZO_AQg z%0RDu9P)?|gIu%JE6jr3MWCPUM@LST5z6hMcIYwKy^(#z$o|Nj;0PoJPK6WU2zV3K zYuH=Z`Ov4}Z_F>uDtDTA1@Sr9DL5K76E-W+KG-#QH~1uYH!I5;q%z|%CYb9j6oB;{7jhSKJah>6vaCg>UWOXlfIbsC9TF69I-c&e z)}cee_QnoozG(kwZxIxw$1?8h67CCnUNKd1oG2tEc(g2#jKo=Sef)Rq12@Yc<9Fd; zQ4voY+ho=dFPi)F1~F>I5lVXzw+i=@m$0;?e3EtIqnN|QZ^Tb^yDif!0&6{H8|!t_ zeqIatU;YNBR_@^Lr&Tkff{2tPRVURewUR$7rOxuB*R4TY2H)23F8eC}8V8FFGVBi+ zvUTw;{3=6D+Z@|l!gHI#-r2tL-_ZN{KXdOL9c9(_jb?f;$t06WGLuYto8C*xr1wHE z0g?a#LPRMIeBHNDC@#r3Mx0B&bv=LFG{tNDu^-_FeZ3>hrzpJ!h@+ z-*GL&Z})obeYJg$b!z(Fte?}(MVZ+HvnSdbCuAS8Nu!&DYWp<%clN<5sXR4*g|Ia4 z3jZYkHEB(}I?tJ>%#C-{bag34V%e@mq`B5n4rT zqibfwGR2CR1)@EobBdyX%wmo9Xx^2Gf{^(+*R@+*rmVh23vz4o#O`5ko@i~(H#x2O zpGJOLcuKoF=U~ix*^AwNh8u<2g!F{Vg^%2;@?uk1>WL}$Q{>rg;1%h=3ufi;L2W3bOX({!lbC?d7z_C#koyMkg;%?X5l%axr&w)?s09(a_{w zd5W~)q7czEehz;je@?=r=z{1-O{vDD5ozuv{ZalO*-x^cWuM5C#J~Bm%Om~5{gi&c zm;Y66@r&-&`*zVZ++26DC9pfAw zjw0t*&T+19TxR!fcVWu)lnp6KhEG$QjV#kNQ@5$u{LJvSS!9`QIcyPF3#@NgPgvt@ z-L?VtHFlw6g5#{C#Cgm)!FA4MbrW}EamvONjbU%WtLjzTE4W1 zt;N>2tf#Gsw%=@n?d$9kM}y;AN4fK)v%z)I<#6wHm!*7=qBk5!eG8erS*H7@ax>ld zp4s2hXgOk$SWB!ct!J!Bw)?gU`+EEa>qN&nM=$3o=S0^fm(#t^U7qrNW6G8kgW+K6 z3M0of!*tJ7YW6d}YnE8%SiZ1`tVPy0t*5LBwtKcg_Ex*tQSbQ1QRY11tan{-+1;Pv z2heY%Y);V`_NT5i@=dc%4@|wx4C88ZfThXul|^bTwYFG0tjV?qwjuTnc7Mkt$9YF@ z=V|98*JYQ>z2Ds{rSXT94^xbWL#fM+Ow&};T~m?ysR7JF%PdQ~g>TKbHd~KdV{LbB z{q3vm0>^kqr=!?;)H&Yut;^!x<1R|+N_jse+3;EF8%DNix~a!hVt#IT$1JwIWcl18 zv=&-dSWjBxZ9TSu_O*7Aqt5ZQqttoaS?4 z#x-V{WuE1z#ot>1ddVfQ13LEg4TuQ#)x7rhfEK6Psxm)tFp+?#~f5GkiQZ9 zb9h&9T0an$dEzS=hn zKPddNIHd3w!6@ET-Z=+8)}5*=3H4j=s(g=dam zxYFFOMILl-OR*XbrzWV~29b$vY&8d2bhduZPUlqDRo5Z6&G31u#KbkOHwRk=IKOsI zcU^ZKb~_ATrbn7Rpxy-wMAGoTF%r*`atpf)%nba%;?lkn53yqZ;GXZ-2TF%$1Ot0c(`(=q8m-elez9wMhPE@Gs%t2j?o9VsBOg6LLK|Cq@{2yXp6%-w%EzelA)%?S`L+w$N{c-*7(%?PtGl z{R;I3`n$|K%pAX=ei!`C`Q`cL`la)V{hYL~ia+8F=EQPJX)V03{k|#wy7+kU2Hq#U zk9jAH-{<{7+r{hScci$hk#~&uarURY&f>ki4|w}{8+luJn|YgfpJWr>XS|tyqy0wt z>1Z{6BmIu^zT(~BwewCFALgAYzEB($x3_qnUq8S8ewBVBSlR5Ue%pBmc>8%D@^x~Et%>#u z?IqeA+C19Jv|s%`3{*x`@FXJ%ajhJ3a zI5UkE2kz-CDYVUE37{jo*I3A##p**oGbDF2XoYZKE-MReTatg`6;_rS@Z%o+&0uI^ zxnRv4);M2!3LLa0|A@GAJ}Vr$?x!@9#m%fx8k8+!`BPyYwEdpa2vj76Sm0U8>ZVdR zQ*r)KwTLArUGC)VT;S&Ma68Nh%AfEOBf&7SuZk4h7<$_9I*t ztX`NL08QNtCK!lV09C83AyEEiGK(}UOn$ zriMbqo5|R7D&MCg(n4W}ID`$7yCSTMile`otn(sbeKUEY6nZY3nIIe9TLHI=#lavQ z-8&E(UdQ@xWeS2?cp5&Wm0&+w$MzP0WK3@zq;E=={u7LYt_*<))C(sB{VxdtkUm<# zg7Qp(9vXJ>S+F)k5C-XuDh}P_fv!wJC@k^_f}kr;z=T611X5qi_^3G$TEFmTLWjp+ z09_;5OkXFO$M=pUf=$Ug8PIkv*a}TM^i1%46YTy6F`@D6U=}&?VKQG1U0bYTsJf

    g`C-lt~r?(rY{xe3>N+g{lTzYzhDuT0bhbf`GAxud;Pm`5w7{<_q0gb7VLHqYnQNFT0bPW?kOpOrqKSXJ~ zeFh#=VB^P zk69yy-}VWWkoqIv*Ay9K(Nu6j7t~h-o;1xXknB_mz!0p$8SJfLLuZ!eFl@=y1j4Q8 zv`DfcS7YLUpj4Ah^yQk1Oo%_h4F&034U;_Ur@_tLnHQ_`m26lnW5QxiYy^0o#BhnI zLgUW{PhDRLESjqlk~!5H2!yTEH413DfxlE%9-bcpx4YysA^mGM3x2&WN0J?s7UHdp z2&Zd)V8hV4nw_vUGh7JWS>a;nnTr``W`!p}=MV*#LK?V|6)pqmh%^C|?dclpy`^vL>#Jy)g_~C~ zBUVqOOEhmXVP60{(AW7tnlThxdRQ-yZv-DTZZaPuk)jZ*l@eeh2A&d`77em8WbSA8nvI0O-#}+|T ze;yMq(zQ#WVOfkCq+iilAX}FzYgyM{0#d1#1%EQM{?PiAAtDbM+BP=noQh$O*Of zbAcDa;;M*Ha$TeC;E-z;ZMYm(&K?j1-9=g!aTIBXiJ2Mr`wH;VAZ-b}G_OnyUDfFv zi0I8^!N9><5tmBsZ@|UuH5fZrzk?A7iG#HnaJwd5M&22$H3{HeeY%3gjnXdQK}}df z06eVMeh(%qnaga^{8L277zMJphlm$gqBoDn0YPO6?#180969tQ2h zbS&8Ovq%B05BO}5a;?l3LlqOgzBnk0%w3?(<-_XxGAX(ChBk%+GrpJgf)(#*7s2SG zoO7ggmG&AFRw)PTL9$MJ5Uw4Gu)u?LS~CnYdE{i)dTlHX-rXA(M%3?X$I{@fguxmp z6LY!n>C*5xDBh&y!-1s10Z{SAU>-Cz+5|2Cg1D`8t2tx|kj3L+L}=dT18tq+}akcY_Vf{B`SKUVttgY$+8^ zazLglq(jc_fOr@v*WHKni|}$>lUtES-VW9wo$*IhBtT<~P71?@_#2=pMkj&FVt+k_ zSR^G@*TaAgUYrK99>)%V%BT4(@tbkRjaIjT!J$shnz|8iuSg$k z%gh*9ivQtvpm3~%43?Oftjy9ipSG0J!X8(wTq5d@^rqoaRe=0cd zcxWKUThKEYx|=FWV1ZG$PLf+&S39L9yY|0_mH5xF{(n0XCN&b6XVQ&?s<*{l^14aq z&zAJBom@M-c6{xWndQ}!swcpW@G=D?mS;$z>-X$nDE$~WSH(v$Dlm-K@j)mP$jMcQ zu7F3>Il6W({N7t3WfkCUI1ug?>jKG@65S~_sq3dx(C{`X;6u}?iq-IRfGz-b{N*Wv z&bkmj@E&_Ku=Ax*0W2?$Fq4YGx;QRudqtrJb%fYV{Rsp0Z9WesO!tUdZu9xj`Gb@X zcYmnhkmeD(OeQq+R$`V2abU|}iMLc>OQVnr%}qMwfVdgRHc)Ae!fG8_MvuRMywZ+vavcVjqDw#3t*Ge6Lpu^ zFjp}|4_!sF5NMdMFhSQeWV%kz)N#m?nY!^@cy3a?NG@*-=D@1PVC1T(+!)M(h840D z=$fR;^u@KkqFW5Q(2_jWG>jbK*b`0S9QPfAk`h71LaGR#_O#aDLz!bO_oj} zAxUr6b<>e;lmvlig$_Bt(Gn()ybf%cZP-_veRMqhq`b$csf`F^$7|$gzdogRsAzK(7t#Geof~^ z&_)pb#Sv2EWb~8_@6%9Q2lF5!6tk&#VU(zoRXu*h)bV&B`iRc-KXVBg)Tu|S%*gfa z5T}h~A($hD_BU_kYVp^?x-(@%h|2TUdzPS;OnKsj5_gF=BAWoWj(5@x&> zt{{il`h9ru!fd`ipSrhv*IX4pN(D{2C=OKdQB=Rnj|zsD)yPvMZ;IeU2LqJ@O9WBj z;Ne8^V5mS}g*6-$=!@W!=|&lr`Mb17s6YSj<@s-XiAbO5yCtidhDMUjBK=kxG)nXx zkZp}nlXj_o7KaQ`=#yyB6vbpR24>_y)n|@C2)HZ@_eKR^6mH-F`7uL9_UrU! z4pG?jX$&~z(kH;XEB(1e2V(x72VC@s)edj&amw(?&fTdX@}z zds`+gmHJQ#TuPQtf`|e=hb(W%)HKp}nqI_*!ifQK5P))=sLH3&c62OyJaq$0i7JoiQuct%vQ=XIzU96ACFR!lABbBZ( ztFoc%tq=~hZqNsUXRE5zhw66?%kUy(zsRc5%FspsQWf+>ZEBfZ(mp-T>m7K z0|iNg1q?%JDNNieLrGzYnGc^&%WU;73aR`|4|J3PqmjSkyp#xChG1)flSc9k~t6zU+JY#dS}=WACk<^9P|(3!j-$j3JGS} zDTFQ(#e}vMnf*!YH~QRv_oW*2Rq8@~I8=zAb zDW~!k!O~Z82ZdhKe+Fx!@W{F3k&4JI3^M;&_Obu2IDGyiZgeah0`2c-=K2aB|51Mv z+Vdhg;5ZvCgJ)->U-ktTeH|SS`u@1lbU*1Av8jc}gU%F!xA!mlP)OgInMW;m7&Ltm z>|J;+JlvV-0keOE5l(gM@z~BnI##zDQA?Q{Ato4CM#r6A{iE0o1Af!15oauzpa&#b zKD@a#@-;BrLm6T2eSIc0DBVnW?>+{LvMTdQ(eL`Pbeav0Khjq~kDHo1YTvevRtsV4 zsBBcx1A- z87k4*4&$V@KhX=xF#N-Ysiz%MR7b~fdFfeGhu4niU0)CL@DYy;H+DuTr4RWhehMtF zGX{}~48vbE*z}q)5N#b8 zsqwPu+0|33@#h}B)xU_(nK5Nj&4ilF+PdmVGyBw3PZ|O9Hcmsu_%IzOBJ+POPbvQU znLH?Lde&siy*HU0`}?B8vbq;yP%&~Alq3OFgECkkxQ+~EQ@_znkXE3g{jaO}Vpge% z;v}k$Wk$o&1mqr@*#;(5Ojn?em~EIvgH;nM6@X9-2Tx>%coF&>8s0%Yy;z+Ae+dog z6cWSf*_A5LeOQMo#m-C?%n}*4khx;RH4bzxNABv3+`u7Y|)Z2!03#Kf{5%9f|jrFp!e9_Ov2F{Rx#nB z+0YDumjj}p#)<-X>3y+?La{L7Ql*M?SPjEzEIgL< zTx0kcmdB~2;Hkn|hYe7919XkB5*gC|{>;5nv>gLYdu zlcbL^yz!z-(r*-OxXMy$pl2)&?Q}!A5Zau|SlC}@meGcd&hn@*5iDO+RR+?-VLaIJqYAYH$5csBG%5wp!IJPea$=?->qV{Y zjRqDhZ8TgbT}_7LFD9+)*I+iBX5;Y6)3CDDi*R@iiwtYvOae}yK^YPY`j4olsMIHe zvRr2CXY zD87V?i%0~Cish$G?oL`HuCgj?Pfz8 z_B^o~4R)Uz{9!LE097F$8>DdPRaL;|E~Hi>I%r5@!R>dl=S|=|kz44T2UH5=!eugB{eD z4M`Ys!J^AZW({+zW66$7hBbkZd^{@}+HM+T*a{8a+^cm#HR09vIg1K=;gGgZ33@2N?H#pbWa7 zB6WTGn%oYpbfW-PyJEFow9;b~Lh~%S1lq1w8@y4wJ;ps8DE%tZ3FSN-S-n=_Z1FdW zLHwUAix-siWQIbg)~F*xc*X~@&?L>aL$d|5JaKVs;L#gJ(De~@3$;Yq{|UOhS;#D- z@e>i5oo<}NgoQ5+UrKIg8w1&JxX2g@o@K+)>{Ddi2J!tFQu2PWakv1acQV9qjgNPF z(m>-(7&Xus0FB#}7IAvcjOr0n`qYjeQ$Mw43fx#ARWne>1O9W2!O(Bla20JJ(F`*F z5y&k?MPaGIU;tyCamv3TTAdL^kefmQl%MBoQ2a6SK~!(Vlcaxk9<&a{iBb;8E`YX) zMj^S=U`(a83=1(s(-_S6^w#hkUlv3hQaag)Tc!%)IdHlvy8`qOzl$7y$+({lk9Z>% z!K?F)5u|FqaV&$}Txk4+0lSqW=8}{p#@|E&@0HfuCs;*Jm&(1@mFoZXy88FBBC+op zk1{~D!6=6Yjb2DI6XP1=Z%jD3Bs-i8-C*3sht}8aOxU|FNC4_&ys!_k1gP)WfeYVY zLlLxzEr=)0JB&vt@vHwwMi;6m-}uNl(I4hCrvyR645cJIrya|^^gxKa z=DzSnLfYyPNziiu6A*Er;S!dpx*!eq#u5DyBMQKOePuid^~a0{C=Cr4wx2MrBF|46 zT};^2X+({0xl~EkcN*W~!5_!5<4u>1MjkxMj5Cp-AB|yjxSSRjO{V_Gs11e9wvmau z@0-43z=`kT=0eUyc^pU|XDi@fZwCj`c8v~z?1}Q!{|ffb9Qit2=bDy4Uhc>w!s44O zbjU9mnF94pwgk3u*g-9&Bh^$Wjrt>kHMAV74?SbD%|3L7#h&g(L?4O+0X)SyOn6q5lL*ayOnCkm=V*PX zthYSbhuX#lNBdC2O(zQsD`oLe)f+Q6eq17e?Z1x<^QAUzjFv-dNzRL4$HS2^zJ$t0 zBYjnoP8uVzYS{nWaV)stZ9l58c+ZqM+C&ag-; zNDZ+ZBB?gn7;x~F;6O5Gr0D?-x<;9%{-_0`llg5`!TbQu4E6@qjc_sxc zpO<5Ys#i=XDa^}3(-XDhs^~#T;`4A0!c$^XeW_J-HQ7EStw$tXU7q`P>u+n)}Gb z_D~04x=pU#-1E696nF`v^d#q+X;u*Aj~o>V2aJpa>W=_QYDNWN;LjsHEb|~t zd2^Hy)&}GX$#sr-E(5l$9%X`b5ejvuh2{<@56KM%jl_&+Xh^OSszhi=Hb~59p;|u* zsrH{>z1Tbmda3+_p;P70f-aF6iF9~a1S}UL``9Bk$3vSC4bGn};=0I5iFqL#B3}1* zKtr&(4;uK*LXbSeSywB}>tWErQGu{?8jA&e4vzBYQELMwa#d7_>!)nOWpQh)!Y|}I%aed)O*axgU60mlk*<4ncmXFj)$hBT=WLX#{PYmV~(N0 z_yaMKFg_dCp)%jhf#>$oc(os}pt3mI%=E>j<(m(|?ZLSc==%8Y1b2aXIE0%}&KcOG zun~QsnN0^xlfnpfeH5s9&K|7?L#bH?*NV;iLAgQcpb~gcmOna)U_uTD+Ujs`*e1p+ z;H`eGUXHs67yF{6{oz6$e2I?-2}3($XL zjn|SzS+z*-8uNKRocf5JMpzTgVmiz$h?jx7Aij`XnQZnj;Hzz;CyP2y{Hl7e6wZ1_9P|m3p!u7y00&;V$ zc?An3hjUXWwbi;Bm)Fm8;~hwf}G#C7{SM|=F37dhr^ zD@6C37xAQLv)Mocb%P3Zw;Pc<(zdJIJ~ZUK%Hgg2z_#E-FG%|;*GARNCTBi0_hLZ7 z4)Yr5+=X;eYReE2!%p)g9*jAk>ks=rH48}Br{?}lxOGt`@M;X9@q&y4eKKQ>aQy&I zRu?~330((pp4!E+;UGGQMcNOTKc<5r8&8Wx?YIrDw42@V{CaLOnS0nAhf}dMA_JPr>6 z=rjkAu1@nhHmw}ap5-M%+a(^lpllt(hXy8Y-K&>*E-xZ~Tr?lyL+Lx}Afo=!+|BlR zU+j7p6)!!v%sit1)$HcN&1$qVpL&3Fzj_W|>_e{46d`cucchE52RNvd2j)FA$g1Wl z!7vNSNcYH`M3z4?ALBv&Q3;DIr&+4$pxPhDL2)k+O)Y=ov2^`t9GXG?#5(@+@Dj;3 zre%tZIueyIzD_Cy%T87#l<#7pu#u6M01|~o0BJi}`OvY8B_PigmaBB`KOAE9Vjpvb zV#TN-sGfgZC>7;Xngy>V(1b2!&HZAKzgxs-61LLfko?yH_$=9iC7z>&q&3Ncr{&Dr zV0bCnQca{9OEwdlH=#JzTpA*QXEdc6JO&GgTryY)stBo+wq9Wpa@TCR_#*5n!<@q5 zc}Oo5DzaOW$ZET#^F?xb?=T5W*j$VJ6@~6>#>RTw7L-v&<-I_yQ&c`gmb)$6kcTA+s!kmj2Or(KO_lp*`Dz zu00KTLWroB$5O!n@Mt(mK6LWySS0*>9BW6VNKCG0TZS^Bep(*#FQu5VVOkzuD&v@V zb5TL*(a69(g_hCLuy##eY)hRb4;tQyLQ6}XB?z9WXUkfqThJH86C;C;A&9D4^O7+a z#fpt(g0L61k6H1~(2do2<15d&kmaql#E_~6OO_B`I~s``(MuK?q!e(}&`F^kJ8`FZ z<2t2ml@GPK*yt7cl0^t*yLmcaT*F)oa%@+lW4%Fw!5er9*N+W{=GQHO>$%I0}bwc|@6%&rHw=~0R8!UYw{}l-eo}ZyV zhw{5T8LZuiU5ePtN5)~JC7`83fX2w-V$|@|iTU0{j8gh?b?hC>CgP2jDd<)8&+Q?Y z7%zZrAL4GIt`HdZp#^QP)BICGz16Z3y0=%sXk%5rJ4px zhQLMAc3OU8!O_7n23Wt_@-ca7kL3#nBpj9|L0(@)5d55t$3Vwk%OddqJueQ5_F2qO z^*tw?LTDJ>6BA7#9^vn|e1vw1BNiT@vwA#~e~HK&9D2kunk0W^aWkM!IZjQ;aZ3Un z`W~jMq4h8w-HJH*mGJxw(tgTf|A9V~dd4yu)=cyFCdknHFn<7*z$aHbEOTj4F3OL9 z@UJayvgB)vhee)Wu)K%kspq4z;5*!)(%}3=A1V#bPw*kn0B$r?Ub2XwJ$D>lG0{jN zdveG5LwDB52A+W&COL4!a!*V;dn_Z_prQq! zDsqoM9}<2OO5xc(p$JO8l*pj`5$?G&)SvRVrReF0h+q&6FmtV{l$IxWbPe?6qaF9T z1@$T)jdKx1Ur1nX@BA1tUSgfk0I220!Hxhc7vv;90&G8x)00-2wUW`IQ{j0fxANeQ zN2O>9vWg&lvTemTxDODv0kIIVsepVQZv?{F}q~0a@s1%E|2Egf2`Pgkl zL>+0pL5Ka}Qihq<&Mwy#Vs&fbKzQlP7;Z;Skd{dT&F@R-mDOkWe)2@CZwd{VcC^x6~d~% zDs;$B#Sd-yx+TrTZuLyXWG`ylfdk{Cm!mgy@I-JF+PSgM(m&B!`pyQn9Mn>OCW*+h z+URhC8f;q*PQ&#+svv^s?0@y1v=>u&ZiIXq^cpHP;)#QI@3UGf zm$=4Sk$8?gVoTxmhwLC>I#xWWe(EI3mI-Ndf}_Ygb=Gg`y!yWNWpy=uYDfMrDsWUM z(8@(EDHGU2$`ESunDZ$gNErHfypk-KXbr*}KNTIUwb@J?^#|RgmIuh8VzTuVv@j=x zwM@hL8+L&o4$G%nh2-EAYcmZ#n`Rw_$_VvzT-K-sjs$2J&SH|crd#{6p?gg{x-9RC zuOfGwthG`oD4P(`Lac1^-W%2q0j&Dk8Ub7HiE#bTizLvr8iND6v5h6GaV~p?D$s8~ zIUJ?RHCBIU8>(PI$1v2rzCLvVpCqrb8quUkIkfNGV&#BxqcydqRLq6W^;Q|$?W|Jd z5cf!#u=co>L#}MJe#Ic@BmNE@J}e1TfSO()0B&g@UjK)+{w+mJAv|9_AqLWZVR1oP z8ptMw?bc>GIlR-_E%KRgO1EJ%ub<}zkj?|vPblx^dG2I%cA9|<&DHP4(Qx&U6~7+x zUl!fgSs^UYw_9Zx+x&w#1ctPGbD&qWkunuAq34EJ2p#7p;L4mAP*$YBvvJ`FU2E=z*&x5Mdnn%`jVh@1-wG~AlP8|zcXNtJM``UT{%5*4bQ0ve#1m}3e zL3G!4Y{9k2YoiG6f|5ldzOgP~kdE`X-Y>j*zp=)_hZT%C*tr*H9#K%sumUb=x@cvg z=L4MGB@c(y_JR-|`g{0X)Sy19PDvEsSy6d7T3;9L-3`aDTNksLR0e3uDnNIN>;fg( z{vYe7=<{~h`aX>8#_n@iabY0sRW}UQGU8Y;_@1>NtX!swA_2cyQ9YTjl(=B2ztBzg zJhJv>;#8t>E(P+QSi=ziZJhJYI1}D|Vl~2^cUeX7)6jw#=={veBH_=hf%KIXw#l@N z-5s`g8ojU-&eCkbAfekhoI;B8M1z`&ZW{}K&8dqbopjq3H4J!Js)5!pn*dHQ1CqUn zl*QOyrjduSHUR^)i8d3Z(+Gu~i8j2@lWd(d5WP_lPVn8|&*)WsBr*{U1{()1{85(( zQj?8OJ~i6r2g7>>HVxcfi3}2zOf-cyw9IlpEr^9S9cDjQT(9*NOwfj-0YztHL&Yfx z$_7*_B2@YX%nbvN34?!@*88e~A^jrJw})zvSNd4I>I!QbY7>HLZm^V`thU+cBx$5=2r4r<0l}nwv@MYf9Zv&8AhN;s z7W;*Q)N6!ujv9jE=?W$XMktjjEzNu;2Q79r=-fkb>`FARpk6a{qU~o`y-8jMy(ig> zY_F}9 zVz~W%{RS^;xmh0sC1d?}fqH`fC-7{n|FVDMm)D`h+sz4Llc%rP4%4YyN(imD1DCWk z;$n9HSr7zIzpr0G72v_%zhKg>=?wUvKw zX)O71scjJrw%(y9kh9Be_)=mUyD%7ry=CJO`AXZj9Ej^9jUkTJxX++*jV%s(*4SiF zx6MBkD%aX1pf@zgC^QYm;QUeuUF*ykw78cU_5oj}3W)Qd6NEI``QGAf8v`Aj3$)yp%Qh*&s7N6k?7-(9 zJx6gDUyKRW63<23c;w!1+p5UiJGL2gIJsPw2%dXXKYDEQ(Oi)ej+%ngI4`eHDNKf@ zGe|hwf49Ahs>_Gy*3@&0i?ZfvobS9xwoPnkRM|O@Zx9E8VQwKl6uKf~Lifw4r5*nT zn+tchu%*QP%=U;53oWq@@+Qrm$AJlBCniCcL@dUwGZDQhD70)G#&t^UB1m0ai0%s% zy1TfLL!R^OH(5};qA-feD1yp46SZDM8m0DcI52Bk1Kwc*dnBw4v9m~Bh<${RNaE~Y zpd&_|DiIpv>_MbF(caGVk;TJnoG9q4vo9q->Ffs>P{~ykL(>&(y}@QzLB!RG8Xp?A zweSUM*!wp!?0b<)S}k@b*H`1Ru~HrsUE@T;lQcVzEKarGqLbAgdo~> zFY=8PBKlnWUnqNEN=rXgEdDQnVTX|+Yb&;|5QEmB4}gfD3Wd`2!l}sKjG9?qKYr$T zv?Bv8EGiH>4|$k^zoR^!4A>Qg{$0qI0in{8&zlj2p{2H@x+0r!F~_O zMEh!pyQa>BHYQq0)m(hs)D?zLzuLOgA42mKTz*fNIt_Z_GVv|h@=41G#y#ZX-B2zp z3WarZ>`FNF0DaUxpJP7&WYZ)K`E9O!5tlM3@9GY)Ov1G;q zdsxdkWI!IAmElX7b22tWSy9YZF0`k?!%kU%H*w^*_)e?yYnj*^`r*K&0NA(C&V#w% z%5XEDlgVM)w=&&7aZ*|@sHTdrEfEGNdCi_gHoRtUkP_Fsb^|*1+>8*A#qZf0(8+BL zT8d82jI}_&HTK(#f#}h2XiRJoG_SQ!1@-R{73B3+yNw3@MvIhCx85#>=YNXiP_o`G zfhCqA^unRyq)#Tv2%?E}Si_tg0-Xt{I_;LDP^AJ|tw5+gv?vIW~+ z9WYr%#S1_sn~XZO5A00x-WI!^4qcR$*Ru_$S-s8PfhWUfI9v4-7?IGi+s^d`BPQT0 z4DkepKQuZf<7$s#vZ1N82%kOvYG=Xo%?uT}_?bOkKy*j!PvoTasy&`V_&4o8a^dze zm78q2YrmkNO^3G5jKq+P4C-yd|9FIuIS?7+!?!2LLGSP55=j)-F`EnLW=PQ;?i?z2 z*9=Cl1&<0{OFEAKjh-$E=7D%e5uQ_20tRt!Qs_PQlw==D;!N=tLypwnaTA62b^HV< zQaK{%0|$~il_NPGDt{_MNhRBnOd%)qrF@X8%>5hM+F*gUhi`82J|+>iES2VZt*sm2K_C83cdY*kV#S%7JI%S_^kTOK(mMIW*3(k`0w+ zZbi#Pti7(B(~m5i>=-~J1ExAqFy5msP9S|}IZ8NCGz(Rq^|NCoB=BX&hiF^48;k;< zdTJa=UgWrlTDEm)RBu}82qO#Mb#ybx-8GJ#=rlS^z=mnf4i#x%?}!n>`60zB@?D$b z79GYk1%#3JK5;Ci!#kS-li|mWfp&5nAL4M?=p#;T;)rV;C4=%8zZ=F!{U2y|#EB_3?eG~#FqF?N<}nK?ab|1hR0m{qIL5)%uct;p)maDn zesnnOE$61?v>6XgP@ zUH}*Nv7+JW8_r;OcAFJQ5dBO8?FaF-r0y-}2k7xVqy*nIMX z^mFG8Cfxi$sV8fWI?LH`IYfj$$ETfA5^=^kCWx}gCV(NF^1gXtjK$ZoKRWqbvf#FJ zDh)#S2t!HUU2FxK=Hf9=IS(uTFHhkCmK+qQ(L>7UA^U%GCZHjY7J%xNKXIJR!thX1 z@YuN&UkM%H2#D~h6M3!~zsBP;&IEBJ>|BoTO?UBysC`{7V#D(1POdN5coxY^e71!D zU%__1;NMh*FX5USMPqLTb2IoNm=`3mP(hO~F5HbC5seFHzNiI*GGD@0nk&GEwlBtq z4=FPx-d9@QiZJFypTv75a$hb)Xa~KBS-n}BcSr+ir9{neq4Q!Ze`X@weHO`sLmbyj zWCGWPlEUMw=-1nJ#))rRC*V7>q1Q?{q*>rPOar0V<+W;}QfLq^AXDsW#Aobyw=(*m z>qk$8G?Z+VxrAuyP7!(yh7#Z@m4TqW!^Su7_t+d#6yicQGDXef!N-(^u=68XAbBOs z^^^u1Rj$D#Iovgc1~>1^(e3QMoDa`Vvb9&TOtaB)MD3h;wo{uXk<{C z3prf0%LReBs(=U5A+FuP?kkNWK|@^?4Dx=J>k~TszzvX*@grQ<=uq`c6%U(7y71&5 z<>G*7Bfh3kcbB52XPAUbx<|Q&(P1tRugR2JmxQw5;iJr2my|NzMUb9aS2K+=*)@%G zy+S9}de?RuSbsr{xM8BJ4zwA#TA}&)M9kK!G#+Xv;fjV#cA;3hBYRdf7(3Z8P*sSgfwlP@ zyi=&u@`GsJY%(fvOMErUSAyK#;@TMr-Jjq-GW3$;uLbNbMT5mBQSv>fU892F()X@n zIPrrkn6&-iDrdm~OGN(4hH56y==Fg`GH8i+%UjNu;<7$+GVuwb3v~e#$4lek>0R8r zGv>@j;o*CZh6-|tq1*Kq9a7v9gOA`A{pPw15xZk!V9$LV_COf=z!io6%zNNcvj%!! zHPfnjvES z$GEjRl+mbq>W_+X&#h81zRU8(#Zc%67Y`cwEbkhcLFg177`nwhMYz#i7Rq$32n`X?*va z)U`Pc^n5p;2s>uWX(UAKewj^92D-!P`2B}aEldt}H!=p)PJ)c?JcZzU+Sxcg%zaCdY5x%p&tqY)y!);;FGJ%$ujSpiiWpS`J*Zr^Kw+@0N zal=UOJoiQxxL5%SVkmK=NugAu3WK&jZj??&szO@&xOwESKJL{FNMH66D(U;Vd2o4v zdlPK#Rc42F6+X#{rbULqlU`+VvSbkU3tB76CX>~}-EIy@I>XsSHP*d@34?0OV&QrN zR+nDymNUqt$!;l++@>Qc7+44h-IjDOI5(~+Y*WAZh zuxh3}w`Cvr;qXrsGO0UZb28h9;KWe3ASY^C&YS&9#>`3LnJa_7R_&&#kQRF*ZR za&(BM(A@=d(tJp{XpX~&Iz|SY@!=P?9KKBE%SXyTcmIk0QMutSdVFR>WIDR4yCvY# zho=xkpEBXphB@g}mjy83ksP_3m5Az_<=!F!!aw7#phMVERuUdSrDmH?8oqJYqf+Gy{6s-h zWIoDx`{X95+b`#nyXTOUNa{uRxBhUTQJM(Pf5AyB?VghW!vElUZ~w)8feA%>rAG2Y zw|l1$)?L@5SKCu`Mo4|8oEr}G?sP^dXpYYHV35qj@IEq{Ln@HiO6s;s8Y}|v`~Rs zP6{2MEWutR9rPlOELWsF4a2Vqr3@t7ttsEj$PWc6Bk-BYM`6)$pfu$??1)8Q>CQnR zJg8tbYP^VO%2VD#_AL%m47V9QrYR{A|A)Hw4v(r@`@c`hWM)#Pq_@dTLK+DvbP_TxbtcJ7X6TR#B1%hwqI42F zJpm=RfRLa^r4vwD(xU<@QUWLnQUZ1<33jEu-?e8Fz@O*5&vU)+KR>UFxZ2*c_iB5s zyL=XTy^4J%9?FyLos=;fi{p65L&|9H$Sk2uG~FJV$;tMkDu?!7 zMVjImcIQII#HB&7P~$~f2Sb6Dx+ugO{$n)>j*xsZuPL6kz9|?&W*(Y#A{ctX^k7)` zY4JW6pU8L`4p(oX#tNpOii9$XnHeyxS!zQK@k?HrS_9EGLM(q)#!?T0<+$)tuz-6O zR_V7^*t$a+qr&w0LJdTJJY=2M89H7Ebso}u$ZHUuu>RQ$SZhqidkW8B8wC}gwlWJJ z=O;2E8vYHxTA{?gPw|z$S7Ou62ci?M24Qtykf9>s`nIT$M5_1&m7x8)BJf@YQ#_Z^ z;zl*yu?B0Im*K-Q=4Zfu{q79(@VbQ=KD2mC4J^9rGh{U97jGEZ@ibL)4GcFn*fd?y z-j2}Lr5W&&`9iJ|q6|@sD;)mOHS1Xogl-3*&{Moyqh^o4lwtLxgEJHmhOAP+NZk-F zWi4wm7C`#69UV@ghIxC-yBRZG37c$ex{uqS26`3tuf<_dSGOI*?J_K*P<_)k(*esszPm1-h zOxOIP^q~vh~{#RWi%IpMqAt|U~zQ~6G+O@-n&Rq2Ort1ZEXrbodJ6q(cZR%ok z0kGw+g@w-zf2c)o_{Y&4cWr-45o!G!hS))qiqN7&M0^BQ-8GuI22!0KGlL3-x#fB<9lRob2I)KaSrpo&i+6T~%Nggy5+NBj*Z!A}|+o|zlw2Y1|(ZXE#973RUd9ai^?R;3ooQ#4* zZ>2Wp@4T-1kHu*8!%-Zmf|{>A3Af@8E5vfTP$q^=!z*Az)vVI46S1PT+UXB~ycj@U zF;cp?Nvr1gE*+(O7gpa-HO^ z=Ki^!4aqvJ-#N^SgS#j>StlbvyWpQI_QTY{p+ z=K#0cd%`!W;R~&I!%1H`J9bW+&E3>>e(v3ATQKCqwo6(at=pzmuvac=_jKXB63@xv z2{^AO{#a@nR#}D4%0QLssYW6N8M!|0HSQtJ+ z0Mj>13!{a4E;sqX8%DEeR)FB(ET9R%T`Cp2V3w@V-H=fCqL?Tu()ED2kWWa8FkJ;} z*626^V>x``+j4t&F<`{7LzwuDhmmesFlGmy!QQIojHEXup25~FqA${9Qxx!EnxfEA z#1usawHSxU+2Rx(nqJmd2YAfT9#YoUM>h{fHFrcvG{qz|WT?EmVt@=BdlH83k{Aq< zhkV7XHeClX8;6gvEk@nvfpq(r25)#7c&H<%_!8lJSmX=QtLqa>@jZ_8B!!Cbm0ZH&IosDeV?B+$^?jGVt^~aFA=~A>WB}|Wj zLE98v2B>|09vGR)>$Z~D__Cc zVF@%|9_LLnXM($Rb3D4F#-@Zgai&frU}B{F%*T(-nyI@3&B_fi{H;eMN`-F< zU0a~Ta^ok+5-|Q@jI}@)M6+&)A&FY3`?n`0S4o0N(F8DHQk4XH12hUHR!OiVE9>C_ z3-Y0U>`H^KJ5;_)lyX|~fli9;n!kv?cxb4M%A0UA0YX-@9W(QihC3a-0xz_sC@^c-|!hkcumXkN3l@{3E*L! z7X$3g2@F()rd((IC!}eB4Qx6BAry@-RuieN9Snu+32#MKsAHB$hV&wWE zWaET2p-hup?@pI)N|LEV4zO99TtC@`ZB^=DlyRV%l8W}g(Bs;$Ab(074)Zr#ydKdz z!~AIRj2I~BH2N@7@8j&k7a?jy6qwT2AR6k5vN@6ZK?0{Oi8_YClO4CwkSr<5pWi_P z02V47rK6}iDM`34ig1aVI+Ag(ea!*B^h$F8OmTMwMA5zjpj2*g9Uj7trRaOYbF@#A zl*)4qKwxb04`t>3^}hgjDt8ZH^)~%C3Vh){piIP zQ@3t(@l*Ve*O$X#Z^1LNpK(X86p5s4ceQ>MoECcqgwo>K`Y4k23V;@Q4yt}d?|8V8 z&e7MAbgq6W`*p6qOiHTPM_{N_qceRn507(pDg@m-r}~7k_IdiBTY7tGK<5z;^WEm~o3cKJd!Pp_LL{I&5 zg?{3_^wlf%bLkUrFlS1y;`1DO)d!aRhv59Qah2YiEncOEj6HR=K8Gz@t>5TMS6@P} zP&A_A<`iPjQc)<2psVT8ISs4vC>>4uox+_RFL@yoyCJ-NFiUw=?+aDtd^~g8zxAkt zH}wA0IDZ7R$#3W(`FulvjoRM8oj-X~52;0DD1;I#q;klmvEHfJhDmtq3W!x|>d-90 z4cgY@37XgG2hu0o{IMtCMZU;@60fWott&)dUawDc+!Tf3)fE{UM#UTSlN@O{;46j7 zH|pVJ_bD`Wuj8TSZA2%~HHC!Id2?tYwfU*>%~& zWONS0R(&>IUm*?RAxQ4G`46X}W_=FTE)y;bqU0-)9#FGHLiWZlxUexa>%Qxox9QLF zR>Lah1MC$HfnmOM-W7dB=z2o5;w`#>KP^F8lvd^{+0awT2OM-8+cz|_P0 zEWQAqFfc!;UqE-ZpsOP4L2U`)(X?+>1W2owB49#)NM8;5T{oZ{KEgw^9YS^W`w-Li zoP12Ti%f11BfgKuXv)Xpbnl1yc~nsf^q0OgTtyc?!VKN=k^Vgww)(I>8z$lphsTr8 zoRDB@ISP8D#eJm2(eC%6ya6DC(JR=2qk5>XCVGw(;xMUQ4MjcvKzDguKa}OS>W8}0 z{*(H_EcKK=0m8R8@CtR!;Q_qIV7*|9^k&CDqGW`FCqo*}=r22}>i8LOEK|zp-Ss7=>hV>PvT4XHD=Ib zyRiyY9Dw0c&DZ);$Md#)tq-Sx-{{{X%T?Y5@N0DK8}vcpmzuuCWeu;zU@GMqhS#v$ zTQUgGht4iUMeL1l_2*njJ|#lNG(YGK2xdkk(bgaFQLVf#Pjqyp)GPRv#&DnRwCjjI zm~FhGuM^WO9n@x@>LmSXlMKF&TA4JQX@1e4hjQUJ{VX^3#h<)UIQWyw`ewsBt2|mn zMPFl+GW#a#DJR-JioD_>@wpr4;mOKx>VNkoS+EN9m*rp+?Uotlx&P0gpiL}DOkZ4$ z%_P~TSTS4ZXLu8aG{5*ovhfDlJw+d)A)V&NZ3w+QRX6j>rY17S7+`oBFgqZ4YVO>_RD55(x^#hi2{u@bu zgM>mc_>x3S*SGmONuiPb00}ksH&px$66#V;Yir%{+37J#K|T@@+;xv*j8(juBy&Ws z^*7YB+XD4^=l-D!@|;K^i}{Jk5&6{VL<@g;0>wjrFuMHl25-Uy<} zKgIT?_A^=7a#k2-)0}6-6R7PkXsS1kHYD?1Gh8!A+;@99>AJ^(2p$jzA&W1J;E&U& z*Wn8rcar~+v#>`E*dbJZ8!cjUrWgX`?0Bug;?5zY5aVof<@gw14@&O9GwVHOezXyrp1O`E>w6bESu^lyNTJ6dc%x=0F$=7Xh@<; z@2K#5fHfrP{sByi@#{b<6QyTVdoSb@O0K=tdHN5qIE5Ow88Z+`V=H;Zwa#sQeUzI?M7z583peCt=j!|=j^Xf@Bkf>>#Uy#7}4#0Ebj z7G{%@C-DHYDUAPir)2|;I48g#*sP0Fl2hrpP6RC$k48L}oWyDZi~&Fta-gYgnV*_8 z&jt^s))~p}Y>~>Sk+Qq7MhzCEoKGs`gHH;+@K$)9UwlE8;*f4sCm7|-kYI$x5^$v@ z45ZRO-8hvL`hzs%Gn_lBCR>^jR%!o+WUciWJhKs%lJ_&AilOa3LA?t&5EQbj4Gxq1w<$ z3AF{OVG_2`=*gNF8fOQvinT^2BK1S4A5c(xbM`3!&u$=^`nieF?f=%LkQ4%4c_N%9~nVV-w5z#^G1!5;)+XC38$IDKzyt5uq%D( z7Z};_DU5t_t_1cb-IYKYJ-iGm;fgYMz*^rjLbEqW21LeG2-MGy@$%&J2jt{m!KM9& zw~TP_toFj*slFHkd+Tzv0HRV#@c`^|>kxRy-(44)$V%TeCh-Nx!aYXo11#1g3fYTM z^nVgq(W~~O32OElx!)cLtdP&{f=T9^;;3lqWU(fZUJ223?LOm!EY^PbB*KtifR%F$ zE5Db7b|3L8q2^1W5;n5MI15%`?;EdiDl4Xp4~&qa3_s|Xz!XP}5e_t~JGZr=reko> zAN$|ftc^#F@cgd)BNEqzjJA-9I$?$3- ztnr7)22+z2Q*qfVnNZgcmT76rB&Y>szZyT`$}ulecle-Ivb`WwEQi45!)mx|0`2Na zBd4-V_Y(%s-iT00SRycIopmd-QvVg9iiVp;=wW8bQmA7PTC*uDB^&~2 z+~wLFSs7JG%}|5QmKCwYzl=@Xi|rmCR&zlXg#^KQQ2vSpmgg7XkkWAyNAi4m(klqS z1LHZ;)ne{Ts#RfE_>?LFwiKDrV)Da9Am10$gf!&>=H`|OQRe?EJyuPv9cnP)$7)`q z+OZm}!)BJj;bp9*l={jtFBIgC%pB^%Z+0JF zmOl*pF*x&>oJRBr1Om`I&;#>M)N-=*4-_+fV&*wF0W>QVT#%C42%QZGv%vWID%t9R zneY5Z%q$dhn>-=MnxJ_RZZnx11cEALx%UHSeOX3!W*)==x8W+R$;*_ocXKmC{aD4Q z%vS{9?86*0n@26hnHs9xR+h;_G8*0(Ig4zKkzVxC(~(=~?yacBR68gD(C0Ck9?bes zW`ZkqJe&zg%_EtiJ^nW&+J;AgTP=G8zrkU&|Eo)PJev8j?0-a|od-idp>=(7BUh3k zFY*o2+?K-Iae8Lpf926)#q?CS#)jNTAJ#f6^F^UC`*tyDU#FIpnPS#5H}fwy1|}_x zA~F;Vm>GS_`hRjBS{?}2;;n;)bLJUoe9cCCtcQ?-ZbEj!&T*dsoo9ls~Fl` zN*U?S3K}z)Lm}Y&feBsMS*&>C(VZHLHFF6)0G4B;Gz#nX81NJptWNd6VX^akE20-)3x$;UH%ye>cVr&noLTA4%+ddA z&TJ%A?amZ8SOaE}ss(52+_eN~==j?+pfK2#xy^;^$>0#mduZFxUdXuUtog!LB((Jndxd-v-fX3oBcpY`K_n7`p;DfLSXgQ|E>k?;R6*x3OzG2T<^ z{XZwpx>|eD=y*@8t>aM*&GDXC0`(6Sn6o{otUYt2$mQ>7+R`3Ho=pF1W{imX?}xG@dB0mQ-TfY^GyNm;!Y+kyuhc0L~IH^5tYgC8`1}>1Bu$fLE6emwN~$} z*Z&i#HiEQERbtj%p7n$;o$Xy=qmBbGm8wV;LDl{eEKf$I!i=asDhuYb@Kk~dEGA1y z`{T2IW`NX=!rq7{ruGT;rJ3&GIvnYf8tsUNj{|{H$T-p~%jm$>Y9duI@adBUfb?*z z+=blZuc%=C#&2+x<7yR`RA97!q;lT$ZUOkQJeR5aW_{y)hPLIQew zBgNnlch0PZKLgJ#$jAaWn$v5A-?W_y7B!5-4{6q@^!!f%HCKaPieCfgm0!B0I<<%6 z(o&AB6>f5EY6bRO<6*Z~IS_w@J8e~lVrI~1J>+~AWczWJErfkMq)iV895g5EIS1NS zxJ3Sq3VtdXK<6qZ1jBr>ALsxA-?lav=6JWKr&?hs!e9LxSeyy3F5F6h+;-$gz$b|R zW{N56nDaLkVNk((OExJ$I?`gSb6k^VW_x>S5#WMw#-p+7HSR& zfg$5VS$U4@8b)AaoeY$)5a^hYwUb*c#v`V~KYv=Y7WvhU_-Vt32%+vRWLS;5(OgC! z<}c1KWOFBHLH8^?NPQBTGI?s&O%BSnr^9m)kB#c%v z#p0|r*!b>C4WyQz!$X+jrL6TL20rc!{w~94ixQSnD_2LS!JA)$FWKo-STzjh_AOho zBvidM>vw9o>IoC2ZCQZi-1HRR1Liht%Ys!>b5>|D0|a-O%Z^X7hPl%`n+VFzTkyy# z`~~Kn)z`7J`E09ZE>pB-#d}ia*(}(pILWrYRP|+6F;kt(f_??eT2LCl$$~kCq-P%< z3S^3Jvrc#CgxZ&=tRZR;&9;r{NA*gez6*8OZ8aNW0%%{)p4hk=VodxGj{LI*TB&dy zObKTF5E(BNVf*~DD}7i^WcF9?wB{@IVtC18ZaLQ)0V`218+|1%`)Lo_o1ERw&ZT5G zLp%!N*cSsaQ>$TZx`t4do z3f;a2<&x@Sm;%)O1uf8#8-dBt;9~>0_XE#vR0>f&eLui0lx@;w9}2j~wmnfAg7G;t z8=B@O`AAw**;D*rCPSvL3rF3KWUArWTVXgfB0HPZ4MEwgV`O#%v?l~12WYlXC63OH zrn*z6`Ls&x-Ge%s-Nck8_D-hMdW3F?y`ghQ8e20qd$kA6xU8N>E$tc!6HUtg%#Gcw z%EpegbPif@`_yc2`Tr;2?Esqbm?oS3^-T8Z2dKByGe_;m;^$?ji3Id5c`wNh<=9dQ z$%`Hu#N{?fn>SN(usf+cY>4Q-Gh$R+=W`klw9icDA#imlk z8T5sUFS6Zf$&lD6nmZ&`!Nz`(eJ+eH=D``6e#wS196;SUwEN3gc!^E{XXS5H-IRea zH>&t68~pGofqm}d>)y-w<4EiR^17X^Bx|HEq-@(`f$)liG(G8dww0#6ADcjv-p9kK zhxnRU<(=&At|WK!$e=nmk9__ofabb;WKx@lM_(Sri3{!?xdifVe@FDXPcIlj^9(R} z2l9}NRO_VONxlwtw_DwEJeb}sXPz5P7z;;c&CFaZB(HjNFip(_D^n6%EnxE5f=SOmeC>dD2P*>>h`cEg#U4MMu`zZ6-n%Ox9 zfxip>>r**%yMVi3Xv~1WGJw0oNnq^?QGvB`qT9^ zRaV1x-~bjM%o*MV*e!dIt!ri#ALd+!&1_^`3YA92c{iL1??aZ)rC#j%*Et^mc+(Ru z{`EcGlq~#q&N#_^fZb-d+!6v{_hG`}-QD2#As@R(CVLsg|?N3`;rG9kN3$AY#y8>s13=4oUzfeS(yPkG&d@mTjKf;&M zyLT^PB~A6gFoJA$g|GZ!Qw$&rjpL$75l{dj$UMkiYE}jG=_)sdV}AeOhz^GXn_J>W0QZh1!$1#D8vD^W>^DRvPc|zdceNAdiz@J> zEgNAAukVoy+|ZdxaQJCW%~g|r*+iJj^~_BXQDux8?iz7L(5Vi{l~a2iJe4#*j>G17 zK(2{oX}RBV)Ngn6E^NGyThu7WO+q{Nc|giS(ge0omwOy>Q!e%zrd)iaI|09GWNm?E zS*SJFpCJu%vcUFLd0O!D_fLAf!m)QB|`(w5knX3mGXjJY(dbB%Ccdp+J@>pIQjDyN_k3=u84mRT-?D6%F#3K++hqFQx zgh0h>;dzuYh5LV;QDyNzD%#Q*gbl&q;67L{1&4C}Zyakb%S{D`@U?JE8E4>;x20MI z;y1qpe{kezMeg)_>|Yp3KZ=SJz>VtVO1SYnn!C}3qzj@=J0|AhaH*Y>b9=ZjO;v7= z8v*+ZqM>ItP8GWj7Ho>Agk$S-cZlf88M*vW%f>a)sqE&A+%vGn+U`+|FB1%hhfnF9 zLG|C|c(URba#y;r$@6n>!%HVOZZZymLYucP$aQbf`zn}rN$y5WyCi`?pCmH2d0Fm& z`#8a^{bdSv{>9u#1p_DeqQL+4S(iJ@jpl;?OKoO1Pf{K5fE9W>bklX4(7Jyt>mAMJ zZOTo-+_gK`on76Td%}YO{oClqLA?+{o`yKmuvyiYhd`Y^jXK@8DJZVtp$HuMbSm7N z)gH<{4!4`bxy@9208&=)dC~D(y|L=v+8cfWJ3%UIJpebq=FdIjs6&f2LGzz*U+?(U zGlBIul8di*a|ul7ZcfHnnKvo`pQlj)G77rx8{Mz~Nxz*2d7}MvZq?uTz93<==b>^A z`MI-EU*>KG2avOTYgO(tk~2wPs+bdmTVKreVW9fX2SISLI-9e6VLWjWl1jxAH4I;u zJ_-wo<;q}MbF4RD5zCccbZovD%q1u3cMOO8p8N;CFBaQ;ENO2>z*(`icQ=-DBR5}6 zcmBwo&*XpQ4uD4;$N4sU^lF7g$v?evB9*(Wjs8x#&V3*6!?~D|L7gs6@@c{$+IVzVeqn?WrCCB+@}l@px}HV zNx0lCkW&9dKg6{$R598YhZp_{+H&h*xb(LC2^umoU|sxYZyfl(9{LD`WSssS$_A?Q zT496&P%uadeT1J2q%Eh=fLlL=ClpEtQM*eYm^t%1q{io=pZCmz|6v#~y6T_cF7xFQ znVcbGkBa~q#&$jL>wqqnu$-Pxhqv~q;68bj+6;F^!(lHG+hEA6b-TAn{|ADwtI0|| zZ|C91C*sl0hV_YOw(Puo#ouS&im~1}8!!>r&DPOqVcR6oIzJwgH;(`i4q%N#^EL|< zVHk3{gv0x#G!LOY@M!}@7_#WDXw9eL9Gm9MWrpIsT>>;1K0Ln$!y~kJ9~f+x<^|A~ z%f<`WVFS{qk8nu8zXe0o43L02HZOk%)Ze`NFBDFGL>A$0tB$wZJ!8Km-#I!O+_dL1)(nK}Ckb zuwHx3=1!V&c#_O17hx!P17Vw@gbE$GFlbeJLH6;7KkUDh!5(kce3OSo#CKSZcX#uG zC&LzlxZyV5zWzJ>SoLD>0bNN9NtGT{cs=h|R@n}0EXNOH+y9*$2M1J#%IKSH7@oE~ z47Pq!btrtgc43%wMq?eJHMgVVXzpdPFUJ%Qpo_ocCDESCVhk0|Dqi{Lgl;@n&Nlp- zHyxXryLr3c*=^eF#v0vC4X#wnVZ;l?rW$IAnK&474u5xxh)zC%q1T)J?>w*|g117G-CB`Zrc zHNbem1@mR~4J=uuKVfeRj4&KzU3`6M^Y4IY*Prr)DD9M|h;1BTf`J$?!ULGWU~+?4 zYLE$ZgbTw=Q`~_DhEHb+7#Y_Mszg#hqQbVK#1w$7xCyrRaX5W0`PYf*)V2rP9LsFj zGu#dW5|>}WR+pIGb7z}Jn+^y}-Uu9r8YE*t1ut=r@~gNUjr zzArrWo`h0z?xQ&U)-V~sT$19=(*;qmb9KRa51k5s(W$p3)F8U*L-iid-{v%}S< zqhgK}{{B9Y@cv)@LEf1p?oTxqF$f03#eM18Y^--zZWlk-l_q94XPW{A!Z6OMcp5KU zH-K|b5n}aEnd(KHHH`JcFTqMy{etPZI|Ka~LO8I3 zJ~}1LDql3Yv%||x-wXWUfq=yKg$8DoshVZHVrqr!M}rjN+Q)*hJzi&enY8ct3BcfV z^4VbOL6cbop6!E4kyOqi@>u%@Q?L^q`~)r8Yzm}?(PhwFbWww=V`BVh^(T|s8hXYg zv-+(jV78myHc5B9Wjf`g2lLe%HEol@nB-lPmH-aM@lm@Ws9e1b3g^RLPl5t=0NnSR zcbW?C5ryM8L^zeKzj=Te`kM#NNV?hl5Fcv-QG3nYqGKD%x#Qm{G z95ye$9~I4um#}jmnQ+iqCzE(6RX4=#=YRS#h|9()S=cwGmwjmBX`fh1{t3_}g!~(> zVGP&&1Rvku!iwb75pTw866;mKt3 zN~P*;P(C39JC;;9voEQI9m`yk2ev0BFE47|i>bZi^+d=^e>cT3>n#(g)c5>%Z%f7E zcN)!KW14falVMSB@y8*5w=w2~w8lJ4pRHB?pe$N3Hp}I3$Tn>53(w?QH=zht*>Znh z9Pj4_l{uua)G9K2vle&rldg0s#H^%Aw>|I?@W_NLx(J4~HDdF4oJMjwT-Fe1#zAAB zLqt@6Kmyvq=MrB!c3JW$YYZ|E{y#9xMrcg$vzeG`meO6RSOjqepqy<#LAVLTa({=O zwI>N`)znb)03;wC+lo+dp=*1~!C-i=FS=@&8R~QJm)XTob2Z=>9JBmqSP_PGJnmev z7k;0%y&#>4OzW-j%r zgoRHe1jvYfdJo4;)v^${vF^tbl)TOU>{_xp)*T}yqC1<^+dTOmiW$0KUOyK&kGZwj zts6%%6Hv^7%+S}Y{jWS`#Q^hFcb23z2Z--sm>Dq4WjN_|S|}J0#i$mG#r!Pg{R(S# zG^!WPu$r@Jg4I0tJ~VTOJLHAe&}j7WAiOA)(?S$RObD?(HZ#^Bt;5U;S{*J=z?Lfv z9a!lNB8xMG8TXfN(5qkS)*XA9dlFCa>^}8W#*;-*9iK!uDI~;_)*M@dy*3o7Up~s9d2a;|K>gNHt^HXEY zvuX47h$uMKNBFVwhs@ohIpJAPN8Eh}GTl5gHtgU!gWS8oT<%66FE&Gnw!|Du=MRgZ z8mq%Q{;|Y-+J%AeyiP<%Io!GBAV_x?|EP?l7Q%}I?u<1I!ORC6TJRCctO=#cU|8)` zJf@Jb9ox+~Y4J*S1ei*DWIinYUGq{m*1p^Pk}KP{&wNPC_8v0-?!hh`HGk~NPPdvr z6%fqv?B=KDBY<0;Hy`{*`Z8voSiJ6=7tDoxvx8s%GZG`s6SIu^qWLzd+RV%T8M{o{ zEijMY&}QDnX-3@GoT^`s#;)`{M32>{;7BOks`g~GH#_r#8TqWzh#!Y{CD8;)!XI_Html28<%$) z^S==>P?lq*oUNS9QWg1w;TL+^AMW)55aI){43@wf@RMx~gp=)L-~3}#&)LddX>5UC zzRZQS`R5PBMCYF$$YuoPA9Q1L!t&u}z7j@sk7K5WClmj72aaR4jC4h}dRLzS)&lZtjN<>H=uNRJ}#eWhevkH50X??_hkND?Bd7c#UtkCK2DV9%;VaV;o%IVHw(G zwhM6F9&j+<>c-yrDE|i$E!6R~^Se;wiHN3__hD$5V(E`V>*ogr(7C_hPv;1| z83wQV`9Xoy_&#?3cR$aEP_vW7>?i+Uf|w~iEXtep`YwN=gT>tPeSUY$u0MXC-^BL* zke?~t@iTARPWo~zYy2($pgSGCnXh7sTlt>}^yMU4^KqC53%!%?>OzBmegrRLa_hBR zQy`+sIF`-|t$`omy`t zDyeP{Y}h)c_V@41jHCJ|`k(BKJ;T(Zf>be{A2{a>8aV;pxa3{%CCz;iBFdw4`(t`u z9^uPw`xIdI8SY;IE-Ys{EAHlbHk858+;CTs#E=f_)k&xP?&CV!f(sxL=o{Aqr#Tns zsp*81>+F9DJP+F^zFFUR;_zK5zPKMtOmU>WzuZ0y(nYG|q5MPc);gs2DLgcKp zxZt;7cI}CR-7PEwZ7mSs1Em3c3*^IZY+g$J(XtTQuEHvIL3n-dB=s>l5&^EQI0kEE$imkJ? z`&1}(U*pTvnbK}uAvsm11%kRfAW+BS+9^n91S=ztWd!WVTJo)-V>sPVj)E7%|lWO z*Ma}vI9?${VYoP6k;rV3h3CQdnH2+fR{m%9%$P_fN+>)IJkCT+Wi{($BGx0d@I4ST zIJ3F>ckERz`p97*a~ifI2!YYM5^vG0@D{UM*@du^1!oF`tkVjJ?4~JF93g>57Z2gQ^Dphm` z(@54wGY|`XTzB-LCnBgN88(5X*1{Kk8cGW#>_SOlt{X>1Cy;J*p@c3h@r4wzxDdOy zMc6Rf9+&o@O*Pn88)_i2x|gAOX8>C90>tS;T8}$^-Ke^)NX)K`F8m1xoV@|osNnTc zKekZJMvg6u z;pHfM_Ht~z&@XAC!5^o>oPk^dAt`-n@W=P{8&?<~R~7yw!wmJOn|B6po?#T~Y27+K7)n z@#&XuR;J1ihs0NnC z$-2fq;dg%!#=i7_q~Yg6tm4ztAP&6+HaF6HQTcEFj}aKyk8={(V*W=?<0s1DKu{E?H-de;C-If(79;Z~t1YrI)F7Lv`dpkOlnh#LGW zGY$Hludyj@+8|Ocudy7bDi@2LWYTywZFa*tE#-GF*k|kl90VaRs+UCsF`KL9ZH&@$ z01PT(3&gC#-4c(T>3FR5z}rTm2zy{efl?Unj#IogxW$v6xnXy+1r1<0039uIF%los zgUadyBE+#E%UQTQ9gV_?m5~-DjgJd0q?T|v;DFN&ACqvkKP?(JEuD45SO$dB;kjwx zR=Ni%nLN`n9!DTOp9X!-x){XsEnkCx?2V7$uBBaS~QB(sEhA zc;f&ueOxeX7A${(?cv0DV@>>!1ue^ukj8ut>m{uZ*84wu_Xlhv1Jc|#(z-c{>^%T5 zVa4YP97<7Xf#&Zsc^nzCaikHVFO%9k@*ip6Q2v3fl03x%&*kJ9mRn*D`WDlM38Hw& z7gWl(FjfFE(;>FJGwd}Z$9`1D? zOP2P1;TJ>`y`lVWPq2EjN$*=Ua7lLY?Lqsz;of^a5E7;0$)FWf3{j%%zk%~2oELUI~&za(lZ(;sBRtM3Xqz#g(<0mbu`@rJ&((cj4|CS~o#ve!v zzVB87Z2VrXKpgLiEc+(UQ+ z0Z4wYh(INOAPRX4N0Z-6<9u>Q)aNsx>8b`wVbZ_i=;Rad^`ALTeth+ zsO=7m-IW&pWEn@doE^`T75r?Ojq}QW2~1@vzgm{N(bi<}TGo67plMqv_|k|%+x({` zfXeNFb0VYx_iVa^YIgbb+wqsBHJC%qA?)|JZkDh_m30%`SUAtT{cRXs{TH73wq?MLu^Xre}RGrpC2$jzd(2&o|FiS#6 zM6v||5E6ZdsB}Sq7rPa0HM_96u~t_Xwkys$)`fsTkE5#3v6QSlDg`O2hf>a-Pq6-V zKNh_uFU}5cc~sfV0oE!Ol4kwRjn0!h9R8$5Ane?ThGCP|defB)!l2`O7A}PoA4&%S z?i5UqTMbqaZaxl5p{kFApn*BXjdULdoL^zIZoy*uj!NI~NIFboJ7MZ@@wY<53XVFa zgj_4Qgrk)GsVyHp;_gZr{GIq1Z|et%)t!}G8EvwTbMYAeI6nq}YR1K7!#h0&+QD&f z@jo^q&X*>Qh)ZOt`Bog|Yz=n@d%n<$$FN#O?3%^8(}m6!S(~WLNna19%877>41P%( zN2PY_5;E&VvE&mBT$e5w({@{i0)WC&3yu^l4v?}}23c1*0qwQFqeatNJyJ-2*u$Ta z4|^!-O!~|~wq}U6>Yt$PI1O$o4*%|08l$8+KShT(%)-YyWf&TE#V{-UfnW0Ih2z6g zL~QFY{v#bDtRvX0kydOtfN)QwYaOuRhCB=>H56L`ohi0%$C(Lp(m_KmwE|`~QW?Pz za)+JAhY?w9&1maoceZ7mb&MO!fOHT_!4(?I-$3pJkb3}ih@tmydKt$5sbc8lS1(8p zV@;E+SMCwsp)Y$`4SpUcyth4Wz2rg*KzOH`r=jhwUOaO=ZGO@UuJknP9{~dJoz*Y4 zUc^NEwiTXVE27|z8aWH=-FK|v{D9e59)eIZa#lC04f6=5h^Sc*E%1@7E!G5<`mXg5-lHJKLZPizC~~;Qw7NbC z9H6h#VbUu>4^p+Fk~=fPs4Nk_ehN8O#2Td+l)Uj<>rGlbFA#^J zTnmJCe>$esj$3d^#vj1B0-$hM^1Zb?10jBe3+KQ4@`6}}{Ah(BrwjeQWVj@-3;hl` zFQ16(uApDZuUfq^8MI-(DZPf?)3gPL@|It-PH?2{-r|92{1*!-YS*pFZjMQG>t#sQ zL4i*o!!U?2mt42_uvczae|KZYezk_W(D!p3pJ7jU=LRORXMeZW2n6_9RQCcFFV&I& zENE_7pQpwd z=}`nw&ngKh>ORt<#bh`Hd1+>QUXa6s)gX=uU?eRXDRAOpBQpmh%QIA}q&*$8pn@rn zguwp>4(cuNDgw~Rt7sBO$@gL9-bGFBY_hWGL%9PbKe;1EOzn|SD;(9#_9xvfe;Esp zDynp$kPiSZ*!R8yp2#04`td(fmKa_1t5X)6No_l06WGT|MUQ~x>pWPc6EBbBj>VB4 zAabflgDZ<=Ut7N-kN-lOAHeD}ihRTzG#|&VWEI(A@z4VbQVrqEzeNQ_qumL91Tf$E zIb2FwThRx9`2lq1nwuD}z#>eHev%=%do3};)fxaoLeFonJ~Cx-uy zf>}Q=!bvX=(9kp4Z)b|Eu58vhkbXD_J%g@`edEZsMX6*yH;Vd;*!16uDgcCa;?LF8 zcvk~oq&sjUY##I&5Z&sz$$}v3mdN%s8z8pLbES~8aHl%j3u3WP6lzbl$IJE*g3^r8 zhBvY6QJVvxK{N-**kV81ORf}fBBDQA8EAv<;QKJ!1c8x$EZi2sG%DNsBBt(U8x8Bp z0jh3Tp2MnHlWj`{tq}Xh>P9dV@9e@}5+PsKKgV__fHv)e=Kq>GBLN0=Hux${3XT;* zOkQFu#>VMg8?0GNLZLFOuwCJ_^Z~4BjI9P^JXV!PtB2!j+_caJHS@?Y5v!SCg9VR3 zMsMv2Pw48$a7K@&9IiOPqhn``G={We@VDl8bj1BR9v#a!9)hP4zz}Qk;H^tSzy+RZ zQ$Qre@#xJ{ZPBdxDcft6SEnAf201 zgTtK`+k8noGXq+29x7}Bxhr*<&5s>jYTE^Q_ew}dI#$`RNLgWXXSY|_^f+Cyy9zMp z6*d?&oDIg2^z$O&(YX>Qb~ke;eHqYQFVlU zgS+>?z8#B}wZ~D8+Meu+h%wS0N_A@c?;>969oiddSJMWKeN0!`6lbw%j{EG1yzf4c zHbvPbUH91*WiOTT`L3X2e@C$=th;(Lfg#a(`?gs77tZI5v%e%1 zo{qv|dN0oIPm|;A8y&gf-R(ylp{w2Px16B_`xYFSXeF0-CuZSLWV^p`eIJ*9{mYAP zB-qn&oI&DukS(c`>?0kQ%uceeyLZWiWalLbxCEz2CD{ki{$%?_T+*1*dC9F5`)|U1 zqfkgrE~$24s~3p=r0PEQG}4bRalGPMJhg)70>|-dMh#Wl^0<9sL9($_xKMfkZlaYKK82~_CwL^zJUzq1Bd>HzzrE{+RdNwY5yN>4`V zAX=M-miVzKdMJJTP;ojPPaovx#hW(|xxr`w7i+YEQU-Qbe(gZ}0mpqz8TP5pN6D~P zdh=^+xF-Bx?*F2iTNx&*9*VkYvkdkZ^74_V;(2wy`es7%+W8)nboRN9+g#V#pB5f0 z5k)mLMTZB|7!wt=H4_MW{Y&T_Ta|#(R~iau*#`?5Nyvz& zr?Sy_hLm_%Dpd}G7JQaHM|i4+Z2NS_tKPFTtzk4Dov?VhU6v{})B?G7>W01e2 zWMz*1NugvkN+xxdTr>o8obW0|`|Qq_FUhq(E0pB@J_RL{vl6EOBaiNLuZA||b=FW1 zll@8InPX7A7e$(J1i2v}=ecu5E!p$#E!i3kF?tCWRM+bJ517>QF-6H%JZ?%lB49F3! zICKHOA))r4(2dj+oj=4MQA?wT*u!wl!3g0}{@Xg9G)0K=D$@%k=(i#EK|&gD9|PHk z+9wLpNJNc18c1IbwGS2=rYF)e(J)Q1?g_Nhzc`I-s`36pRywk>khOIlST>5IgA0Y~ z&>%INSBJ_2fb8n|yeZ4=j-eQbyc}GytukIipAENLge#Jfn#-@K@X*Fmzuk|d!P{Yk zzbD2af9O11p!yN!$ll&VaSYF(L5`V@-^)bXM%hEC^q{M=$+m8{JDQBYikat*7v7Aw zQ9dm#wTF|w*gmwYLJAydJB#ghA&tLnp(8D&#QAxMN1DZvwynfDX86dnI?}qA+8qOo zS7s4Un<%_hIHETGR@G}={psi^G+A5Vplso1Vv%R3DP{J_LX=-Qh)2Pk3ysHjdlf28 zk4h7A)yN&}$ZeHq-RX&PXW!s8FoZrSw^s>2$m?n-|G~QwE!L8&@Pk#G2lb-6k2r4> zg#yE9XN7&d(0m$1hof#I3KBzT(Fa2v?Ihfa-+6@4B2ulI_KwEmv|b(RsCQl+BPnI9 z^H=#~Fp3v#_0)o~HP-Iv1!+hw=E*(1w2^eI5R>00Tb@Y62~3YTX3+sCP~t2AANPmt zg~H47UoNGdkJv+SMuAWh7}w*Rjmm4iOvt3Ba_lG1jdNbbTf3ZRINppuVg=r8iX{=^ zX5S%&!u#^qAC1(dmc%d$FT<4H@wwg6lle=G;TP1zLmCh`6pF6`;M7}GL$ifTdG~q9 zkq`Be@HP0k(s5}9vc@9oh9$8t9S<6c{bcF5o;cgWS?RnY9wzw&`%}W5?1+v-@kT4? zLd_HGPYbDh^giN9g$jp{?u+Zuaw*9}9OI@x@*m|tVoiig*hKp{p|1E_k0<>kXCLP` znLyhnIcF*U!xMQFXKTr{3M!jyw+WYxLdK-7j6URD>8wTGfhN=FN@uU^hiD~Dsm7S; z2!}LFRAny`Zot<|Rb3f4isv{UxMt0qUesuDzBR9~YDbZCk#WM{#H?5~$kC&Df1kp0 zCkk)R-{Ud7-Ao(4Cm1N{Bp>0NF!aS*ypads|V=Tmv#c}f@8hiu=XDVra1 zc27PzKS84(vp+06>p(=Gq~nj-pAn+n5S>QGsTgT`g;qi{9(Rr<-Z`c_GAa~WFsx`7q;eUl2Gpe4=;`UOXcHQ9FUgFRp#8RuZZele+YiSWQAGT)J_ zO6i>O?!V^(o;g|0*J6A$FGNM^^{MpKKresVHw0h&vvZsc#6RbYC}pmFmQV+L?X(zm zaAQa!s26kXjJmpP4qtdO5Ty8Rk6=xA|0u4zFCRTNKnt>Yt@C<5LDYBVD~2WF zxV-21hYhPZp`ucezm%3fWuGQQdGTeWeA+n_ha&nSk4BSxVqg#2{|&Bf@gL@BHU2Sp z3HNRumI&SAaN$+u@`KP&;#=V8e0<=o;2DmA$IGq6FF!RL1Fy5>0IIw*)3A%7KBJ#YU`cq@MO zD^&i1v#;>0SCf4{ex>~d=QmY{v^9=2oOl7BlK7!e2l@}`Np%wj3mt~PZKI>a@AI5r zU4G*xp62*+^5|MVaxMjH{VC#6iG;py8k8o?slw3WQ`@V~0zGN+0_QrS5XrAOlap!R z7_n*9CWlnuy=9F-VDfBb-P+Von9qs52eCL0}DQPf@) zn}nGIc08UV9M#U7bQ6`=*&h*p@-aj=)A2g{&aNleLQCtN?a%9CD~|?I#8UeNp$NZ0 zGc8@}yaE4uY$KOt_8Q@t`4qMtzupuPlR*_fVzGbxslhoy7O#=F99e48z3A*td{TIu z7QckOcKgU7O1`RaEV6j_e8-WITrf=g;A(US&v;z8Eq_La+g^GOM?h$n+b0OA{3+jc zqz3bTZxK@YM7$HJNgugFjnm*9e|#~u3#lhQgsfl@Qib>A54M{wH8^K0{y2MhR84cq zZWFo+pYZqcJ~8D(+^=&^?1^v55Jy|6P+%YKY$(O~G(H1|V0|kc?O3Al{k^^O zMiV)Hl+TGRbYg`aw_9=FQu{rg=2&X;AAR3ZWWyR~CGsiZ1K86zrvyHD4)AC=HO0U} z^?@OJ(2;>_u^W37`-H8D&b=((4jjVMK2?^eqCz!}2kYFe^`eX6(}XoMuayrSWn#N# zY)AX@8v3a7a@=0~;7;XZTDk_y=`uA;wuSpX(9(w;xwp$O``%k!wqJINIrvxn6JErz z;^yyrglZb`g6c-+wv@m0QJ&_gCO*!OIf@9cs%f%2rafNnIF+x(w_}mY@hk%#T!6MZ zawAlZs!Mxtk$ZyY-g}IbbYiWu+WG8ribo@8;;Z%|p~vwx|EH*wh>FDjhp+Drtg=|% zHk8mw0wh2}DxCxpKvYT^O?r}3KuQ|D6GT*Mf{NEdRmot1Bw|+#Sg}BYy$9@yG&^2_ zU~jK8SURMv{OF0*xeL;&`VreI)x2*M@c2}mQN}!K z0qowl1S~LnoGjxz?$eS^$jrIXGro=l{a%80f<2Z$79c@Su+{M}_`>)S{EbSin=PQDIKuh*XJgEUL_zU@AF^)24F1DRVp2TBO`vIc`7F z8CzL%DKw?(0vEC*t(zClp2XpkpDx1lqd7ATHuv|>7vZW`&O%e>p8Um>kv#c{oNTj# zT!~+?xV3%icaZEc9$dl3so9VJ+eLEjiJaM{90k&O4| zBFTO#XSP|1s{#LuE3s)=*TD!}#q(;rmP2!=np*mgOWEoIApLETX6WIeY_O+~&?;nF z;nDCPA}lWGnxZN$4JGD}mjib8B6;tboDBw-lXi}{VZ*!PO2=NDVrdgGiX)flJYXcb zgZW(}Wbb5T;5RZLy_*-ebthUxYAqK91t8{4?uw_@PAqH;6FSnu?89~IP>Icakiyqk-c4g21{e=;0c01Cp!%FBdzr03^C>u(*V_uFbl)oH_v!ALQxNB#p(k=4a4Q( z^WfQ3$o!wQCakDuYFoLqe8K>s*fYcA7HflHX4gZYM%BwXIi{8yC0uI}m%x))`YYCm zL3S4*VXr~Z_L`T7+!fofp^xpbx?ev)+Y%asn_rDuN&i=^9!jy&P5~)DBqGN14Y%@M zvXS-#C^w_@kCe@?Lw3%82Z?6TJbq4cKRw?bR>)VQhyvNuD+xD$Ly=_PAyf5q8Us*gx%Hi#8Kxstgu9?4L78n3bWF`RIUAOB>Vjq% zqyd=7-?irh(q%U(d=FDu^P6iRF74zh9?7da0XpS8miJJ+eZ5n|<@>?~ml-RmX_V8J zC>8IcmD=8^NGw%jr5HDS5&=jm^LbmgEoZ zHdniQ*@u>#xJ53(VuUSi8X*He3UEeTDj}cXsJuFDA>Ju^4hxiBGg13bbI@f9?f-Fr z`hcJEi;^GQNSwB5a*Aa1w@b?H0DICWIZF)%kWdHWr;8Ui?IzEDVs$+d=pe04kONP7 zn&A=bIWS7T=GHR;ze@GN_btY`_Hq}0zNXz2Mfu)6GY6icOIt7|mh_*|&t;%10Px72*IL zgZa$ej&vM7=AoTMgY8UkDaIo7$DSP7>^NX-jgDIeKtj!uGsn^Tl*>|0SD^5Y znxJIN4VO*m8q*ajyg4k!<07&*RnlwhuZZW8;;(YzO8aBi?mFnXOj4@GE2VLRxd~L> z$H9Bhj}7}glK z$()B;;0^KB;Jl$<7bqEU4s!_$dawo3$UcPKTeJfrpzN`Qeavaa&;omtXR-PsrWTgb z5$g&}|1$u}x1h^t;84Rq&u#^_8p&D*>H$cta1=a+=;ih|Iw4jNyuS2BYZ2izO(oZ*gtL>?27~sq#Cq&20iFwRFW3_drHlc}JVhDzMdM(pT9(*Np0BmyfQrO?LiOO9 zisAxUbH)E@?BkXhXJNxdcc7rPAUfM2O__(6WjnQDlSLto-+y6RCF zE%I;n>R+kLT?NqQ-|>Fuf?spI=7R*(F4bCGsNd?XBcZOAia)?gvwySB-V&f+qjd$D zc)&R%Edg&!nC%*6Px;)|!^MesR!tqgQ4pw|-`CwO@p&zh`2uFIXh6E6Q3es=@$R+!JI;jERD=iw*GpxuKTBjxmU0PSgPK@@FU6$BwjhcCl6%vYT4I~5cw4>fVQ zOf4tDkhRf_tDaogSK>P7C>b%<=^mn7CRro#2E1WE zHGOevugadRba*XLwh^VutO`fGoyAyOwv0FAR)=Ag5ehnL;hJJ(4Y@)&nQz%Bm~rA@ zyDhq%-l$&=UpEXK*UvpG$qWHf|b=$8;hA@MVMOX$`DC34F92V{4sYprR84#o#r zY%7OH!&$y>-U0-GeQzz~T-S#bntMN<&;|ANm$HZK)+Hf7EER3=OB^t8+Z`4V()(s8N5vic{S zdMab))d>rD++fJ5pIo?TfG7n+Ez~9o}d=OhSGdp#5*fw~^RLml`5SiO*;)-oSY!eza0Ag{Ntd!t+^$ z^UDiJv$W{{JzEfA`qz$lqDM+Hy({3 zgLDViHuJl&D88WI440H>pdY<$u{K97=tTuVF2>k>K`m}CX*fP7wFA%dpdk3!4Skb_ z_Yt;vSR=%kjcOF2Y8!+&AXHfdBz}ywoPz_a#-B3BwS(~GN!T%_{R)bbU2!h8=2E!5 zgq`5pZw66_;>$p)7}IW&G}*FpfGkR|q)`CSD+(Gf&wsrDRM8u%YHUXf=OABY`|F_U zndwF(lWOkNQC^cXom`P}?pfSrGqwv8)fLytrHp7MP+liWeCZu6@FGhm*G*=}WTXBK z){7}p^uU|ZIY9QnebL3$4(V`RdKk+G3j7saTo0Nr*sXUYv#V>FDRSe!D+jx|5@cIn zZ>DqmzehkP8M^^79Mua~s9I@0BT3f~s{ZpSC?1fk?yenXF(|4JaxroiCA7n{b2z#f z6(Z!2TbFY#5Z<>4Q!hwp^D(#F2Ns5R%A>$=LUZc+hCal^9zTeCwMH7G4}saop`UyN z`0fLZac%fuW4BZ#x%4PPLGrPJpg^pp?$jG3oc!G? zM4p@eQxMgI{o8Q43@NKC#rT20Sv;Bb=Z8SER;C^|esVD({}8{B%BGN2Rd0 z>q*ldQID}0BLn-mH1dm*{5b2{%j11q1;&6(b>K@hN#kbDD@pC8atsoQr}Yg`-M*6E zm*A6Kr;wi3kSiiCl$d!W`I=CKu-*>aR@IZR8ytDNGm;MPMKY=Mi(O+)XUOZn3BXG) zhV8!0UHAy#B_P z?68)c@S_-U1#b3<6=s~aAEfe9FzPXvx+a-Mc|iX!>r_&WLh(ly@q*kGWH>wUB@SJV zahz0W)-!HA^g9_wXIPX}6Q-(niqJ?Y-rWJ2wx^|ql=ine3W28;xcUynZ(;PXu?5TgjM8XC+qPVCPLFD#qO^b1`F%1u!gcE)s#F~-qC+W!alS~6T$nsOY8$5)r4I4;@^sL9o%XGhvY;A~DOev@g=~2b4r_kcs5G#63z!QD328EltK2hM8EIo$oU38!6YaXm zu*2>FPH%kM0GtBT9*5ozwVUov!~vflI9z<~71SP2I<(7_J4?aCXc#n~QVd7>;0t$Q z35$=h_W9w!>}Z7&l_PoORpPCx1?0v4?~vG;1t>?(qyTks>^6Q;>H= z>M55R$EiMd!qCoa!$v)@&8O`azg(<5gk$Nf@Ln=o-xO|7MB+Q@N{pE^Gf7Z)1+|fF+kLu}gKzWzeD`)7>i>6=6Q#vO*IL69N1&x7 zPFdVXEi&ZDM9WM0gPyj!y<|;tO*a1}i|vJW7ndQ<%WznuuZK*w{qg{wtneu5C5S)C z-Un;lvDFK@%j0KV@CyvH{E3DcJc&Hnr6xNTdrRgNTO^<8!y+DwPwIdNKc~1{W;^f{ z-j@K}4V;4zopC(E>`G^xmGvg&gzFQ?SBo~KzBXe3yDDUtYXgfzCT7e+SM(|#2o03}2bP}X1gqrnl#J9p(_QOLE2B_^6J;h8 z_0XJ_@p@jaddNmAkt+@%6sEWnQwB--pO!0OxnfG2B-t^Syp*rfD+j>K3B1$b@)$aTMcgpX#H*!o|5=b-`h-WPVKRUt{XjhD z9R^c;@e4EImI*Xdjv~sXl2R`(&3LCw!`y(Q35b#O;^;>5)`=-;PThG7p^+SqGHRYu z#YWoD?N^*#YOA+F1;i}{*a}0o94T&G!kNvY%au%pbR_D zbqiWZ?G`rm2}nn7s8sc5J( z4segc5fO9{MCMpJFXAa-$Lh5Q`gr;4P(uP;!Q%;>Va7*togleIF1625{9J)n2X(;f zT`6b4@K4`r9sIOEO#~?AL2#&o#g?~lR%DDLjQxWtU`KxstiWwb68o>Smr4F2jL&WAAHVNMD|-$_@|Q`3eYqI1!HoJd}u> z_|L(Rm>)AmQMfw_0#HJ~&1fP~wU~ol^{o%104Rlilpb=+0YDuW6{lM0@d%d`oN*&t zZQvE9X$mcPtvzm?e$W;<36Cs6z+}Aj*i$#1?nG%I#qYO8a?hWxhMnWkEbS=?X$JAu znn6>8n#nonByo>oY{x!^Jpy`IoyhfBvSzt!mHC2t>ulM)0@Rs&HFWQkKMLWC3}ggz z2vtTvVLz8e#3?_D3nBkBz}eEVOXn#xaZc-|a&)CN{&M@yC!n3AUz2(p8_dhp&T!PVtMQ_pPwLP;dE6>H>2T2gm#zM5!gE=tKtwS{TgDWYW_j) zvRyNi`^g=}R#w~#k?}*vJyNg%zi{l^r9@F_!4&?FT?+-m=ko}27eqpY}z z#V9FXk;b7=TRB@Jbmw?RR%Qk)#$US=(Y-cUYbihfF? z+D0EdG$kJSS&d#97$dz&;fQ74;+kSwHLe}hX0~k46s*3$tAaP}MA3?Jo1~XoPtX_? zZ)Xut;k1eilTCY|)BJTDRv8+_cy8qfcW8sSgVYZ!tN7S7wVCEZ_hvw&C#4e z4)0N^6mPXB$qck85_G4f8~$LcY`)X>h?H?Bi+D7tT^dpdob)KkP-D4E%IZbTEmo?kLnSJ4B`V-E71p)13*XucLOVcp3j!2<<37q9` zWQUyDZhe3p`F$*QkgCRMNXA~h0P0Vm5`I6S?IimJLbUZ8ewi`H?xzZTxf}4Jefa0z zpP*MPdcd{U{BH?BPxwIN9(3JnYI$sYQfu+XjfpZCXZue<@f1Lh_l$rOn*TcgW1$W$ z8=$8FdT(FiNNd`L_ue!}rvHqBa0h*Ayk+{h19{=#`7Gd>Rf)*W##XDIo~G+TJ#~*% zJ_YaAg(&k+UcMffb37O0^Hnzo-OP#wVBb1x`v{$s9u(>tCQ zU&POfI?8jY0OyI0fORQ!+Qf)ol zuZ5hNwhU}L$ckuk;{Ey+s1rE@8e95fb1%u*-DoarzwaUjF38rg|Gcay$97rOo$bBC zI=$w{9sjEA-|Z?glGhL>7vVKI_yoje(G!^BzXvXF#v_Q%GrxG9P@O00%Wtp&4^~98urPtzna0e47aIx4^7_`i9_! z^3P55L>!ye+t|qrb0Y1%W3fyyDJYE5YmU36?@Glp*pcr(ZS7BFO7Cf1%APjOWXv;` z^HGM?5HQ0`0eR#Byzpi{!qlk_vKTGKxo>nlN6g;)68{`HUG=6^cw86{=de6};D-iF z{{m#yF$W(CbMrUO*M~$=UlCc!M=W-d$|db_R4TP9AVtx~egN`pKJ5mj1j?;X6k5AE zwG|Eem@{)3H_*-eDN*8N!P{=Iy1*c}&j^T;n3Z^N*bLBd;QU;2UUX?(438IwSd5gS z+o7z6-VU#C_N$3KrQcp_3YrC!FPtbtSc?^v45ukV}#|!Q`0%_tM8@q6>OU9AxIwqDpGk#W7`P=^u$A2gzx1QtYO?_q!WXG zzs{v6cwt__+8p{Xgc(yb7^^)8`(r)n^_r{1@Ul3RPlA`l%*3mjD=Xk7q1t6osdk;J zC*59m-Du{C9*onRr*ZAtz(D>k3=+@>^`1xuV6|xW4cCq4+bd8$Q&0B2;o5G>6h=Rp zGLkfZ2;N+M1;jlm%k+YC{WE$I|5jqV(zkpH;>IS3h^d)3NpZ#k=NH8pE-R*Ew2nZm zUlr6zHkG$;TzXY<0~z_YOQXv;*S{&){C$K8dNlnS0?_aEr1~9fvg~(UdTE9N`VUiC zZjd0O^Ah@20yy1UKB=z(GY38DQ(6wJzb#g@47@r% zRF-}QvVMK$(9Tl+AXs|AF?R~%A5Ehq?SCvfUW0kxrx?)pW~PjXm9rRB(7QNc!R(>- zII0WhIMjC$Sr3;lmJ>&P^FaYX=M{vjobS6dvkYe*{;Cf_D53Z0!4T@e?}f_bNxngZ z#!5y)B;J8z@Ob$h)_A~qs;AJBgh(ViI1ef7P@C!#s?lCNFCjC?M`)_@Js3{RnZ9Q9 za{UpFmJJEjlNiY*m_;}iKeiH!LqpzP)}_=WBH#GOcFYvAjq5`hsPTzo$0RiYsGx?& z&`<0K+yhNfTT%lj*U@qu+zve8!Qm^=57JCBKLZ!sbz{7B$>+|Trc7rozHUzFyJo*q z18c!z9~t?%6^3*Pv}9eJo>(1MGeV^15ER$kbl6fHDhU$Z9O;p7|#p^@)XgwKFGTAaDxuK2n$`RpBIm7tf!aqPe~lrPbhv=5XCTo%+vj{j)mW}d~TxPJ1b zt56cp#ou| z_LNF>G*&@fC4Wq0j12z9THCV#iVFb6^V9w&1jKBuG{EB(j;QM99;DFg@73E$)F2W# zi^s=B;t|MiEeTREcElI8Ya?-);-kdj|I(RG0q_=584Yrb8;vZP!@seOuv?w~2dVU9 zgcX?n?RwO@s3=9dC}MXqFIc;rH(XvgVHF;(SvT4LUo1iBsJhapI{}!?A6(N69a04K z(7LQwZ5{Q%MAj*>kxM1X{*ySP7PZ7nGB8opu_^Z?6yX^yp@yZOhfFDdqEIjW(z4S_ zp$LZWCW{`OugrpGn`8o%S;Y4PPgstv693<@_mY^ClMnah-;` z*5XDCmHCSN+p8}DdiJD(4Yy})fDYJCa?ZH$SksptJiZ(6(mC%Px4@O^q=`E<|zI% zEG%5IezU4H2|QEw|A94p_c!Zu|H*9pbZ#b>+x=hA03^OG`QWdw`R2s2^ao znL2kbh7qcwgi|t{#ST*Vr^{`|N^W(z)|p_0jFn!cB6fR(^#0454rmM+$zogC_m|5V zWjspjlV5EREH@mPJjkhFjK&5!b}GKDod3=z6E?25>3Apv#wfgllzkUJO8Jo^(tE7J zk%NRAFuUs%)%V8X({Yk}*0sfqV=#(YEFylyFp_gNLlpUDwyZzrT57m6MNAG??QF;T)AxI4J$ZbJApFK->$30R4CjqU`lg;1PE?ou>j+?rMSv zz2k{+{*J-DAl`U03qcMyjSCU<1Y+Qid66O>1|lqQVA6=`7J*44G>pysmdtX7whiM? zbGQ>slmqW&nzlJ5Gx_O;h^UL5B6Jo$J2^B8S5yD zPyoOj11N3o-ejugqiU|=?rQG7$J9~&&GUbc-?gv=k^#VcKLCEpuT$HU&PEugv70y? zs0$P=F2s%et;16FZj))9jV$y-i%cEQN{h5^SBP8l_>q8%Sf|XF1iV8dOXtYap2k&=T1g=H6-A&PDMm7PI8V#~0x0IhMoB`4|9}Y7@g`+Z>3B zU0o+BtKf8BEgM_AHL8*9^BUjJHhqJLpQ68jUN#(okd8og!oQeBC{XaztmzlBO_LO^3XS-pe%Tg8(a@-3)+yLlsPq|6^OK}8$I(yiQQ(c4?~R_+Js5+3Z?lY$#lIU*>r8jjNwSexIuQux!WOGty{y5DKj>*4iELkxwo2CBU{_V zx@D$W63k{aTQnLsNh-%6sXvUrCYoIPHW%>Q6pq~CwEnhCu(U?vyoLm! z=njePvJ zxkE_o*r~F3pl7R$z0cFyz}5h^H4p}a>zKxPy)-N$lO8T;7af-A>d^vk<-#m2wqc zmO_#=gGF+>$B$LfDk4(T{y47SZ_v*Fzj46=QgP{j7W7L0J`&Pdehp!r8+cM|CPK%yIG@Sovr zVYWxpE3V?BeyFCNBd>T&>q15#XsxP*qg8Hy&pZQH<}vH2m~@7m_dp zUJtFrMAe*3Op~!g7VBZnw8IU(3vcJzb}CWa&0<@5zB=`GRopfvyH#POj`M^<&z*wh zXOr6|#Nm#A5uk_fQB zpd>#d@Ah_Ug2?ND`7AJt=5}16{GU^78eu2)CypDDU!o;pl3;|X&j?QAct}I zd5OB%Dp_$reL)_)*h*VUp%|t>o*m8c98p!Ax*B_TdZ4P21a4~Hh!@G7g(>LRKkN7kYw>4 zsY$bl+{y0(@!T&Ec;<-B)L*`U&iCZmbho+`C`fChUxr%`@MI?kq$0zuP9SP62U!f4 z9+_5-Ohn!LXnM`BuFP|B} zmc4L{{~z7@oES4qOVs}l$(a!#{LtZUz2HQB;R~(HSlvG#2wsbV0rIb%|noHHyxJT_sQhH2LuKmfCZW1J$Mk#EL6QEJi zJi~mccq>O*_RrCNC0V1~5whuR+&D8jf+IrsYeKaF>IL7hh9k3@pHZi7zfY(j!?C5Tn^;#<@gwPI*c2GWYc6DI57hCCs?l$Y!t#j@T-K<=kT<8 z+=s3PvNmjbDwZYSnD}2G2Hx>w3RBvyOqa-ke{1-H#9 z0`RO)HUyqIlcki|;0O-!bO1Cy+wI{_VK;02^?I*(2)Oety?EcNGIT&gC(j!@z5W2b)>@up_PW?-wwTB^%lMX6GnRB-{RjysfZ?W>W^`QaTvEM`!n+ z7KR6kz3#cDn%4Ng{M99Ib%tXQ1_uMU_8;u?_$#AqdJJB& zFf4$RdX}Jn5(mjU|Kv87!`+cGE90X{%2wTgpHn#Blg90c>Iv=wDcWmOZp{MxyfzJQ z%NjfdSp84pU>JiBcAt|jp@~z>brmnLF2YMZ=zzs4IGEZ`E}v*SNU{oYz>CfzB{jX% zW^K3*^(1)dza@O)+ic(Mj&H{w2p@bh1?p$rBm^>G4Ui5O0$$5ZkyAuz0iVh$vvASr;xX2Y_WdoW3y{~F9ra-MZ+k~ekq!$LD~-{GCPR-Td5 zb&~jb0q3F4SiRK09p@p33eZJj=UZ7z>q1uvT7bdDJr>(k9-EKo1xHYEH$po}Uj3vf z8K0GpcTwTJHWN(|+g&ObxHYxWNECaRPJWe;8Q=nW=RXH}OC)lp-~u89H5!NKL!k{emfSS<_f-CqGW}i9bi|V7ip8 zuzQ-*mLVZ4tu2YFUZ$+^70I0kv)Ecpglqn90jkgt$zSEx`vBx6L(#dgmW|--9F>M- zJ=PKi|vIZdNk*B%ca?>=$maOdHCi~&jE z-YdgR!e{emGL5kkeVsecao8BgswjEs6OYS`gd~)Ok&OEdDMJtZR==U9gP=3az$xXj z72aEF=gp{N^v+RGJ28=#j_vJ|RX5n}O|zVv#8P&ob#J}ch;Vy^c7enBJL{3nYps!! z+#89v)}o_Pr=a-dZ}zBKOS&E}YyJZb`S6#x32U?-NB)ip3O)a2s!#t6d_Ld`LiMD; zBkV+jTrYo=1bvNrM2TOt#74RnP66JSG99CmIY~C(WIIMVGFi%Q!GfJSj2p$qu~ykF zzps?s?AD;rr+_krDA9O~uSu|^9BH!7p7sNyopOnyk*1{UJc+;6ec_3Vd`#<+Kbyt# z!~wd{>Ak_N@nEzB&R`J$ujk z{x-W?D6r=Tpe!7NXNDbd2nz^pEyW$1chdB3ju~61(CktOv2nc;f@Mqs9y+eR1Cq=U zMY1RWuX(E^B!{@e%1};BQ2;b`i`#8T#{?o~#pP$gSU`p*6l4-EC%X8nOA&tgzaSaw_7>D^{0eiUj; zq-r}hTC0rtm+PYvzB!cQZ0C`y~X*2vE(2N&nKYy9kve~$&CS6C5=0pdw5W# zVZAp$&j8AQ@bfiyMD&-E`vS~oH)$=*c0D1U4%`+KEPLA}fh_rW77ap7&&y%mjJaY4 zi;@vw-uu2p)Mm^ns5g#~;#;KfAuIZ51ilq*A8ellPcyT}I#OD!ll+I>8cW2+*R$AJ zT0UZh5GBP1sn`i-QdH||A-fKYuk#qrMndCcQzSCaj{E`_DjbIQO$s$}1ZBhQeX$W} zbh81JO2dv)TePr_3VE9khnR5Kowcx%kKL~Arth?e@!_bugLUf-#gn^kVKGvZ z8#rESDwT>#h-PD0Flmq+QNBWGciM|^i}Zb~xei5pTM5uqsu?KW$zrlpS2t`fMY{s{ z!d=?Hf!F#4%j~UJ)yeXDw?dC~fckOmZu=Rx|2+i6$k7|1Fcck{QYUyE4vb1{Nm!R) z!eF{5hrYC3p=G~&v&`_RC6!6?lU8ZsY?Mm{iiQSh(Wn4L>`&~_gL#7W%%6eUd$ks} zxM*tCPg(LLXorFjmiRov%p9py?*piJOcLHtJ!(^ulL_3fwcVxQ8Eb4HEj_@EzWX<1 zbny#0(_;#`2c_q;_L$8*`jCX}fsR`i180_@Ta)$Jk?rAOqUe&4cpqW0z3kg#4{Vey z@KQVnQ8Is6Ya_T}gbhq_3V+3O_9#!HgoENaP=EID)-nHL$9h~2KJV6GoH!s?O3n*b zNu{c`OZLCuUSbAAO=h?3ei7oOi5sS4lk&lFpn8H(RY|DbJSnkz0}Q86Nl+CyP28WU z5zM$^!=W56Gd!&*`4O--I`o!j6cBq;YhT=9KG(98VUt?iW-#D1vq$3O0qMK8H$I|Ik7SQjfj=d^AiWiML|hjUp?KznKViWOcY zjQv_SXay$4XHG^o(!i%KUNR`9uevpqii-5hq}79eLnwWE$}`@`Dh}lpTOFlQDCNP^ z)ciIBuvaY%xx4P|+)zH>(s`Jg-786@H!)rB6@?&z1c}2<-*`NEi!I0HHHXi_T$aWyYXge|>}?ARm)ynRZI0m8 zcjWCi-AfEAS>3xtU6a=VVMLvtJ9@@%awI}kq0IB9ZNA(MBbM}@7R+>9EuCh)?3_yoy>trkmp@+qcx|79(s<@3(S z!}&)|fUy2dK^SxkD2cGLb_?~cLlXNw=+u`gc!1-=&=&;WYM3&~ z^su(t_JZ5vf8!92Nbe6Uo1w~clto1~ae@5Qq-ga!vn+ zpn>Cl6m^5pDfuPq+Di6E?kOgMo}1w-czr&G*0pa8B#=o&7Luqja9%*aHqd(vHuuOk zrVO?4W4oV>Yl3SPe>e;Yv_5g0+#Rg_&ep=wSP+ot<9l1(UL74w>1*bRq;UfCbgWyF zK2m9F<52s9*5b{@&)iw2C**PeMNdA*43#Zv)L1_I%stjXs1*HZAj|zUeUg7%vq6JW zE|a1j_LAOQ;GRO;uM)*4lP*{*XJ@^n*LI7PZ;;G=kdCoMnslTEqD) zfX?_nIQxZri}^8!^Amqua%b~CG+`-ePet|5s4i`oHc1l>IE-Sy@QZ?mY48B>j6fT% zX)P0<{YqF^{ibcD*NeUz0p>14b@pVv%Wy5QezRCbH2C1=$#NSCxe~D7(aVzhO_y3* z5N`FE#yMwy*w66FkvT{p`?MK1Dd|sqRuJ{CZ&3ZEpjfq1Rt|Gtc%J0%2Gaa1w>~$* z+53loSllSBi!^?6jxITM{-+R!^cBuP2MYwfidz#riC^m+HMa2$Ads4f@T; z0#4Hi3-LpF3s!1hu+bUZ1PvwRdn?uXBfJRkJx#(*(i%zG_qG+J#o%=NnZm6@-QlO+nU#r{Mf#sw+bm{8ZzX;$LnRAer#+DO?uUV%L{eSt1YY4GrAbXb_PRErzGWQsFN{WIW8x(*Iyj|BRrJWExdy9(ADM@46@5e0%2YF zDZqCSrFD7%)c0t#VZ_vzJ4pP`R=cDKj+Tm_-TElyFcf1r!I{r?jFO%E^lpVosjdNV z)N^Ak!X>(EQG3=Ss0GH{>!+$|78A6>sGuR|J6q7@?f7Ji+{5rMEtxXi@l`L zgaxV22%t_1XuWo&S}{qTomm$xi^d=Uy-~G{2y@u{x=7HU5Xt3zeTx16CV7L0;ZJL( zBHMvW;ZN8+cK(TwQOBDZ-4(E8<^t^jH$e~9MM>({O>GYQUPiPdK>Z5adzMelCi*;t=HNSK2zQqrAE0Dnwu* zA+TwYYB%mU1bJ|`gBSiX?!hH%$0NA?C91whW1OY~c>p^4;-yKs7h;@DJ5(nI%EShq zC1%`I4F>^p<%-S$d3g*Q;R!I6gnDoVJk)s&?JK`mqB;#<%(*OS9#v_TkJLfai9E2R zbKHgN@r(rE{?3q-!5$3*9*trqzTX$#v8|-`PeL9on`+Uw7i4h^i&0E)5iVzcKpr!m^bIn%)>ws_d=+MMJ5DX*q$b1z zOYoqiCZ!=ZUW2?aOY0YfcqW*ZNCere)9_$+KSwe{J&O#KN`Q;S_EH?zAXGk%$gAT# zaZ7Bt{gz}37ecs4ua?j<1Q$ZMr<3%GN@FkTV`gP(zetpA#^D@%^K5 zIk9cD%|BE;(Q0noH&-Y;Qg-c03@%NG43UE43vf>)@Q&6*#ewZ8@EN~JsO>#8DMnA& ze1qQnlpA9*K#PYag_m~n=p6`3;VD`fDM;>&&;sTRb6DJS0Y5MtzSR>Q)8ggX?jAMY zkZKI%YH+0RY4}ab2p@&>B}y|7 zT>a?^LYCWqka)%+x@K^fWp+se)xKHJ1`Hs*pUOS%vY8fPs$7=rG0J@gYG@tta%4q;CRnQbUP>U$5 zkp5ObB@JAyAfzSP1DRqp1IJUS*8pDdO)%6<9%UWLZm(5PEYpc1^dj(mwPkWR-P1*i zKWp4nQo3L`8`JHKl4mU^RIPSoEh{89!=rYo3=~(&$-&?>>lcUG0D`Pc`vTG&KvxkO zBaat_UMkg@9t~`{27uLmfJbVcn5hX$4f`U=6cegWHmV#YvS^4$Px;gk)?g%O(mTgW zFBe|*GLPdpQgU7gc+k+!K3yZwO?JJ64fCuu|G5G!-5|$@dpa=YKNweDBM`(s9?v85 zVM2pKl3(jYiI$X)#=+aml}Vp#O>ERYZzP`15;@6#r0{Yp6$rhF#cqPIZnZB{C%ake zHjc2`1$7X(9WM8@k?hA38p+um(64x9%#jrFRzF5_soarZv{k6}GvF6?=pO6*c*vX| z_z1^(sTl2PEBR-`OsHfXuPs~^qcJ1?ESF{;I0ASnMti22TLVVhAlunQB9DIy+0!pX z*U)xk5IO;2vqiuwh~r_d$NiVaa-;wONVFUpEHdod{8(ULsp5E+?{?Xp<3R{)w$;kW zvF^~ijPbBYrIe(GmM(}x=GuN~ePv3Hm6{~fEksj7^`clRi`^i_&`Z+#NU_VKC!H|> zh#$~Q&L0JferrH<1W_8xrt20W?_l7=R$Gaz1jO_6oh){c?H}RV0WLS!iQBx3P~?L2 zcnZv5Nf>u?uod}jk*=6&b~7p_Dc7N{3J=Bv5Av8LM!@4&frov5C}@lOygYpw2113# zEC>}EI5aGB137$Y+6=|hSE2&bbilPiyg?J}?jxC%u};0#EW))h!ILO?S9mm@YY^%x zSO?Q;e%p>n&#|!=dD30Uq!5&5GYQ}AhgWMU6v6IX;gULRF;@BZ3ZuxaxbYpF>#PN8T%nG8c4h#E&fO+WUqllF`o=9w&S8h zFF2aa*JM`vERM;2Z!CPwL`g6$16AnoqWq*(iJNN`ya!NQt+hB+T{#Z+$4woF;@laB z9ndKskAY^;RLB>$m_OJM0yp!1$2XOC!dvb4)cbe*OWg4}tDzd7%1?#ujJe>M!p9)+ zi2E`T4rZivm$h>XHOoHb?ko1Y1PNIk-B2D5&qLx;B&RXo@euo}MYvS57Fs2i$GX?h zsiMN9cqLp$JDUDdXL%i>!W)^!ucQrei*2qziGf- z=%&O~v-@rID!1NS29n6MfBl`gE8jMdZg{t#xnwQ&sNPB2**on0;sbbe6L*pIeJZnl z-2r>Ip)|kJQ)K!@uJ9iEy?-j=N6M$dkzkUWI-+=M6rLeXrZ3d@>dmL*?gtcaUyessvT4~oI}mrmz(`&baF1h`{*H+_swfAZ6`NP0~_&cRR)JW{U8u0V<-X- z-koG0+v6_y5UuA-N5>0>rrCs=jq+L`Y0ejMSj_9Ji81kO){S;>M$78J(El=`NHR@T|B07q@*wN%rf83MC~DD zZC>Vi%wVzng<|1Mw%l`_sa=WM!&-|(A1gfD{k2DYwdR&%${QKY7^!d=r83$lS9|xqb8kQD&Gz${7 z_^)wbDz?L>*7^!+BIaD7-Zt9-E!CQ1we#OksZRmaqKgIlxaSe4f+}OS&APR*-(Z}KPu<&pho zi+~&ne8Hz|yR{q;_6xd@F&}%*Y-zkjjmYM}e5t=G9Fd`8(^|{lH(UN1i%);^fy_b5 zVFdXdpo8bcsKxrhIRPH?M7Z<$aN{);`{JT9$0 zRb?FrxyawPy0yH$&NJD3O@-qhcvRMV^fi(ND4#WDgk3K{i~z1fkCI`Rc=cpuz9FMN zcL4{tIr#`Jugw^60>I}R%8wg7YSbr_zDRO6dS;k4p=b#TM*mKC2u8G0poOMhFf25l zPT?}TU4y_^=Nzx2)mNmR^8F@DdYsG#zEA)1AVG}7@!U}2Z-WAn9E$vM|C6~qn7|oE z8RiT%l8v`{E<`FfMyn}{umnAC_h=3@&Q}wy?X1b1_(+=hJ{6#*60!yVDq1$Vf$Yr& zSs10eW{oxRlhZc~K&iaLTH-ta;Qh@lp1Vy`WKk_xgm>q4+-BLnKD2$HUXJUz25%Vf zaI<6!^6FJ>2#r8eUYar1fEzhp5D$^lrB>nNQ8ZK*iD$FfMHJRyoVqtddj&kPmj01V z{qF|6m2BK<`?Unc)+~;cm3MlsG<~=cb!}J&p9K3l|BcWzx%Mu5fLaD9o{#iHL+|!z z+#;Kb2mqwK7Mdux&RGuY*P8*EgYl63R-8IEGzqgl1SYIDbi}c@BORTIZW<-c2D+xm z!x!6qUkO-y3xipIo99FG2@m~|wmLb$DMBV?Br*3^6cCKGj){R`|7nO2-gz@%Qb>nD z@apa$Hpf)M?ptQ3nu{GH#pRZs$Tnj!)j?lHKt8=2-(1S?3lEmi3CT_5`3Ld_NcMm{ zy}5NYFyj<+5$^1(mS>iW@+TgUils?CcN_3%k1mLN6vdZs%BNJk)^S(?;Qa=*-L zCYAdNk;rMRRnlmT@1*c(srjI7T!!7M`R@VG{ic~X)OPpP zns;iLD#On&dT`MSC#4}x#e>%JPsKISR!5dzn-neS4_W5g1)wA}mic3Zd0tU<9rm=R z)*`pij}dUCJZyVPmf1@|5wi1PPmbvmr#0DE8~cby6LDmqHpN$4_J|!-9BO;}YLg%J zXm}7gLLb!jUWV==EuzcZ=P->^!1h&8PbU0pEXVQ|z_AmkWnZlD_Dn9?OsXCW@GtjM zP#amd3K1g<$m)8Ef6DFL^jCpn^+d&D|N7NQm#CmazO*_yjNNScNIAGN%q5uc!2RICw|P5aY>Jlu^>lkBb3f$r z=^-(J0hW%ObK{8uhg(Sd)=UwMjPlRUOfe6ko0;a|Cj_9B4MALZWy)1}qcJcS#}$fF z6&xB&n?p;fE`nf8udypkCNz;K8t6?HGfBUw#iM+>?f9C9p+ru~WDKmJ1baNa1iQg# z6aJYoNIZ^DQKSwsXbtq!s;4cZAUWj%6ki_idt3qzb~75D=JMp|%bp6;2p2A2F#{im zsCvb25mHWpY?Z<%j7# zrocc8*P-bdtzxtND7XrGT@q5<_M559$DmL^&j~;|eI0@m;y#&IC)>ze%v0v|;7>(w zS>{TPH&1HbvfGUOWWFM`;|5nDO?kCI3g3e*v~m`Ls?1V462vZ4=;?Qm>91sU2fX!m zQz53Xd9FGUNPvs{C<{!NsSgzS>ze$3gFO}#4*^=U)9izq^y-*axXrI5R2Pv3sU^79 z4&;-w(;}pKO~4{tMSv~>N3>Lq*H|O$aulzIA;zOuU0A$^#at|cl*M%>j{ z&(GjZ_mIHpX!XqT*)}$*Mc$dt$ zQZ^T*A=;9UhB#g&z9j$?W+CTco$7NqY#!&i$8QCXH<4tfxikOqTwxCL2F@<7bHZ0) z8{+i|BvfK9dvcpLXo&Q@-hp`h1y7FC$*@7r9<_lfoc@jIVaPi8^2g2s2St8fJRWe& zJ|UI|_$K-Lm{nCd-M~ z9okM$=31?jsb`CVq6Aq_aX)rFjO!+$heO3E1=8_}P;0Wkwiimtd_-&8G3VR>r%JgM zpo;Y{JY8_&`)wAc_gsWKW#c!NxJb2kA=!>cA2MX|Zf&By99UN$;;r#4IID2U(@=5@ z;P)sTA@$!`&60iE=Bq8c6_;Euyg$Ip80J%|q}zd%xdGT-`g4OBWf=#TBwn4+olV~HQ zXK?3#@kl(SAlxsw2DbARKihv$-+G#jt-rT*AH9IeXe?u=;z~Rd45PBAeT&TTW2X+A zK5IrsM)t5wN&FR56S6H|gQIy?e%9bcNy(1Zky3C4By`QleAsKha%)?ofV1ZU4w8G` zmedrt#nIA86W?V1NDbpTgN0m$J6bmb!sZ8;jW9Pr3<3xFyg@+n(S81lLxGm%7c3ao zdYr8SO^q*N3J%JVvsQw}FG~Z;w8CLw>hk zK|P)M-(Es(!Iq?0Sy16_A?D4{d{3bon`vEoS>eT!-Zi&z>AgrX?@0glih`qL)GT=Y z9Z5Z31?Ws!NQ4dTGN9$R6#}F8T@j6?=w?`U96|E$1WQpp zZ#xOC=N)hUNfXz*rgEJr(@yc8MJnrpY(xy;H{&9t2F_RcnC{^9dr+0@v((BJ1n{OnkLa{t4o3^)3n5F#3C`GK8uY63^lx&3!S4cXAts~IV&rX2Ovmhh`p zuU)NHO*14qI*$3_&AjtXEoJ0!two;i=3X`0XQB2>cDB5#%jI(5nnIlJj*eFchxv>@ z`wE}M-i71Lg>`3sJHpw+ulaq^nlz-Kodk7&0pR|@eG^<7;*DhA2Rpgcw+g7fqZQsh z9RRCfZTO@V^NN!BmU7`cqRchDaHIG=dU5csq%l%^5*HIHpI)VenGMhh1x3lFcPI6d z2hJkA#gXy#2ZcvS_6ax~Qv7eMgM`)8OX~hFhjH{exHPiDyn1(t^83eNDR1SClIQPE z3YGIAlQkQ!=_FVFqyb0E${Z+(=E^ixYXc^)J7vHtO_{BoHsu&eiEhT^ z8gP5rCSAhhrgmP<#$muD>@yY|8G^0J^q^*P2>A&kTyqc9%V*lyt;gD*wf5f(M8Ehz zXiJ0a{C)m47>nW6EIC~M-+)@a4eWSRg!c;bN0RODTH8TF+Irolb`EO)(Av(DxT0IC z)U>s1Yc@cCDyWdP2v}<8)!y@@{a0{<((V5mFZ#EEz+~_!9Qn+7Wd1}h`VX4hcy;3l z88;L6ZcDn^$%q5!tb!21uofK9ah>WM7rFN8#+~F?q@5_P;(2_reLFbBxM8?h6qxJf zBH0<`)kFhqI;c?tIXb(sX$jrupx^bq+~@m#6;ZC%@{B6$ow|@A~98jU_~h}gJ~Xf*xw}NF%57&8X<<-i?k_a zNyyi=5S-TGqJo44wDcw%&0$tUa82NaSmZ3@r;}IZq&v-N$X*ubz2LiQCMV;(dW5Hv z*qp@<(&2Fsqscy${EKC(JlwUwbQF~*VUo5@L}t9Y&gYf@?@sB~g?)u@c!dp+C%uIz z2)a;8H`85wzLV!}@Ej5;c}}PgXZui^J_BP!!=$j2w{vMHufA5#(^4x|Y1}x;t+m0B zbsl8)VtRP9gd~GDXtio@WBj?ZSJUTAL~Xd%x+J-aC1>uf2(4`=kdw$-fdtcLDL8<( z64uQwEoTCG%DQ=xpt+k@Z&ve9NLJR(8!ZF6dtK%;jtR-iSZ6ZoG9!-Z7AHCizv{gM zQZ6*v8z$w$;B2QE-+V|$iB~Fnc+~>S^HH=9U6W7!nxo#sOn_pfFwv{wbQG^1q0D8yuz&q2}54CEM)pV9c$>6{k{e^^|U08SF(s@@!(Wvr3 zgH|^wOz~fv}ZyO*rG7}Fm50M2gMjn1^)$qGz) z0BISzUF_9VDhp-4G=;5RZvMbGd;ST>$c6(n! z{eV0C=A$@#G)z{cd-ZJvPWN!h&+vwqX4oyj#krhSDw=ry9f7GU9@q^oZx3V`O;wy# z?w^tT&F)U!UPA|&k&6%Ll$Is%_Z`?1Ci_Ubz${N)(r3--=1i$``c z7CopLTOSPN4jhq<`v%vW)C5coV4SQU;;l3zWVf=UYAB{YKd1f$q|KREi)P>{tY} z1}43QcZc5s8p_~OcwR<$^UXkb{)c0S(Ab^Ok>Z}6P)GE_r!&%WXL9)~Na0JuVt7Mc zLtf1Mku)a)l-;#KqI{meLN!=3a*C8m99?0f*6k%bxph+<2m3qU|C(&!HW^^{=m70% z3YyKip&QJ#5nfoNM_YA`&E_h|YWCpAz0I8h)1#4q=K&u7c+%j1(Kqs?_gJr5gxOL7 ziwGAR>z!=og+ndWx~xPPjJ`f>OO-xwEgP@IO^&`KOH)9=Jn!5~$Q z_&1&5R-LYJJfnHH%@DwiF~j#&a<*5U;!&ubiQ3GU>a~$=*_NG=`_DpcVI>TmEpqJn zhm3nR=kCCrQ2dKgZGOil<^XwfC8X|uQESFD1hsP|)8*AXG%U_zv5SNx)NhACe8ezO z2QtW%{PPLLW6N%@Mu>8v7NFUjT{uj|dhE}r4=ildU`ts2csYN49z?On@*$oW7RgDE z&dC82wn~zxc-6Sf30uvtBe7Z+ zrp$3~zF4~FS}9IBUjp<4zqT48$8s&Da!uBtw&F>s;RX3nh0^b^r%1Mc9f@h*cHFW( zoM$JNTl;!^u{Wk|BEQg3AD#hj0CIYIZ`r@3Bw9Yp^EMa9YglWgcY$|-;Q?Hn8`*ST ze76pgJQKflB)a;KL>1cI$enkSY(~*6Hov?Xf3JKB=G=V??I^e#Zb9v-nl|lZXP5Ed zM6kIS!X@Zd$(?3}4>ihl^6oUR%P5_uHzc6-?9Hz7w5Zaj(HY*>H(wL9LBeKw=h0De zo?ma&Q4TI#GH05^&%{aNKqJeZxcxSv8}B`hZ67krZpt{+Zf5giE7IVpoCU(7G-}TV z`NeIv!KE{2&bZPwqj0+HpOk}`VZ3B+hD_mjJ6bIY?iMS1521UGX}v>w&#{xk*0un1 z(KGme^px?s&)MlxX+PJi@r0ZZVRv>%b!#ihH+sjKSgklzZ`Gbi@4}oy^J|L1JJI0r zwcX<6(FEHdclceBGvBKbN?iK8fmHERm(EfAxuJMgpW=&sSSsVL`y73{u7a;(U!2|f6u``JQmdRK(Y z)?(8CA7SquSXJ@74}U{T@1c|Ah8l%X6dUalMMYW?A@|yVAw3!0&lx_nzDn`MmF+XJ>bIc4l^Vc6QEg$vZgY>hiGN6VhLJ z3TSd)#qmo{cW4~1=i%tc?x&G~gC5aGy!lV%2BaOZgKx zH^$L7qMY081CAW|bA+vVqL1DoaiRVL=7?n#P*%N)l$+R@c4CsG)hIncl5Rq&Gu74* zPyCa>b8M!aF;wiesGRs3j3z3Benia`y9c0k0Cjo|4#1jcK=AsdmMEHp^+Y!9NtlG3 zSvIglF>DBzr)JqGC6PZ54D*3pf74_NunLfpc_CaDTyMj86#a{+xw0J=bhQI3I{5mj zLK4YK=!+I*(N9?AFrhAzW1rcl+NpIfqvhe(5R1j6s zR3|oti)T*$I28)nu$fqPkBYEa(g;oCPIFi-MMeGF0#rvy9QbSunggl?sJv@?jeLS9 zcu`2va=KO#?YgHCtDX87<{U3qRL;}kd^21|&b2ENdw2_IZoUJFh#(dUe#GW!Q)Ax- z==6K_8q3IeR%3|#9g?)Qo<|CQ1yfvSk?#VO{$4%&z&95l-jk9@14q~6JtM^{aM|rJ z-{o$@wl%y##oD$?EMhAQCaIV2L8EOtj%Q#oA$PMp=ol%+_X&%CJ0(r#--6B<;Nr|9 z=mTVB-eM(8E^h}}*&C5qJo+|ZAvctX;j;Gb z{He-I59;m(;;diWXT>JON_5bb_&UodeZ`%hUx{E>!EUSvPidA3T}4z)F^O<{Q#@qJ2wQy9U<6wc-Aj zEf$_=?~wtwTXR7~-{F~yg;rFwuHT2t`i0i4b9_GlV`)Zsy3G58DjMc9@dOIxM}Ts- z1ckEy!+f(x^#Jq}J3GD_ntk(N9~Se_7czb(EPf4sdP~>}B00m91?>GGVWrtA_^;=E zbmy9`htOT+796Y0DbF92%!$_mEe@jv%;(@S*)wFr`22oqUmQg|0(HLME~He0qr@K; zz#|K%?*#tb4f*CI&jZ*o7_SE`Cphxs;ZpWM{$!<%bLkhhEqgLNgAPiaGldi3GXFuV zD`d^DnDTEXdCa{?)MTZIkl*lQ;X}42)%ACbYG-AqrWF-dEh)x7*wyQ6abEuSqv(pu zmXhHC>`%g~^4c_$uAc+u$`1Yu$PNT>hXO!277Cp~e*@I53c}JT+Wsll4zjm@04o0r zF;v{QYYqWP=)Wi}^0xLETnyyv7|5BE;S%>q{$yRkXf#fNBrahq&u@+G^tr8ZBL8`a zik%LZe;=_zppgGB0wc&+6@^PoE{C4erf`xGa~MHgCgG6Z^8^Ox3W(Q}oW*um&ULeX z1XTS^tn4~gYKr<@H0%xJf+hK5l|0&&hCoX4!UpzEmW@mDa}`2$Xk;PX60U3_H>}LV zm4WcebjA}LjxNgIWKPAgx}1&xsM_hB21$G843Ct<%dBHh0Y}N$rw@#~Xqd??HqO5!@f1;`s17}I(`t;}fWJ;gMtp<>d$>?cCY@^1;U!||E zJyA|6a{6&g6Gzb)H4hAG=i$>RIOxL(wAN%-P2}(sHU>&v!{q=^+E2#yo5}7c?cSIS zM;P}hD`K{70kj*=Y2QNjKV>~V4yz@p-POH4w%%SGns7m7EZ753D|iJVo1j!le#8)c5_Ce(}^b|A|YK8egV%5@3t}|Cp~i0vzC*jE5mZK=931=67!rrY2bKTgT%^r z;M40P?%d!hSIMI`#J~B)K4}llg}YB%>e@m@#+{+iMpB|@P1|W z3PcM$D{MfTExVzQy!YV19{blCKMzXJK?~XNGS-ecPoisAMDSdReLml0o)bZLR$X=w zY4K_cxne4hDc;=1fqf382`a1F+A`@yZT=wq*fD*SthFCO>rxi3u7ZU%zbKBzd z_w%~z=|xKBD}6lJO&hC8x%2?8>fLQyac#q4#aL@*$(Wu1&5y?c#nSfxa_s?n0T(S) z`SPAfD(#k`1{Bxgg`S@FM)Tr?=Blmk1J6E^@rs>{?8l3W$G0M%Z~ZGaa6v`wYr9z4 zu1T__y=o=x0VEssGqzzLq`nH*6IEf3=3+FesBDdcoG(DK>j9ffIQ+bPSZZ|)T>IXB zvir6CiK>2-#HF_D4QKbj74pv`W`cdDu@H2fFOxT3&mZgbc{vf!|GH62S+WeFZ&#S2 zzbt;kYCXqvg~V^Pib!)YKuYoH1ThAhZjr<_dAx+h(8@yjR6nRYgYY_sRa?$h+@7^L zA8+lwkF;G!*v^Ckeavhr)U$}$Kkd~RZ%$N+dt>`)>WKl zm~@PgU2li14kJNU_I9`)YG)~XwKA0bD1eH#BVvo7K@YVu4jV?3;w>A&2Hc8e7CLRl z?hDZ@@y?74Gwofw`XYeVY=<^OWpVjx=w`DRpe;G+%{T##D9G+H?5FQ0^n)pD4ti)p z$FjI+6E+NL*_pbbqi2(GcJz54W?(t^!Y-_YjF*AitVR;aRkCTD)mJJg{zrH}Bp!4% z=$SMir6uKTB8)^a(IPL8O-Ye1AJ_=F2asz}f87oY?eJ@ujSP?}v#1D@NNw?EM1qP= z0_J+1WwK1!Zi%47KSgS`TNR?Lrb^xp>yMDLCHzd5(|5JNgKddEbC$_w)5NA~ks zlJ${=a=n@(OFy#RQe-y+hC?a_u~UwCd*t*4u>oEqW;lpa79tMeB1>x_)Du! z51j~QobCY0xD^qS`HhVdu-BD99sdz}UH47?U{ze!ddyG>#SQD>yTbh^36oW2bG^f<(1!H%L!9otA9^h*6e-^`Zwfz z>+Dh%&sa#eKwF#`f1f{0hr{7N8zE^w*yK>^*mKBs`yqdf!YP|o5%TX3`Pb+-t>kJ1 zv40E+4Xpts8Cx3LP_k>SSZg~?ENO^;2!FDY;#k+A(X@}53Hwt>9D6;-S{)Z|Eh!@u z&9JPV_MpWEgKO}ZA;nY14=%<-OvCe#Vl>tUF(`=VBc#Q_{Ht`fP?9g8lbs)9mwa*< zw%V@y7+#E!QwQy!c~1Ef26f10xpI&%N63*w#)BrNS4h~&e)U^2bYhY%>21CmAzvO2 z3HiRpYUTSQQ%(Go4`n%1_jM3c_F*&vuI+raDHU(9#jXc4LY!=e(Hr3?AGM68H?;{E zDv#stIyE6&=G0PAMw^MIY%7)^Y@ut_VGD}Yz@|pR?>%n$Ph6G2)%R2U!vTw1CooCK;lmpZj*zZ}!{7C<#kn&MZ;3+qPi-bR?|ME-C+%XQ}OJBF%< zcDG59DV=d)fa_P_cY$sCLkNUwP?A5Nu_Nx6-{T;=z2B!Dg#{gd(M_JU-@Ev8zBRt4-qom~;K z@h_{dRHtg7EBYLpmm^=Hsw<;qHwl>k0keJC6=4qDAEMc!&#_F8?ul?C_YB$t_4cC( znfp(^xd(-F{bQop{{j3xjq12!Yo8Ey`U7k|!Fk9n)am=wj;6E-zhO>Y1((<5cYX#6 z;=jOR2nWol3>VkWQQ9}9oxR!BwH^Edpi|vaaNXu(b*1nnK>Mx*PqFvbaEduK(IIh? z?}?C@(>7AcA?}TkIj608BICb8vLC*cJ05)R1HLNIhCqaw81yRk+wpXp72Ztx?!vB& zyAbVzY&sAjgTj0!BcC{GfMdfWG5B+eoS}cBRf*~A2Ra{?1`|t1+u|kMH_>A|n}U34KKq$OnVR4&ZP95BSG4A3SDg$kKMHT1D-Gl>TeG z@Z`YFI~ixKjbr~$av) z3B^n`YzWNj1L@`**%du)B-u@T`FfnFn20!>m=b3s;5jl*#rgNzpay%vy}ZqQCIOg+ zAQFg|?#DW&n+jf^q8=Z$B}!tO`%FZGHKU1i(aFSClG_Wph;$T`@iYZ7fN6Z79xj@$ z4r6$~xzE@*HjPCmLk^h5()jAy36(8QcD3~7xc>!=XHU~Uf&JB^Ay+a$0%2vJU=4Rx z3dQ9Cu0&a$>Z{GJOp^FCpUI#mjmaRQEDR3oiZpBENLq@ey!7py#*&ckGl5S=e;QlJ z^c8SHPoM9Lk=z+J!-a%50lHoDaIK=|1_0De(;}1@+J&Z)Ayeui1BQx!U7mJB7BW&p?8Z9}-1A-mdI?$KJd2K_6)#;Tjz4a=u1O{tA+Xy-FY zgJeuw>CoO+yDM);J`HQD2}x$#MRe^W8&rG)Y%l@H=s-4ulEG|-4l=!i?^^w&;%Ca? z4wfDK$@nZO&h(j_T!w7VmYPgknIh{bqt3FrO;zuNuB*O*`=+@sp{D>@l6STpD^;Vj zRGsZJhaDb#b|I@)e1SE7NJs01l9aA2-uwlYf?z1ZWWwmQqsY6l!u~ICmnlwXO+FDt ze-4T{Id)&)4z~+_i7Z-%k#z15e69sgADn?pP_HnNUE}*wEZvE8!zJ}`jC&`XP|8%9 zLv1)uzRI%NK=X6HhFTlKu1%RM6#qW;77m1RuZ{F_+j#1d({OO!))o+-Iszp*?k3$ z3SG=HV+UB}6148~J=RNSHFNmcPgGSOHNyX)I?fE`)`47?d z2ihv4)#>G0)vf@jvZifIW6so6th>Nx#K;5c0rJ5GzG9_X=z z=^7)yiYqrK(O^Ihc*7BRxyTw@HXnl0ydU6@EWHS3+O@mN0jTr`oR81#VdHilG{}{Y zdiW-&p!k_bK_%{k_QO*)ddSIiC5yZ4!|G&W9XJb9fzS*M1>U}W6EnCn7{w+>hFOJ3 zJT+{X@LvJ#g8RsP;#$5B_nGM7b?EV~kD$3vG{Jww{L|H??tY2yW5vVWG2Vsl<1_IO z+MNP9j8E-@(BVXsy&sw1C0?7cNA8S}cf6Ll6yiuwzUxP*5;i(@vad%G!OC~r*U1MN z&EmYpNOwUtpUJ;=jpmf?#I9f`L z$5PF;PSH`}#>$9GLgw2z*@aJKI%mmvG_3l`Ij#>4yIlp)fuFF`Ky0i|TzmqFxDZLn z_$kha$^%?iV^B0vVG|>8LrswJ+7&6Eh(=w1hL@*lH{4S@*6SME@<38w986y3GtVzl zJ0}5Bk&0gzG@;G4h)*UDOKY%BrLV=HxmD0Xb68Vk&*eUILQNh{wfM^iXW`wsu9=O9 z4!0-a^(rNg``8kYSD1<4&NP3`_4o2LfYxk=Ls@i%{p*HEr%T5HcEuv4Gi2oe-*|_D zYbAc5&m?tHxn}~!+=KXy5HDC^<;JU6T9evyotz$Mml2Xa3peRl(%JBOOL|FXBy)!k z_L;l)X$xmd;b1FCqPYP*#2zr#AUso2hMXAN6G*wKLqaK&tb-f|-7E{fFO7bzaU&+MqFV+(=3lD~v#D99z$ zMF8aX@e_Q^8ujUas&&7 zrV4VxIcOh@l-%LIYt#qeit~_R%EXc}*Ik7x>+Hn=6j4Q_bROXwr!8qlB~q4*@R>L# zzaNg2h>^ZAO|S&%8uW<4Gy@6S<7X7Yv|#mBwHTNcNY*`kQ?z>2*1BGbry~I?JvLzW z>CFLK94P}v`HEBl*~1c3_xc$VXG-EoUxAWB(w2h5qwR*Bp5n5|1`p!Jy4gLdgWvv9*hdTSmQ|p%9Qmpfr@tr zD;9dOPesbkF}_(2)~6$7$XNT$_3;_N$NFX}oNMp1=yjak@^el+2h{WKMPB8?=di@{ z9#T~h61WQ7lfs{osI%q=bXztLKj729)(Yg>YE~$F2S0a!J7^qA@*3bx|F&MHVI1E& z(q{Cu*RomfmYHqH87M6x5(*=E>j;ZINY{WvEu3J7%I?-j%GwFO8CsVv`UcjmY=y+m zzEga-1o0Fut94zz@jPg#{sr00z-1mzr@5t>e*yBJXt_e(zliT^d>$!j?2VR$Bk0-r zhtJ{kOIoLv(=X#P-)k(VDT!AApJcx&|5pK@u)g`zUBbmwiR^p;g{c+Y(6pjc?Wn0f6%0jEG)GS22s9?7G7_6 zDGzjPEc<@L^%iDs25-K0`hJh7t0qKr2&v%u&vYyBr>$w*fpnV)1J!;TO7Hu<(^=B> zI_tBNmiLLQbJzB*jjl6H9#p0eA}eNDU7~t#=VvRKQVg9eyb5s}zA3M=ppN{lvIE^vgzH7-|AGH~F;GJ%rCJS4_r6yT3bh^D9h$Pe z8&9~*ww9hF`jE75Xxl#1F-2yNM*Hv)uyk$PUU^XG(zK5OO8XOkF=3jT?Kau}2}|1p zmkJvLG`>4E@>76TWP(Lyf8vsXpxNu{dPD0oQ%A>-EeKqB9Y0C=JW{TiV;RZ${)K+0 z%D&Y0Ftwb*uE!?6RyPy6ipN+AaqU@m_pS0{L{t`dkq`vJ- z!s!9r9M?_7r$AEnDLj{K<^mRUrIw614bbFMka*ca%T&th|Dq6FJ%zvj(ZDzrx=G^H z4U^I`8?xh^ttVmSK6A%IPkh$rXB#O?%EAvI5h-}1qcKwY7JWY1WdHR-72GGuDqTK= zfyOLkdd4 z)uMk9kY5&-rDh$5maC*oX;PvZRJ2Ibfgv?8^r6hgpm29smb#qgiF-#FO#n(gPs5v;N-bpS>?xQ*%VyvX=*{+cf_IoVx12oQ z!-IUG`+WE%`CS`wph~xpu%$jcr`6aSN25?Dp}3`i#!qb&BMEN-O4mSHLyc)g)+C(5 zs-T)rdFt5c7pH=(>Tesh=6Zl*m*v)jA}Ec`j*M)YQ43S%6NVe@miutca>%PL9$>m= zVcYgpnh-bFQI!lkz{Hik8&|CKnTHOE3rVFbF|-vY>j(X~EA6X+bY9vJYGN&vdRvQS z`ui8*DwWf4{j@vRjlb_3Fx|7-14`ayePGH(HH&no6gT3#X3fuFdBEpRO^ zaDeNi<7Drdvik`i7QlyneYliqIazuZ%grenr@`6$OqUgRHHen=m*eLFTsvgU5uqKy znT&>6cFSTep9#GJrIUfASq`?f?ybvwj=O!u&y?t+)c62T=BP4KfI-b8YbX0O2yZ zmQ+5oe^lmk@w`BGtO_|`xljhLwx*NNi_oxdc$WG|CN#{_9vpsAeXqKgaQN_!j#5F> z(GxAo!ZG!0Z5JIv)m$3mURLL_zr*!CJHugjYb%D%fZo6}`67?y!GEtQ>;rgML>6{F zWQ6N+doRiB*1+3N_OA@({j2m1`sJV@u ze%U3mZG$~{;%7e+^D(4C-(Oivfm|w^p0{yDt|phEQdVS^E}4ichCcXyIVqmLD!q$Q z24}fftHY(Izpc==VpTePEo&4Ed9-L(NY#t>T#O1dKzhGq*Ib5k2g(~S`OKw8q;?Pp zNsP*BBL|+hJ2sLxI4GEC@AmLuz7Wp3qz7ljLoM>eCO0Iv8hqBqE9x3WGnd8(rcvrM|!-9l` z;Y#-{Lz^~~qUfxK#>_G@q2eHq^5yvJR<$`GA9|~7(GX`Cq5CyIN>}?(s$QJyG$J6Q zHrmq&iljhxZS>u&dQT%Z91Y`Qvh0?T&-Q7@w2vcX_ZA=0=wq^Q{>jZfZH+={YeqWI z7^&{*!LFTaj_c|6QLJK=j~>uyex4=Eb~OrJnz>3jK3}0_O4wGs{#EjBM0~}Y)_J3Y zRRmg#ZtQn&f8*Lguzp2%7? zZ^95mipEGQ?Sx$e|)O5!f-ZIb+%AU`9!u_d3p(d*5rGS>kC97S_GuVnuefrKN3L7b7#V$&o{a8)Ij z_k6v?f9Pv1>AQWSv}4+~*`{pQiH1qMVH_T3AN$}up*Ij}l#fHaQ4&A$rAglheDf))9=7$;=+Btk&2n-y(~(W?Xz25&X00nTE4J}hk;{8#w zV84yL^80}(DLe506Wx7K>lngG=tEJGP~$UC(s3=Q&~F>JsO)7=$S7yAB=26=s6P%Ebnr z`9ESVOQ-_Jo4_xrb$MK|{H3hWsww;`wPvoE`!d3goW!r@+Sn0_swRc9oE4@w_2N}j zXV2h@+^qofzPGBuu*AwJiTlCIik|gjpgym>i5?`FNFCMgaiXmp8DRnoYDSfHG^0;| zbwB#%>i8-BCxK!9&$t*s-As1wOq{2Prs`05d*hwDeo=oK&BA`F*UBh~Gc&XsPP%9C zJm=3=JxS8DWOLh0dt%@^On45U(Jjm>R6A=|vGb|TF>75hjMb=o^Wge;0h0@#&OMLP;!bBgYVUewZ?B7-jiyl7D&t|UEVkwfgNgYfjfY%9|CPO$WJnm0-+Uj}Wv0%%(O zH=-p)&kQ%C=#851hSW@vsR^jGBi3)YPO9Du!i!!^iItN{28q!Xj$&&FJVK_dPKh!| zQV$^_t*)fe@mWmboAW}zjL_oJlmNi0NNJ=jrS(8iBT_CjNxA6RhbQB4!5 zt{3TiR1?7YxLv^>Ia!45Pvb-Qf6di;hXo(=HBF>4n}1O_ww+N@eadI9?xx=F(wga# zz8Mb%kM7~GC9pb54xF~KBEh>cmTs>%PL)w_;}JkqMI5G$ACX3cK2$-7{zFp<;kNaS z6Xf5Xexn*aCL0lC=)}{Rx}SE160}}D^)tx^PAVoDW*?gpX>Q=(^^~*KQ*<| zrB8i-Ksmu4Kf~Hx-*4{qCa%wewY)}zY2v{q@+a4>J3-b*$eCots@7z zAi9XTX}HE|yFUbu#e7(Ua1$V2K67}jg*gyXN#nL~0xz)&&4JW}z;Q20S45e0G_JY* zbx5U#a;&&Pta)CiYfhdy?c<{Q4VIoLe_Kh2@*9tVjQv&%jR-gszYAhY?Ut1!|6Xc` z!}i|?LA%;{V|gX14OhD&K|k0^745x|GIi4o{G*p^->aFJ{D{= zWKY9L>3S>P4shAL;|A`jXxji!@$E(wFK3XIF!}n60e7k>y9oz}i59~7Z~~C9%q(OH zB>9cU%lf~<3?%t)bPW4%rdBr+1r3GB6~wB)1Jo@u3t?>@E^sTPJRqz8fH_a`o8%D= z;!i^bt}>&!TQy*^nK0@nQ&Ij>FX22XKY?w*+)l|&CGMKhCjD7)(wX@?3JUHwE-b(Q z(Qky1oA`5`O8?rzsHP!>CvD*!p@pYl#hQkoJ8dhiINRGm+J7+LY&Y2v5}O+P+sNO| z{N^fPmW7$JS`wDHmuYE!bA>#|hnsfN{3h3&^CCuyJ%0Q+=vtB#0Zmb?fx1^D zg;Mr$gl5!Pkes&h2I{Ow3ZP0P$ngxniIsDL;F^1n-&4C9X+4sbghYS;thgJCaOTT zq)PEw{tog_VIIJqpWX03%06+7m@C7z(9{$}rIF z*TQvy180^Cg_4%m26{TeZ*D!NO>bk$+{=Qtpp9rt>ai1h1OC$Cepc5xj&=r67G?QG zRP9Zfvp6_dIvBtimIkdtEz6XZo&Dzb2M+%%$>`!Y;S~~owq$pMB#PR5lj>OhU{JfG zQzKf+`uk0mB?;|Bz}ZrnHW3jHyfvpivWqMNOczv?&IW;F^|c;Z+Qks>*n9<0rMoJi zB53iQp>#71-1go`JwIu>7n)W_}yI5o*mbT&B8lpRa* z57&B>!}+GnlZi#zG5byz7Z_-6W>&1R{OJ#jH44P(c%kI=!hBhDzPD+u^QvvBd>2{h zsHR9h5BjbOr3`x*=-%$w^6bB=#JH|b+nxqmbb&WnJy!)#LcI)N-3{%MDo)!lA^F?e zl;>`4f`{DUz-j~1*vA0weLTcntsQ~aK+FE?%sM^xWt9vW9duyGecfK-Y`L`>s!K!?=lJN3pJkn zdWzeiT+Z_La?_{~gVLX6wKL!fQzf?t79Gc7bp|xR^w>4q&PGx((3GvuNogdfFUX3j z^-;AMr@$anWd*(5qdnl0(L+@zg+16nD=+qs)-q0pAx?`BcT}OAra2BYT-LapQLRGV z%r#XGTnOFNM;_^tD3scqXQBP9Hgg|Z>WL(e5oapScXa7 zWtMeROB{z#9GiM!fb09iZKTtwU6Kbr13h>VHrsSJox%7GpjB_^tJ~WQ+MIw>BgSoW z3Jf6O3M&rwGu)I__J#nf<7_|Fsu2dzVt{`<5tGX!O;J%FxWJy$;e9kXr-X4{jB=pH z)l>inI@$ns(b25@WtMR?6-uOq23ppWCy_xvQ=#Npk%4X=WI0cDEtZVIR;^iO3_n}T z=@%da?46C@LmA+jYzqjN+#%NXvc))qw0ej|N*x(*Cecx^7yrdi8gCB0ogrLhpnG$y ziK8A)kT9P=O%C_Mei}x{^h?0i2GFWE0Ne{J7OKod1Gw+Eh-6uJ9IIeL`nYbof!8V= z+cm6`A#-|X>0Z)lI!Vhb`ru$~xSLcUWjMJOSoxoYeu}|3f2hC6!8Vm;xI}(f z$iyx&Wx;4o?R=X?K&HeFYd}{Pxk9d$eY%AaPESqJZMVEMEWI({Og_?z%^NFuAVAY*UitF=P<{1P( zC$}I(CvbB<0o4T|3)YQJS#8v%&Oz2qtb)rZ`@#zB9d0K(m0*qm6piqYaoB!yNaaH# zELD{HTmzUoGNf#tDLWP{qXNx0Wo4JbWNf*D`}`<>Zr{)fI$+472ORuZ^b94{O z$$ft`Bi-lA%5Yr4lY0(s?3jN}GNRX4+08up{s4Djt&=;m6z27YG3N*ml{NTJ91jAA z97{>=#0nS6+8motT@2VmNIJUO|BUuO9^V!Eoi971lcQwYHQ=MGx+wSxNsmd6k%a@i zjbzo;Hr&V757&a5u(b&Uay$4)G!p;))0{2!qx#)U7T46%Ahroja5RfCnhlp_b(Z;) zuqvK@7coDMMgs&K%@V-(U54E$;P|;6f1@f=9;qw^(kcGQ$~>M=EmP)QD5YQHFQ?Pr z*29CZa~MZt7QE|tWH~BU48j40;^hAu4xuaHxlgq_dXl#?T4G8<0)UT!oT|%l)}T#! zilKa`q#q~WvwLKAutV8~q3}$SsT16VKViw!_{4!u$#L?=G`~6e;c42FY@RsSn<43U z*nq5q`YFOTcpcQ_63=y&OC4AiYgl#nZ-BV9iXwxpjnR)vHOQcZk=+x^+^b)&R}4 zHAsm%c8`sJ5yRSOnR=bYK&@Fv49ABczo7blTaAdOQh^-& zo}$FLeyDk#s4{ZAZ48V_c1k$M^g=XV?etGm?h@aNtUH}`OJ>_}8V|x=0?kDOkU_NH zi?q0+6#Po#x?SgGOU8jW@Ec&lYYiLLj$66Qf1F>jb&lj98zpy#-<*>1^zK!5I_+b; zV+G<(T%7;8y%sGiZuI-~#s(#rSYD5og*W+Y?^<~yx?+xhveM5s8;L6MZ=~Gr{nYlr z;!U==nXK?39F_YSi~*mxqA?>IHb=|ooBigO6^3iJV7x0^G;K%b|r$wxZm=#?G<7Lv#;8bCfDq^4q%rO>dUmR=lNl9+9!{DXmu`=ZTEf8Nyz+qH@(B zoB}8wSMvF`Xk78{H_7K@#QUfkHWJWd9*7M|2aU3IY>xybVtWU1MmgM zwQhC&bOZZu-r<*Mo0o&UL)s0e*&a4ql<&38IPsNm61F#5Hs9+vf5fr(uUL2bEF@}b z_u7T)zs`N=VrLE%1MoUlYd=5X*fVC;q&?Bgt_fli4q5REGM@)@5iVh#D!gbz<@(`-o_+W@_JZ3;? z^8PcEFMOzMg!%P)IvO2UDzl~dWD=h8yAmhv3m+YW%=^v7I2(<$e;*G*#b3~IM#rX? z;{7>Gd0&*CKxx&LS-7`NLJZ}vKq{5bAtmDcFRwDgB-?*O<>lAa_ej}FEPY2`n{ST9 z$gJND2+0V;z4(vyVcP{|@&`bluNS^3%n3;KXvu!cwKzQqVH^hihsT@-$&NMGbIia}iR*-&ta}wiF#MZdig~ zf7!6C2(~o4NBSCJQohs%Q2AL;5|`OeIw|#~e3^|d^Al-`%lt?hFTq84moK*qFO6)& z7$ijHBcTqX!(~~E?P@@Z8v)ekN!)p@19!dP6fR*a{V8&EOnta;E5R05I%x!<<_;RJ zDv>c5-Qkgt0;(F3^QwR-px>2`B=yrP?TSJrj%MXWN9uKyg3Bh=rntqRa-uJ*UB%-T z12rR-v~R~hPS3P2s$)&!0Lt^hmTmlPvRMjrTR~RDOV1}lIFle%_*6CECLt01d%gXn z_`3Sdr1C`l4Yc^p<= zKUwCF%&O%jrv8h=yQxFwxOE;^?YI2BbPlR%;~dN&rMvIPMFtozF8>Wp&1!97$agHr zN|4Hk2`2qmrNh`%8!+TpPSQD&y4LC-=R{jJE6v42LJzNnALsgExgA=R7GyOJ%a;2# z_%C+nOtd$^F0x_)-eGs8Q*1z_dBRv$)slyWKP$p zh-dS&wRp!s2k|F~`-$F;1h$Yv;}OVoI?*w6)^q|T)0grM#aE#O%QpD&2hxeDCM3(u zpDd|)-k#6#vok;OFv2Fkd7+KodKWUNcqG>O6*?BYJ0^WEuCNTn~S}P_Z-3wRMp4-Uz@Gzq%xk%;$vgIs*J{fP@B7BAYminOn?Y; z{>lk_o*aGI9-~q{&PS!1BDm`qt$A3>QTYp`_*IMyt-bD0;e`a4Qvm*Ba1lS7%fGMs z&5dg8r3Xmzdiy(Oq7g;W2v0FK#A8rF;cHK<-Bb3w7GiRGN%89;vh2;z|0T;l1cu6z zC&4O9YN{8bTelQ{XC!R2D+AfzSB`JQz>elNXkmhZF8O8?daKSK^88{lH+w{b_KHJ` ziQ^Jm45xLozfe15?SAO6xC)7dE&*N2%Ij+y7y;?9DWJ=wbc;0<!)Tx^cf~oB zchrSK4$ZqJxJluTR@qGNsz|)s{pJ@x^0U}>oIQZ+;B3fv#aNr{LZOa{X;6`Xtt{uLjc1v;5sp=^vYk2mX0ukiw#XBJJbP@G zZjmq2-))^~b2UKSE(qP3YDW~?K409h(I3>Hm+d=48MV z$Nguksf5s!P9Y)Vu-(Qs9@l{1ZJqhj^F2*nLw{2^y7-gP~=}!{N|0Ir@>` z+?`KxOsC{ZzpH11K#p2#eOlrfvj1aSpGt5o>xW&7>${w-s#c{I%#R9(X*jd_j*19{}&pTtJ z&wji8Bu#gr@sRMYC`tn_r+9vZMV}zx6oFsnTCpGU;E9)ZA7t%q#vNit3SQ* z4r?`B9>B_MtJ2$g;133fC`KG{|F>42ijG7)V9}K) z;4ov)w|;X2Ip^GiF%2FXVs3Y;6Y6`2^M`e{=-g*7v$c@9y<&>#K*?hV z%W;hUvptdFI>09`0o!YPZ3r+D^yIXx-?Z!16$vB+Ip-6 zLnE-y0F*r)1`ag1Z#a1tkd@Qw<6z^cRXYmrIh3YPMhraRcAUPrb{(q>2zNXFN06(P zGsC5hAVY)CYjA#c+|E>rWG%3jPKJfpG8qTy5Q|X@N?@JE7L-7LJq2=HoAu;G#_UkO zDvTo9Kv-EZ<{CI?mRvISd31KP99+=Je1VSUZ@NKg9JS}O|mMJVsD z)>?6ec?m-PEhG&3GB9tLg2g=jYpi9}xwFkif(GXmrSk@;rssFdVcMBj(W3ekykbmu zoaUWB${561&7em1?yVVagKMD}x>i|u^ABH>#!ewf~*OsUTZvftBDjX60Ly0?U zM?*FORI>th9X}TKk%Whfj9sL0--L^p|3U^p-(N;uAn*%MYW_;PK*rt} z$Wx(F=iXwAE;qrId}?d}`+S70wHYP%cCguoiyJhPyZ*Iym1^=1o0Z<=?I6)7Lypzn zW%1gZFi{r#3l1*N2$)zgrTJcr>^*6vNm*=-ks+r-zPH6lW$?Qd;P1!CIj8+Ygtxxx zJZIqtF%QKBYH#q{9`n$RfgF9BK&yCAWe5H>4hsx*7w(La_4NYgUig-%v?~S|^IJq@ zTy=~rs2>=oY)QrUZVqI6Tx4UJoE|_`BsQF-poY*i5vFvm=!i%!)SgdCj<0o)K7sC~BNOYkJpcEHfv9xUBamK~JE-WXXM z6)kFC)#$%;qD{as;;<$sDE4Z=n~2P82KA@ol7?lpe~iIU*Iyy_$`1-H}P_` zGqf5|5@Uzf9GJh3kzTQZB4=pdu&15b9a>5ISq0{#t_7-n8zU{^Y&8=69jo=7?`NH(T3vBHU2q8APGZ2E-BL>qE5cIv zm8a$uVAVfuk0!|ZBZL)|!i9Lh9yra1Qj~Wb^wAjkGs#Mc-^XI)n&go0rHR zm|-baid^zf5O-c@ywCAviscTw{53|pHVzTVZ_viZAtL#m&E|iMi2YrCKvM3D=GJC? zkNA%mxv`0@>jADmS$Ezo5WuM>c3zOuzgWEV7Kp2)X<)D#80zugC^mMfLwmdg*)&jl z~9e;SJRVm^(3!lV2YAWMD=4W*?DrXdpdX1*Yg;hPWEahE{>v3)vf?vCN;A*-ZMRhFFep7VnRXY-yCpr4tc^6?e}} zk+d}1goq(>H=4C}-3CTNJ58--D2|b;^gy9A?P6Ig z>~^e|^IF5+<$3}pZJPbW$x@GH2DK_);xhu~s%nZhL2k;hP*zTq!@*DbVM$1y2~0`i zLPXk<(G3MYxLBsO4)k{ia#Kjd^70NXNrTxeIV^8%8%pAD?UUuE)`8x-*;o46Z4`ZnE{jP&bA)elo>ELQgNBh zAnhyf^m-}=7T^XOcojMC);Ye!&#JMi3I=5-2~y*xF%1JOP=GZxPSa4^f4V)^4GxT<7L zQ6W<%WCaQx9Y0HUWm!H`Cy>0~*?!V@=_o6^f|@~h!z_Rrql6q@Cj*#sw>P#TlFMut z%QUhj%K%^YB_Vybi*3d2RA&Pi=}StI+)pt$o~JMf$f??eKr{K6O4n7EcY|D7-2?rv zKAUo>UvQ4zhNrX>9S&T&1`01e${wisAx_qU*h| zT30kIa6cb@AwhZfBKe}#d6qvE{zWqVJgaR~z8-S?yueu9PG}$GQcszFejr5-j*BJ% zMj+JrUZy;4L6G)E<3T&$@l%!LEa|QEEWpnK&^UASM3v$Ym|7Q~WuX28?ENH<4}OxJg7HWBygB{cuP)b?Ff@pDvN*J?vcM^y(+6J?-?O zKrbbtX?{GMckj}`rLumNJr*acE+cGN@79@8^su$8#DBRhZYe!xf=#OaNADeKm_TFD zpA~jL5St=hd)syt-4!g3Tj)*K;jHUzwSpWUASZiUy`(=gP_FG0DAoay?}Oy4J^>TD zrkxs0JUzBYwUT4&EP4)X2#cprZ-na;y|$PG%(2DI-R7r`n&*l)8)~UK=d;>EIpne2 zs6RiilsE9H(xw!K68GpukW%i&fqr@|juOp@GfXyI42-=m37GdO*gW5q#g_#YNR^*a zQf>e|Blo;}Eq}24cszaQk_}NlMHfn+G)_>G)wB!s3QM*FfH6T#z z##aPlL+};z6E8r07p3^jMuCbqh7^|ESXc4J+Txbd`+*<_w_JlM$Sh6uf4KJ>hwEu_ zFwd$7TbG{h=y+TB<+gzN@0!fL$`~Pg+lqcIj$`7)Ovzi#m2|CJQ;4}dCN4$Ft_%!U zbt3^&ZL!C2-*pmRLRkC)ycvJ$fEou5eE?4Uwx7$vJ*Qduy^{{~XOP)-DvU}y)537jW>GwdAwBXPz&a{>Kv?Tj!P&7C$d+KjBtM$dq-Al?rPQTlUujs92c)+JH zP@u&e;vG)0$KBYK!FVTTNij^;;9@IUD#%@0cx^B~a5pi9J&J9dZ&X1~ci8kE3(IhP z9wmTUbg$E_*Q1#KYJyyPxIin!HE;9Z1@k@&Y9eLh0s%!yTey(LRS!qHb!-t!YaT@$ zuU4O3gDe9VuL{giedhSe^}D%54aI_}MxE8Q7tWpgiEqW?GtQj{EX>Xw=(7n5(Nla7 z)8uNqg(aSc^t-v7UW_S@Wyibfl*8R>H zsTlw?O0(iCs=m-b2WUR2l7WYyEzjmlcGSwrYlZHqr~C7((dA z+vl_`>0ar;<2G5!6-LS*v#_%aUU)ag>*5COal$HV@ZNOJG#gi;UOvI%!et>_6vxm# z87pbiZE%Aer$8Lvdii8X%-*;v5oE2#pGvVrPa0I&fyE1)M4_o3NgnUoS@Ie z%Cc(%v$S*0{Z;6Erk(rD5Lk`B5UvY&B)(f)Yy5tp|BCAawF&84qg5a`q){k!TqoC( zI`0Z^YfP)!Wo4Z$Z7fBD;;b#9>CtBFemyH3=z+UzT@Kj>Ta4h;^??F4j#P%{QEZ|e zEfN|_>g<5Ifx-jO3$ZeLc1U>g#aQ`qwhhT}_PvBf>xMuxnS4XY;{LK0j?{x@hoo27 z_lA{-=ekIG)fP9GeFuYP(Pc$ni$GD1C#ZeeBFdeGB|{XbolcZjO%6ENYaPuL9# z_ovDslKrlQTVI766zz(7`d%zzS0Q@3HC9I7YP)9iWm~N5y)`geadE1@Ph4p=X)V>P zW8+XKkM#q>y)|jL{%xX-igGWuJyy=X-Kq^ayCYT}y**(5wIGQ**=)d*NCgBZ^#n?L zVo&AqSqayry5F)N-PI>cEdafc2;Hm4G?w;YdDHyiePaXcLa) za~9`3?X?EyKMMQ>;YZHKuM>e$47)&l+Lq$`lCY|G|0g+nEF39Q;H%~eDF^9q?u7|k z7?`M8oLb0v@Rb%emwAf6+$s48u3JxVeC{b)@nwVO^g?-tOG{tdmY* zeCHG&yCS$xay^XkJu8%Fv^XnBMIG4J^%$Gz69YO`Q%Ny$B@8O(B5L={jsz|5} zhq1^!8Zw!WU=e!M&Iz_ZibZO%-3oEnbPRI@-zqeU^f(E4V>M>s)@g9pU9S8ugw?DD zi(NxG!Q!wrb%*k+Eye@RBMUSxVLTPC+`q-jk)?J&$X7)jyK;Opuvc8zs}9v!7g4qviN>0h41%M>U3@$a+~7FgFNMk7H&3DjOZAD~{KU zSDz_hgnO`72MX0LIipRGytRS$a&(QA07I{dESuzx=_JKLSn*9-8vEo5=OkNDGbwrj zipG^&-Dol>1u*MImu*P#$gz)Y0E=WdmYy2|e)Z>+9*P$E-p>b2hC21AsjS-oNqJuk z?j4+np0;2_ubIqy0Sw7}AuvSC$TQgM7eaW}!c>WQ(ejLOyp}TM#Xzx6b^1T8IEY;f zqEprKoqo{<0BIFcW$;UuyHuVusd~xcW>@L_Y$7=?TL$uI)FT^SwlP8uB12lf5}58- zmDcPk`oi$qwO#4viijt;UWL=O==VUH92*?KqiLZ}6|^O^_FiYalEcbruh~&i^f(Q8 z%}yRFO9xiL&!4Z`DpcW2nf`i6pzADtS_|v!T9qKWuV>4oH$sAn9ZCAWm%U9YHd>4n zawjR?Xfd*bEPl3Pm?lN{`-+V9I?Jd{wjRabMfPm6d?lN^;OEglt86frBwWCn zukRWs)sd^`XU!UFyn2UwTPA|*Mxe)ccFA;fP_ntw& zvt`x0n1=jk+?fs+v--Z5<7%s`sVCw~;wrWVeC~|MesQvQt6fMbpG)H;cbh%ur5asE zbj5GL@!@t{r;k3k42)=w1>rQ!&kze5JZ8|u zYiIh3XGl$Cz^n0gWf}Dozl{{_3JJ;N>NlePt%5r`HEUaLwGUEqqmdUUf$G3$#lhKf zB`yT0w!R5jJd`6T?b!q~W4GNZvDvUVd1|+9M)LCGB<@2Sxb)!L$7W$I+clFPBjM77 zro8$ID%zcN2AKyy@JD8@Qm+f*WXQ*~Q#ox^v6Vo$AMrisQ@LDa5M1XXXwaW;IyrJWrZzU>I4?X+0w@5q(R%#C3VC! zK2G+1Va3G7{3kIMGvKvV;=eAs)e0v`lU?w_+gy%`>xZ-{q@r)t znSw`C<0NgLl?C;#Bu*;!*?lopej1y0yD;26Yp2`NrXi;b&N-7aw`Z_c{F}IF0H>qD z6fWfdDw91xel32(IuH_cnF(`K6B3=jjtENLG$D?roT)T{vruY$ks;U37;~*;PYeun zJ2}_0UCo;~xLR>2@(kOT*%po`q;^9&`GBlqr%Aq{4)(mLKwrHGL8+!{-xwz`-&(nG z7Tgpk^YN|2=46>e?7Np|)Y<3V%;NI5a6x;mrt*Sz#Y=h5jgw>F*EwQ@_({k8~VT?!sj`hd|1ywXoo!;pDp&V18WZLU0fJ z%y`#(hLF;|7Su#458Jpp&Bg*2ug`1RLi!xB(}Lo;55+od!usNJ^0h~B%26F<^WWIf zLWt|AHR3dai}bsh#3bQ}&~wb%U?%RA#mTH=fn4R7&N@~v52BynzkUn(@wm;HqL+3* z30m_WvH=bqw?PS23T}TMAZ+)0-Yi`-kN#p!CC|eiv>=ZZoq!}=R^=hW@uaIo;N= zT1;Jf)G3}kHW{}dgr0jZwlMrK@5%ReWRRq`0C-u9Tq{;4z3u|rIEkODwww8X$!7A88Gi&u-j*}u!;0KWxFNko{f`br$W^DIsJw^@B#Sf ziczkLduVP!Q`wk;V~Ua+5!iI?_*Wa~^>fu>88x(5pN`emhbes*Vaw8w5c3$ zPTDjBfp6jHaLJO*adKs3feBqY!q~#5Lv|s~<%+*mvUue#MiH6sM4fa6CaWwQSKl(@ z!sR}_N!X5Eb=;@7SlW9T-V3Q+%UmAE+pJLhM$8%1;vEZ0mTA=?Q{K3aT=(8(g>}^+ zcYk8k8OVLl!Zr45a?gTkCtC^1Vt#FpcpIgsu(!oYS-cfCCHH=uoQf|n+1#9UA1Fz? zksQ0|$96cA2(@wBEzB<4>fo1zTS+jbU`L!>m1yzXJJh_u=T0B{iVey>MmhtJ zE!=9q&%$iVf-88ipRi3IhXfBaV8J^V ze@#%%f_C=Am>D-Z0GgO@V8&AmYERw1)o(a8sRbrkfR(-@f)R__)TSnAdoBat$H|hk zg5hp&=7+cn52V90=Kn4>KN2|7OP*bbpmZx;4MELiU&5|E2kVSa>U0Km`A_{C~2-vM=jc{=aN#Q;F|fU~*#U z=l{*(<6qW2iui}cE7!KR>g{BNs9ZRi{*99(T?(4Zf#@W+>wQuI#`WeDKSfMk8?|%NI2(6OH3kY!r`CmEO|SPDSHY zhDP%1SwKL7TI?|9&9dm>CqlP%0r z%3KregR$`xsp(l@uJ+(uYRpgkyZKxjt1>6)DZ4zxuQ>Iy{ZpC+o--^AI{t08L$&jHj z%~T7rqi1@LPD;AbY4P%Vc7gfLjY^x&y1U=XsI~WwG(8rC49yy2MNH(4HX(zs>Kd$j zbm`pdUTX`hYZx5T+pxmPnmX2}tu1XTeZF?AUg-MOE?$OQYIg+Gw)Sk0`!zP3IZGfs z$I3Yds6)K;z0A52)QU{~Mgok3|35pmvxxK9*L9Lw&W@M0mlsUdJvf($j;N(u{OO|` zB}-a7UWVZ^p>T$IJGtParZ=)jc8+Q(iBt%EqNc#aZm3V4+5h@)z*iN!b4sPWqwCm( z6?T7vL8lEUFuw>ZGA?{w@!G+Fg2`H!o?bWBt$L$rXo?@xXtV(ta!$PT8WiGdof|LP z2V(wJ4=U*AehjZWI54P@W)PNJ7_shpD_Lr(m###Hp(nq1bprL$4n{ZRXI4QraSr?e;%<#3SoCWC%Uu zWkoKA5S%*Z2$9_A|t!dr-4KJK6kf zS`&HIgFrXKR0^YRT;agHKZf{r3a6Am9nvRLrJSW0-^YE@HOe7{@2$WsZf)| zPiBlFl9EF>$2vOB`gKGy+7{bQG}Ob)1?qkw_daoNYkOZ%&>Fr>Xzznwf=N@BBW`+lLqX;XBvVGiT16Idf*_UpA~}V$PXq>NyN1xSnKnJX{=XPJ=BR31ac@!muPtXTL|7ioTW5|3*XqSL zoo<+C;w;Te)&6Vbp!q6LuPE7V3rwWk{M0m6GcL~-viN@0?&7`d_=_;?3sNJcu-f!8 zM5A_UQ4QzFUaX>MNkF14!PD)tQ?-LB@uliJLPnGYYCS}yqzqYRkSh&fIq({+g{n$d zu`%@1oYdh;aB}ad2*FBl<_(-QRqV0YevR}qN1eGfvphhaoST}dR_cs@cTIfBtQz0n z`&{%rm`&yxCgYU2f@PjLha<{2=A~+X5e}-ACKv;JzL5*G7_DOP@|WA-8V7eU*>5$2 z)27x9<3aR)+tFGRAa5)%a*_G04UpRw8g-8utYZe}*R{i{Yyl3Q-v~DQ05l}6CwAax zXzv{t8QvyWZeXzA!q^tLDcjVLyB`}FeDP;k$#8TYiU^#kYxXEM0aN->oo2YvXU*c& zKHg__ixe*KbDyxeo)V(4?etu{A&z6{b#7btYexy6YTs1&?PA48)Oc zuTzi0I`3xxx$Xw#3X6;dg0#3lKu#8=>dO;ol79fR*YZ?tl4mDxQ{UKx`W3n|`uV1- zYqkTv+b9~W%7ZK_@i!dJKwM=jP4woi9Rbqrp43dm=_K6F0NHYnF#)n|y8`6;Js!PY z#th4TgVb8R!pKF+klh%rD^h2KKpd%~{0|utjG4S(j#S(Nj^=f0Y9OOmgy93n; zYwznT9>Gky%A>$X88zj16o>()Z)K~=<}qNU>v3is9C#BU9uJVqs|_8=t$Q$$tuch* z_Y=(aB(uG=#*ACa_a_ldcAK6%VeS;U8a#e<4P~ARkiBaSWjJK_21v7Y9#g^70aCQi zP?G$!FF<}=XDqcO(f$A#zurS9EQf}?-qeu%cK~zX29rOhmS-R$HW(2>{8>}Ag87nE zj0truD?BxJifN#|+{65TVuC~W5Jb#s#iJI9Q zLFt=4vN;+c2REnci;}1Eq7>zi(KP0Gcmus&nmS446^-vVkyl(^To~Mej6`E)-eNZSTXE_w=~-Oj zUbN7uFaM!7{5DeTdMYZmri5Da<2x8dlB%^W7WOW)si+Qu-Rk~SeTy3h^?S&nJP#LD z_h~&x)v>cO>MHp|F?#~Zz{GdRW}o@E=z7-#>~%gqPZh(d_9T+Wd=uPK(jG9Ga!6Jp z)E0}2g{mI#m^`XjgQUM91^aC?!OY?mgO``x5~HhdY@3l_4>oY@!9r_9m!T2ePTvePs!8Nr_+Mf1T;2kV!(a4RsbYDzU6(GU3N zz+-0PUc8IDQuSq1T$z5snlEb?FV$rbFJ5;6f5~!2{^SdDYnh=7+j?3x=sNC}RY|zV zplVI)&>7@bIXkWcAj%O&<_wNkEiMbjow+W$)n zF9F(QYv|DoRTnnnG6}!4XTwBYnW$%tdAt{}E5wdZ4DBpM?-|bMf#7Q7Ry{8cbLmqi zl##C@GQ$URd1XXq7sQ}rvzY~L@4RWu(L&nj9UyPm75LeGj{S=y;afpHqE z;Sog}=JgwNmZwvf)bPyjOb~Oc&8l1M^_b9q4Hhazj>dQ$dK|8|kG}^mD%G%;%9eEd z1EP7q$7=RZD9Za&=cr=(0RM|woxj!A^l_Z`#Pfy8xW>CM4vn1T866s4H!$FNu8stnk4IgB>UOa<%(C?U3DzD0&Zbk zXHBDcFrW9C`k zf{JF=w;Arh0Ml;+u|BM=4{_V8^;_T6XR1={vj%eD#Z>(gFI&?PCHt=p2a}Y+=vFku zyu{ldP$M~hFmmyzgs~FJCg+A!Ur8MKTYW0YM4ikHVgu z)To_Kuj=k4lLCMjE%6W@-fgp}wl}bOw$1B_r$D5S8r(ciN)8(-$Wnut>_`J!TaRLS zB!|pEk_97G%+fd|JO51*hA{orgnF>VAL@q3PL@I`P=nu#ze7zd3?vM<8X?yNnU^h% zGeX<5*rq@>9p!}=B#$cPz5G z>Ya^O=d@-vBYkan9h{R5rM4=9yC!W6hMUqd>)~xBM)2b5wiem>rbl7zP}t5Qcb#jv zSS_B(_3e>il6~UD$$7Z>$TwKx{cILZmCCNO2dZc+w?l2T{HGADY`W^GhI+fE4orhcLFqYxts1@vqc^Qmtk(E3u;$mj6?>$HS@Y zJtNyGy%Lz(wRw2(~YgbE+8Y1>W zvYhS>4fDpoZ>C&3lJ#b?Qx+R;S?4$f_ zf4TDxKu_P(&TwaO-6;c=;pr}<8e}Lkw;*fIWI6TC1o$PSTkFXS-}}LZ_}51D#nPZ# z-O_|iU)hzAnJBg$8G3Ou6p07p!QoF+x2P&_#rF`4%=#?#OV#|_@qH(o9}!~fEhUwB zmE+iRv-Pl`L>P)X<*mjIuz63^!?sUl8t$5}H&5f7_l9Y_3zKRak7_B6%EJMjypB6` zHzYc6M%A-MBfykjrfyL;_a-4~vT7b4hW)$`9;}M18zi>VsVOQa&Y&sCDSuVF-jY3R zQUfW=pNAVsu&=Igu3JU|AGzn2mXdtNtPZ$d8l^+S|{ z01QYfvuGq6gJjnm;0c^ge>;1W9(IK&gaTrsQ*-79^=7!3rmrjPt-7 zNoCswdTqzm;azShVF_M^i9c_uNyTJ5paY9;jg!1Z!+qr9MGTG`S8xGY@RV8n-eIm@NvrH>8K33G3$yF` zNmRQf`i+VjJR7*(h5M-0z`P=OdDKf;$N_$83T|6QSNlocCA5jYF)BS5wz(Q_&A!Id zSC>rHIC}Fm6fI{jr6#KcT*K$Hz$v(>8AO9M=iNN*jY~c*ut@pk)Vozwvg~X`-MkSK z%d9J=2g$N?nC;YC6G&FY=(RKOTtN9#Z?#I<^jt`xd3@ss=T}sQ9MAKBkKBYCspG$z z-$0_jo)19@H;J0Y%x8g{aJ{m-v6{|o%mNLy((SCiYTC&bEz}ThYrlj`c)TvuUZkNG z?AJa*=T=n~#o}T>S2sa-N&UrGI5_f_fCRso`G|AFQi_>P5w;F;*Z~p9+A5(-(k~-+ zG{P1uXMQzYISRpr$oPCQ3{U07SjH6nW}rI&6=^7mZ>8ai)rjf3>1500fTBvUL@oQ> zB;b^LH$q32#PyQrXN}ULh|GPD4u$_fKx3HZPt8DHmB!pig<%C|i$6UShL!5OshoT| z&PQhbWwvB(h`GulC;v*-_fNAHtD(dEovH^%GQQWKq{1z6gZbneE{GgC1>E_bF@JF0 zS*!6TQvF-1RSK_X!U>?#a3}Ly2fWU`XjRHJqiu6Ytta8CFW-_NpCVZG2yHzZ7`c;w zclkVH@lC73jT+*Gzq-Y+g&dfrRfAzjx`{b1Z)qDK8*Z3_7+h?Edy3_8pT=9;Ikd)~ z0;t457}Bk5@sjr(F0DT|+}sCnCn9XNNMs#HiK-oEs4b|%m-pexMa+c_BqBRYvpvVj zy@0OWht5uWe;)2i#0B4qM{I!ZV=-6fhW3z%4RgWrzs~C{6|KhxDM6Kr2wN>O>{dsa zO28`PF%#H4V$qU5bsgH>{?096_q?qY+_;iO^P}-^MG^#~L`Y75T;i+lg8h-H)9jjEZpC zcRx6BQ!LKTV0^&H@UXNN^LB}D2`ts0TtTtTp-SO^OQn__HA!y0#fw8f>_5HUghu+yk9#-K99#v0P=5gWxAk@DygzKx`MrBuMm!bF}Em zMTDnJgwc}uDDG75E-q}qJZuyll6x;otFtpMS*kDL6=NPHhw-`ym=v#p6-bmZVwfmU-N)$C(5_fJQhBg}7ey}&GS z#+YKrct;t0zO${ry!9Ankmwc@)JZB%2geMAx2sw@^xe_ypb7?Ocd_-7$W~Olag(L$ z47T$)=vI8FIkeuPsKl}FGPM51S>Y8-U9pb46&=^$`&A3}030V(QZnFcNILx6CPO)T zwH%y<(AQbm$wBQVNsG1)-M!504U4R6=eXMshM^mr7vDs*B(ZOZ>?9@C%WyZ}wb8m{ zY6Negd;1B!62+#G`su9UR&tpN)Elbx7(C z1JB7J3$bqyIT}}CdOkGwE+Z`86xmgF$~4W+YEOmKdXF`uWIWkFp_zO(xOt#NZ|T%P zx&@E!t2Q+h0jP?d009yohjx+C$&5?x*_=dZ%HvRb5!y?bunI(mYRu_ z4E6z_S$PfbEPVjWw_R7JK)Nj*rKKC;4=wUWg5!SGln(g*2+{jKf#*Ci8TItmYd!{a z~?wn5UU-S|3{u+N=)*sT#3)XRws$;n2@$v#7HuD(3Mvy=7P9u|^z5jvuXiVo)_p4iEW4ddj}0L(p{mK4Tm=?7wclVFl5 z={5yA(c@6iXf9J$2M*025rLy)NOtms8IqXWF2p}&pnY7*?8$Q{@P@+^L-*@-JIQwxEtQjfx)fnNz?kb>>Pu>zMH~M3YM5o-PJCaoS z8T>WCp*>wm;$PG^j3Waa`m`RK`77qhfsQ8?S#0?JjrG{PzHJZLbqI8^G|o(rGlgSx zXUs>0-x;CbhPJtC5Eo9xAhvulI!INB4f!uBlsLHEL`vEo;>Ey!Xy`7<9$@p6dP$@7 zsTq~D6O#T3{QNU`&ne!;F!5Q)f3pg7d*jG|6 zkV}R+v^GxN`agldt`4*fmZC>61>}x3f{y#Zbpm0HdM@rZ89u`FifSZ#IO<=vO^yvr5&dpYjEQZY;+WUl!h^b zmZ_SvW%Y@beO|wdRDB2KDtUg6t~u8$KExU~sT&JkJMcrnKsdcm(xp&-Hwcufk&X^> z;(06sEYHq?^N}aqP{H@Bf=IGP%&hNV+wF3__aaQzqa1q5-YR&A+&>lsjc;jW zAN7j>4Rw(XV;$pFRwEEBj$$l%XmPY(AAvI0_9nc6pMo z3CR~f6y&?O7c?nOHIwX}z?!mUr^9ZS2S0|8=JG*Nolr9-s~8G>3oCZ5=WtyW#^ACu zVFP5m)95)|CWHsdKBrkGkYvq~aonM}B$+h@BFw9eEdZqlHcOO%6qrz}D<&p-Q#%6q zb|#&)(3DHcM6%rSypWcXkYQ9FQaQ>5C(5$l@g!VjUV&ypCW%%pD7iUFj0rYL3jZ-0 z7_F}@!T*^?J!bZ;(5y_;iHZ1*Mcw>2hIWwjw_&qM>fpjU{g7GOQJrqhyoV2JYS(@k zl}|0kJo4RA#rkB=Hb|d+80?ARgjHeXWUW0+1k^TA&P{MUsaS!6p&g?BeK@Y4B;141 zb!Eg{&GekD+6PL9Nscut>S}y!c%L!+(mAj1=f`2pxG8CVbm)^wrrXApJa>V+DNRC@hT?U8eL^T&L(8 zbepafA~j6NF+JGhiDR(xRD``TJ5x8*2COGiq(p|mQW;8%0?brOI*CoFCRe#mR(KR;(R6^3VkAk%|iP&W1bZ7!gGl90;grS>u~okWK__;2nXb^BlPI z@HMPFl(qEA>v?*lPy`GjTC1N7#%wX)5iS)U!&M}2ye*K1W@TWlU4#u$ah9od5jCSb zQFXO0s`FE0Ji9mFk*O$1=`b{~?$(Ns&U$bZzhElO?j5F(Sh;jp-9}QL?!Yyr3mn=A z%ay=zwCU;;-zKHA0_uxxMgUxtzZ!Fsm>i+wBuY{IWc@h0q9)b7Wb#}chYg4ByN6)E z>y|j&!7x1SCf~CZ6AL#*Td}i4yNBW$+C4@Fifysu1@%1|-=hLcmpHc7F!g9AwZw#U z=QEaANWN;An!P**^~!uR4vGVz2>~0y^*(EQ(0g)7t{z(h%N#eGAJBr0S_uA=SAZkTi|>C!wt*@@^v#Z3s7z1Q`M4f=B zr{0E2v}=Wjwl)!=d9Sx=CBvSZ=k29EDNyoOI;N;jWQ`|d23cuVd-Qglf|MiP!3(oU z2`B7TMs4CeHZ@RAu5##$i>Ve(3zU@A4t;QdE41lQ!B#t_DkHVZpGN8#fpU1QW1ae5 zgzuS1w9fIgoGl!qr!gw;vjXL>^^T*8D$FSxm9U?{GEjAKM!XaHv(soNV}R$7)s`&@ zY9%En;@aaf^=VqsRlG>$C)Y&iCS{FiJO9R6D`4}Gc~&J}WX*dx1ym}5v9$c_UI7w* z5?)|$6{AbN7Ek#%M~ByqF|ag{^0poWw7?w`cJlfX9oX1UicFrhj6K^UQmCBt&Ft%~ zqR-KIr_zp=My39l#vntpwxW&=jhkB=#Ydtm zyH7Nh&ex$fnY9_(!LBVv_D~`&08~~9k)M1Jiv~()RU8M=LO_?NwCZMTW-p)9QlQpPPY!A+&?L|w?}?Y$dH%2tO%mBzuh z6s4U>ZjHU04zLAZ*=p*@S!)>}%l)xdEDw_Ik8Ldv494*ETFDjG#xRT32J&=^`H8ak zextO1h}IC2@i9#7rtEMAupc2-%+oQ;@r7Nc6T7|v2`3)-;~GqKpbwzeE;+Z zuxb_MkF2%dtx$+o9cboT2{rq}xXEhy0NQ400#byoA~~|!1P_%F-Dje?cS5gCX_%#n zPtAEX>azdCxPh{&Z~`QOrZ zgf9IkuBTQIyj0NjtVc z{}VW=pZmCOeJT0~Tfbx?RUH!s}+rt;CK`XxLKz_&wX(jiD5oUB40BK^_mx!4vdPW$WIeToOQ6 z>je&{wOO+Rb*B5VK6u9*u6; zWrABv`G*-;o_sPA8~5L2>PAt5mm$lNi=lm_!XN5<+|y>o&LOay zX`|-_<1M_o!Pv=A`Uw0kRGA#j519yf0Pa=P=kT*yMBUg1>|u+r?P`0S%PjI8Q}$mM2PJ?CSKK|1`WF zGP>T8R^>3amlWKzG|I0cQ!AMuD8E9I|x<)vw@zkgyQjRbKfY(@B;s;;C1mY#lc|#kRl348Dd>iRwBp)8OlX zj(?46?|iOtgzh1l4;1&%eDH=F&;BMZ#x&qn(*UyXo91GvGDs`AYJGZUEfz3!XZ z!Th;F5)L>0xAFIdyTtFoDP<>(6rjTJWo^Rxl3i(O>&>pG6|7g@tic&{ZxyPZ_XMun zLe+2D=01zwK4qxO=;t(bLuKQHCtUzV$TL_*H9oo#oY3o9uxIS`h zQ_(Z={as+`$Hq${2A|)fr#^P*L#jX2Dqn$trcVED==4BWjPz9*89!EF+1X5nNbsO1n22)keMx*6$BkM1S zfU{;Ji_`kwEMWN-91g*uI;c6@^R@D;n1ti^AJ}Qnd&rDy$ot7CsI`~Qn*~1m;W|Pq z^ZX)ZSa+xH2h{}j!wr^Dw%P`F-fu9+TZT;0HY8dL{u5MJ(*DFDM?l&+Rh3JqPLS;T z+U(-c%yLT*vN`ojr?F6M{R- zCMy;W$={i6FAn~OEVt8y-~?_g>GdXyXvE;6wV0B6rI-~2Ytk4Q4Qp9HT8<1{g5AC| z=FndmAYVX#yc7L^M2*7&v?^^%qVm*L*3y)Ow*<+$pA6w!;5#q~2ZigLu7_*XCKTxcR41_j zye67=(NUl%*b(2ssO*)W<6i z&4Ogf6?hNvIBOuiS4itHmSfMGyg*7eH4GQOiwkP6^$2G%8Ji(b3^yfs`vEpbd#^e= zW5u8F^*k6nAA>IzjT4F>Y($G7Y4o#Wl_Cfm907t<{))-|$j@f9kP9LaTKy{+4WSJr zef2E;v=tXjQLI6^wM{3jwTYF1D#UC`s!!MiQ+Ng}{xf|h{>E&kEN=IrcRlk8;UM7K9R%lU)ZKzJ`WteY` zWas`1E6ue(JnWTibSM^K?c(c8;h&DNiagZX+6Kv)KaIOtKEB(5o_{$GDtsos+pF)6 zl5(^`ec5#&s;e?N?fTnT{T@b?4k}6unYG2XT%m=p7HYXnan_NwZR>0o$I*ZV#mc)9 zrTm)-dL`yUgifej;9qgk_35rKY`Vlu>yF!dAFf|tQvSkH;OI3oSkn3J`S)_WB6NdsHO%Iadr4zNolj0#Nd)b$N6{Pag6J=O>VTBY`0fP}A+)_-{5>T%osjlbL*LrcAw>PsX zn}8G1DV?HWOL!-`m6RP?3U@q@i_QC(NHG%OgJtE2&r&rHZAAusQP$?fHk)L_vDp}i zlhIyEIaMGBeZL^N*vP3pmN@ArA%00DoLaU$GEJ{FIHB1PniA5|>>DU&f6n2g8euyt zkkqXmPIXSiHv5hGIXDL8c`Uy_BiMZE;`B#N1TTO0+gYqV7knGjsuv~x(z2* zZ9f!f$IvsVlhw&XF|<275q3 zawEX0{WUU?YKVqXrQBlHTg>ZDgcje`qN5yn4T7-hDC{C$X9R`Qqw}HXKgXCn8^DWgE{2qZhKG^w|;{Di z%jayg&LmXU7SvDlh(z}Wl?jFP7#(SV2ks@G$GU5BJ=Z6mk$OsHFJtH!g!JQrq`a9^ zyB+iUF1B)WOP{2g47E9e*!Unh7v|igC`8jxs`?I*4dG6`m%@3>5hVW2o!Z5gD@SLL z>}>ATt2pAWAnDY?spmOnnuZGIjID3e4hifys)7B1rvvY}vObQaMVRhqvojF- zc7l&*v%So~nQX?n-8Q?`vD67ar-xr0^kz*pU zz!CNP5!1G5g$)2U$zUN;9qpV`LzT%vk`v?Hpc+6&nJK91=Zky>=^SW~R5{QfIh8pa zdzk0#$ReuoUVi`65TW!DTMu0`uNF>c+Eb6%g5^v?1H)s;Ku>5S^b8ZByUrZ^r!f+J zronKUuaUK*j*#5kL}0JYs?EJw{gygHeD7&`jl`;Fqg5jtTXD+vQOI{yeZ}F0-x}sj zMx-1aDMqe53LmPx3)qLt2%n@gVNc|0ToG3_Uw0B~pBE&{+8H*Yo{|r?YVXtr5mvAO zNfUgnA(9#Fu3u%+%vvkYY>lfvMb_=iwi&aU1E~B_AMEa4&DKJdy5L-d?(((58|OJp z|0xIQmjla!>PA**9`H(EYY)jdo`p5jH)a!$X7TwXR-HX?RLlHv)J^-?3y^GtAF>_3 zXO>=McwPLl5b!cTYkS%F@eG{1x@{U3A@Flk%*19cf>vmCu23AqS}tZ$S6}u?@TjI& zf+ax`9dGF5K!T+xz~*lqEh&d#JcI?70oBaf}~!#V%m>V2xCbISe)e z8j(Qj`$u4c{T;Dy;dTCIQ;;O|ac)pqbMzLg?+~dBw#JE1U*~Xza8@h{lJvet7;^=> z84|j$Q{T$LW#<-FIOVZ$OeZ&>?#CxV+R~$mRUyUiC2lLXb&>7;(DB?pR(aDpdmlz* zl2d#1u|Zo=>mp{=%VuU#mQWfbB{pY~Vpyta!W<(H;7Ww3I`B2IUU6Vt!L?Xr%PQWF zY1VG4$ZQ{Af@6Ea6KWFZWjC9*4U<)Wr@jw{-`i0I%f%oZ|G3@E4OBZH)S+#qV$*~^ z-a9}$02MZ|_L1pxvC+=cZmJWg!t4Zeu8FmkVLBGAEA71_B@LhTvh)Gq&1@cmSfl#X!jRd^P@_Xf$ZWal15V>a<= zlvVUq^eWFLq&PJ%EC9YQNWM&QYVQet???QSucPx-lXwDK)rx&w&RPW?!qk(HHVgKG zQHGEcfE@rwjdr#dTTqN&X{5Cc8;yOX9hth~6A|SZ6-9d+aP~CQ1DZCTGo*m}di<;qgjXApFJ|1(un= zh|H_D<4n>2W_}RkbDUE<98>>)iOq`s**`{>-sRNB1@^%q2HOt9W{6fvZN-_2Yw*xeD@d=+ zlw-iF{d{kO4^9DWe20u5Bh}XmK(;yoaFBfptBk^zac00vsvQTO=jYo`7P|~FI3K-& z&;xrKj^>szq-jf|Eo1_E6;M=5Ybwja=^Rh-{2HOF791eG1v|Hu-@0&?;D}j8Pesz# zf$xrLnW?L(n&CCoy#ai4D`+bTt*q^&BEyVG4zo8k1OxQPfw6?3Y(ceG6ryVXmd5Rb zU?jR>BqE9THf|@#G9#2(y@RNYVlnf074R-XmzhwUEx?&S#l?e=;5~NI2v=YdQ-F(m zjf6Oc`l$vG)z1@{%_kXEH51<_nIQcY+ju-gWZJ;ySE7iGt*x011$ibLMUJ{j6;s5# z3f*MKbaYeR6sKlwD$%EcXEx%&T*d1btY zYod0x#faib`5NpaVh_|{`;yIhZHj@9QN7(`+BqcS{1hCj8)c4`a;$y=`8mT#2+jtd zB5GVaYl1ADIIpLqPfyn++z;q8mXK{}Q^G?kJ><6cOg5FkJ_lQ8J8|1sqc-*BSi?4b z2G=XyfM#E+o{S^WgI(J6L2J$zD#b&po1ew^mnb-;ahttzt|M+IgqJwh z%s8d3dV+)aG%A|wOjq6*Dlh?oXM*IxJm*Aj%f?w+J(q83+g5%lExO6-agNx&8qDkY zqVsHDMQ*#6a(K2G$Q=7$G5E&Y82cXt<2(m9PgMDof?vbnI>*e1J@EYv8e!`MWfSuu zj1oAjqS{BoehVnC69}8Ud?IrB$&5Qn@e6=oA&S`7T{IYZjo(fYMh)PnME3wi+&pKVi{_f)7a9XV<)WI$e(j?uUHEZI4=~Jdm zE|{D%QLYY+Z+csQ`}h&~8$5V03~#9kU1MW!?>}M${vVeVo1K+s8w{J{?O6qLQgWx~ z%(2lUwkAC0zb?c!Q-W9WBKr^vZY5t733zaYZ{JL~q66U$9k z&P3=R2;H|XdXiXHLW#kpIEkRpfjX|>Xi~@t3jOJ(&>horrq7w|O{exXbo&1_X)F@3 zE(6y;jyG>_vE_eZs=wR$jH1tCeE$tf#B{c1dAP2tGjf);qqVJ<68ImoSlKCCrc@8b zt_N40ir|#&*DyD%be5{`h4{V>iMG;Np$Z@$-arACu2?-CTV*biB2z_JqgllgQ)}Q;Da3NbO*7# zMp^@eW_3pkAs2Ar+m~3(6mK+3i)^H72>bjdXNc_h*oG7Cf;d-J6?7>rP1n-^g+wD2 zMSFsCifbHPTI|eGrz$i@BFq={Qe$@9^x4xVOrJG<&cc+ef~;&gADP@t+Nanpa_+YQ zcpH!`joZugf;S#1{ZPS_XJXq){6EkgsxF#ih3x?Ovqiy;W5ilws7gr}Koa0Bj?UQJ zz=4Fz@TgP$I~SNGSo}9Twc8xy1|o;0uhoMl#Hc*Gpd=Gbl|jsAH5g z=MT<(G3q-^EdAhWc5S=KiEBy5;|$F7`n=OHM;FO_*{DD4kTy78w9}~%8FE$AmV~;| zy-lLr6#)n6il)XkLbFXfWO(kksPQ@b6_awOW3X)6ZKM?qE}eqK`jGjij)?PFMGree<#U_$4t)x_cz=2qi5@>%4_1m$ z=Udcvu%XJtz}ql&hH*3MeeQ5{#c zCuO?;${%R8N#T=5VsgpU6`|z=tutj^6+Bs~M=18lIu^I&LivGh7@Jr zHVc>By-w}5NL{|W4o#9Cf1Bg)HiY(Y$35C0EJ_YOZL(Pls3)M~gRSAR_;mO*Wlg&= zox`GFx>i?85T}=p6C;iGJD*bxp$)FL4#jTQx!ROkENubYN5kuRfdIB64>)yxg!;Nu z?myssUeS>G^#h+i>ue=&KVq$^h#o0SgI556eyU1mooqUfXc_SnZZA<~SaK%mGMJm) zjh76y&TzM@ws!s9@XU+0nNswE5s&Q4+ez;}3&P{%fqq3;2YVh}8-Qfx!@xWZuj2Nc z!c?Q+QrR4+p%)EvDgAPE3Rrz zErm513{1@lb7$cmzT4*(%$qD%M>PwnGenB&EWF+O@~t5#|GnYXwz6|FP7%|nrpllY zzmsKL`5yNV1H-0em|1yJKn+DQ$9JLqt`8zEKxm2%Z6bcZMRb+&-y(3qs9^Tu zF&EXv^ATyJM zAu)CohR&qV~H&*3eS~>uXxxq#^IgiSDe}nU^b%NrM|JPu{=DLX?p#SFcK#PzR*lxL&)lnSqDJFjgv7hU)v}<6_?H>Sjdy`LVO4k+ zLcHS}&hNxhX5*Dqb0r7W<}?%M!KRN(SV2b$o=uuKpWqPIyf+sS#>W9#dWK%4Uo2EfDS5DSr zdLklBLY7(g!B?dGl$j3F5jxo&y7V6$q(1n{Y_QS$PR#~XBB!|V?8aes>DChKTPh`r zgQ%2Db>kHun3csigih0;$+BuR_AR^;Om`=U{>UUCjb|YA)QG6z^6B}-7=Jk2p(GpU zwwb|F{)w}REc@75q5>%|W|=_iC&rXc%4Dm+G8O4=eCMd|5Xt$}`J4*mkj*uLw$F@J zl;qAcfz|0&T$kG+6P*2x;Q-Ez`ItjLH?tUPTM#V&eD2h~mfRhfjXkz6jP;)@`8nzv zFHDy<3751dz%fOVrdeVM;JFHhYkDT^LDx@X!5n$Yyl=1w@H_*Topx&TC+EcZAW)qw z%!#Ydn2C>}3v?)64SYBpm&Ea=pnz#PT9Hs)GzyUxx+4{xbuK3zSj9yMJn$+V^v^k` zsi>5hi`}8NMev|dKG0r=0hYLN>v`u4m5Q=$sXMg#XrmUj?%s;z+#y>QES3w-X1Fi4 znem6Ek_18}QSTS2xR2x`NqAxaTDB1IIVTP&_PuQu{Hbh+R8L|3$fH|Y! zbrIIxo^eVdERCh4sRc(h4mrlFe7LN*N5#>XDzO49)OS-!m}E76$6hxft*nKiPq`YI zg9aJjnW~X3R+%^@lVH)T^@jE`P_8y{PE3L(SsTa8G`hx3-xCMJ@TxX_li55)e;PH{ zy78pRFr|C>BCm5pmLHw7R9iWvtw*6hIa|om`~91Fdqrr-=Sxgr$|cjajJa6_;&s4FPVE3r@^3MLmZ?_o$j3O1 z?sbpLy}@$svQr;eVej1MZr8Fah7rlsTiv0<&oyrD>G$sW&_XF9#Y}_FmeR`JP?D|( z)RT&%4omH2R5dIi!BTS7sgKsNj`!=7coc3Ll$4SpD_&e^V8>mZ6~?2Kw0*!G>DbTC z>8gv!p(c{{UJG?Z z;-+IcJAr2vMBqK>&JpG`g1T*s%%}3F0caN@)tP~<1;u#eZv~N*W$yGTGeD|Z#`M~Q z-mN1+@#1L)uhR|>0Xj7UL#MbMs9b%?Imr9i-NPF1u6*`Ysdu8(N8G7S&b3W@hEU5wbEx=;ki9`D&*ZA=3;O%jga)=%kJQPTS( zXhl$^^(1~TpuKJ={Il?Ew-$}dX#Pmmvohm6?T(X^W37o(t7-3Z!@hM~dP|6j_Pawb z*Kr+E#AD6Mb!epcuA*fE&*FBs|-@qR+UY=zJ?L>B0{8(X5+Xp`JyYjrq_> z>Tg}rN9~cQf~lcD2dFY1i!vNqfO+Lk7cPVKJR$l#aQ^};mYf4|bj?dLzkp%y+}EYI!Kuhs==ja0pPx&gKc;cvxDIVAPv^i7 zA|=43H-5STdIf~>cP&x{GU}^{I&v-+C{?XcmY4U#YZ?l}c;G>~*epEXio5t!QfBqK zE(IF*JN{vEHZltrtIf#JPQ;yozu|#5mZC7buZ*x5>a*xKfnR0ODrsiRESM&rZ zkalE_-#`jCoR#cD*s@V_^GcaHbwJhf12^ti0A(*F46A?M52)HV68ML} zV-`ZL7B+J&R-qg=AL&rIafg|XquTnh4u#A8LMuE77D5ZDcH^w(Cmwh^PM2tGTbI^T zM*#m6BDlFL8jrc#8%s*EAGUbfyJ94|Ee1F}7*&^1Y=5RAYVDkg_~#~JZtG@vuZC^N z>+RBQAnCq9xu+IclQns~d?mkRmHI-LO^<>&R^-WubZH@W7m}WK-I1B8I7kH z=&{8*=};s+3ut*lRHQ^MftI9atc&K{FR67-;|a1q+79nwMR^!j6^%y&KdEoyOM+DS z9&a_oy0$2sJn;j*+oP7lTG=}oB6wa;`J;g&3%MD|Mv!||YS!QA|Zu<+NF)N@XT~BaL@;1Z$?XGoFXV*eiVKT-)?oi9#VAZua zUg~)PZ!HfqM`1x>NZ!3>Pngk@YdnE+gy*SZ%T@~P#wFwcB*d| zzH!6}-%TVcJxD1kHyymJk4)26Lnf(%m&aO<)mm~YQu+Y8x(1f*>Q45g8XZRqmhr$F z)Zo2b^<_jamv(_=r5bubTU6U9{~GB~tlH!3*eoB29!?mXsVPE_i^dvv>bfL-WNx~k z5b^~ybggw*O*L*JrXTS0YpqzK^mW~%3Si#4wU?S`2oLfm+I!a&dsCI0vRGT^ znkwU-A4Mb%yJpB>_m<#b+1J^QAve%Wq^wbxhW@Qa;ef7gw1!D$7yE6d#z#!9Q<1c} z#^Lv4bmJ~!>F5gPDP9t)1+=n3t_M{m_#J`hJ2qL>(O{)YLkov7Olcits7N-TPB;5q zH|56+m_CYldN&Wk>rqNBh$eQRn;jiG)HE&u!7(T@@Cjes<$9YPH@&$bE1;HcXvA$; z=2rB9L#EfgR;}E4<|e-&xwL8KO(%zAJ@ESSZi%&#d@%;x?)B7EYmi`stEF6y@NX!m z@1N1TbeplGQ#r+*=L)rqaE()AnXCV{DzHd>b9LQLeaA{la|=#!zcvkSvl)~0#6kO1 zdj+?Uh#CGE7~668O+~U%Qn_gCPzyJLn<5_S>z(3CR++N~9SvNb;&Q1#60K8hAa3jk zv-?QjkuH7tQ5V2*?uLIM{&1@qg=NvPkyufAovXIGBXt|)N>d!b%Er4x%U^HYSfbv+ zmH^V%ZW$!F@ublv73-7e5n4}@dfIVq#%OG(dOcUxSx0IvCq@NegLEWn1v7{NVQZde@dx<86+tfFn z40zqQkuDuHN!(gucE$4%r8`;lU@!Z0y&;j@D_yhxJYYSDoxLYKM?y}*3SIQ(6fH3e zfb~SBhOG#n$z)jFl+T5aK1QLc3P)kD5LnY)+D(pPv^Vq0>ti2Udq>M6^N=b7S z)KJidOcDi#=J;?Jn!T2db}VVq%`8rfQ-2+`iDY;7kCH{JxzK<;qlPzc50Uf?mp<9E z9`OfY63lSDukf|_9;m*fqz}&EOUg&Mf3&a(ZnNjR6so9I*vcx#p} zggY36aBP2qYj2GV8q5TlHprmli7xHfPCaA@%9?};cGuo!aC=^fwO-_5^aIss;^CWfjwdNIL#hpF!n*)-Yk5<7gj3Cw)fcf3rikG((MWTncXN;Sg3 zm#4V2EtA@FvI#6Xh{xB|rn`Dvg43Z&$X-kd5z92wi=*&8Qhhg(s%gfiup5D+&?_-J zu!u-`Jq4F7^?yR_mWEiLMNVzrwkI*6#^fO#`tZ>S}yEG14U0S1X>GJs^YdlmEp7T%Ch2Sb@%toGCYpRJR)cA54#ZV)(^Z`P2xfHFer zmL7=hEa&#e-llCkl|$gq#e;JyIqg!JDvnLA6}YrjkF#JF@*J_-I#Q0s&Tdc|f#*TI zj-gHbExiz6S6V&~TwET=u^eK26FNU^XnvG+rMZu~QA~n(ul+6+uhnG!r;tsC@?0Xe&IC z=?1YZG|`#I3>8>alYFN7ZZ3IM;nQ`I(XGvcDy-2g1&7Iw#V+knz&x@|;5+Yy*Ej%l zmK*cUg(NIH$3&^Fg5a(3Q&6RH0OW!$%Us&$fF#TVU1FYx(WdyjW_T=D-6cNIJY~!+ zy?i&mr%@QLou#wrmHpFnZ!QB~pz-SecbNbP(ghm;VtX!PyI|Fk5W`sUzQM zhPQS$VSI5vTTzjg-PH!pY16DPVJsCq)bTY6eNe@wO<}E$U0Q6W5l#{7Ob*F?B6~3h zm^@F-Opu~iGPE#ey7g#m0!)L!-A24p*6)7E z^=ENHOvz&IPpblY0eOH0EDwPKqONYQIU=eeaF*R>B7{m$mmSVmG%^*&n5Rd%<+hy> zvRqipsT?frahrxGhdhX*#M@v5J?sL7)t%$$UA+iby-S|mq2lPNoh-Q%O*{S^#^?U+ zW>L;jyGw_Tkd&XHAb6eIDFfvHJZ2lv0xnUWH~sGhl>9ulVQ`hSR_|5YsJTDHs_%Z# zKUfZZ9vCizJ~KB{st8;pJZvJglLntSWmO@NiZwyU!dnq0YL;M1bWi!``n}hN&(fr#%rOXUbgq>rDi?RMNOmQW9VzD;uiLqYw0j@FddV}w2 z6=#krn!CVe?)XjMi{OmRn;OM94%>@UbDL&?E zCFT9<;kpNIq48#NRUQ|^&nS47_me2kBG0nM^|2(r6^FubYlj{q?GgVu)~Bd({g@i; zrW-w;N7}-daMJGn2Xpmo+7ZcKKx=%tsy zziD&9q+ZX3y%ZuNo^<7T>#m0o|HdutaA;{oE_!F>bX{BW?8~g}$ZzZ7NSF_fobi<& zMcFRE4ioE_6WvjK{Kwp6dN_hC6JNG=khS$;weY&e=P1O<(=L6z0a^bTqHcZ}`&+}h z!>Qe?Z!1FN!F{edszeU4<1BGsL)<{Kq#>^9DSpu0(T*7Ml!q3OIl_oicvXNYCRwoIy-v+xARXQ^7d-ipL$QDn)l z(VINZ{nFU03ZtHU4!n8X^@yq>XWH|KU;Zi%tKB$m>|Qp6ex*Zu%D5+UwCLcn@oOF0 zwDeV%b{eDD`bGzWOCoXY>JJ@pc8yo`&@f68d>bOyUv=qgb|~>LAbR#|)=_-s5MzNC z6L?(#{v8`wyeJgMV5gdkZmBAKj|UN6cdb*^CWHKdY~o+H-li{gOUhfU&9Q9eSinC5 zKJYsB@QN#YH!Qs;w5g2Ef=9X6_QOv)PA~a30BxN;U?kbLXdQsq4;4yDmVQPO9 zhw)YVfD>kumU~aXY5Wci#plh@8Ymm|J0`12D881HF6}zT!2g=SU9;_~x_Ws_X`rAiES)bNhozOF?~|X_vp)yl-t1RMPij&57dlQ%Nc}cI_<+*4wr0s$11J7NQ@!CVSr+R5w)C zed4OUHK<-F>R7ZV!Vo|;!t3GF`k}aS@&tA=Dt9-H(KYqr3LiIqV!NfGL_ci71c)Az5|L00^Fe`XUwKu3PLS9 zw3$>_;+!sOgIy2L{lGvsUOY7%o(;TmyYdW9ebB0^3Z%RWa%XKl@6uOIv%p|<*Ljy- zUy`3gLQB7PMai&-nzm*}IOytWj0`2JEy|SdO?2dq-&ycH-V?YV-0SX-rlFGgjZ5GC z!41@AsKEIsOK<(gy4UJ7OhfQ){MNNXu@)zx@KA}oV9dO%TXQnZk*3Z1OZE%6@-_N9 zm-hQ1!?!>Vwu)x`O1EJBbnMQ%^ibiv5CLTAYmr^}NP#|(p$g~*C^A$weecpI8u=Z? z0ut9o;5xs2>?rf{K{cW{WkeIJe%{hWDt|EZGbKw5a^LlvnC`fs@ib#qaAZR(=;kVoC|Y? zswZz1W&_rq*r~%&(Al!*>MQ%$tPTub{!(an*?$A9LA^uKlD*q8R4!jJjKxihPN7m% zZQRA^+ZKoFqEf!T&TTB=>Pma4P`U=*H`=C;hrASJ$&KAj zq|T+2pKP=j9k4!-<9~XL$-YRGJl!`;y{gpUFPC<_CuRC+1P_?-88h{48*PoM zAsmcJY)Iv5dw*IBw40W~xLURu3@q>k1QsH|^yYa(6=-Dp#|Sv8Uj0$n@xzVGvrzlb zj?u%H8&0~8|wkK@O+SX0HCZ-Lfh!an!Fs)2Lj6d(%Ku$;(e>>9RQZC$7^&^ zH_XT+SqHJgPpq{o#?}LtD$-yE537sYt||)iu{Aj}U)Re9YzQ*ydoZ-89BQ7a9aGd$ zp!3O{@GQI~4Z3k1no-ta5Bu)K`OUODx7XcOLu(_3={Q%KPq>2&!I^ZJLK9?fMYG1T zF>!uh<-@8XJM(33+;>!xYJ z%+{xb)-64YY4%Fl;>NPSZd#V&Daw$M3K`DqhqGD~grh>`XuUM;&&o+)Gzd|6)|vtL z$oYC;Q!b7PE%iyeuSPnI4V9ffX?oh>vSu81eHx_ccYbN)x{I|vyb(sO(?{`2U`eyF z(7SL_Pi4UT#)D%TrroD%Mixs&&CXi~Nt;G#dTO>I(1EIy^=jLbz3%NB>16)ZFNThg zYrRLoX2N@oJ@2q`5sxXhcadwpX_^7ZmuV)rsbu@5jrKMXq@%Eeui%Bn6slfKk)cDI zF~L+7$_YFZp(#7Uhe$jBG)*IFR#~_u*FQ}wM6@1EU}-0>MRb&b?V-rWXN*_Ep*5h1 zp|UL?ZI&VjXNpOoVzH#nQ{OCLGL$w;TB-Ubd8UBAfrkF%rKxyqDKJgjS;^VcSXAGT zCV19(D2nphZ=a6JR)#dOO55Nxy_w1qXP~mdro=Y*o(a2paM~=@ME2+`bja6@aFXC9 zcx6gR+E|4!Wwt`viRD%F?2RuR5Zp73osTQ#97W><|5mc>Yi!qLe7vyMGL&nuCSv<< zBOi%)&4qWhJ#Sda3zb<-(h3yUw?k(6Oi+BUJz5$yHI$A+Z~;`2rfGT=lY#HqXkwiU zxKyrtGt+m;2%V!t2gmu@e z=}`>lq8%C9E<`1H0jm|b)qcD77e97(+)Xq8LKMAjpZm~1mz~U-%g#kW&cD^Dr355m zi1WD|)mVz?#msf{l;+)JVGGk!Z0wRyscK>B#-3RU>w83+-jb)5vkZ7dnl=k@|FIAy zB!7?n$8+&;#;g8pHRuZraXXuko%@8EYg2DYSt();SQJAmdw`;whXrB67lkXgooc z!Gl8{{kaK9?#8+;rPJSV49RFQv5VSnRikAsQWgU*|H+EGwVN~<%B^}$kN-#6dj>># zbnnAIqGCZQBCzyg!OAZ7g0L+10@&*o>=m%b3MMhdSkOeqBo;I=CN|I{npiQtSTMa< zFflzUrpH*`>zbKe_vXp}!~5}`>&%&UX6DSf&zzx-Tcn^=@z`zrkl;hrGS@Uf>lD;Y zREC8%7e&o=RaOC7?*%o5;s`V9=0EUYcYe%7JW=57X!8Up%^{fhWleF7g?_%0e_t?p z640z&p*CFA9Ey$b!%@}Zc5MKjeibIWQCUt^;aub@M(URPV<#g%lCIDqqza@Szn(CM zKgm_!xJ@h@LyFtzkxc~QQy?7lNsUmEwHe++&$Q9qPBZw^?DqVknm7_&tx;dJ8A*?Z zB)ht;Kq+}9MC2yf?*&QQ#0>j4r$bD_5Jee53{`w%*+Le!8HL@x1`(8aY3LkfBH)DC zg4M%f$7?obyLvg0i}hBSsIEhBmX%wCkpHf@;g#Ev++t_Wq&;C8QZ}=N_GR`7g1bmTis-FyF0t|mnrvR z#qVORvrEvJ!c;6pJWR-JJWT9Hw)D;67IC7Z7Fg;adsJ#mJdl=NOFV6Z9%8r}u@`vR z4H&mjShYY=`vCdTZ_CDJQGZ(K)EOp+Rusy zP6=!!oL%h$We`3N&H*H+WlWhkemY`{PQixRC?JTr>xkTLT9Pr3wVE8wlB9b(-Ao`rGF5?d_* z|Enmd{5PCvfZ)WJf&ZF@t7ESL|M53O;zdOU$X2m>hwrs2y*AVW%Re3PPHe+yY`v*= zcVYcEtQ>j!P7vo`DF(L6%B}0f(iex`=Y~H%pPxi@OBgKz@ z;Jy%O)%eKPlF!>{Qv9E=?V+{G*Hn%+#p)gCTs`fJ`jfGUjMZ9+J;&KM9W2Zs4@3V7+1 za4A7rPUy}m#h-G1nq6V97l|7T)kW+KvaXvEsYT4$S6`f9hn|H-(^rc1pT9YmL&UD3dh7;)^lOl&-Ey;LJS!O|Lki#m;NReMFw8z)v>lFJE^RLc z@Xi8#>uM$7D-uo>t#3BPD}7U9F;4$B#V`!VdTwwC`4)T22>T-3IB+yFL4UnFAGyCn zm#AfSa8CZ*d9!8gI&q+XQbUosr3cPpvnO>a-aZuX%y^#)zGp|?eGuvauS1j^Rw5%}?!BxQ1?n3pe{E1J?cw4V-uttGB!{A0THm^=`L7r*{ z7nfg=YwM;uh+}Ejvx%v{A@$r{Yz|1R<|F?*pnvb;KMNrFCtm$+_dMhC2g*Vu|3AA# ziNGzLYTx4^`-dyvpR7&#ei$Q-EM!aAw|}7s>yNeZm;_5VYt%*f&RLWxq~R2hGJ=4 zBP;6Tw^^!veRP-N=Z5>CY6!_}5SBa0893KJ@jv9`u7V=Pg>ioM=wqbxIIfBR7YeR> zUul{o)g2sVUiveuI2Y3bx>qfBSdA3j1uO_T3MpB)Nn(0UW<$% zR88<|DjY%FC!=wOzZJCrU9MqnECMe_*DKy@@)gb>qv1%GIZxR}ShBC8v535>x=^n2 zwN+|}I6u`sPOb+O#5$p(&oui(avXSks|%{Unr83gTU5G2FWd z2o-U;_K8OK_dt+$_498j0>6YT$J+G0RnCAQK(W5&R#co-H*m-rBK6t}5pqAr>r!~( zM7EMAfM7uRkHy3Z=K+Q-#Y4ps8Um;hpyR&g0eCcf2wb7B%&6uN6A~&aXWQWf_msbH zaXnMr;tykdMebazE4+3t8&4?=m1HYrKaIe582jn=Z-H5)#czwzHxiU!Z$&I#OeiT9A|wB5p0PtE)oO zkQ%2_$BV!MJxQh@HJ)|8-!_#*3H0)O?{G#f(}#?r)Cf)exxkPtI2x4V(va0CS8e zY_s02 zf`|31DYWOMfqT&3^_ns2C;r6bFUEd7d#cj!i-231*ZF4pqcY;X@qvPLX{}xPJ+tXOS@_Af zAU4Me;m0VSwWKI`R@GkMRn^cOCk7sclo&!*^ad0VjBTr`1S81+^)eS%v_626gR!FZ zTIbczeUX|IjD9YQ&xgc$|2pjl2-k!Yic4zv62?KO?p$TRV*mD6umw-a;2~~<33&ka zl_%`#G8A{Hfgr8eS*xYua`p**CZc?0LLi^iW@I)#3#8WiwPbx%jA<#mGXQ0W3eyJr z0ofqR)F6;H8xz(-+}WT-gj0DidoTUn^c))caOw0nZ5Ou!8zQl5O*$)EB_3i^)~zML zhK7nQLLWQ0FAO8C>pkoXhmK+IifO8S5*Gs-PORX)^f~vgf1C~&uLfuz@DZrlHD~-z zk^Qvp>ZM2>iPY|4=K1O-iy=VmC_tryn>Y7*^Uk$ux-?kFA?IkoWdXk7!he(I$6g(S z)Ei;umhe#2M=LHNV_7$QjapG+&1FqYRmcwFxVLbp{@Wq5)E@}4aD z`%KVE-dQwPoz{9I)kT))9&4T^c5l^N1@ztCWeX`+r5l*lS0L{?Rr*m%qBBy=9%<4UUwUKr@*5*Od&yhY+{Vaj7WqXFCwv) zs5*lmfz}<|Y}yTNCmlB={b(?ps_6%lKq;_xBu~@HVWM=0{c$O*i;z4W&8>gSfj>j{*O$A zSaTO)iif)>5@*Ka%PnDOI#HCTI*GdR(rPsR#Ru%!az*10zhx4Q z77Gs8)m3C}^aV0eZS-7c3sHjswuTmw))l8?!>#J&AbrWcNH&lB9$@qS-c&P2tclKt zUf4)m#E3l@D&But|L&x&^bmdkzM?fvZmla&Oj-gCdQk_pG*i8Pm>dh62d0QJBui-? zgXBj*T$*4WD*Ti1&+0@6gy1p^!K1)OKHda|rz=CCuzS0KJqBp>*rr32%hDE?Hi|q3 zt}8>uFR$7wJUiCoAZ^eDI~FDwTI2oV&8kq5d05*4I2Nlxn%h1Rw>3Cf<K%8|&p(6jNUAa%Olh)yAbyWYT=dbDY$hxI_ zBAhH(?1?)%N*QbP1fbYac+S#%T>t&&kJ~3zYIE!!i0{q|FtQqiHn=my{2lDi7p>K% zBXKTZ#lZLOjh!n3%Jp(T3aNq(8?Yl}ka}{(ymQjMpDY_gMc_%h^2N#n?Ng+^{Z;y8 z`15KOLy`P6lXD(xGC(|fIA1MfZ0j>jKKpgOSYd5$t}7}N&Gm)l4J{kgTQ+Hew&F07 zWuH+|-;7ey;DK&tT8!?|{nvz8-!4EPOsxqQ4)@e0@vQC1WN*H za0f_mEVniyU1s6cQp4LSFA=j|!KJLHqwxfd!HV@Vu|XSslMqcTSBW9V=oKb!x{{6v z@GLLj(!6`C>p`qxpV+Yoeysl@a+D6qL^Vo-@ck;T^*49fChAX}T@T`B2~TZ~%H8(8gr*2$BE!IMpC2Wh~(h%);|_b%B|^@+L@Q+M4@G z*T1_6u_QZ%$Xg0&Cvq>?C&*N8A8#YItgSg#+`6FUD+{Ubu#qmG;NIeP^0I1|FWyCt z+;%nbkGFYPn25V%&z8(6L+>f5r6_2-tP9^0lhq{heyF&0No#Rz><0)5T&bNz8sqyE zCQqe(nmnE-;QKTxkd~1*FL$m;&h3OdKhSvtX(p%WoYA1T{8W zN9+%gTF@Rxu+qj$_|l|0-4po;$TkmRn{@Bo|1k^6sv9i^e20}}>kIm@$3i{`h)w;RqjmCU7zXeN zCe}J19fE}nzrZ2%d%FtF%VWfs(C+Xp#hLh5%-iahbXY*H;jR@=-m*lVyw9V=(hjf& z?E4ma zlm2%es@!YZ9KrSAdr$J(n=lSk70krzH2y=C=a!UzPWd08a9p>~mfx(-kMcV~_*dBn z%SN%OKcVK!ch~Etyg&_)z5T2pI0bYz`?7Ls<>7Jp7X`(N12^pIl};*nS5+zk=~iJ? zu3U(}B;Gc?Ypk_(jcK5W9ggncQ)9AjR1mJiIQX|cQk0EF5V~uB+FcUi#$Kh7qFm`2F`VIkmFPs{W$xb)S@Nbmt2omiKen9kG|Zca=#sHFwN+~& zxc)9l%4PXHQT-vm9ilYT-&ZXASFhmoQu|XPy~MI?T;OSQOV=zD$X^n%i4%j7=hQ8o zheG+ctQFqP9qf-AyQ!FpaeZ;?mcEW|2hM-)k?>atT z6f~JVkAGzduOo9&3HDdeFmZI>%pS&YECCAY!%;!}EkB3a7C1Qr71UhB-RFoF|icuc3k>MT4H^W@1JIhtepH0}2MT$=>vkSmdwAf!baph=sr7 zAz4{!a3P>N*9${d`6nbEBZ`phuBGtq^}eGX>L$t_X2Xw}3tSM{m%PF)IZ=GW6j8KK zKg6|d5Ra$h9WD=Ox(E7P@rUo&$1}SNXP$Pm;O-F~hSlfthkj9_&!2cPz_86lAa`nS zY_k}GIwJ7jQ5Laf$9PrrQRIyT?&@uRMZ7vq#~GIev+tq+9o?xr+`f z4t3y$wf9RjF^bYEvT_mX;>_Rro!Tcr9t&uCr=W+$wJt%vBJ33I&y^)EP~{&19;fh^ zg*{x0$abX0BlVUm;Te$~p@)SPNCHnm;FIIOmh}L z&^%F?`;Af2atc4zZ$folmhl=6z z2EKWr`iGI`qm)toz_B6dRcGKA{X!q7N`N?Qec|@G+B!is;vwYkqVQ%|u0uyCqPQ*a z6*D-Sbqy0olO1{D1l}E%?YSR0yCJ8~iMq3d>!uT~JO`JmdBDC;2@_-6JJw(!@fl$Y z7ejkZ$4%`HdgzaVpgZ!e8*4r&Mty>}-;ZbOV-eY=0?IubJ4{Q3Svvps*Y?A6wF6~?%`+;g*jyL5n=Hhz*3w$p& zLljya`7)J2!yBYK11GtT2&kb0`xMb+c^w-A! z4BY@bkU?PDG9n&T~D^vm~;hd2>$i&GO2 zmw4NX2K{xExGq>b$Ar~9IK|x7$Ah;M8wY5)Y6pC5Sj|JpQ_ZdK5wsPNnVNwO1a`C( z1j|K!Y?H&H$N|lMZm{lMFemZ?dx&`_iP*u86|#kFkt^)b zzQK+NA2HxLy+|!bY7R1-YZbbPD}nl_v>B=6I53-9+BT?06eKRdCH{2ztt#R{Vjkd0xIhjDn#WdEh?GdZkg@htyN!aByGY);%wr?7I%oqlaa>vyMB(>SL>Jsj&$8YegCY4RIqvA4p5 z@7)=beL{QBbgWx;$5ce?)D-ILBdGDNfi~ z@;gyfeHuGRZu%nsNk_6^Bu+`g#c8&ri4=M5Y_&zfN3ofTu&M!^DC5ozRollR5YD+r zILeEe;hnL$XmIwYu}#I#6>vSHnt|8M!Kh@>%L56Jpi zY@DYea4E(CP>$k4;0qK!TV*x>tKI0I0(3v1iU(uY3;${E-OM>vD=!;?FJzakT@hPE z0wn@eJr*&!qNDA5)zxyV-u=AZ)!zicVolIXR5e2nxt3xbm!cT(-5gc1{h(Tc($6e4 z@8UX+`yaJ%#dh;dU3xL_rR0*-#kK~X<4!oGOrNVd_8_ojaMa9o+>(vlhwlRLs=Cg< z$`O!wgTHbiprS&UL@H8n2WM*&7yO02V=XObG1q4`>?KNOW1BNz4-mW9%_cE*RCpSt zJqVWPeVaU?8pXTuQZ%^6JrpJ?=Qv8Faxn?tE65?bD%K&+rn#dh@z+L88sbFPc@7l? zA|Ip=hl%oe4i)s!h3`kOA?BY%t(u@%7o~=c?RPxmP0hsugfrD^G;ZRkp~1zUe1lC-r3^7wJ4?EtJ_;KJ*haJj& z#8Ff2l;rhHm{{_tW1ln|@&L05RZ7d4Jy8Tbg6nB}*PxdwF5n2xH|ZposUX@MCaNBD zl*wKqlP#!F_FA(`xPF3Bsi5h2rOeZevK7#|UxLRf=ML;M;*0%?PurtfkNBsG)4evtcl`ISxoxG@m?2(z4(jlH$eF zEdBC84xk-?uCFtX76C9{dwF8-P3y}R1 zxgx*!;QN659w|z0`{7R6U>k1XY=ndw_PCccw^h$L)K#PuWPDjBCJLB&5gzg>b8XRY zldb_xjjuq8H|eE~M#+QNCpSCFWVxKBhvavrSo;rtWv1Qst1DJ-(S6M>dR4=1Zuv*C zMo{X#=geyozGn!`5(A&p!oD224@)=|M_hAv@yo}+G)O!b~%NsCT?bOp{CcfWP zStCUov$MIVctM{%2)*TowjK@dPu8#^H*~5APyB5+9%r}x7ygbLuk0S7@zT?{@?n^h z?}BL1Hq1%MA0DiVKga$(1tIdpw20oKuoJd=x&X<(CjLIwragLX;tA>lcX@u>qwu5k z1qWQN3-q5PE;6Uw`1S4P-v3eFX*ZtztQqc@rTUb^qRzOXwB6BtSQWM9b7?y3hI02i z)D?Oj=PTssZH-?-bsAE5IZv&rWx&pbi7yX0{*(&>m4*+&;rtHs6LfKdZ=|@tVb7m&v84BC*v?dwj(`V1lS!m6)TC1fv`yr{V zMI@Jv9wZyY)87}01zZGI2xa`ip zfG%QUoX|r~4dxO^bLur-r4)d>@>oqSNe~Yc~M83!0 z5K!y7yAu988YYRr)4GrzU{d9V%06(Yuoe8{c>@~sDTfMML7m`N2*WAIDk&Vy^_%<- z5sObd=E+3X=Xaf0@)F!h@72U$F8KrV@r*--X=C^NiS4h#5dsHIM}cI-hW{lK!$rVD z!BA-pT_$PRWq(T=Wp`nX|8cjY^itCfUYQKP8*aLZ_MLO6xC^ZJzi8}-x^b-2E%}W* zVo%^{s(ZyVV7#Y_w>8b;N5b^b$Wdi!l7^jjN7J-Ch;sw@;BTMzzmL1_${gF(RPyL7 z_U+Xnkar;a|Cumr3g+52i+4WpGF$ZBvXQv- zGR~%bTaNatVad!CvknKFgyo@Rfz1X~X5A2IJ%D-F+x zL2QK?h@ZO^;@kjBwD|mjBNa~M`lM6~3Tu6EJ$D42>elZXHxm`Lm*D9*OXFI_S#cpE zYg@u}W%Z`PUe@s?@N9rAvkDvU@oEy=EOEBkANZPM=I!G9`<8SO=i_y7@Am-(05Tsp zzad;-WAyiK)W!&|)q#L+95-X&r8$3or;-pkf+HJG z5SBlH@g)T*(0RxfigEu5llAWRj%_lL=Pm?Xmx(8SaO{wYvyo`hiFwOyBNU^!t6D2$ z`QZ{iEH?-;j$omw57;J?7Ixtu9k>YV{f0v%D!lGZ^As=Kz%a@rQm*R5-z+eUHWgoU zyeg|hbtQ(M5#szcN3rZiYNoMZX?`2_7THe28!>T!oSm9XR}d@WscAfGdU%DQrrc;H zTW+)o$h$u=RJNqrLllhxjeRRYH-f68WJ}et3E5g+il6A!!15Kk$JonFO~t=E4$D&5 zzs)1uq1w&a>O`cTs7RRR#e}^ibFtf-1Hbs5d8TTYq1r6~mA#L@KIV_&v;PBc zsc_va_LgU?Tbab8e>+~4rBI`84bs97@FTG7A8i2P7T$&}ytL9bg)*nA%wI}LARcha zETs*mtx^m;#nHpe3_Rz71cZ1!pD>8^y4PBZf()(2NXU9|B$7?Vw;eCbPgbj~2?qzq z0r}0&XlD|O?r4!=m$o-O=?oP=ueJ@>&G#1A4gi(qlZM2Ok~H?8gi$2@FL~{Rx^44q z>@BaA1w&P{7Jx%%;A1Py`Cj>}UeL{E30;7fuSv-D!YhNEN(?#DUD=zRBf$TTl zB9r~be(lBzoOAc9;h2&F$P(OmlFDj;x+^GD6ntowQXt!=G!FJrD)6(1aJ{f>y}O$X z6DLjK(?xk5r?P!fxUH=0k|%6l!d+af#gd|tj$FAPn`K*|wxZwVyx<_&nDw1q2{Tw( zST=FAlo%&)2C=;78|R9^t#G(udMaG^0oNPW zNU~;)dXe?j4G>l~M0P53^+vAapPH-Xk}z}i0lf26`c-DUZXGAJEQXW1 zAB)-9vay`TT8sNySav+76axlOoZUs5jLdO~ogpfCMDxoKyX%b6}U)=1>$CPb(o@dlgzNK|n71@n4bmeHY+sCA7}sd&P7bX)>?_7)Y*VCbD0rBC`+ z5RC>==|#92ZApMr`xn;0`eMFwvhr)9<~PP921Gko$-biPaV+Qqey)3nH&@_+;?fGy zV}R_~-D8{;l9aY<2S~?O!s7&P5=!zuMyeC3vwp05TE$8==ol_Q*4N|TpnB3-+-#+{ z`CpJLM?rl>NzeEIk@T!>wIW(JPWSdX5REh8u~}!lrz5BRhcO(x@vt1VxU4~z@!)&% z3e3$F!SUgEQw3Ik^kE%k3_tdgK9ln(Kfxrd&75ZpzojQqs0N+Gyxcr14$laF3!l&O z-to0X=W~tmAjMR;|8)IloDzpu!DEt1+-dH7NYOm{VdF%lEaoL>mkbwZCZk4+asx++ zPeaiOPq)-^dIiuFK$l|bT@cx~aoIEX;!wr81khBI_@$NeJ1J;18%=|tnVr$%_!3jk z|1cd**KqESIBrVw3%4qnn;Xmw4LjS0_f>GwYHeTV)SM|{H8uI2E5Bnz$xm?b_ZX8T zH^O-uw%i7{|Aq549G!;Xg%b9dk|c{9u$U!jqD5Jq*i=PBbCQIq6wTJ~2YYP)Wlfr+ z;j~qe(l`W1d#fHQQu%Y3jb0-|F&-Y2>-RI z3Cd}^45^l$O=gLU`)ybf`8WB;m*=Sp(W|3C;m@fv8Z~#Y&eSwk3+# z%#g97!JqKkGlWiBjpKGd=Xxn`9H%v??*4zwPSr?8yp{581GLs8^7}gYFsiDueNIGf+`u=n54$z2q0A%mAZLF(Bpbz!YPoC_5bk$)ru znvA!Zg#SpVx^O=P-`iR4k*$grSDN-NP{%v=__M^8Z>bAA>)~7&0DCP^IyF6do`#T6erTM?Ltxa;HtN9DiI>tCvtl&&!d=ZsM-C3)*Xx@HU7lg+d(uh%q zngQ7*S&CVwJL5rxflcc0$`Stk<1SeUjV?(b*{w^RI(~3oo>(4%CsY?T93LbC#}?vo zRn#w(l&s$#Z_2r1ZEkXiXt`%D{!upvsdwDpy`U6n>+81@ot~JFfIA4e%gNRhO!g5v z@|F$vTqe*R!l`}J8v`pfiHlCBdOCzXR)%)@)U{f*Zz?WjZCihTx;S3! zXw;kycplkL8nXAif9~vwGbh@n=H|>^IAmhZ?D1mx4nzV5!`iX7YTSC{%*~O%ZPJIe zwK_9}bxRN2cR;K&Q9ge>{!wll-7ta0@Z|Bjp_r`PA@gQW${9a#03I^C(I3VV*C>R~ z^t%*ZPZSQD40Gj}8a2hu3w``W%@jE987@k`0an8GE(RW*p}hSmJ8z%n}Li<-+jfC>-cF_x-TGt@f9 z)OV3u>T6M{a=e)Oo=L2k=2V7oipBd-xTZVRUjWX74=|q7ow<^}9llSQM4cH<6}X6^ zcN$!S-iu0PHI&*$E@w<)sS&+VbdAPpg`T^LvplyV8hRWxezSxx%d$>6x6YJ^{1>o`}1QQEA#b= z&rbXVr042b{;Lx|1$48H?k-tRcGqV}b#{t?Eg#L5schcoNVQCekadCA)C_%lo6r}4 z%)wC_DxI#c^s{TfG>KJn^b*6a{R*U)pN@h#m?O$QkV^*p;5=Jf^j?%~lj;OAzrf^m zNzuAn4Fz8Ww=X6~>w*#Xx>|U?OU%3cG&T}Xak86OTqU|`zH_c@ANQxr;I*wD_NTz7 zadj~(a5BXg7uQ-v28q;# zP8D~aN3U;9c2?29`cBec``5ABL4Z<^cSQgb{AZ-_ngRI-!aFRWu3kRh@fJ zfpfjg<3i#!ooEiQSXCZ_tK)UlC_UJM>$V@+hKZs_$n%`GUoArRDmPyEki%UiPz?2ldV* zr3TFg(eEZv`H;3SafttLXS(ZSsV&M^IG>c2qPgo&H&ocr0$*zuy1M;%sEXb;>c12b z0-ucO<_{mI=su)W(=8WF{#N*8)hJraJpBK0^FJ2s<}Vv%P!DfXTrYoyziM!b#l=xP zEGq|v|LOlV3CBw3b}8EZ@O_K47e0rtzINYvB1!{1r&RzbTLz*i{U8^ zS)1-4xP#gp39)n(yH`1tQ;cVH?gFX`vCI&6cfeo4;G_C~;aForEx9TX!`h|Tk-Xm` z@d+12YnfqZYX2JMQA7Axl~*%q#LHNwTdwNCXzkf;5c;i&Jbe$nIZNvLcm zCA1c?<*pb+!3$Fo4kZFV2PFYd;=Ue&RFefKd-oKO1e^l3Jp}pdovUOr9k}Y~Vt0p6m?>{Sc5Wu05%Riy8b?DvV+ClKpV? z0$k(j;ARRVpvtV&Y63uHjj%Kkw~L*bRK2+>1)`m7~appoFEZ!!s0gcc5N}`U48Zy8E;~vywCnq~<7#7pdezQWKzAQI;O$ z0@Y*iXk|4T2q+u|zfI03rJRv8B3w8&JJ-u0AByitR_@$o+W^@{Nyautg^Mj)oXSIs z{S}Q82EH1ZMyhJ&-6MzF016N=y1;fcNT`je$#NV@;QCj zX9b(FH@4YhWIJ&;X0x{Yu;&tql`k@7i2N7O%ko09;X+GuJn{Fu^J!VzEM#bbdaY{{ z-bc|F9GO%t)_+Uj+3}WCg=5qZm)u+WS^?jCHmWCawV<+!t&w^z9=njN1#kMw0{a4L z11K=Tf~V}Y7_>EdFeCvVlwgsKQ<5&nn}cQ!7i)HDv0zVI*w}zeCM$WVQIsOX8YB~2 zQZrn4Qr4?FIs#?AEsAi&ntH2jhKG*YF~@d9b_f?q z`@E(>N07RjcuxaaF^*6tmf&+s&8BMepmTU}ne#~xX%~_Pb&HU*>AwW9D~g%f)FK5C z=V#o`l$GU_=>}+TQ_Dapn;v;f0Y|PVSeP@ym26jT(P}nlPp*Nwppx)wr zUu@lmzfFAr&58%qH5hlT4KY;uvamMSZ5Hw5jJ#}F4tJS;$dNrEu(?=zM4!iKa_NuM z>Cr)LRiKW5qgrrU0UCgn?x<6pZduWRAU!$66ejZ8z}=y0h$%?ehfnV+S13 zNc$iVp{Gf1*_x|_cqGV&f&AnpM34&ndb|>u6r>Jk-}}b|wHE_M@E`3`ZNDD`Yy`38 zzvAcOkFRU9W)CEf3>Vfnw2H&;QDA(#wWYfZ)+b|LN-<(LjV6cmF7VP~AU|X9urbIM zc`XYj$k7BbBJT4T{2Kl&raNDU!Hp#xE#w$caT^z2v-s6ur|v-$bM?`5o87&8PGqkLF07q*CMg`e7Zh z<>Sff=@A}VC$pSmf7x0I^JzVc=rcP7ISP`YguDEwKzZ9&p!O6RJf;GQI;7sZMj$0G zGukvDGc*Eg(aSoHbp$d^564@uPM7Q*)@TNc&3^5^IC1o>u6}DI&jg3O*X~2ql^H13 zTA_8Qo`7<(A60m*mU$rE+SY>CdUn9J&nR!QQ>`G)2Nci_XRoVmVQ`bTCMrK1qmCJD z+$@EoalG<@pv*We;=x5(IZm9}tX%v}EN&(BhN!8)bM+i1pS)?qG5%xy`-XL%%jBy! zZB3Yr*r|BjO4fvSj(I34rM;zv*z<{27n>k;K60e>m^fbeKQwxfG1%$?Wbps8VRQ9{ z5#DmI7FFwh7MAmye-lPMQ&npTjtg~inAlgpz6?2w$@GhUJe&Q!2ut4=+FHR?axu!u z{H7LE@;uCH!_WIA;llN$Q~kV81?f`kU0*sMk{gZ{-^;?qvah`G0&HXFHO}c?7%n!Q zcPG^92cD`fFdX3L*e4pi`o{!!}AKLca=^Ax4)!qVe^pquuMEE$A-GWBj|$b zi|X}Iuex4;8CTP3;FLHOmq&r8mDhvfGHfZrczmlBaq4l8fz{W}Vp(yj)GNb9+1L7< zJOtm5BWrmV3xb;S!C1+fK{%y=k$x42>*5tZC~_pY`?cPrn0GY}h*z}E$VGMycpY=q z@25!RO!Ofp>?Yn1M888z@5S!bQaQ&V1Q?+Vs|g{?6xq34G=Y>(BSPirf>Pz=;2#|24KMg+DpfeQ8dqr@}@3pPlLzCkoBeD17VrkVKIhtf z9?(_=wGs9gaf`L+bEo=IBD;?gTLS#cveBtVr8l&TohF4YH+D~4p8&OA>BlsFK`MoIM43@bUxLRB6i$xstebY z>gU<%K@Cm4M1#3l{7PocPc{Zr*@Pv$-!I^~P%n@WkQ}TMv#`f<>e1 zi-2~g!PGkTI;voJT6Y%BmT2iiZK2c!TG$9JjxZdUx479QGA${;*{IhRQ9Vrqc< z!4E6%3|tZXJ_LV*CPF3yrXX5?b?IQWb#uxcV5w*7X237Q@Fts_zZ|QVGyyJ~{o_7#b-C2gCt*0q(fv7G$sVg zOPd9L!nJ+Ann`5-s>Wi}CVu8$ry2tO-g*r~u!ZHy_Z9fxNwVa$KFn->F9j!v8Rr>G z-g|dCf^0=UhqPcn!1&}Hh4d((zzi5v^U4<@-q*7u6-V~zF>)+2#o|fu0gFr`ujNcN zrP;s7Jy_VPc~;{Ba{{ZzUEN#kk#f>HTg4}FuQNVnaWZ^Ekt;-8&A=WGse%G>s6d_n zxIny4RQ=;%ecnKC`M8vwjH3D``EL87VQZ1<3muown#fs6$$JaUS~nuNg_2c19!Q7_ z)!WQ<&Dt!1_Wqkl>+4Y!MHODNqT=xd}6rl3! z7+`-pltK9vli?k<)2wEZe?}`727)mC>zex78Db~>GoV!Yy6__w+j3KG9$Y5R0+aJ$ z?!?TzT=-W^7?M9Vcj6qYbyWID;aV3RBNBH)XEnTzR)NX{-im1?x&>t5noVupI`;iJ zK!bv7w-G5#=T-_2PmpWLm-Tw7TwYI0djhh6LAGewt!cVz68AgX}^^J2j8z09E^DXF6E^_?dUvG zKj>rWF6&u2aEO{${eWCxJsk}*Y!WiWq`rtu(}vV1g24@Nsky)->?JIEfi7j0;|aeK zl$UqaXIo(}g}0|%fT-?V4i}k0E@kH9_t&ypf-~4|tppny{|z{;?T7tnaz8vszO~O- z)o?m2T>&)f%aCEB`BzYQ4Y|Hm5aND6gNFiKYwGzGH*yV?T}J8u&J8yR$*3n*H}aZk z-?Ki^AsI2^{Ums*GG4dLM*Z#wWV_rSi$VFeh$h^JmC7n{EASr`-h~iK?Kezq#hSwI*xwj{f1Yx)tSp?TCNgVM1t>Ytuc@erxE}-L{oL9OK(2w7 z2vKCgQ#U8mefcnm`p?Xz^;ZQq5o;UdbdlSvERScn-&kH`XrA>TKB8Hi_m=$D0g2(2I3`rjh9$YygWx??BN>3oZCWMiL^tE@V42d zO6M<>idZ)Y!deuW5g}rm>M`V&=j(!MD5$a6-pn;vZth$u>#|zuQ5mr!Hc=Ntb)X)IeFnqZ1*u(SC_Py@Y8ZZi zVg^H1uh-nAj#oS#)<^XE=B`}Hm-TEwzUQJc@OtPPX?i2d$FOX*q)QsaB~$6BX6@k$SP)-b&*Uq$>g{Ut8l#nvlF##DTCY$!Xw0J zi%b32E`D~M{=OkB7JU?b>>oSRe_zc7?+lvi{>zrgl8IO|iT#>t4U(%m| z#OMfds-0_>B;pbh6Cn!QyJpL8%4TeYxYgdZNPb)K9S6LFOP${-*YOb|u%k{!+gc2zs!qq<|P6Xm<1yQi%eg*OX(_DgqHo7-LL_x_DbE8^wZeaWe9Q8<#kQ6#vu`bH{2wgafaR0vswWqD+qr; zYef2s`~ozO+LxSW?D@8or^~UCaM~$%&5+3)uy#7RgJQ`=N|r{fJy@0wiR`GL*f(5C zXvwVuvsET$$kBpo>7A`3vd!AU56~ zM|-=}zr%4LO$BM;rRXu7E%%Q1X(}~RSl^9~)_vhExK`lj`o+kuP)h~{C7o5;-zoza z&>>HD(7 zcLGPI9~=2dxRy)`t*kbo{usn8J;%vl07zZqGy~aiQVnFoOOrBWX*gs(q=$&}JCivA zB2rbK7?@{8h`7O;xfP_@AT1vr(WQ8$?o@WvAo8C6B<4-oN4m}fkUSW)C&%MgFh@Z4 z7Dr%+N^K-gOwN`QB~BP7(oo>36JYC&dti#n#jzQtQn3*2cEQ*+aiUVQ2n`3cc4%ZL z{2CjgGFXv10;$IeBCD|(iPS-RG_CAG(vHF^I#N#ydMk{^Dmu!g;+(L3V~{Ig5`JoM z9q|bLShl*nty}2X2#=`P<+v9%i^fYDo3yB~BnQhM5e=0Vb@fm?C3WmJP`fs}Cdd-T zgWAOsPQD!-$rmZH5eZnIE(t73hIdYKG!@QZ-pkxL6qP*1EtQ64ZaknVQ(!HUbAU^< z932YJ1VD}sCbeXGChEipvC8FoSXPVzGl@l>>yQDnmU{>AjPGPNaVm5-2^ofV3ZOgx zn0Pg9jv6T*Yo;Q#;-@g{J?WFYD74c6`AlJxyd1#A-izU%xt|6&<%&x zB{oxH>pU1^OL&UNMG@EU74iK9HL;j457IVC%RVP_K1eGABho1Qo(*W0WFC_oAq!TE z0>ykbiKn}}fzwU)CVOvAgeaNfS|wTVoI6*th<`HzPfM?=zGu#JgTV+t2~SDGYBnFB zNbZV-tL!Jesc^|%pp*6Pz%^GEM(lpc?_6Fsu5oomVOz0#d&X`dDjmB#QjeWs8D9jb zL9Sbz47nBqI$JAB=L$$IP^EQ2X-i~jC-ZStv8)TOeM^}ztUXJSd0#GcCy%&zrsc8- zu_@0rN2*P-{_NKR$#MUz>^NcVr|a(l3nf_9q96_`NOf?z1Pfad+)Qv8-;!LdUD76i z_XDU)Nj?;2Io&)eo2PpoRQP|1_CtVz2FA#BQPNX@RxoiE)(x~xNerjh!?GSZ-AvGe zu#I@ulShy)qSD;!346&i;T|Pcwb}Gv6YepTQuvX{t5%*KT!{^KfnL}+)gF%!1MYVz z-z3hjRb=b1x;F*GBC#55(}#F=983&PyVd}Tp98xFCW0)Bja>_=UZiIWc^84m=bU$b zIZPy7CwpLBPKul}h-V))PQH7`Nwyx@0_VcE1KyqjpFm>fTy!L*-!s}zN;c`^pleoq zjsZ3F*akLjkIlVANnOrhT`G>KSv(K!aZ>IKYoL%wh`8RNKyFYIWE&}b8zV%1p$ldK z%r=?G8TFJ*R601npN=SAuKS#Mp1~o1xoa+7!qpEyk!BMbboq_QNKv#6;wGzLm{Oa8 zUz_hOGOt zuoJaBvQQ7Mr?3|y#7`@=36Z<}i=eVBvUD;i9=kZmH#V74Higc`%BYtP0s3z+@?MscUBB^Ac&VUsbwbHSO$WRLB+Rt(|#_c!k-vw!#TV z3In@a7GVX;gAt-?y_Q3E^C67R6D}1*iQ3t#*fgGm;k{yN3Vu(l!g~$ueJQAh1KSQs)5g}qW>0V*Y z-i#3Y@GVE5-*2Iu{LN6%qz`fI*@^1FJ|589I1X%f?UKt6N#8*+X(IlCY#bYSGNG(V zkQ4k}3FZ%m86UzPCk#vLdnhFR0k>LXSbN?FwB!LupMl#4lH28(DKTnaGpux{*xdXr z-g|=_2u{h<==g1V%CgR9AOzdJHiWa#bf48GWy)a%ivRLKcgfEmDwl`T$Er1XDR`iXDhN4Asi2sC!qyZG<(Qt;P63>& zpW+;{Q|nA5`%LmE%2!*+1j(BX{#>S8oDp7og6s_{USA-n%nPnn@|%j+muUSi7w!`+ z8rw*?rn;s{g!A(&i74qO+w-iIPh*&L42sqTo$RG($%aq@E)u&u+`XL}miJ5O=70zO zQ_rddw02*lwoVvwT?TZ*_doTluVu5>J^DX-maGoj{tc^>JJP)$a#Bdl3hWB8{Ub5A z4cg_mOrAB$eO6{&Jc9Kd#^Zoqz}P|GllJ;OTDNW`%a{OyA4pI%IwPu@awQ4K;YUrN zPv(XS{zUBT=#0*W>HjlR8;pUd7#5IU&`pKyaLOnTFHlPojVV_nMCw5=%h)wo+79aF zki&HyOV%MhGq^fcVfj3y>Dhlbu(!Ud1(4PK6@{mXq$%FJs9Z$|{)Q^-U*+DuIl-lX zQnG%R`SMqVd)dG|;2#paIJH26_YM<(g5TCNZiAa)C;1CA{D_|6oEU#&h9A{4k>&j( zSz65zx;z@CWO;1#O$lyWgTsK-pg7^>fN;Y93sCqt-FutI8YXko8g@(OF1ZxZl#^$K zJe5k&3h=fBeQHKxxBnkw*d4MS6j$9Cc2_6=moe=BBB7hF#RUzy(;1F5K9L-Xb8FEG zX>+7CObRN;{k}+1`MMrQHn~Qm$bCaE_%wml1mEP&QMiIcb;{5IwZK=zuZ+^WE3KuH zEycq(QbfGvnk8*~a@fhcHh5L8bDxn6$EP}gE)0)$8(zH)WOV@rZgNA{-=EVCj|RAu zNqii*)&qWNJ-j~MrxLX*vOqrtxw)c(a`afi`mB7=1njQVV?5eT1LR2Q7^AGL9(nag zYHoY?5yD^=4ggd(*(CKYNlzgQM51LAb{tMT*(X+{20@)YrKb>U*DzAVoz~x6!-Mhf z>9n3g6wO9*-1aX`iKil3%{UfO>RZmJUEbdLa3X)uX-azz5V+|q3%Hmp-o?DW7XYMt`*Rx~nP zErxzEuM>R&OWEt-FCd$`)1?;825kqikcINfjr-lBR0nBpU5t@hIk^(5jd$ zoN|zWRG(qhEnbZ=D?ZfWG1jcdji^zSy(w*l*% zXP_Gzw4)^UO7VZIT4v;^c*d>JP$p#cDSj4KziK8_dUEqrKN&fyrn-NT7&NW6GRJ>w zTJ6xIm-TstRc#M8u79iBx;h|r>@VK7F4_AO#*UHV>2LIg#P3d#BI$}gPqVi>BkPHk z;quTe1%ioP*mRM)La9NRLf&ChyGDwizSSH_+$~Zp`_9Xrl>&}uwy2}FXHdJdjcK#6 zsTy>FR31Y>Me_ig*hB zy?xIH&fQDg`>0{j&yZQNwz?O6!)n_D`O-&tD^apC+?spJd`lm3|Da+%*?5X>FMuMw z6thLvks6Q0n6Wpp__^*LFr3NzpuD?=Gr25}3rSyDUdh0~SW)tjYnn{ABE6qXuPVuo zReuHyhp+yTqVR_P%f+$>NMiG2sPzBZwFXM?+_TZu?OL*|3CNx)DdM+h-`hjf&qz@8PJHIQ3~0yXU2Age`kPzj4L? zp(99;_j_@yly6M^|`+(nLk@?2{(0*d`zuJ$5^|dqejZ6fDoff%F1&o!xy4>v? z!0<0^ZqE2ABF-1VIDZ(|7WVySqv56oQ;_UP%A7M&+Ra*T2Sn@TyjD zd}eeGmfjTb1i+4a&V=62txt^HkevgewZ_&*G;&KVar^MXI->CKLggON0l6oUM7cZB z32ri&`MgLk{Kw#8Xt-%NB~oPj!bcVt9aY25|pw4ISd>?wRt&UNyyPCh)1dEMvsJ znmULK8n^3IhH9U+zy!6)fGKR8MfGE<B6jk@j3SZ$-F($**@BIM}_sF6W1fCM0uH|owyjBqh>y5)uZHSncN@&aq-YLS0f#okxSfT(9jy?sE{8l zVI?%U(40o1YdXznW52+S7o?=L*3+r9QC)iHSqOrJo}G&fnskid;qfp#v-sawgp*z*6#^7QPson zTFp#E)eON}p7g?fVJ8SgLm9~i48Oy#P!6{*W!#;SH9)=Tb6u#d*&go$G>TkvEpMr*EE2K>;EEF*SB69FHet*KhP6_-=(qh}8wEK-q0dG&#@rD7+a3q}r_7 z>t?;@+aN^v_nun&6cj8{_oz~<-QXzYO4Id)0L(}^P>5mV8I!U|(Y$%iMB@#P{pjiz zIU(ZClE84bLdv^moDN7-ttsr}mr%UVs}>CDAxnTrn~s`i2Vv$K!Zp0?CMthBAW$tD z>N=$N?bTNl(IDa4Iu%zh8tbbUl%0cEty<-%LTUav1kypTTEfMQ);Y?&%k|+^9M)Rr zC^tIhdQE;q6R*?2SKN8k;;Y7_w1Zv_Xec@iPUXXcEJ`9zw9uL+cb6mBUX!#UM4kUA zNL{btKm^x8-1sV+4v!6QpXy`4H@{{X#!#3_Xt?GcN9v8&a9bchS&O`9={TXFMqq1-GS{HWM+B&eq+Sc)Vo+S5j-+cP}2VOVNNpjxF$;rt{ za$->9pF8o&^*ARXKZT~86jL_)A$YC2T0?}Xt97bNb`Q%$B}UW$N1TOhnhf8Ip@EM& zq0@U|ugdZbHz_5NA}1xH#sPnSC4YyA!M&|& z(|aruzYZ5UeXK1+MWVH#sP1i5w{b}RjYL#-&fTYUMgML{LmyLU45Id}lh6F|QJPP~ ztb7{yh03Hr@lT3%x=Z%YI8%#Lt;#dNmN@H7tvHZ`EjD+FKj(z14>&u-z{Yte?%Pi{ zJte-{nHu>|C|qyoUcyX&{5&waI=fn?`Vvj@SH^~0?3J(&kE!G+G<2WLIP^n4G zP)DwRbY_?$RDqZST93jdUiZkk7o9{qn>ClsP^XZeoOnj(=8~&AHqOrq4-^ON&Hqby zFFEm~C)1?d!*AfB*FhW?zc}$6jn^ukU!73Nlg@Ex$a>j{pSSn)5fyKQd$SR0^Ivh| zIfL}T_dmYg64=@W|+N4K=3;In# z!6IsEWSv3&yA!(myn+yoAVuFF<{0@y;aT#G-o_L>bIPlZu9E*$FkJw(nZ9khHEpn4J5L7Nj1F3vt2<}Yfpt!oh3c(iKK@xbqK;;Ck@u4497 zH~{2(!RJTo_k!1gsJ@dZ>l1|U%9&{GQM!1N+E{rMa03P3FA*_is$D@30&1wB<|3u=PfdCcWNZcM_E^ilwPw{6R!r54~Xh5ig?h-rLQw< z;?js|M##?vJ(ngSMBWR~p&0H%f&NZB^ZYynkv)<$O0|5ML%*f*5QqYtL|2zbOeHQa zk>Jjj``f=w6;Y_j96hv;sF@lODc-W_RR>2!V1)3sTh-Mix-Wu2-=P~KAQX3kyXn|4 zyaLNR`m!nMp$-NfeGG@e)+FKHEL|0{)oE&nM2L4C*7=f@R%|FpfAMPALKIu)c9ZWK zN_id#D2(zP^2cgFtK#HUXxtBX<9Mw0~lrwT?cEL9vBveXum1k4c_tU9!v> zAr?|POB>)cuUdd~%?Vhw&Z5*RhP@Q0pl0IsO|L)~UT21j2T{Qn(APCDr+JFkk)LTyHDZ? zPuJ&5jGx~|V|cf6nYa9?Ix}Wl6yEns+!))s3(Hp&Q0LZiJ13s_CFTQJfnmmPuW*Ds za&?WN0Xir=QmoVYW&hPSKqByMUnWW3C1m{eBn5|xAr}<;y88`vm_f9nYm+259t>A1 zlOsgC`POA}cYzJo5si`}*3ZVO^O>S1ctmu*o{l&>bc(?9&~P-TzUAj5{JuzPtUns- z8!1$rEnx?- z^&Kl-Np7qovJL~>N5UJV@}ZA71#FnVO=?!!FdC+It7T2Zfn|D$N;Q^R3%3^j@eL6! zZ=Jond<{Sr!HKhPEe)cuE!Vn&tJZ!R5d|yslul=7no5O~vbwEXXRG)L@(YFow?yEy zp7lvt{$%`4SM+08Hjm&;P^y$Vus_~HEw+|OQa0THMH(!MSL#Itw`B)Ll-{Rz3aIi1 zfhgrPBAQm3mOynsF~)4PP6h)nd%Zy`F{x%gj9<_B$~K~LJ%n9d@OoOJ`1gM8)#Vor zMRMu;p*_XA2lSM|YBRvxD!5Mnc|dz;*`UL8qG>FwfRYET>Xz_KAepsDmRPb%%QIcc zSrSq4VmK=dhu!^<7A00R0t_ENi?O}p&9qi{;1>!uYVQKG@t*ofMZ{;gf$zSJfKY}g zA)|mF>4&SWw;rK_PJx-hxIVIJQ&Gui;8o#42&6kBD27DJ%wRWcS)~SJ*0F50 zinnkWjM-!|vyEe`wGQg7vK@HL%`-9{C2pJ9AW5XG*5X7>I032F^9U7^RSH4ieGGEP zt;30|q3>wK{n?u3(^_0vgHNcQg(hIMNLe9=;3Q3zLISl-LLr*WWa}k=9M@WFoiC|J zBYBEW&JgX_X(l*evcbgN`|D$L^O|q|E3=g%q;%u}%FnBhgFmIH|89j2&cHd#s-P(G zUWs+GY*Q*Bn@SB91@FS7_F7ShuNtpsK6bPn9sf8S36<-uDo8gqfkP)2^b5ix)856k z2hwsSqONMvMwu#qZzOYgr7+n2VSy{4YjD{zsGEt8m?r=ClfeL-)K6Nl&G@0<}X{;H>@$ zD?wSN;fz3$6TkLsQ!Nm@H|m-Ziz9?-yS14pKZpzLsn5F2|0|uyv({;noL_LCOjOr? zsSNJdM2EIp7s!6$*m}TOXZCZ}d{=Kh=uAETJ|aQb`#b>X8VMw8I)E$}eGH$aXRuM|@Pm zI9>cqFj&O~H#|oCG1IO(hO)m=LmLl=M6*(bP%KLo!rSD2I6cv2 zL9GrF3hF6p(r486@smiEf_nF|vyTmjl{Pu?sp|vVsIQ22mw~68c$-zBaT<5OZTz$o zKesl%h48m5P#Pf>yl#f3-D9l{ruR&Q$a&RTXuLqZ#hJhDXK4sJ{Xd#>s}nzP6}SDQ z02#)587g_h8jg?^)_gf!*ely~;<1nLs{$t2gWfOP#6 zP12Om8oqxgwEAAyc3BVB?;xJqtCbIV+9eUyGW@+;{tgve_gNQ79@ikT9Li_E^-KAi zR(VB)xU=87LH?#n*du?3imdlTqr@dU-X`I529hx@;$H=0OZ$eW^BcN}ni2ZkLQ6oe zNgL-_TCf;>&4ifxR9fQV&{Z%VO@s?>A6PRk>-YsHq1Jo#C~TH(88t738Vx z9XyG!uE`wrFbwl1AnRMi*L~6;N?a)dUh*O$&>BLqy&WNze4zKkS|h_d_@WQ2D%jZ$ z{C<}OWZW{9smja#)-Pt!%lIDb+mE#GmHn_6d`90ugyuD$L9miO)_eCfIQ9Xu-oUQW z?vM2lXPfU=sjI~KO=Csw=s<}5%50Iir4H^@~M-Yjzu#8DX`dVQ)VKH8iIAx)oZ z=L4sa_mRJHZd?!MS7#wok@`V|C^=+>`uz*LP*I<2aiF^WP$N0Haj~M&M@1?cn(S!W zg&%3SIybJVIB*EMk-^ej@W&EXYXnXOpJ?)}HxZUlmd2AZlAQMBr%(?^t#~eNp-)3` z^00NJMA#yqX{73(2Jm~>;~I(XtjvAc`MD%%C&v8^*NE3q{S9p7LlUWtXLMNpj>H2% zaUtT&VcNPUFkTrdO;*TV>?7#IV|ooneS8$d;Fvy{!ge`^c0B%1QmE+t4V1=zB!-=I6Vz|;6<2L>sjRq%F?h)-K!vw(ZENvYx+}(^ zoNpt<(XXsqWHYeb)1dnIR+3qXbH&$Mt8$2*!HE0XI@=c`_AW=&S+I~|5!?2`XOil@ zypKH2B>Rhfd@e%dowBwP{->;?WFn>Wyi8Qb@%XzMwN>7R=Pl)V>1wJbbOBKD zS3w%OJ7$)GxD5FYkbeWjPoz(dUXE~u%>pAwHZ9Spkpdr-deNQlKPvaTUA?+?J& z_lQTVgR>*m8!WPcr=XG_*}ydgA)%sVNr<1QIIjg@60nOJgLC+o5p{MDe~M_Z@nLg2 z;djB!+52;Zn0i4wd%3*41fp|h+?4(v{mQVLvV|xkzrdaPoprVB4_ZRMlEZpt>_yfy z$C6%|5nRk&RuJ5^EQOLIb5U}x06JdJJVex7hZq>b5M718<_D_^J4F3+4W!|{WxjuGZNnXd&;-Z`P={FQ}aDf-RPfcvf+@SC1_Xu(I zC#wqbMrHX2OEiyfI#51}CqJ6({uv?KT(UkXf49N!8xi90C98`4N`3to3LpBC58gFk zY-Pg%@|*aaU#ySHZX@a6tYPJd7Kq`0Q;Rw0@PFWKziizg$>6(NRQu`2Hfxx0;zxT3k^N8N$WylPc1@6eLCD}Q5&y2lIB%2v$2pr0WK*(5Ht%6_S!_&tEnBirnWa9;DQjdNWX#ckIr@I= z%F#-{^TdyANNmrK&X0738gw<52M9_pru7pAf9Sn1W^07h6p>nk_qA&Nu&Tq!Baz-1 z3=H!$57oS(CdnJKcq#~fwxeE%6L&+KRQ!CeNU{5dZasdYH|~=EW!)gNQi_`(YoV7J z&lsS;WGbnAkh;gq++WoN&Q)DBZG06JD30WWby5Oe`P@VmEtUJ6e*Eh4pVKl`-o(!) zsJt|T{E>Ig_JB^JBAZ9CxyeF1NHz%VpaAB*J~cRq&-*EbPoZk+N?!I8`k1ZdlB7Tv zR@sIgvkY4sQFP0yMiHAW$VE_HgV$&L&9-*pTG?bpFbV|0nqa8$rN|KQTJl<$9>a{W zEbC;BQ055uHEk$mN{$)|2#(cEx@#?!RBXd$q}uaqnii(~jygO7J%G(o_|+?61OG$L z!U3)Ig`7#<=Dx0s05aUq94M~VvnjD)wUH_nAqnrXDb3Daic+Zq)Q~keUZR~DD*ViN zFHH9Y;#~Bc)Kq@V800tMvT{RPkVyPCw3q5+wt$4$0x=TCn7fM04pPY(;8+PiCCMqD zEp%y@ed4<6PoA_?Er>@0j?-|vgt$Oa^lgh)iiTn*i{J;vYntLlwqlt`J)WQwi*n=J z7{_HxHvaKz&c>%kkhwS{T1ARpo;IcK_`7waIOqILachH8y=*0to>JL1QZ)CrsiV>C z*mh{Z;>uV(I9Rmoa~9Q})o!|;HdyLmIh?4nIv|Jt?if7}-gO_kiGc0~ng`cPf-HkPRLu&K_Z&Dtfhw5d%+w&MiW6(3M91X7tgEeM7mi@xR-h_#y&uK6Ra zopFS9cS2QxHs$1Gbv+`*lpxzvl5Gk{PqLQf**rs)Z5T$qP{MnMV)ZcMu$N8FVtNBI z$2Hg5($N2XBE{tpo7#`y7o>o+FeF+}!G^ggHBy+uY-%o|DD;gKm0?;G_`4sn?l~Qk zPDLQAX1}GW#9*-}i zoz=XJ;VT9MP7H@0sy?sPElsZ>th%tg`6#0n9;%arM0L2ivtr9IYh<9biJ^FS++d#! zL(u3LTd?T!fNzq{WV5M(%9%Y=!fH2&12YSSoV(Q`jqfn1L!I(t%z%aiGDVu(xHXw! z`+fxQV~ra*$LtGBm85WRjs&ze((E=krRY;{je?go&i172IjZf^{7m!hv;m4)ii)e( z$DkN%6xyre9xV%0TVs)$ncV{4?YXShF+2`$xF^_ZKWsdxlA_Jn+IP=80jbLeqs^50 zhZhHAWn^R`pvlXfg1-)pCV6C|O(Kt``{Vo8HmJ--Y%;Q?n#}za#K^%C;-YE_pp_(JS9IH$qr<0Cx^jSQ-s6oh`TKkuZ=7Yp`;_hd!pZLCXzCTb+0LQUPZ0_EJf7~J=;ZIEBbz2IJ)_pkst=3^it$bE!mBPO`g9Q@%GQ&`VSf2yAC;)#Z_5SZ_VV zn0*C!T-LuytwozG46&YyfM?AL)Hr7u*QhUMzwh+4=ut1_$B=g=@Kkyveh80PV{_@2q_&3YcEbuLFgXQ_37_NspZt5=^mT^2QsWQ;@mDdgB~hHrsNM5a=SJdM;jsqtzCko1mGijnfO=YtM)5p3vfhkBiH#s z7QAQZ^b<(_nC)3)t2AVjOfuW{NYP}hRuSy+=TPHVZEsTTK93daINJlRYF=P9!#73_ zG_1Z}#B;slbt&wQ9hd_q*p%yorM|>m{-Ks&F=bGecj>+9I36^?Hs94$FT?DdsGExU zUx^gACh9qaO}P^vFuRKx6FHh)^1^1{#Zs?_TIvk6-S8Yw)+3FLSWdE)%{=f1p7i8k zDwjo2&?<=43&sIEQ?{*GCQn529#?W@wr#acrZxPkPS)12VGeo?Ea&KT4OxCYQf#qm zUBWT^20p>sqJ>jyxLaCpVhLo^+LBDY1yaBCD5vye!6F-qrM!)q&Y@?zB>aAdq&>{7 z)e~|Rx2(#U#(Cl0NYNu#_aw8vhlSNtTev7V>DO4?X##UW+OJdvvUt|KSJJ5Ij=%Ro z9;dnW>wZ|^)7<*y0IJ{CywL!IFjPh&SiDUw#pw(^$X2@$Y3U$7pP^UmEc<VhOKsH8Xw!X+nQpdjLiYJ5$htjwgYKbE(AO&`2S)tY)n?ZXNse4k)8;u6mF*0)X&FH@B)tl_x;p&W|?5R%3Rux$~ z-)0h7XG~t=g?Tph{FWKOaRTaM3|*)vP<&-}%budLcsRW1?hjvm!8(Rt4{=+{N{vCa z@FlXD`dzR6 z_rXtB&@~e`5&Cns`3|p?WzL+c6a{l$k~BMH`_07fUnFzedU^(maF3W_e1{Rf{bS>m zI_dz5Un51k2W<1@Fy!ycl)ej(HBAtH73etmm=A9XN&*V<6%uS|V`(i)%4|4nRgC?O z=yCcI@hlKr1^0QUqaC8S5Hcm>GVvjK+05L;xCT6Xpm{9c(3DRa1#$C+Mc0tq^x3m# z6wJfpQ@>1TEutSBtDXjDZaTNFv-X2+Exn8fA$~)dE4O)J5%RF^I&Rnej?`g;%pJtu z>o{%_`3TBl*U3*|*Zsl53fkc)=K1*`7<$ia)pHbvKd~Bp#HOBmXpi4FNL${ke!R$h zRCiS>lK(;#0rgs8NApoV1GYx$O%lhnx5S9?kJ;1)DwF?4nUC4j_V8}}{)c4w?JX}L zu|6|XUmWmSdx)^7s)BxFR4U3YZ_rTq-9=;y)Ax&TEO?r}w#}@KK(s!?9401T zT!gnumuQG1v9W?8#FY+s^3<>s?}_M;8*MXPL%=Hv&6Q!sT5s1hTyFQDRBSfBXXTBu zu4b59D!gLDOtV-wb7TZ} z5cfrWDd~j^th2?okNY-9%qyEW$y5`u;r*gSpC`329>i~da@aT7@{U;h6pB3mF(NYW z++@WxdQ04GU^GC#$Dr1K?B_o@gwiW(QyR~c}Qd6?bk$g?EJNBP+qfuak6cj4@ zgf{dLH=nkRaE+Z{vQRR(VTiap2$;;vuaX4(st`@kLR{HwQ|C~r>_c^OgqV`m5MhF! z(Q_nCjWE1x^o*_cJYKUXF=dNQo$8{b;HqBD7B@)=2YbtgLsGKWEX13Nk1fPH^Ro#- zQu}clh(KK_V&#YMh*v$9>V-R4c<D&DSvb_96x2%i zZPOW;8tu$bROAU?rW2xuFab&#VNMV}&+6}OhtwE;@9=!jIB~A^{5sd^W1~d%v)W}q z+7>9GVsQ}8zqOsOdX1@ZNUa(HO@LJCX_8G$YapIwhj+!=V#{-ybAD6;GV~e=<1^}c zoyyaTEs@D%}6%`%sg}dZyeM*`f zwNW_EE54|e9&@!NH#yztB5~%BoH9o{jfIJ*9ky|@Vm4X(C{esauQw<&9prDkR5Z%m zgoWdb;0|twxxWFKh|2p6Z`wz^^O9DrrO1iPto|?C&d4!$6u-@&Dmi2NMC^Y>vsi%C zWU{fVhh@50u~RqAi%9MW;<7R3GBu$dJ2xL*ym ziHPcyd1z9d(*oU)Gk<4r5nXv; zpWQ6V7Uil*fqVI>$aZbKd89H+42K2!0_r;f{?LyhtXvDoo*jxz{eYNPdf{2vZve3$ zBwOqUyo;9frmgn%A`3_l6#EQAA&{iAkfh==eNrqFHkSu)w_5AyPfU?=G$ zE8V7_)XN4m08qV2=5aXg!!NmF>nIGbB@YC?YmynqH{bEWgQ1l;nUwOjt*ffkaN~85 zqRb-unkfT582GJ8=B!$tD}J!4T@Ja`z&6e5XBj1GzSp)PJ7j1SE<@-J8H3*$DCOv6 zb0$9pXGng~O8_d3VNqh}KD`X&nmUsuu1vFxtgSK0TBvHA3_MHYL1OoQ?eZWa!%;(% zZ1ZTY2y{1xUUOrM~1*%et;`nDu{aSV#7$T9PCV1s0hRbRqJ8q3P-RRtu9 zq7Sr6XNGaeV797mSDsbM(s-z%549}O;W9x{g$mQ_0luQ7sT{vGz46 zro0f20@pm^(h_T?Ssm-`qVr7wNw!ea|xZgM#FCBl15;ydd+s9tdBbI zN!gy8o7qHGjh=3vLGmV)QZFyORK0z*^@Rm^P11didJ6c;MUemKMOY^kKkI>55knT> z`NHz)YD}#_Ay2yz*+s=~y8JbOHY;eASo6DH>+rbpGi1gu+tNpDc>vdadn~Z4$(I~$ zAr|vS%Z^~FWh0!EvrMGd1LqQRhvRB$EBy-ZJ0RT}^BcEz)_M+0d*S~e zTvCZ!^lb);+BS60zqS>wpd8PF7L)1xjq4W&ieiTam!^N23p3F@a?y4S-di?xl8(NK z=U9Hl5pM)H;I*U!w{5jMZO`LFE0&ux)FlLi!{r4)F)Pf$cnXS+vOBghQYyHPc@c)_ zUE5swyAyuzh}xKBj}iwu`T1|01g96m+ewTQ+)Kn3%a5f||1z2Jn`%iHebV6FlfgL- zlxkn+3m|v}bA3JgWjWX=0XxCQ@*=aH?l$Fgkwx>vc14xex2xcjTodgErGK&c6Oq(t z84d`)XjhB%j{udk)-%(h_le>*s5QT#zW-MNtRhP6Z)m?EE99Wr6V;%!am*AN?P#nw z>?%e-7oM-O@kK$@v_z3rKQ~Fnnv~f%GG0UNmG_w^$fuNX&I%J<>J%kxd4OLB{O-l* zX(H?7m~LX&Dea=%2Uv`3S{}ck@}WO#rsr8q8f>s5YB$y zcGV~J9KIb@+QhET;P>l)tDc$Zkr`ZxsW_OYvdEv}N@V{*GMqGH9> zjR@a)6>sLsrd|MSFM7q-zE^Vd0DkWSH|tm7q|>_RqX&sQuOlAeVL!W?TXqBAukaTY z#9JvcD~0C(+PAY`obU;-*TxR41pQE*+C#=FlvQ)_br4>J0J}PPz(w`@Al>#5{SX20 z5gKl$egNpgL+19Sf9JvYb`MDOA&`m~%pzGg@}yH%vcs9^BOpgR^3KLVo@86-2w`6F>o#!HJ^UoUiNwj)PCZYOq}edSwBhu|-XV7FjJWSlIPh zd(wW668s-Aw`buvNj_+lY9T7pLx2+FeMX7Zec+$FQj^hklY~V+Lm!I~}C9bXnxf=gyRD%t^abj09-S*T!$3R~ADBQ>=ThHhzPlQTx zS|n9~wmgb;R#yI$9^&Or!_~}C4(K>^K)C%o;d^JH5|hV}cmmT!g#Co8x-XEweL<@@ zQ68x)KNPRvkhmQwx|TXfq3_{U3z;JOnx zhoqoz4e~YgUbKCKR5i3}zaeeKw@n9#?k`WSBUGoNM6VbvR0~1-Evme`#+;8wtm|x7 zon|T4^MUE&qgcE0^U?G>!{n^k*b$eTF?Z|eX*-F})o)%ft@t9*#yw*?b?dm9TG~TY>mgaiu0*a~rBY3i?PS95+ z*{0ti)!HI2w-8H^D$A!3{0jF>d;6n) zbQI0U@5^Ytl>Rfsh2$06NSrlbu1TN>dN@hYKX8M zeuFX7(cWGp1toZj17UE}W+&U#b_wC%>%fgfUbBP{;n`8sa5DNs((II6QxE?MPAZxv zbLQy@Aw{z=V7n5HJQVX%}}tKz{*h)yWTQ_)~6Y5pP1Ky4c~#`>J7#Sky&V z$Zq*tBC1=M{~s3pq`9XUxL7~r#ngYJ#O1E`+WW}2qQr`B_SKTjKEQ7ySKm$MuDr;q zrm%FR-chL`BC(CXzASEd|LU$2&+K8JC(Gi*_kW17acz^iizpa}7zW2WBGz5c1@=G_ zd!Lq9y=d9{d5*LNa~E3YFPNJ|uP+r9PnR4>v|Ib8Z1fyWbOT0I2LK zvxYeB$`7H~GGy5Cr{Q(1>}|*Dv$s}16eSNgNS)_rY9qH)gu?sU1Jh9|((?P*)s=Xb z+BjNN_p#SLI_{}5^$`^VVYWp_!4SE&PzwY*!As#0;@px@1fFvdF%k!@aF*%K`0m=6 zpk{4?CKhF@paHu79Tg^)_0^+*;3qX!OIDDdL$w5wzGc{PH;(ml~ z>L}vf1ZN(ojTxUml8WQFpAF#xUy* z(zSnrEX`5S?eLIF(fTYdTqPl}t*nzvKRlMyXNY}^oY_X3NHnsCgC#e-Ev; zV1Fvpj`&0`PDmD&59$|D=Yia$$eRnlO#4>Zs&r7rAhob(^f>N(!tEzAPeDq3_HWq(zcz7)S(leVz5 z$voHtST*n0NAWlo+Mw`jFJLFQrd4PXqwg#IMy=RZK|L91U!`)Bu^m!py>w5k@Eoi4 zEKh#4N7RC`cJ*k^Nc`@=8tZj!+D7z#9Q*3>yo&2=f#i!5QKF_A!?Q1l$z454t#ODY zX$&E}Hd42Zgucr z592sV=BTbZk%5@rfhN(mPoVyUtwSfMrMaY{>~_{vv0`0sTHTX$7ZK~Cv7w54`t@b) z^y_y;?R{UyQG3alL9V(fsFh;e^>wAM>Qfn%z3xsLC-d^?hEmS7vU>32h6he^HI6z8 zOsFThDlElVOlA`xL%kOvoFyZGCs zupdiWgylxOw)8m|ERy7XnT3NtUH*<1zN76iBIk)z4;(LO($YCn@L~tHvm`M`U4KkZ z)9sJTZshP85Z$2kX4C-j$pQ*Jx{kCr~2_oZPzp*RVqs#UVMyh!uGva*rkz2x9RJTU{+M2kZ*o(3of(1m`{%|ylnbDk9oFYtTe4ytZ z@8%`S955=uN63|Li{y0|$8#KINbw9pvjlG$u+Hb~*VLt8_?H+gS0S#Zf zv22Vn?977_QSsyHKUgJyw-5yfaBN0aW{5NR5cr7c?UN+-|FZ~ucq~w#McH|npOw86 z4md=-RK{a(BtOFBlARX)pr}D^9z|)@52FiQA|pSTL*X$%6$`!L^rO$+@Y2F+K;=7p z;bQ+sFS9!#!O5dK;ieUIDc%RZ&dt-~;VTn|(Be+h=^6Lz)xLHS-!KKt%= z$~TND^=Y(+9w&CaX%mEt!)p<;BiU9@=;T0gZdFgj6XTA8-^@alk9BU4-`?8K`8F}S zL}q6F8#M;wb~{3y%FN|^^+`+0btwt*%PpNm*_TsmBY6uXr-+<&y%1+-Xp>-3xk_s| z=6MoLbA16)^OyRWjIVcZQVvAP_`0V;b}bYuu0D(srbDaxhg;4 zowYLTGgWQ0<8r~^qT0dtjKVvM)IS#~9x41=B#(t9cvZAuu|J|R$=3$Az?L&y!`+I^ zrhA%dp{AV4k;@TSrb_q&k!nkR#~&rq?ytXYgDQK*uC6uH)_7L_4#p<2KMr+#jasEp z@vM<-bC$ZDOmpa)4{!i6|8#=2bysN7JDB*>!>cYB3{DR=6s9;rP$+9Pxf%d^0G>e6{Z$2 zT*_xphB4&sQ+fsX%8#KEYnrdrM>S?e=S8>+{+^X5ub#aQsZr>tg3i zJh9(huOush?M7LtO@gs$xOPxuvFmg5CKACULM$oSMsXl2X8{M^!C9@OSM+jWHIgfs zeBm9-11kI!!(_>^8V_s_%3XV>ew28n*D^KmX=uL69NYeF+FzJCaaOX< zS=PPaqPPvBi6Tc2c{W&<)&kk~G25KIIH*=uiXF*y0wU&T)jlB;2=*gy;ZcmJs#oG&@#nl2lDS)U`I~K}d$7J_lv}p3WU7fGvXTJ~1b4PJ-_1zor zK`>6WwZjyF79RBjx1tYzZ000-`c0mGbr(9;pr*c(urlK~KYfi#EA4_VMXo=-fmK(jr#_CvQ*db=v~Q4I zN3HcOrpkl%r6iw@pQkZXzHi?xMdM!lKEqDAP?Jyy%>rY$!@r}HcGv{$EU?l)OzT}& zaq5JMKZnmXoir!A!k(B7@2{N)Jp3e%5H+4uTqnRlHK3&@aTDp00E{WWaaxY~Jr~GO z<=BJ-;So1oy^JB5~){$F-=F%4~`LK{L#H?~3QFJvTVd_pXf5uvZ=T)e^ct3HKC^XFFx#38$KPjlUO z8O0p@8d}42*gjtt&L!~`mD)#~J*>H*bYEqa*$G`m#UUtn!j}|{4v(J?s!{2}K z6N^8!v=Cd5V;yG*?fxgquClL{v~0f{q&+Yu0l_Sf>knZo{e>#aPMKTaWG1St{5c!P z7&CO0#BXADK4Dk!S1AjBM~h!h=p_3M}3-jPyicnqza(@TNv2U?SE_=T`K=(d{ z=q{k%V-s47H$R(*wI9w&OAg5V|CzwS@y{*pB4BuBpq>eb(@$e49lM0L^Qu4AeieS` zJ%I90V|goUb4QFn8UE+Ay#nc9h;8ZRPOg|8la`iR9F*&#QF1DKshR#BA59bVtOp9)~TFF0+Cv94qknaxp*YiU|a>U z_G3q3T)li6JMnau$p#*bY{A5?AH`K?xbzigG(kg54NA{rb-US_$iWzjD#}Mut6bvP z!p%cjOe;WPET;NuLNl@Md;3V4Omn80PDa%9AGF=U*{!)re5cG#0*b)732**^DeMQsFq+JOlZ=Uer_Ga6nNevG$@}1%;x% zi8hI(pR~T=C^DHumpI{O-zu1-Gl8WCgQF^=YV5RQ< zRSOTT&sHoUdJ<$xmS7kkt%+Ulo6uP-dJj8diGS!p$!~3A5?)vADiR2FV_TGd?K^WE zi&4C@1=_KMV_#UL7R0U;_>;VozxKp3EEqh$51Xs9P{unjx$T$OJj%R@4oSVHeR`CU zL{w7zJtkBLy1ZM7qj3m!E{mjKB{6SJzl2Vrs>V&;%*f{V0|c(R0q#KaVxG*wv6IXk zZBAnBpw00P^%7jdFgS=iB5xt{=KZEi?2l}nka{dNAw~RBsiQ%8a=iVOQBe)olp|6@NV@yYnz zPyX&G&R;Y)6jT1yW+X>L8VWs_n=pg*!DH`k>{=8QDDIvDKWA_0FQ!{O-3ggy;}`?7 zDz(C@`n#Yz+E+>6>j0!yTtaM*TX!@qb@4!?)?C7^F)3ulceT~TUKnH&NAGG6Cdb%d zHca$2jIqzoV*7wIp6nH>@F6HB^A}9#vKTd;@6c8`HP=w!g})$Vp45%JSuC@1R?7gi z{ukVme0$PxLeWw z%~Ri@I+wpku*86~799UOj?TTcN8b}*4~~RjG;k=pj50L})tsFMnJPFv36tXM*u163 zT5?CLax}}bu1M%9o^ImkE+1rg zC4j~O+H=`Fk9YL&QLoHVV{;Us@ht40bJ(V?XsnxI43Z}xNB$KYDnnriD#XWc__~ec zflGsl%vN^Z(qDLNn^5Oe_9Ttr#_Ey;#I^R)W3#Uyrn zJJi-c$6+?8))Xc5p!n5Xh3J7m|&DrXia3{(83UL2KVyyj&? z-s)@Es95c*x#4=xj#Tf32?P0dSP$c2LxxwrGHy>XAvti(9!S(ZJuKB3ZCO zrjmoK4GI10w3zH35==vZ7v?7n5EXYZyYs%46r{0$rkikj(xGAv^Y;vp`u&oSB|5~R zZtQGhzYY(QNqRQVOiY1+4iyKHR@p51yR~>?^0eME)`F~*ljm%dRP-By7^=t75aK}= zk^yLrNn8zbY>=elCou8`Z>P!xB-@P85|hY_aHvK55d2q6)5?@4KtkeU1!$0%~)g-Q=%PeM}r?&4AORgVMdf6Xm~$pC7_(Y zutb&8VQ97cOr3QY*8M1gzevdH6x_Rg| z`80yx$Qii7SfuY=Eds%#P?GVE{jw*hwjM)S*B?!oCm$V=#n4P%&0=O>f$s+uV=#_{)Bl#ucWOcmo7%NqE_>wy3J4;(rrhYL$`tjB8>Z5-+(7YleCR58o2W|q7dOs^-%XT&w`YqWJJ(~AqM4NT6shW&y|NS2&a zgKflvB<&oVC1(`dQWRWxKP;U1_F8N?jR>Sx-NIF@o$Ym-a|}L-)H}CuXr$yAHgM!| z4n|y&Ek-S{iB*=>Fh&?3wceVPqukfBvQ!W}g#uC*Cv;~4xH{ziUiZ_0R<2DLE=uwy zKxShnC^bZJ-fR-ZNsiUB6n5V;inOOlx`H`y&f0`vaiBZAklb*Ryl~>%LS70Np~dhV z8+MzLb%-JIxfR1J*`Z8*+8SkO?2eA-WE)XkZ!?Mg9UZ0eH_Lh!gQk;~_YwHL9rE7U z5hgr3J5(szR3ttp6XzO(us@I8#x8m~rls-%Sa0XqDjqQe97E-H;{v3<2wqbLjIg95 z+*j8X&x))bQ&gia1h|7Aa%fk}JYFGzk8`=D{xPsas_!hi<5hgDZw!L9m~y7$?B{b>zq@-mUIRRZu#W|*+Hj5jbwI~2 zK*mEOXJBe@zuo-?hC_d?4@ZNsH$ghwE4img@k$O9&92Uam%%--`&$Z^vk=zD?oT_s z4JjJvP*-he_`Sn68u}z=q?Cb}UaJN=lrxVr>ARRb204^HZ^G~QASZ(yQ)T2fK$@a~)oENyJ6JWQ2?UgJ_=&M~SSGs{Vad<<>?X zv8BTtvt>OTqaUEKyapbaa)zNEzFI2fh(&zJc3598y`RX+)XHQal0QO@ng$4uC36@; zV13LS2OAv&Nmk+B{Jl2@lz+2qyjc`UuGmCMG z!9BRr5fG*LCC7-_qjXkIOGlBK?U&p|lzfQ5PT5UyBGRy+JO-g3?NA$rbZ}Og#JSOq zW%5h%@cX#>lIn*u5ZNxXTHh3%wCn8rI)fN?6VuaLhmAUR&_AFrot8XaG=lU8`7;{?G3 zG`ith&o{_nZkik?&QH`c5`7=1nB&alCWvA3+)O;%@tuxz5CiO6h11NHdF>I~xQOvSm<8YAAipDY=BK`r>n@vyk97;ceblB+l6w$vm ze6p+U`dNwP(BBsU`G+Kjyyfkco>NZ@nzvld_v4W5Cva2RBt1*)oT~etQ`yhRaVaEu zpxD<9?j{E3lPqyIzXT{LGV|6nV5o?I^f}PKYn)=_}Tj2X=3Os-9=oYUt_ac zebW)lkfAg(uTI6$VE41DHOO`$42=KO7Z;H@ak%EJ>&zSAm;R{uYK|^)J&OE|$=m$W z9}!3V@sNhW*Z(`Fy?n=7Im~FW{{dPk)#K|k;I#6JhvLq6IHZga{SzLL0*Bfe*ofaZ zSn^%}^fk370AvHxX8Q|#9tcnFDpt&ePBiQ*-ek7jJ0g;aDWjIt!r#cT?^D0FrSo(m zwfsLwTwm?eN%%j7bMEK+l=Ba0eJ#6J8i7W zOT~Db*x9D(`X*@hPx>KA8ijWtk_#NlmqpsUq+RXVMAH`C(vR8EIQqXBQMOPUN2&N( z4+C|P+wXgD`q%lJqJofGS&JR&UL4h2gBX;3`}6wkMfuw4YI91qwiy_P>4bY7>cS|e zR1ebn2d4*%YeDJZBJl{W#N|9PRjm@~+iIi<+KKj8asH=f7aYKxq@{qcw#FoweCR)G z^NbOBOZBA8+Pq?L1W?-sEZjQ=T5VN>_HJd<5SNm}FKdFbAvwc0W(0{957&nQe7?R9 znN&L!@~b!z_{50f<&FoWoG_m+%Jq-HG3#tAyaT+x>tffB?CvAGz%pb_I%K(26bFuS7Ip+b(*X%fWR zD&{oHsXdw^?^RLMf@PJtqI!043*6NU_7t-wV8@&9P4PphxI&nHcvyNL8ST!vV8Y!h z|N9;4pbF>Fuo$uVeuoNwML$$CNX-Kdb!C-!b6HliblmOx5FwciS4P4Slrdk_)_-S>L)Ho(ryh`REg7DVk7xeMb_j@`^e65$uN$vVQ_u!W zAQm$guksXZfp>(fHpw;@5iL<}PCOP7GTXJlY~>x{BD+oh?(=zvn)g*0os$A~^c zuWk9eM~pCSa)gKuvFUisXS;4&wtCN61k$vynV%?m%F$Ak2#B;H&?-*Rp$N5{=129G zzk|f-7n^CWi#q11k0RVh!#giIVTfv-k4nL-teYWd>qa&A5T>WK&SHK5P_M^-nf4%t#B^ZXi;53beiP<-r`}4!b8z0xUU{I`UpNto;w&lut3t1EqFJ+-wDL2CYDJQ5+x;mXT3)`mjqZg;4Y-dwz8fVA-E*ihk}l#C0!=xoUi zJA0Ud0>!eqcrZW~WeAR&2|U1@99sIko)Fm_SvpaaIa68GaFvSqFjCdHTW%wq8TS7b zs})G}i`oUjmK=!*X@{ek*l$iwtj#XJXC&ZJ8t&<}G+0!$!QB(ttcD^+OB$uJr~<~o z{CEYDRFVu0;p_z!cTSc|n|G|DDSg?YBCIfNoX(fj78X)%KEr*M@tP)js$a5Z*F6C7 z1PQBgLG3va0=gTlgm3b5*PaGdH%ZeZc1qUWV0e3avV@z76`hjXxp8HnnIdVFx0o%E zt@A8CHi(m^&wjh+`EF75$!FYgMU%qM$>%)MiRl2-%!a&FisUj1p>h zLVJgYV2YzYrYBFE1$% zv7|Ay;C$5Er)RRo)p&|g>b(VkR`!J6!#5~jjr;~KGZq3W`~hQWXDFSMIBzW>nxBgl z6f27TJ2w)My^|-iNKwL!Oh8P0jwjAJFld7I?|&EzeG>LcTvP zaem4JPW(!OZxi9)urHj42OJs3UU<++wA|Ah;nwb?dx+HyTKEe8(+(d|wp=$lIbG$X zoEYGb=Nt9+!ME$K7#?yGJ+oqQkf_=m<{>IRaUkIS^a*C8ih0;cnc6+QE&BiseOJ%O z?BhqAcyUKujKtU1@fAPn#IuVR`-y_TRZYX>H6_^usVL+zCs9Qz++qenSgoKik=O(4 zJk`agBH8y#=;ff<-4B*Q)=OHl32gvmUfe87_@#v4ot?fR&4hoI zmTZpajS9!sg_BiT3%j;7ru4WrKloFS@|R|KeCoeD`AOjGSI5Ybu;1qHcHaaj#*&Pu zV^|4ao0pZ)Bl8sQaD3rV?~`!l__QJo7F7}9KB6)@T#HK9;^}InQ-(K#NPcg{myY?e zD9Xk&n6OX67EXGhX}n@`Yx*?RXl#is5>^i{aUH)^{tg!9&u28kwaGv|It>?_%IeSr zaRjJiz|<()z{hb%3%JeyM^K+tP>_iH#xc(|__jM!%bltG=I5NLcTRQp6_u&*jif%Y zSe<*|((-w5aZ3AS`1=L;V7}GA=_h(IMpS>RmnCFths47~&HBYYBIg5NJ;Dup)GyW2 zG!mvOgFQsS>$pvNw{Lo&R;|ky*BP5HgUx#PhU0BhFeg8W%)Els>%RsDiOQo*<5aVA zJ6Gat=A8=fq9B7R+68FOKex3MUj@9{}aHv}y+)#fFO?^S{3{z-cSEOO0!jg_XG53-4 zh7(%1ET*lh#W0D!>BP_9j13T(>F8DG$f=!^-U3m;kYp)As>3PYZ^H`yL7xbqHT#aD zLYSsG9esrV4bNu(QPjI2vff7bNMSSt3V9EZ950bWlO?P0%XZN-Bi%Q9op@?yU_+5T z6s~{Csv+jdJ~yHOQ8pBmsT%|R)SQsDMb8PTC~m)#viiY5Z;}7W1TBDupgjlNh#HE5 z2~A>^IyYSQtuzo7Rd@FkSs9p|C3jSbvQMcJ4;m=5c4v5tss}rJh!s!A2mi;%yzeBh z+MVGkqT6-%5Sh7t9-L+I?!{`QYxyl7xDon@>WkQGOZviJ&xD4r{?JKTQPIRlm|nnB zTD6phIrAeoO3zYnuLQ-QVc2}EXoAJC?TZ_WN%f((^S3Wnr<^FmpC~-K^eI0%Pu1zs zPZivYZK^U+*?;CNwB)<)frvuhOe@!dO9*UexFr8MR{7W59DIl5Z;Y71=`Cto-!&l} z)^N?>bgg=6c}bWG=?E0mUyk6?A7BSn#YO5`D|akb&Q1=rql!eN4#mq(cZOiFaT=2) z5I$B5w`ctdVcAt0$s7@)4FxLeNw|!HyrBj0J;21C+7U+P`4qO__a)2?W*z{ z`8!Nhy$+f5`)P%Tc;==7Gj1};s!h0D< z=j7f(_c8E{=*u+1j>T3m>p8n zXVnP?_KSjo#FUZgEfvl?T(0TwS0}z@B!(pN8$%^t25cIK_IA44ad#H`gtAYPz~dFn zKn-%0sM3#q6&7y8T(!I18o#f_Akt=Tf!xwC<97{wUf4W|dT()vIDF5vDUy%&0Is8= z>*JCU#N;ge`ia@_J~Eo4tSA}yZ!w}kT9lB>Z0X zz!4@E%xB_d%Ts|iM4gq%rgZVb?GbfCaCiXnpPY<&i~DYd*C!gqirr0fSIbgpcs2%U z#nP7Dgl|x;JWzyhKrjl4rvmo`eqb_W2Dp+WeuNiNFVAn;Ri?_GVXAj5dZuIod}V9P zXR7buiUKxCad58k$E4!74>C_#&{7tu&P>P>+5+-r30c3#wGhjP;e?9e6pml4hziYB zn#I2-exoX#XK7I$Wt}T<& zXRCNB*Y0S&#a+%|uvnOr3~$ur*=iLQ%HcxKZV0la48U;7Uy$dp&J}A`X48|;_M$${wf$-NyRja%(WP~LjNn*(#pO8qAEP5v#9Bhw?L|Qz>Q-N$S|2MvpUr}lsD^|DEB2E!*7mGmJxy?m;d-^C* zb{8>^xO!@`hWBUbL+O;s4jMMK%Pld60!;)n=Y}@uq0SB)Eb1f$g^SD? zupV47wPcfUS~deO%4>jF8Ltk+?W0j}R8~!D=+C&9-Q`g)Nl9&ytSGyR;zTWq)czfj zn)H=#Cv`yV{6{mDl4bXHQV?D~nURd=4%}z9&VW*9a+%si&v)ddi%Mi6e6&zbc-bLPyMGiSuMnaP1>+1jg<@J`NGj;pNpHu)VU zPFnK4MSYKgbaOP5-Y6-1lz(?ovE8AhAWgM>RB9-5O_MxLxpX^z|K=TT2zo~sH+lGN zx;5OllQ@}@uWqMM*7QRu7iK}$l=aZ#z<<{tsX0@_d__cI@&w`CE5A?W5*@v5Dd1EC z&T0`jjj(DJe`}fnRat_j$ebeLuN-yMy$E=^ftSoq&UK1#=1eodpmFb=|BU=&i;?pV zBXz`MA#jgKw)d&5)R8Mn01pIwuqe4eBBEihj z!H;92Ma7#jxYyt47|4mkx@peH`gy|ia!I~gZ z@iR0LC1t2N4I!3Ol+lI17NOLz3dldnKQolr%A$;Jl0~JmMP3<(g(Nj!xp8puA1=S4 zP_%9rg79Kn1#p3gdabgV@eH9f#)?;x zu1TFL2Wi2`UoyFqs2G&54l=1qP3a_(hUDXbf3Q{vD1dTxVvVB*$crYZ?r=TKlZZMq zW^<>a_RFQo$>b5Jq;s`UN&_?v(18ohn>$^l28;Svhu~8D2Ub7T=F~2D?#r}8ngPmn z^OMs=>T{!TD+*S6IW?#^)+U46vSj}S(iHX!*=NN3|e*;Sg+ zo0z)fz@^^aNhFNO-z3MJUSAH>78xSUyY~aL#Hhbr$e58~(M_LO|WiU1h0?>toQ2*=+5Q1@LSFKD*(6?YP=N9L=WM%*fufV9PXt-HX#gnH{Jw99lMWDcuIuWgOD z4>f@@q;Oj{7nzwzc%=<*D3r=SzVmwUmUd~6o7Z&1VUP27E zFL{vJ7GS`~-Nj<&EQBdIYZ07!jdnC^=t51^)KEAW&bO=IOI}_?f^!S8O=@gS zqX=0{Y}aHLIPI{drsgc^B*u-`8xQVomO>vkzBz)+I*Ggqde6w_FGtVHKWdFfHzq)I$dQ|_9e^tfCQl#w7Q#b>OznTyO7({%d_fUT6+ zM4zd|lqO2Se>bYzzbv`GICdW#ToD@=p?2X%$9N~)7TwcHT+Gw^6dG?=f!s5o)j;vq z)O^L`9GiRbC#U7Bpt0PI-p9IpmS-fwc@)nJ%+sdLG(h*Gfb!g@juMorwDT57xg~%VvLN>4?msenun{C%?JD0(7DKnbIU*9NcR2=$o$n&7iQ@%2z7xSo2Oa}6hMr!YdlKuab zwyBe-nw_sMlI9`LW_Bl`4Xjv2`AT*&wbDrSE_O;4Ds{Z5t<&9`3fhNNYLIwmRR*3; zWG8T+2f_cG-+S8vBASI;#y?x2n`&(NG$L6T^8>yi0D^Z-I>gz72SbQjaVJ zncl{EL_tua=jE@Evzg<$9n+%}88QX%4hip(|8fGpccKt)LIkZkrG#|BhNG`QP>!P}xE~c{r|*8L8fDP`NR#ipBGQKW^ali}KYWBAfh#ky^1> z|2dA!UL&>PK@Ztc_{Fudsj|4Gd(sJaf!P8s7yqpADTAoI&;>8Z|4(#?F6-G)akUeuE` z{27NdP3{KV29h+URy?cWD_^@g7eQu{Mexr&r)jbmcEUmNDm`x}0(o8{P?J{WtDCdr z{lhwOMSGW?s?nyqYAs}n%V~S9mZ{_XW z7Sqrk*EEkrczcPPd-HoJ@nIUf8eQRi{{8-CEKbOV28-jb2FlS_vsgBY1OKXqJ3o}H z*tW5UuW4l525pk!-*{ao)@@QU0aIn0W;inC8yfDm38Ny1SBh*C!@VhK5ZZAQcDI}; zjWxcdk=ji#ZJUs{J5_FmRlxnMPk%G;jsn8p^l4kUz!RE3WQ{D3Kl0rsP~>JK9P-i` z?QRv>n>EP{ki6GOGSK|9P7(+eSB81?Xp+6MWb)tpL?4l#Eaj9U?h_K2Zsvv43MdnB z^flF6tAL8Hqtea583iOfoSZIK5?Kc)*#`<(`!Ey?#kddafzJo}UxnVjCBM9}`yawe zxivpbcntHD@*eAl^M%fjB>cD}=fC?HTZC=-dm6t#VUfY2|5oEyV&PJ&MINX>)&I)- z5w)&0EBrHTpLRkz6%jVgg3mQ_@)4-_rr-62Ms{}T&}>Sbm55s9+0=9LJ6IfhtCMCk zQ`^q#f)aP2a?|v$)5!WAxPs6${Y_J@UekCynqMvjG5zo^KoGY$9yY=$lj&j3FBRTV zY%hxGBkPn|Ic2^A)O~4CkeIU8MK3R@6N;3KC-Z!*@JIn`mq+DnmqgZ~hEtZ22bymb z4O({B95?a)YS>mJk!eBxRuN4!YarjL)L=1tSB4h9rs}^}cp!onCTEyg?gt2#z4_|V zRkq?skY1?5{yXVHP@1`xpA%^mT@Ec9s`9D}as`JA|^=`~%6Y`5jUa5<86P|nUZ%nf5R|%_mm4_;RgPwH=6YGn| z)&FQFkbiH2;nd*W%UzV*7mTy*d0J{wWBEhUC_R;n(VrM1sA>T?LTiHsZw}bU`-@RZ zRhtR?F9WZAHveHcupIqsI<@j~xLx*rXo!c%T^b}AODm}+4*%OIqdKEwe{*xLgHK+- zWG`Nar&^1j#JVSc0hK5@+Ek)$G|_0~vr!Lj{3ACdjhg11Isb_g5l8ZaD);{X8hZ;# z0u$Mb+E&7j-69j4BwNcUQF}y7wmjgt6O3{@iY+hYKOie73$<32iP-~vrAmS0GG|T9 zB}xx*e@7IC7=J^KBmYJl0~%f$r6+h}-y{*a&D9{fy{;1;<#Sev+Zxd2qgo|qC)|wG zpY|C$2a{IX4*1Hale3$~TGqv}c2`i4*tuWrf|@C`9>9GL;~8f8n;It*hk_&Fso;)c z=mA63H7)X97|s*oAwCe_(j zL4jiX7C11~zph7}t@JaXvi-Px-NS`U|q8)YG^OD@@5;Sjt1^E zA6}8v9c5P55@JBx-_5^YrgDHokt(Nmbs(m0pH5}!9# zJo&#(JIM$qN+(T0mXwE*BAxKoqGCs+)-z43PbY)u<{>zU63GVt~7p>#9uolcobkcqouZi7PGVcp%(y2Q9ozMaD@Qyj=QR zvSdE>i>XD#O9Xze&l@Tg*aYjWI)|M)rZtf?QW#Lvc%We<~sK`*}xY)8q=gzAxOUS z1LPJ(gzyn_U?no~Zi<5Nn1{1)>!DIRinU*83liJXQ>Ruw><;_rQDeoBtjYKFWLGF# zdo>a24X$aVZo`WK=QO`iSLhAVbRLUzzw`NPYGmN98;fvgAvB z>cp9urs*rc(iRP}Te?w5^;dcoAcqf7sgo3!a9b^FWhr+U8LIL+I4e)4Svk-knsf~N zJJYh20SFxm6sHD8>JI-?G*}Iy(C_l^X=JcWBXvawWv;=EicfXZ_2%c^E0ZQEE-l2WX3dT^01d9!CV9%gtBK9J(;5-yO3NK8E zv$bWofM|K+-ihC@(dQhP#9nC8m}MHDP{42=G_U!TQ>kADP=@P>A^K?F_ZuZOk>wsPNe95KX4}G+IG{!t0lO7crz& zCpU3)N4{M?QQnyUPA8%VWGcnb7$db|Wq?}3B*ZB)7Et7nm^;m@?>LnjB5rPj#4t_v z@dmVHmv6JOCK%AZ_wct&%lSkFLF6tA?JxJevIX1)O){Xolei%HPiyC71NVFz%e}-+ zb8w1*hy0bls&OE4jntP9qC!(!@(ifoAAn5!Fx7w}{?_u7mXc{m-S|%OL~}9IRVrTj zSP|7s49@_R{w^F9B*&WAB46bS!gK6V{mm=7&47|BT>fn_wrhNIf^A*nuXPxS=WXf~ zmn%d8R*YM0;fN2THbqS<-%O2UpD`@Ka%Ia-*U{8?$T3UOR7)vJAy%mKRD~HxLiYFzqhwKv%b z7DS0OH=EiWGt*rvQ;?{&Dnp#!MVT@!L_o`SIunKXB9&>J;t`Ynu~>*KD0wRfjH~Vn z1A6)nf4!iYYAuTr-afik{)Ls`4Ii7bY*OmoEx+-e_=nKBOxB-!B!XLdzP5WMYccyO zoml?4!H)l2i0(y`)E4(6=u9SAbe{oTy49tx8M@zq!jJn(gHJOBay5plgY7BV63+28 z@;gv;3$Us49_}>OszPwqzQx~}$4ol(1C2ya`{NAQd8e|@fXY5G)IHNqZoPq@4zekO zGdW`eni8zXm|lA281s%cwR=y+cLj1K-h&5ff9hycXOKK(+=$fkpTd~(*UdacT%4#Q zsnb>d!6+QSdDX$_5FDzl(mXnT2r5*lO?mY8!uKX<#i2G8N|J}Hn^9uU$(FdZzXGAL z`mJb%XKD{42FB@6Mt2wI!))qU_ia#CvVs$Dw5btg5xITDAK^B2ocbv+LG0jDZE6{+ z2f=qH+tiaBlI8k<;9*o#^aaB`d_GxCPkK*oL2A_(xOVbWq)oY_(e-UB3tRqfbPSUb z^~?Efvu)s|PPRh%N4tRb5&7Lg44)D1Dn>`yMoNSo-;Svhr8~~k;vJA+(Kh7?&RTY& z?p_jwSS!z6>T_`oiHo^S<*`rb7Mx0GKat;8|?kKS# z-lmQ-sJ8Bb?V+=l3b*6?F_d4H7=io6pTn8NtBXx}_Y?tooPA&UO@K59dZ+FQ zWGktIv1EK#n>tbE58aCvb=5zVj`~l6>e36bLwN3tS7h*Vi)?&vKu<-9&%4>mWIy}k z`)QD-^=XSI9T-4`ffyx3_In1<^?K|)q))R~e|=}UC!l?RPSnGpp!y~pJBIJkzSS!M z?FV$FK6x=+`*0W)o!4E2#c1E$CBP2=FRE`->V%iQnyXlhZLenUBXVNk?|%VakBLN%`oP8$`p_-$UeGydf(P_h$s zCd%t!Q|@$qk$42Hz3~;gTO9+BL%QS33dzGSvBJooaQFC;p0*}Gte2TwH_azOR8B@X zG}9xiub{NruQ8=6-t}-r;8KrJ@#10lrZ5D8{JU(&QALy?rrf5FbrX?d`o)Q} zNR@5qhScM%49ED<;`)0C$(h$%2W}!(Lu}5)jA&8&6nr^N(d15`v>H!j0|d{8!w@H? zM?+o(p7%D4ap(Hzxz8TI#u9H{M31N3t{Xu?@j8=ly@X3he~*DXY+`jmcVTiScmuP$ zuT5>Axlq1|(kt9Lj24OgwD{!2dkd*&rue3a>@N^!s<;gAqnI92c$?Lf&Gf)4RsHqA zbV9awknQRkw~nH4fKH7=>bop#JzgeUaTnrK@(D6I(dgR$9)xbH<^u|plMuS8Hg#o{ zQsn(8QIuv=vHD{1eF{Y#{SJ<2ubj?p8u`;~%~YFbv^d&nsjH~=oIg1>ckml+m+%LBHq6PBcJw0nToWxf5-M5NE>tWF zLs-_l7CN#qJ<~q~-`-(Up6UGWA3@C-XbTecFZF-~sr76tTUhz^WOcENg85@fQX+?h z%=Jl>2+pvTO15GLK4o=2zj=nKtOIN+Yc!zGqJ*c_rlRn%w$IVmlAf^C)F*hg73&7s z`pE}d%pf4zJg-YE<(*hDi)Z%i`7<~i(3Pr@Nuq) z%_pH8y{NLVt1RroFBvIf|41klrXXTJBiq@FP)q=+pG;-pe@OMbgrlCrf2~ldbd>%j zs#RrCKwoiwEGj>6axz|7cJ@L3Rl?XdjJ7GmGM9kgP|}5}ws$ZS;-*wgQBtA|(C;uT z-)VbO{w^w_e_%|;=&Zx>{U@?sx}0oPcjx$Yr2I+Qfc^qB_0hJ2S*mK^eMr5gQj^4r zn)Ehe=OqM?lrQg^u4w!XqP&J=e?$wl&5#9VAnSD_H7Pc-vzRgg9h7h2nl6{!P(;0n zt6B#k>rIu4x06$%yAnc3&*9qPawzcsMC%Gy{F#xciY}ivP4#6k;1&j+?e2D~!V|Ok z4wSQl?=1#V@z#i7vYIQTO?Pt~)s_a$&a1dFSpORYBOUZOzE;%J%7AxFwy6hissFY{ zYQt6BSdkHC8Dmn8NhTl{1qFzgrsxdh_clf`*}1l9l9sID>XeF$HIpM-i=hn=B61{8 zuf${2rUjyHw8+lW3k0R18M4i@&i#R?N3U zKYMzNYB5(&4`dthKSYw1kSXk`C$mldJ0lDcPVt9|2WY*90@jPz%D*$B#gyq7G(HL? znUYJHceF^Gp3F}wuKl#^fIAp)gI$|OXtxM(N^M6s5H}HaGb3JR-&uel zq^2qY4QlV1w$@^Dsz-`JsQ57#B?dXstdirvej!?I_AFcc<^-!#uOylWw4+4TKAl|& ziLNZPsk3Mn8OqN%F{Fhx+2|Jz8rJKp2(*ud8I^{N_UbEYX6yN70~8Ku;~&Yn=F|uy zgZH1XAtD3LgyXbP#DQFq2JYqI;V!C+wc5`DJ2^p!`iPqv=GWq*q7nVf_NwgM-Jp+_ z-?6yD?L9`l@?GC6UoF>*0LMreZ*l3JA4f-+uF-s4nKeT5!AR@d36?&)ug>Z;?g z2c`Nh`HPE37Z82%Hx>^WsZoY;`<1kBjvBr7{t=351M`2 zVw<{W!TKzmyW89O;N~ntSLQC)GloJb1qFP60~q=)APqee%F5S6h&u>HW04A@z8J#31+@p;y9Njyj zMfE+}Ai#acK>V~-+H}hS%Ya_AN;3_W1S=YL;1CDP^OqrA<;?7G)BO&S~m z!E-m$kT?j{@=E6&^XD$ePa8f>Ol@uHChAggeXHRD+1@!={@`f1nAnudIXPws+q}`k z+P(5#tnw>v;JF9qcUOiIK0gB-`ojHsW0sCw!O4K zWIJZ#dW9T*)9s`zVmVQ5aZ6zakY_@HZrE)`)i{@>%sfVh#Yslkh zG<*j~wa{G{jiU8al!1oJ)Kp>>-+N3FU4$-@D&RCUOxRS6C2Et?<+qTqHQBf$E*rxx|8f0Dyqp#EglT32{%t#xfLDNHflXM&lx+ZITzks*|+ zHO`W`@=Ha{Ae^Oo@6@|?LWQU*X`6qlD0yS98eSg5&4v`OvaOMC%{H<~5%jy=Ha~H8 zmn}~wk}r#8qQ58$0$1|hHXpT>DDU}|Xf*B1zyPAE1`gjGjg)v=U~igSsP0C|jO5t4 z5}s*(=3<^q^bHHArd<{2vTKp;> zbFwop(7*xr7;K$)Vet8EMdK8^i;Qz%wd)-s`#P>=B|f2LZx1whA!P3pwx%aI7Lj&W zM{AP0-DP_IVll?t>jBq+O~uG6Sj-Y;iwLn|4`JLFYaElM(PICT+DIA;+GQxFtYs_Q z^rPL1?@CBgDzwXSulgyi(2|!|fHd)W*Z$&@P6)07P*J;I&lw8v`wVD*dke1wAzX7MM@Axn9Y6Fh&)wxY!nARBZ z);%FzWZ|Z(Olu9e?o3FGxmgbw&?%!}Q?u3?@RNsZ^JF)x+7pU(<4%w0oANg5WI>JJXxQUJ(EHSD8UO$w)%H9aH_+_nERN(tz`P~s=B`n3*Xy7z!q~>fc zA6u|VIcCEHtYc@mBUbAItoyM4QL&5xx>cfnB7N)(Um2dkB#pLVO*?K=!ELxfeFSUX zaob!uR{Y+MuAG_SD&G$2BXW!N@=J5x4tC|eSZj#LeE?>!q}CIY%uibER?w}He!#WgzkPm*aA@lAk>&`H?v)9%m%Gr zW#iP|gKWheE&avV*LB7DNPP^l;&t0IvV0VS3X!k}?3;F87G<$ck;w_unlgvc{PXezGvCQJ5=&&Zft<9iR5}(3$ z{B5o2bBLd2T?d2Ux#;nZ=12awXHdej)Dzx%7+@CFeT?a)> zK&Jip+3#wVgcg7UC?s-0%NWsfjgC-5QCN-Cl~2RHw&*==i)V!gS?saHtwP24lVE<+ zW4MQig+Hb+wPB!P^jYY`C$&r^Cq9RQyH9T!fJkyJ+lry@>+W!sejd>AFiRBd7>;2w zoTTJ-&WXd&4NmDJQhvVx(t}^e4i#aiZR&ZYOr*YuCO}~e|8aPXs4MN@D%Q^H;H`dL z76hm)jRN!tHZip}6^(^F^b+Q6t=<7p4SE?m`v*ACsr_-dix_dnrb2{q-F`(PAtL+( zi;M6|Z0#w^QWmM@nHi5tSY72Nj~vqlrQOyV4|JsHcM&8Dac6OyLspW2okd<6jy8DI zCNb7jL(Jz7_XNd05g%&PHbwmjjrof8XK=_<^`ZVnWSv)u&HCM4Z^&r>ll`W_>ow?R zAL*S9zh5V<*B^*wi1%>hOFhlh>o+jWQ!)a(2z#H|&0hL=Gg`QPVpA`DaI^9j%eqh! z94`)kqL&_y$lE9(asoC<{XWG$)TB?mgMHR#wy9=A-n*dpiLykC)CXrN!#A1cJ*4JF zSt7)~&-K29taXxA&bc!hPXoE(EX)*rU+8Vv3ecXyFFt1r6qlkbQ$_d~JVV2| zC`X2K@wA3L=7oYJmZG=>e9@#|x@Xy%9deOEk-g43dNfGcllh<$c zj~3@{0UvrN&qCL+&5`*iGrrISk>Z=LY&miSxE7xUdw*@aTdoA0z~@kX_c%*{*w@1v zAzY?mLu*>I&PR(0-)MR=M;+*Mw!0>YV_}GRxbq6)4VWIZtOp+1+0s+2sKkMIc4&bz zcaqI706JeDhC9RYe0>m`UfEcd`Xw8CwU;$fr2eQo$;JCCCMT5!Knk-o9wEvs{FPr5 zJGCV|N9-7Z2YRpkPe(IlP2ZpW={%_U&XG6NKC)*JU2n{D|e~52v%k zpQ1&#KW#;FTSHa$BC<%IO{6?KW0w&J$Gpl6t#wYn57g8$OTcbqeqqs4~5Y$dYi z6Yzb7wWp_AM~X?;^t5G58_?;B``U*i&P%3?IGSqp6hnqE%mH3$Wq0`ZuPU09%8_I7 z%F&ZB+nWv>eg^zS5^NLqpPz^`#l8A5&VI=Gzi2V+x^0!@8=iLj0@A|!+gruW>njjf z!ui3(Ujdy7h#$h{p_a*WFr?^sj%>q|Ed!L2W(v*q zNA%_jyK(`d-1-xgwaJ#we5_3E8s*rKo&RFlKEFrzdD zU&(Ga*_?~X#@kV+OXjK-jWXdscqylqUAgvVqy$q4Z=?-1@l0h#PI}$|Z(v zYZD_jxZ0HwihSc5gMOrDSmonAPC@e;Ti6z5AMau5Eo0`%!Q*L!8?w>x#lbu1oe(f(zwR=R7%}l5ar9(sDH1)fDQRb4Eawzk=c%%!z2t%b+J{}h zb3H8xENev3llj;yuNYD9u6xCDyxFU?vHo4flHrS*y*%#|BfP!rewChfbtp~?LVJ}a zHh9{VYRzfqD-)HM4vi~*=vDT8(dpu>m)6b59{xz}a+{?qHsNp@f%AbJIgU{WAS>VX zk5}d!`eexVa%~Af*mEDd(%Q+3fgmlK>lZ6TIMz<*cR7QAEPqfT@TtT$Le2!LVC%?_kHJ_aRn<1(r{6A#O|~94=wyS4HxO7(9h%4-_SLAp)6oW^`3sbDK z&IqU*VviNwN5F2mp~epmOQAgOv)j~o*pa=9r12NQBM`0!gzyLq#UCl2j`qB0Jy8Y-e;fM3Mt5$-yEkB@kOa8Urt1 z6f+q_6Jx~XaJw>ik|UE)MgMsL@vI%rlg>NX?lIzGg#B(=ER~F87MnP_Z7i7!-Gxt@ z{FRg`*aA<9w$GLJS((Twm=YtF$7rEPq1*#?O}XgON&I~d+AM+wVVCP{4eyB`6|3o~ zE%gF@UMaLCcwaROUw<2*oOH`j(RUP{`ljX~Yvmm69U~Ir?M>0H`mouxBdrPIMi)47 znY`|9j}iSl+tqyLr0a`fyA80!iYuM%>ezzHVLw(jz#wF8>Oum1&@c0rDA)f$I1AGW9Fiq~S^adIDOU7_#N<{+cA7oQLblrOUU8JW==4X}>YLm*vw_bUVhI$* z{p~7VJw;%SPTjmd5J8f;1H4kK&3u`FM=BUsQVE?-)kY7l3!@YiAl|(r7*9&1>EWRS z9&JE*yF(E8omo>$973EguFZvGDznWd0V5HU#)Jg_>IMGNg?-|rpIac!h z2H7@tbCSaGqQY0$j;q)3z1XF8y|5f%gp z8R9f!XQJF1&1w@~!|lpz?JOwmV7|%--w&~W z#on}P%LD}2dTXK*$)|uj$eej$zHc@XbQedT)ec8nK~R8&ILlryhjs+MXQDyJ?hVTn zA>AS1=!q*K8og!#dT*p<2q7@fzDeDsr-_^hoFj#-E3!O1Ui#v|JnTo~@)`mKur0g;{b2vZMQT6J zQWk=!Vj=uyvL`JGQH7XdoG$`iHs5D}$QrMu7I}BEN(~gp$J_6c%)z6dC5Y)g!M;!` zs@y~^MGLX{dvA?@sJL&ne}_gbNX>N))-ufXiFRd2<-)z3ow`~8mM(u4s{^0Fmc_>d zTY(jJl3g7IM&Y}Rv}rS8g`Ty$4Ewj#Ns8OX0$WM!;7n_l=r={Tn~ZZe+MT^NdWfhq zQmN?PgH-Q@ZBvEkV#E}4_R3nNkU$)mw~Z6$7cIeo(+^szvibJ{uNr5;qm)z8S`&1i zf&#?RJ{JGVg>CU-(W)A&v|)S*lBlqBNduLX2zygIgoA#9dzB?t8ZKuIseMT>1S^n~q<)O8%97DZM( zd^;Nlx<8$VbA(BiUatwl#rk}mkNms=$(5N1$a=m4%jfZU{oE+GqvgzzSlYU$sJ7{D za*tDi`oktd;2;%R%sW~Laxb$nMnpL5>e?99j|b&9-feN%^JK5vwZJWrm{u>;O|-OX zArfagEuEWCY}rgV`G&ZP5-MkH8j9J>h8!q?&e?aCQ$s3|qyJx#Q zF(Rx`Zwbk}566i8g?4pJPTR~D`Q1VMwF2g2WLFC^<@VN?R-4oO`-}W86Px@zwxK4k zF?d?EIu|U(_>z*1xi5GmMpTs8mCjF9X*)=7{t<~l;T6-Am%1e7kJ}L=o|ldlVET4&*^GNxBD?Ee21OI9vD_ z@Y+1+eQ2QKb8g)qk7-po)sm=Mb-8c85v`iFU%k-?|YWW5|?X2+HvRR59c&76*plml!T$dQ914FYDuOPL-#WPMBz}N{nL&!fzW5mgOw7$mC zI|kB?zueMA+*}0>*yM`$(;DSRzH(I<1i~^%N`3+2$D`+}A_U)5BN(bZR}Pk)vjHyKq8kSeT<0sh3$! zqhXotU%!P_e@wvDN2qbL}eHROq2%(>{i_QDJYo1@j3CTygD|aPeD(mMdAH`V=n)Y_wO( z*~y>z84IgfVT}|159;w6jpWafx%^7paPiWEI`c53eu1C(kTx)J)}4(J&pf1e|BvGP z9CD@4vTPHzoAj7%N9uW%8YogX+uxJp@fcF;j8yNtt@lYc-qYtx;l?|1uG%N>1x>v{ zbL026Yt&}>c;yr|F&_th!NAXr>El;fV{ta>#9da2e z_PtPqrTPc0vfc^kTLlG)o6o!SAdWR`xVFR$0sb9^b%(t|{*$5j{+|ED>ss6$;>r&F zPx6uc1B%JH7B@vU{lGDZX)3-l7ggLJnd`X4BVCm29tBUt4)ZPMyKFxp|MF+N28;SC z{ddM7+eM^$z7>EM7(%9~!w7bXg8vc=EEyaZA|eLI1&CXBYw1nwvc@b-R^n7nNG|MG z(49SYb&N^c2GUl%A8ipEcyfMvk6t_|TCYNjdCacv50D*yMr-m)AzM5rLRUNem?ouZ z>3<-7+^&2`N8$Sy7JG87b*QK*%Ykk=YyyH3J3qAiE6Tc23P%WuOJ5<=fc?e-iXN~c z^ z$;1aGZ*bCGLrV@UunbZam3P$f>|y}@4XD1T4GiPOI6?E?RjLHrGCw$`I*!RKa#IoY72bW|0g=03Z~d1d|9@g-{s3_~p?y2y zX=y)7WFLgA;k{>B$so|Q08#Zq>=R!(pqYeOZ$WCqLd$$+=q-+CPgIj(A)uDA;(WFJ znEd+=e7A}fQx0JXC_2}ED9QVX>TNngY9Zj(51E zy>*B(D5ZU-5C6GdyA!*5BQzHAs=#(NH*)Dy<76xKz&iMX)A@^MtSEcYuABnBF&My^LfA;jP{LvGSUJP9BDRw?nlp$%TaCsXvKJ9wKU*ql2)>zx%@+PNj@z;e= zs-4ZkK5W=ATWh?pcEmnG_Js?5dz~D~Yc|RNEeD9J1C)YwFKLG5!iz%(@0ayvjq9U7 z26)5vFr53ptmiX_wF66>cpQezirI^sytey`?=kQ867$?1*d!x(48C)+jpCMdRTm(-SNq z3dhmJ3M%n4%b@pez)3J2qa^3@2Zw@a;|dF2<2b)ut>tn|xR!^-ilsI7LP^T;3kT_Y zEXx0cUO?Hxh*(j0!oEsUMdCXWRFP$tVCnCZd%u1%hWR@I30v*oiFk-z>O&j4|1B+56v1(ocCw-Y6I4090?JqKZ#dKevN#C_Qku22?( zvTQ4i6ljUk8#yae6ofdw@7a~hGBfmmSUG7A6=e=DANI`bvfQ%?MI5=4_A!#5xoPMn z5%t^~SKQlF>2MCuLF-L_w71cw@H6hXxU>z1lcs-fddZ=W6J-aMB!8Py`za!B2Ojx` zSCH^{#ERE+IL^G+`;vDXk@T=Pu2IY4 zjklI$$7pKkFA0^Yfvl8jv?=Lci@PYhn)3g)KFx_zxBlj)mVN;Y~_c#cxvQU6msTX zY!+^N7Jd-iw8-+w51D|j-iu?xppUc!A}bGy#Y2hqQdwRGz6ZyO%OBa-HOkN-tU70> zwS#!%V|$@YreZMEIoVY#PxC-n9U9pAj)A1202rpJLPX6ccJ*8lwX@+m8OjsiVv#SD z@WQFgM`dP2W9hY@YW0iiW>#Zz)lRr;m-LyQDm4+=c-lO&(fH5oO&)h6o zBumM%hV)Pk=CjcfW6MTqEJ0?Gax_rgG*cwXzB9Jg_?V)=P};7H5Nc#x*2<6ZNFbUZoK zzfP-3G$&3_P%r5|DE~E0FB4U25MFEV+eUaswe}VDKhAAt4Vwg_6Z?F+iqG%YcM~|V zC&!BF3pkRma`bUWp+2r+!Ueq_P z>xc+({!9C4Ihq8f#)@NKY3mJV+%&u~`L*`JpuJ)`f5OT=IEtN62E!oj+p=Ti?-{7B z;Q{b>$v1issFCF()q5SzIdhKnoQUGYv1WPJJedOEQsGbQXVlKO@9bgI#Goq85p zo71x0K=J+$daK1#v_hn|*kDN$k3Fbuq=iVGjnuRamVEWp8gIkMq2Qk{0#rROxR<)N zz$-M8jR_UA(-)qwc578IFMpn>9x($!>YNXUN|4vH90PXlqHg>|WSbK!>Mz-o#OEvh zW5k+EPRLw|JSrq7=m*8;8{5mhdt&y|XhP(B+|VFe|q# zKnxN~KF+V5^|1xOBlfp~K6V&8$(sx3t4T@2z+J2?yqC9&%{8cq|2! zI_07*3b~% z0Zw_}PF5#i)En<*a@9VZcMrb~Gn~nF<36DgQ%zfUwFv{W#Nhf;+pdhaWDQ5 zOQ{R@9PU(2)m{0Dl_r2-6SB4t76e^2YLP#ewQN?9pO}0Q1_4<#L#xVGj0avBD{kE4 zP@c)OJ_y!zEyZJ~Q(f$m%)l-^3`<}u$5#1065m^(v$b+OEW6ACx3Ylqr#y3<3aI{D zzwgQ#wxLDYzj_XH!mD;`-)WA+BM<{Fjz`*a`^oR^D3H#4!>5Yw+i{$bw*~5~?5OGa zoE<3EXR8_mB`hdyb|N*R%zLy`Wl1}W)DId5yo#;8@|-oB1lW13*XQ6ffbEJEUECZh zEF4GqQCI}r^iXl`?`GaPg&vs*wuisp_!zbvd&=oOsQlVC2tWf@$E~!(JQge7b$2Ki zFRmbugEZkZf^i$9xcNO%2R-1ZKLI>)jMs4D(p=8Oy=*|m3*auKs@;KH7&!r+l%pn+ ziu;5%P7f#mh=|1XQ~oV3lTWb-j~A_7#MV&TZQ?RqZzVP#*we)Ny=a{+-tng93x_O; zQCWC~*o7mw(NY`kP=bJK!agP+eAffkU_YHK4{3CAN0Hs80GEpV9AhQ_P-q>%!tC!* zh7U^2>R9o%zeD*!@%tcp9(Ex*S;WP_=3?4w9RlS20w7d|ZPqgG`kb7Op9S1*j%!LY zn7@aj`Z+f0#&wwQWOiN$l8vIi{yYS5phLM1u)Bw`;s!a?=_nPH7v#5}s19<>ltWI* z_96;|E^>TII4mGj!tq{|^B5dqb;n+@CUeBF6~pU;vN{(360QYybVQ5OyDjbIfJ%e| z`m#jS=_g0>75N<^uGF;l5LaGn-A*Jv12>|Qx=BgqxLHTh`2;rv-ISe^Q4CCGi(_m~ z-BFk=8hbDKmc2iYyk5I42--;dq3LO`8bE_~hL4E2*squ~h zd7ZE#C}sk0T$2fM#Z_=NPt&DT(fjAnLalN4-rA=M4whF-q_c45GD1=fDA~vtxu=7X8=2*Y6twkEhO#j-tM+LmhCl zB_GC$hy({-e=7+E`!x;@FY?R5Ak9abCUxKcPxG;+nf$RqbNuKWHAk%|;u8t4lg*|y z{M2aHsRTV!J0bP6STP~d;gD4P{v1>np2Du`@4he*S54OkZiK!7J0&?BvW?{Gv-p8Y z4&{u_qrG$Fa__frpo~k=&Ah44qu92WZ;2Pf_d)@pFQu%J#nb`X@H7@KgmJ`sq|PA; z>H!^l8m=P7W7oK4?d+p{0r-j2{_*hM#-_JaKXTdwg?QsRA-$6L{)Ho%>_fT(pYPm@I zAHamY9O{rV3g17XS!H#%goyw8&uH?&_z9`&t*(d=eVaZ^=6?4gQoFds^%Nex)sl9T zLi7?+H)OR>6dt#t&)JV*r(k-g@-pzW{g%YaKXKaREbXrVC>a%#B8~(VtE#zKZ$Rp< z{qW^ z$&JQE(^a|uHBm$G(*OjEpW;*M%)dxjoe*=(e#LjHBTOWmw0b#N5oxoM|Ah_sP0}ot z-&FX2mu0|(DbfG&Ym>b?6CT$(Y5-MxQ!$2gQlm!|1$9EH`?L0+N|2BwF5Y{_d4o#7!t{eE3YWTwZ)3$a);r`<&!Pey!~HO@+f@ZLE9y#<9gLadu<)Pn`Ubo&Uv#+Co90qI#f1nHkB|x5NqW z3`eP)q5LB)6_uYz+Jnf_@-P4MnocVxB6kt-hP#_;k2JiR46Cg{x$zMCn{Wtjyt30g z{E*dggW=MMXF4XyRN6S&sML<4+DPRjaaE~3o$4~V<+lZX^$@%Qnrw*wY`U8w>e5(< zVonODc5&jV!CJ;q+j5VC+z34q27mEyvG$uD4D~=UD^|Pd$BorJA18Io6Yw+7T2h?s zZM1<%ku(fNd$H)NrNIM5;4nScsL^=GiK1bSGRf5xF+TDeF*Jrdlov%B65BKHg)?v$ zsWOtOK=|t9o;a78uhbD*2>f)iuc&#_JzBLVYw$?b>j5CZ#`(4^hcd_Vy91g&`+0Z| z!c?d@ST={dKSX$)l&)!jPfxIq(=^8@y0b=m?Jo5PWeW!{4yS>xrB)Y!^6 zCf9w0R;K=@0>{E3-q~S39_60x3x|1$C#@>(E^gZOQpa)b%mQ*h#%as2;me_XeyQzl zk-)lunI`Cel7jE9aiV^LL!Gtr(6Afx)_#m*=Oruk?m{+4R=66_tU;V}VPGxtc|6e2Dq{NBKxteb%NP2+O>t)=J-IwPW zE{|;~Uwfh-+*^xqx44cXW~$>(+5JL5z1YP$j<^|gaaDRtBe&cZ2TwiCEj%IYP1^K# za19Q-NE~BfDU;=JDeFTl@^jcZPZf|af+wnZ$ccJ;oH#b!ai65+@99h0jh|b)krv_R z#_33Pvw-zO2WmF8nL$slX5Lx-4usE^2(4m~bDaVJi+7_SsCW|h! zwRk7l2)NMC)@0PWvPf2P9#@!yi!>RNvmuX)G#SkoIV8)j#Qjidz=PdF-S=*EI2Z{6 z`G32IwZb{;!af0FJ3UQI5^)q5qr{=M2oztV(Vg@Ykh7ghzR5%V4DEQu3hlKc)8HAfJaS)N-RxG+l17m`nwaDb>=2DiZxUt$pDaaj0k zc};U_il!;Q%TX-r;a(|MCx$O{%$JGma$ZwnTM_=cB}91dpW0ij_(m(DS)iE;U(ZDj zW#8rqPKy(@iyX?Wg?q*67}2~I;Uh)C_=#}yjG3S&{%SxoIHJ40#+hpT6326LpdUbT zzD~YJR4f5ct$%+S?n9r^oA1RSu<<9|(Q%&m>lD>VxU0y#93DIKR!ny5)wHHJ{y+z@ z6QM0b#Ibk$B;zcx*46^`bzh%LjV=MH& zg?6Jtq1#@h$N;$`uEK)?+(pSyKHLe~7NRs%a%O?A^7I~BKD!dG9YKoJ(L=jC^5Z4hoB3wt7azqk)b(}8U z4ZWU+PYzdZiF4UsM*M1Nhwj3@)}ij?QfXg~rE0BXu51RsSD=Oq??KyvGmBy->NsUc z9pMw#S=2w^P_btC6IP-~7zT5GZ52>?5sZ=7bH4fuoP~E|7OvC6B@N&A$nP-3ebssk ziB`$PAaQNIqgb|-n)1DD>A8zmsCFB)Zo+NheM}CyWJLgoa?L=r^xluA1#ZGY?EZ2v zkSUYg5 zKYz@oIN`ZP|5EB+n=!sy9Lk8xQY+&`!dAWMqM9Mt>xzWdxM8uKA|ng_9VTa&hf!B{ z$M(7z($HvlMlvl%&eCrHx<^MVLV<=L_K}Gv#`Y<`6h~h-Fn)yr%zx5vD=|;aFYx7k}T{u zD>#^O2TY#zPom(8k1XM8yX735tBVU>Ej z_d6tL9{k(_Q_?jYX)S$uK8~~w zI8+Ok1Aj^39Yj@W#8d|4M3-?DSmqxL{AGm?5M^^<{ou3 zlfJ;r>vu>GIl1H*Qde$s?LxO(wRxtFdz?e#y#`N#yz(+cu<5~~8fdD|ItpcNTo6y7 zgchIr;BYn?ep+>j%awvh=+(H&=V4v1pY7?%f?%ZipiH^f%9+K*={1t5wTjJo9fc(X zg>_MdU1_K9ys-Um06K9NW~$s$Ke+FlO#dQ;)JvUURkv@B7-FG@Y=Ed}A} zh2O^Dl<4pT)kNlcJ5KC)LECir{f_*`J9py@8cbj0uOJr%epJ;0h$L+m1 z(f^3v8^q%KB*(Ges&;tW;$>+3oM;?3j?MdoN^T;0z?07bqCct}QYw0<(B3pZFFE>h z(A2+;0dyKr{Y6!v9#75$nU#~J7I@XYc<~q&sx@eE-yELl{?Dd-fTnEjo*Bci zUD9Vsmd2I%zsR@oJg#LmIc1v;3_b*2QtXBU1I{3mXVOQ2vON&#^}y|LQH5y{8*{`k zmPOM#^s&MdD?_xi72Ej<%$>&_>H-bL#-|`k9}wDEz&lm-hJE}DsVnQS09NPeEBaho zKS$~-*>0W08lUOvGOHB7WU?@XRrwJlea3hn|{5(YDtD1+oSk$2*#VuU04s6GdH3e?2XE8f&$xJZy`a+zz z@tUJZG8udQWt>>>x?_Q?oc;ZZxvm@t=)_@AKF5-lD%96l#NKdH_P*hWU4XJDZNmqq zXqRag^R0pcMSaW6HX^STCeV(@uy1iTrGMu{qpij!bNu(fBX_sM(cN2)wqn^^`Vf=F z|KJ3TlfRh*_oGVfB(@jMQpzX)^G_-@O#Jk=BTLd!{9k11MQ^{#yS;-{(@ZvtOHHu9 zIBs>|K$+gFrr2ti6-`fOhkCMaMY1`1}Rps}czid7C1=7@U#!!J4I@Do{EBK$w)L?&;aGehYl+D@!A5#w_FPTAnTMr65gMfQuCSoCz7$G?s^yxa^$&Y28w4^E>`m^4d`DO zq>5t^y+z0wJ?og~8dA%)N2H2<-$Crvf1p=)YEpkgOZ=}xh0mw)@H&dGY@3OIFqOqV-PD-BsNWIMRn#w8tgPi6i~qzU zU(NA|eqvIN^sSw#^0CYo#BToB$tcr#3~|`G<6~`H;qbML$9dkiaI3C9Ko^q+s1=Lx zY?pbPIJI04373V|$g%Q!tkzT7Wk!fepJN(cS)g6}*)kV}_tzz&a>><%+9(Kz`|U6y zr(DtS)H_Sn4;}*CHD08B;qa?0@6%pH{D82hVfYu4JIIx76)LiSz?^QFJ_k~EzCH*} z1MDVY6_tz(*$$`RUpN$VQOj_TuRQ0#^N=2>y6_%dE{Ds5fHIDd&J&Gn*c^^$v{{Ae zI+7Qlu&v>U*5%B_-tnU5yd$C2o%8bNii2Ate8tUs;A_gOD6;KoDAXrjOsaFJ#^vL? zeZ06>=Xgerj2+*;D0%Wvh?IlY#q%hLpN7jylBN(eexQl$(J`N|2PzIFgZU$M z*+pysN}_OvZMvw^0novV*z6ehd0?}_x+8@t*PQ_V;PYD%&wt#IJ5`BXA_9@E`!P3! z`~AvklN_W{eZ`1NFgDBASs(0S)qw>ld5#7phG`LvZ<{y8{Y{zDA7 z<{L+zY#g=iP&RJYCf7tU@msBq%|&t;lgoWFXNs`zbQ5xs9F8(hEs7W?-v3UwnPo&E zbxuwQV%4|Lg!fHnICGjFLX6aSHYk-gF`B%7teORjYTE&1mbFk9=GVTA5)uapY{eGF<`TQRalAU8;c8wQ(FYDbS zWnMQ_voj3=@%OgE{@)ZJ<5sr&Du!^y72Rao%@dHif1Q7V5PKJ^<&LUrB2rIWh3eh( zN}nl~d=i^<)j#tNapO+-zF&E6LU(zwL-Ga9w%ti^Funr}Ok;UHs}eq@bstPr+SFta zB%_8&KU>BybP1d~@OGq}h2%8c$13Sq~IbGb}Dqe2-Yg zvII_UJbB4gt5Y5+Gc5 zcTql5UG0lS&OU&{BRs;XvcTN)r;dvg4e0iG@#61}nX>KO@ZDFoy$h~TmBoAEz~D-X zbalXOIZQoCMNOoiro#T5S7e;^oBpWl{Xt$5f%*(53z>^LNh)(C20^^bT&DLo(~!;U zS4*(d?H!X^l&)|b;L@jp#md2Bu>)|C_L^2{DQNBhY1T*HaEagIE$>Rf`RN~*zy>NJ z_=K~(;}&h&Ca}Wm_VRvxU)2<-Ne4%NCBvgJU zWb3SWk=?2wL@XJE`@*%APD^OEM3ha7Hm{s`QPR2~Qrv7+V3*C}dN@)hKET-VTn|U# z=(kNl1m7I>6g7WZyxCN&y?9McX2}H2XiZadIRl3hVFe@2tKXdxR;ync$j4yCY*V1V zX-pW4X1Nw9$4%-9ejZ1V$`#(@Mv2w z-0Z|YAzpa96)2kpdomIKBXWp`uc&q_P>1MLD<+|F?et%xHDR)%LL``LFv=J&$*Sw^ zQp%HPia}KDUa&}}Qby()sa_qT@lM-m1nKA1dWloN$usbVZqcypoW>ipCe>+be5=a7 zZDFBpvVkGGaHF`I#%VdJQ)aTL_9;-yFWGE5N_}L5=WQG%wZO?7gk~_unNFG5fSrbE z#N-s3kIJIj7wh9K{|9AAJ}Tuecx=aWRa-caf+;AgNn@RpYp5NcTDJmIk1MKn+( zbu3VRIpm6kZ12?gOlXP@baf z**&bXrb{Lsgo?vOq;s7M;AD<>J0u^`61oZnc3*`{7Qo@~Q>)_+OaOFmyr_yPz(%)q zN*ZD1wJiU_D8TngIJh#hm%F%eHpK_8dKZML8mev|)n3w#1Ks_S4k0eOW%m4+T2@QA zN|KKS`5Im8l-L4w8a)Y#Yjt9OD?A|U2P6FPO{<5$YLuz62bvN3h$~i5qxDMP$w6W3 znrLu{`R~KB9>d~Y?kR^wNfPGVz<=+WGNdt=T4F~7DhG6R0jUF!Fbd2lgO}Iny=;zrS0-I+;2KsSl}Cm|DAO(LW8Tn^fvV zRg7sEHiPpL3Uxa+P2>F_5*(uMcTJT(xUM*@#lC+a1N{P}RaXh?Kg!nVYVvLr1? z*o>_xB`Lx$OAHw|7MdKc%E&$u+QvT3xz%rk!c0=|5oBxOo}v#Iw%-Q=2T91?`uJ z5?&mG1MFmaF9=u|=tI~(=~AFZfkjno0=QkaD^qJI%Q>i%qv5@Uopgi$NZfavr;jtz zKyV2CI_{lP`*6UzFcqzvXQ`L_?ULx9m~l$Q*Hon>uBB2I?i$ zHm5y0Rqd7BN{nxx0w)YTV$C9NysRkyt)s6fNOu(N#%2fWs_YWi)uX_tyyMbz*s8{e z2XMy#MRaT3O}p;YjaCqQcN|cfFIg-@j~s`mMy1Aytf2+aP5Cf_$*yvOzp>+GmxQK6 zS=kOQTdyMT(%CpBfJI!LtjZOQ6Yn)-!-`h7GbXrlkY-z^zYZN`kXBGR_ufDeD`&V{ zr2DKanA7x4q-KxthGNc%^Uu}pElBbq1yGouo{9G!d{W#*#5qeFvFsUGS>?Nurme}_ zl3FPltn(fD9m5_^lvR@N-bIzg{wcV#IN(veAhxAXCVe|RW)Mj z)TUx<4F!LDnz*r(fgkN%HM&5)8c}n zQtRBQvyI0%LquT__N$eXu_xh5BFBS^)rb5Afhjqv>b$|D75hyD^bw${z?9L1uqz&= z?b0QHK4zzMl6>G+izF1Z&gif*n$Na>PIVKfb;@tjeQ#`!%rw3MdL9RV+ce zg1vwm!Ki>Gb}4qwIS3j{tY8vTEttX@6BW};VpL4tH;KlAsivryUW{UTQ!u?)zWbit z=Wrg(@B8Om*V%h^p558q+1c6MS<+_BZxX4_q`^fb!z5v^d9#X6&rb}&P*s+KGo9QJ zkp9JP`>9k&9og2m1Hz&<31YJ$HV)z~2?^)E&pb%7r#FexVYqDfCQiYDJORtWx&^q) zc4&bUKI;huT0hYJ^|`?M!2>!>PGdTlSfHPsNXGv&5X6PxWK1UmTy0eL%ZP2{88Nno za=H!XaoIe?#b#u8L@!7mpkDa$I+Zusn5@(A|0@)rQAqN5l{W=DW3_MH zgk_HBJJw%o9P8?>LjqKGd{A?h7@FKglgnk&s>i12ETZ{6{($F*yqM91>sB#X7zO#sSJHik)d`iC7a_-uE4|SVkYQtk!KY)JV zZLR0%7y*pIC|#)l(2wjXM~0>6*L$07C$4@nI5?)K_Y2m?;{dg$_ z7suzXQ^)fQ%4A${*;|WJ>)qxn~qS zFHE^G`56Y*Ok?J9G5?GFk5lIswDwY^y#ku4Q7a~+F`k6Oki(dLnsmSF9-?YmAjDPO zFOgo#_XZ4|Z)O=Qmj(U}zRuaEz+4IZj#9n7x_Jf*jCTg!nbt#9JY(YWtpxrDOFhvy zT%EZgeS#$##J4LkO|=9xe_Aw$@AeukG*=4n{LvYIvud~nG=IU`b;12oJtBPyPQy-x zXK7r%7QyX+|2A;;$n;zt_-|W3b0V1P<{yC({jWD}IaT*;*%FU<(;9xMaSJW!fll+P zK?$fY)>Ds@;%uwM27}G`z9b~r*6)QU-fNa3C`kC<#xPt84wnV4k``Hm|5q?C%`1pe zbthYCjwceAAdrc(K-YjD{ZBMWUY9JaaHD9IT2ydUnx8eiCfW$HHY+_(zgBL0`ukcB z+(+fT;?qoBbQR#TeHzXOtxuh)2ik4m)YziTjrMP*&de`(R6bG$sEG&UFmW3Z2Sjj= z(Wb!tqmyBBdWz4qd)=P7`Xj4A*-TR|(M$$?VD4ygh1Xm|2u+ae<*V{s1s!$2smeCm z;ARlD@F2=mrOzXJIuALCczDLZD_T`A#wv2%SoBan8f$mSdkd^DUDN;Rga0>!e0MiA zd&!c5`g4yh>yf6aZb`vJ`2_Q{5+qo;ZZ4QCiFCyJN@9-PtLSHVGyL zCU0qhenO8^Kx>m&v$SBQP$nX=jY(V|lZ<;|8JL@FPsIfoc-~B(0CjB0Z2ejUFO0c4 zAk>A_);AU$5NWQSL|T#`X$SdM+=zmU>}+v60s)PTO^(;^$NH``Q`W7>6%?&-eDWCOTx~2UG6_fOym&A{1YmtxL9{k))re?p zCqEbzql#~Z9g$mRjv=!xB9ZIw<3Sjdgs$9cbT#iGbVM$d^m`BjBi(B3Snkk7A%|}Q zWFk3EtTQ=Sgig%iKPesKm)uHjGxA~?I!CMHw-xB?qqNMrfK6Ef#*dJ0SYyh>2I;Dy zK+N3fxCz)DD#i1J-F1-s?F9=(@~lEM$o*G#FjXj;VchcUx7`38d8&idph3lB2sH{Z zxoky^jaGl&S)gwYQ4n#gR`L5GSj4d`yj&%s<;UYiNK8H_1O^Q|z-&3I9fugv&N2_*gjkuVr2Xb9bO8&QYN@fxA zrecE{MbOJawo?j|!@j{`Kq(m;O1{R1>KU!V))#1di(l)7148Qy^j%;!PH!Cf+W-e( z=3@m8_fYC1iP{BD;o@j-Vp+jLw zy+#OZh(RE|@LmXRBw6j??gXiT(guYZ`LG$JTx^CkmaFm=xJIid;E^x}e3Lp+tb?P~ z`TLDqpUp5NTFrZ)pjg<7)Gr61UBqM^c+?4 zMb-g>!=a4S*i0Fu9r*3PHq&sFW|L_qHp>mss&126&TycNkpDsI@}I%D)tt1TrSiMI zK=1Lg@FO9UZDTxpzq{U}qSdm^1=D4t(_Pt2(C0txN$;*v7E5_4-)dXdNV2g;*~r$f z7cQY)-72HWu~%|{+QJ_ZhE4m70bbe5YI{QNFOKCG{TJa*q-3)VtZ~S8xmUx1bt6(& zy=rvijlj=|; z!B);SSUWXunx{VGcUa;)u+94SX3ob&>Bk#3^K{Vr+nU+kEoMZksXIJsG&5SA+EIYF zSfTPnn5_0J)0IvEiWs4E2|F3!a2pyR(#K)dByEJrcFN#+CpDoxd?ZY+E zVoZI{6_oMf;}semOTY^^xjMQJTS4-IU3Lo-y@_stWfo=W9bMbHDM#Rh&sg0XSfu3^(LK)>wE4e? zR%oJVtTW-Pdv#wyro3+;4a8;aO3EaxFnxmh?PIKGY;jChA@8=dWZbS_+4bu3;w4_` z8=2rzqABA%O2I3)g0nuQ7N@GkUF(aQBa#{gd^M!MzuSu8zZLoS z3{G~`UrQC%-2cA~5T3U{O#K69s^`dEgVg2Yn?{@zLLMr%CRhY7Z6Y=P|fuy$jqf424mj z8jGtu)pe#I+)}&Sg5G|&bBxM90u?hTYJ%P&l+v*C-J^+cvLLr*n2P9ytxVf9BWpE| zZ~;dQYx;-QAvN;)WCSN9*Te6diGf3By@uMVX}PUooV;vuaS&_(lr}86gBsBbZ*Z1H zEZ18=tVWpy6~9uT4>D6!8?Dq0pN+)Z-A|=r_j-uq2GIaKb3hGnFAl=JYBqJ~hrJJZ z_O8Uv{rdgOd{yEE?>6>A+^_Kfwc_qfKULk|ftXdt3iN2C-|PYOhu1w07?jI@#2&vX z9sUhgHzCIO8d-2<;G#DQZjwI4GHo)6Wl!{lYw7on@LqXJ??GJzBR#Zw~u5v1} zZ-WTmF&k8DrwSC}m{E~rza8w4Bh$9mBe7>r5%>-?BA~nF5rOr-HCy0Od~Wp(&^3ii z(!*8uIiq|@`WTeK3A4q`F8a78#g^kKFK;{-aU`CVDGIw%r?yw~ntO+cu%#>%-d+nzc&4wPDoF{EZm17R z3lUr3S&Jxk(^Q<*yj(Ec_I}%QfFm=L;SM{ttc~*hxFA<@vK9WTp+Ty+nUOs!@ITEj zK!xn;8=wVHRx(-lH45-~EIK|heT{wV1$3TI&1NX;^CI%2ZQ(BsY|%#U*&-#9?u8Ma6u`l zT?3*cHlo^9STxx5`O5|`FV765;==n7o>!Y*v9iaVEm$s!I^+-Vloj7XtQe7}L_smo`_rKEXLLujajP7BRi zcg4@h|8{EC?HD{lE{flFw7Le(?mr3asxK6{q%)AySu3^Tp2%2x$Na*A>V7s(f>dPq zQm3|8C*F^Yu=Vk;G}6be7{9jYk8REL)gy_MuzR3>1N_1yXxh?I0lv~I0AYhf%C-?4Lj?!dq35>7^?E^pM~=4+z`xR24SOeIEGe4jrIm{xWkp&G%ZJit%=L zdJp^jRcArj+cROGvV+(zN?d@)@gI+KVLwNgUo=(=O{rh_x4kNxnyl;mz~SY(UKHuC z1aSuIN)$A`r|s|?K6-X)GR|UC7U3<7m}vC=4(P}{6Z;IU|1uE_J^`? z7j-J0$Nd9lFZ8&!DOlG_8p7J|vyQD~B-y&_Wed(bh`CGJjn|Icz2l0;P4BQ-tN-by z{F*v2&0p)#_wXq9Mv@q)N*X)#l`m>lyb}k?Ktxril|s=aG%*^9Lb}$#12$L&8`l)> zUf5V*edsq8CqVbsbmVZLO@SFZad!4WBl{@b@%d+X45V1+5qUU8^wr@DH@0QB-JTU6OABHOBM%Og>Nex{UMXLitd>c3Pg1W=>d za75I+g3;k#JBe&kku}u9i+lT^Pk3?-vmn1;TbQMzZKWKp@qTt&Cqk#TDPMzF7GglTGb7w!*Kg6g-=8x47 zwAWnAinFQvI1Be%>toctt!D8Se)^1mSDQvn03ywicV9C<_0qiglzs)H6qFP$2O z5d6Nfq#tF!ZrEGn#$s`shSF>tkbNw!)$2O8cY9D8_#OIoQx%;Y+FYhC>nH!)sjy&F zNz^>A+X_$4ko`5!HtKX|hu-Utc&B@FA5Qv+)InA``bWGX*xN_qq~<4vY3=U zAL7wf+!IhLv~aW;uPjO$=tmdAlK6aUx}m32N%*mK1_R$aFL{97Eir^&d9EXFn|cnI zp*j_dk;vtKYR^|NyKKC!*Er0Dju1=LVn6u9JQwW4EW9}0(OA8cji(=~{xl0jN;TaB z*PGWgLNct>+~a*Qrw&8!O>Mbc3!55axP`kqEpN$EjkHG)x&dvK=zvwXox4k(ll2He z^jyy+(@6Orgm~pq(0ZPfqYRJI6PBle_NH6tnFcOC9BLd9HCVC>%tweX4s z$!0dQX_IjlUj2F$jx%}AkvCdU#QLTk?OkHL1(lqH46vZgv@7lj7M}PL%=Ih5O>t)i ztAD8~)#Jo&<-HwR*7Su>;@@Bu|1O*y&`21xc&Af{=^3AlUfjo_&uXy`OtF}!j*P^4 zA5YiwR6zbGF(DQAH8P_8G|ft_*x9+2>UWY|)pHo-5Kj*?TEXUH%!Q5C-;8x?$2|ET zq>jIbdczl@tz9Dd`Xg;s_5fqjaKufAMKHjaG*nPCG?g)GZKas0A>65H-%=Da-d4hs z*wX&YI!oj5L-dY9q?`Yb#+VJt?1_;^W7uZoIe>~nn>AP?&PD1COOjy`crLvQx~aF) zOx;-*M>kbA6jpb3FAVrqcUjh$%`$gtn(`qYhPlh+JH0f_Sg#45UW-D3+o{~a%_8b2 z1NtV|cJl<)7vwm=i%`@fH)BW4HnSD$RFv&VW=mBPgZ^g2=Ue!IrJS^2Ey=bARsI4@ zRVr_c{~%+8VaN%_A$Jq8?K{&4O7E8rNj13;vCGmOcMBQ+FCy8-PF?$}j9o^9v7au+ zNB6FWPiRnFv#u&)s&NT2#}W%VJlvt37xdrUjHBTru~?~Dh6Py7C=b=JRFL*+P)981BT;K zSDTb>v7q(YX2r?l>9<;`b$f7QzIvL66Zkd@dU~`&``f5L)>x_8|7(Fqu~@=EBlx68 z$H`jBu&Hmyf@F+C-_ha!J9H-Wic#2_vc)94Q$y|5I{!|lMnz7usAP}23j!JEST6&Y z61ZE_w^Io>I&|br*83iOaJ&O6pps-PK=={c6_>RJPJ9+KwXH9#6QurT>Uuc3CKzSP zVY{FVI;r8!+r3_QrM6*y~o{cDC+UjJu5bkVhGPyt+k8`!TXB1*ARksmS$^1yxq5Bu%2NVk zBV>(7*REG_PYbNg3U;NPhCF*YwkPZ6nD211e#RhqbB&&0;x3k!`ufB#El?54$<{Et zExhU)c<%(qAkmtPdS;J>?_Sv~-b2n5k-NKAT10E_(CV^*a@lL)gPf+5Q>8qM)RT8& ze3rgM&w70mdPcQ*&LSFcXEMCdZX#Rn`>#c`>`vN)B;p+wDIYt_|18{@7HK+5)^dYW zQ#`M6>>)F&4SGRC2xxE@U7uI6ei1n2+g80(SXr;75ilJJ%66*ab@M%SrV7`Qt49Op z)`)OrKYK&tZB=cMm+5D=0v!b&IVutbqM&TE;+uey9*3K&x=WO(B@Y`#_|t}cOB2ET z{rBGaw#M73t-Y-wX6s__0GA<}Ffr0o!P?&cHtazI9ZKFg4{`TYTH1dCoJm)o0z*Gq*eRo9FDGr$Bn*UfSt*82Rv!;^#?ce!| z1lbP<1ghaH0=lX2wGJJdlV$kYU};Ql5cuF5EGE|(_Y8IKw-CX`2?5Cz0b+BmGn#<@ z?C$_Ymcw0?Q_?z4`EJm%XmGycdrg#tU^5xMs(8HvFXDMJ`9b5?|6lokv_4W*jtj2; z2a}&Xh)hX#zvoP{8$#zmv|>(+p6Z2QTp%f(Z>|}Jq9*5=UqRvGflMtX_U>AWDgK&P zq3YDW5*$g*&28`!bshTGy$*doybCg1zzlw$!zE*Y41SjXA!^Ul)4U}Zq#)5ie(9!s z?l+#d>+t_q6ri-PU#RMTpJRa9lWUY}S3tjM2%(+%(n!KXCLAN7-vJGr;1wZJH|_7y z|ItmIf57ai@T$t6Aid$5R-GDDMS6NC;C}&kZb}XKVwd3*8sd2Mg; zH18FI>^hFC*AepY{Nqk-P%-0wVpPRutoR3Rc1+@eoTYChX}TeJA?O-IQ!$bcF(tUX z%Ws{d{c-U?uZ?M}c0GuVF^Pp~yUfry22FlsFkZ6(hW&LsZ6*HWSY&Gg4EK!7hRq3A zrA=Ml^K|vjPl!J+MmU0CjYUIwK>^KRgY~DNUAxCad^BHNXizWsRf|;%72Z} z7I-!8lhAf~rpd*`MK}o%e1{C1Nnew%Wa?kp~>B$l?oRl;=dz~MmFK##1ESuJ!C44A3*y8~wTllHg z8aI{0)9>2t!LihaH8^=PJyhjB=Fr!RqL3Vb9E~1^X~&W49(ZkIRO(|6moy!z+UchA zAK}$TWj&103!p~aHk_Si(+vYfU<}TQ!^ls4+&Idp=y6{NiS~9^AEolq-TiFgHd&(k2r$(t&kFDsh=AAa`r~*(Z zpcPw^A5`Jvb8u>QIL^#veKJYUt(=y_V$`^&9H-nL2nYT4t@r>`L~p)$DhLNcghtw^ zcb<04lvEZr5~&xqq7UWnH0qAWv^%m2Q$BzX`izy#J`%;`(~lw4LtP%C)PuW-ncO?Y zsQ6uu&g#Smj0VeFwR{gvU>^R(L0iCjEAEaVCiwPJNkR6-sUNEyVl9hsW!a%(RCRnJ?kf^jMYnyy*p$Q4&fTS<)EyCZ0!d zFy;{PFh_UvzL&5qP`l5osS5cFuFW(1plj>W3KMOufY{0w9il2&B=+mt$(p8oinq7Q zn%)Ybzvi{_A~8ZY#+&yi7%L>jO@|S+&x|M*rl^1rRx{e5FEHRV+g z?T6KP<|_`De1$nP_?5CV@Uv_=f~d2!;6+^IGCW50f7Q_P{|%)5X-tz)RrrdL30aIl z9V(v0dQdp9dcuJV-jSGVUvu0h|GAJDg;D>yLq|HH63vWJ`(HOol>f89YU39zho}?# z5Coy7SB{>ThXTq5l=N-O0V?*WF}JTr>Sz|}pRbX8d@SPl?TNuE#AfD?iBUms8Z&YAX-{Jwten@ind&*eYmhqkrm0LS@NujR@$9L~N80Zj+{kZ@UhF?uWlh4jBHlKp zJqteGU_t8eB)p?iSvxrqVNuN`o3S96K!V~iokyr0bC>J5d(sWlflb6W&+bH!tCC|J zbDmG+Oak=uXaxS4Q@TJu*5Ot*lgV-8S5P*6Z${uYTec}Njmvl8Y*vLAqU7WbF-LRQ zucv}xP5&m?Zg?~sS8{WkAkuAalYsIyxRhUVIS(#+JXt0sL9FlsRI<)5C`B!sxWGO!JAjl`LM?a17-Rz}s-Pi)n?(T0FxGu|xic@|4_; z5O_8-aGoi3fV#Xk)KBI3@l{~F%_xJHtx^=D9zNv=#p5uUO;yY(hmLMZ(G?4#kJ+T+ z|9pHZ^}E)6)TxcQY|SJ6!h{XA0JZwuaj(cK68~@FS1W(OiuT9T(111%!a}ggdltJ5 zKYfD|ht8Ulr0PC0t9u%*i-2!`7AwEf{XWgr_$@2YVLW3bFV=XJn!IHNf>|{()(MTi zB{8aZjl&@wom(C^gLL(CyhinLW3Te(u-elhf3}-IQ8X)-de9)&3PS>gBhrrW;A(i8 z#v{sqLZeIQD@N%On7VMehVg*usA(9Kn`Uc>&9(y2=Jny-)XGfELwRqScR0v&C7?6U z;Uf69FHK)%>MBYtd{>JeOx6*P+tT{sgjxcO@4=0oJIm!#RSNy`_Ge z5KQirqDFmZzBUc{*5GRu|3d(nRS%%*dyXwYTo2C>EVly>+udw}I_8J*UOjDglHKyS zBSw|};LuUZ*c0yr{r2Z^>;uny=_kP44SAP_VpQ!`yz(WFaAkcvQ?ELh^=_C6KRII6 zpcj%`*Jqa+vme|eu->cU=wAz`;yGic(&@7<24}h*I`j^^;Cg)S^A|8(tT?YHSsKe5 zkl5%&Tmk(DiBd9>mLautQddpNr3Fbh#;D`9hChpUFQ(@@kA1HDkZI40un#J)+>E!L z9ygJHY4F^SyVVzr6OE3W2S7S{AG+|B{Wz#8kMh`jVdWafOmPftSN*fOmBFJU3Q+$3 z<_MAVi!nbbi%mKe`}bp-`>5rko8tsMgQ!Y)JKJ>gAf}uBm~O;~;R_xzyy;2Y%nlKG z#4l0#{f6Of3(fry_*V=Kiq*?$iBe2l^0Fg8%)Dv8!eWv2wh-9fFWRCR1Sp^4;K1@O z2-R-mzZG!!OUbe7;9qor!5JOIF+ykGHn5g4QsgR_nL28^^8XD10zju9o0K9@%U7@n z8*;-9hSyDp%51{oZ5JMD?-$K*)#Hn1t@LMXk5@kuqt^fBSRw4#WRH^Fg@CTHqPbOZ z&$;O6nC^uqMgExPS>3j4i0aq2YkM`kWV!A^w4NSEg=!}@PvM|%uH28N3j2}3pCJ1) zFLfHK{{F{|>P1N2fj)fEaX>mE``nXYlW`E;bI>KzJ#Rk%*TA zdOAkAE<0Wp22T9n3DUen$um{%S*WX=f*if3+yv+uK*jHbZ#02eJR|MyV(B7Y3qxS= zM*6J|w#06xel`zpDWikH!~4rTJ>jsvdk_bwkyBfX?BSL2KR}gTzc zBKfeJdf=x%-H9P=`kPae)Zyki+KePs6{_Xu+cI6_5#IvmUan|eHuMoZLFVJUMasZV z@iMvk_w~gA?v_ry8N%u36|hcr@aQ( z2fCJ~7eNz$-QW+LhdWPTTbS@SVpOZvPJM_c2INOU?)wH}B&Pe#qI=|Ror;q!XAx2sY;`r+b>GoYtUA;NAA|c8 zp%9~tQBG^&n8h%$ty5pPB*Wu6H9$>z1E<7MmOd)?3*##0Tz&$0*&CQ1g7ap!{+9>r zt2K@bQScKf2HKP6nS!h*v4#qCJ|Z=uR(uzvXV9#WHzVk&(2>-Fu{Yvi_LORrxKTQAe)96TneUy}ILM@E!SVY?d(t8{~`x&L>uE&sBeqY>D^y z&(t{L-NE>qZz4b!fx!>Jla43P^}tKtn62YGEdc%_?3`%liy~+K|4IJ0#Va})6PTyB zdiseOTHn43_#D`k?re5YId-GU^f|gm;(0*ptD!B|&BFS<_>3b-hKPuS9k7;F346O! zq}my4M8f1cWINrwb4N*)Vledrnmf*^ZDU$GKQpucYX3wvEfLMF!+#>Uws0rGFCwVB zEP|R#le88T0&2luNzllz8P)RhM~B=d!UDDF%Hzf4AzOsI$o-Xh#t(pQP=!0eL_|7Bfh~h zQS%ej$^U4ihssVdyhwkMysr3W^c32bhV~LtF}xbJL!^XrZCke1@3%c%XyS&JBjaVL zgr3e*Qu){M{|XtcYSreDy8QN(2Cq*4Csyt6ZInBws7A5y5>4$iOl>%WfK#@I6|Vsl zIjz$O)w8eBES&7GMe6(yVW6g7PxT#)rMT^aT;o_3($A^)f>S}?1oSl@Vmb%C*30D5 z6saX|G;hd7WXD|1fMCh< zWe#@El@NPUd@5Ui(EAj5;k6O>w<#R?Rx0q;{8ppD3=ynWEEIPeCJzeCkZL3N+IXWs zsk{P+B~56aq|0#PBgCPnGegD$`*>T_XP9$={AcfNC;tOgZN+jN_lv>XblK_7sRDBW z6lmbok6Xm3Khw>IBMXn^N#1Y>*!L0Cb;}pcTBwS^4D9O27fbvMB zU!ZFKD@L<-wlmirML$&I9jsvZom4_Licpj7?5=F_-NQ8D7~+^qJj1PoYX~QUF81|T zMIruhqmMLda5kzC0etW0$+@b0lo=E`NR8B~@r-ltr$(KIH)i}kJXTfn)fgS)F&ji3 zHBl&zvBKD?%5>^$C1U|a>91lRG|Rb0y37QmcCu2#LNfZZto@W<%v5RK8v%E=VBa78 znyKoLj2@(czbng_N%|;|Z9n)5du`B81auvH*63E@*($FKqUD~s zF<+ap>w!fhTScqzJL=yx<|dl3E}kR!y8}R3|@`2>RGBlg+0Fb%rLi1p!I|UifA5_3EujW)RSX?H;Ql zCOh?J-JST4H(V+vJC954=i+}d>visph&=V_6qAi>h7{~)O?7HtAJ?ZnNtQIB^H8<; z!P#1`(ihbW->kk7zNlxWIY+j}6e6CH-XP`@eO#j29_Sybt!r)w%U5ZX_aVD^Uj{{K zj0Tp}n+8_jSamtabTh7b`{5h&+xa8pJ2vn=T!8WNdv^|8G3TRg=!@ymiG^;*IDsnBZTPJpg8gZA#&37;| zmwg%J!Pi}t&uCA0@HrEr|`jw|_0WpQcmvX%{IS_n@*_x-HLz zg=?(nRpWRi$I;04w~eESlpM#PBRib>AqTP=+#`E&d-In+B53}58MYY_F%`{ zwn;pZWve{og`f+Mj@8=&QV$N$NwKP~&JG6Mf+ z#g;F0&X&JisLqBQ7n+4@XZ)W7b{kjGs$CE1aYsRVAeVb{**uZY;Xu>~zne1Yw&C{+ zV%70QX3WxC;2>3MOh)JW_to5E)@d+YB^a31OclRANAG92hoFlD)sJHcmOX6h*abWb zQK+OkoL;G3;+({yv*h!1B9_=VJ|#lp=ZlaUc@DQ^+$Wga@+$_k_Qjyi`W3y>w`OZ0 zQMB_hN|qW`z)`Y*Vg%34*Mdw1-`47_R?iotCi4iCC;GoT2$%y7!;e&Rtw51trAp+Ghj27fKf~> zJRyl&;2!6J5g}#QER9vqlse~11CjA+kmlDyomM?)T9gcL(W!x|aQoWtTlK{xH1*z#MQxtogKJ51@gfxUsB$$j51uKoa;2$ZEO z1Dgx)d6rHfve~GiAdjaG85mb^&U*I(f93*Q=+geS+2%e#mxuV{K-a@qI8lSkDo}Q8 zP#7$W=U?s{h)r)qOsl@zET!2M9$-=Hk}_~bjB^M3f_#q>Cg%dTQ^a$ z%4e;!fC?u9l$&DJs(>_IfwS`Q7j8JteqG?>VaVtpu9eZ0<)vco+L(z8Ev{$B5) zLbf0^;ytfmmADVKeD$lC3$K1Ea;qkSV~WTAY>m4OBVdDRT=t&|EWOH{0jl&D449ft z7zH-h;dX)b=}l^|M+~`76b>Aeb?}2lZXL!HX;EK2imwl}sWLb87@(@Jpyi;RplzOq z*PoNLd!RhdMp%8F_rIyOCy+PX&;Q?4+YU@4_ZcIGqI(jgInVnCs)+lY6XoL^t4~>} z+s5F?K)>v%{p|j!r!|hyOqModRu0OYELKuVMkrMZOubt3dJvoK8O;3;IQ1i@{J)E| zKiX89G%;1$ZZvVZv3Jy+(9@=m@eM`2#y4sTIztl1jb!vQCZv z7q$2bApbwHQBZmTBhsz8-3(vVxVz*tb;+p~Ut>A4$7w@*k=u)p3 zrCU-lzJc}Sqh^zeI^ig3clXIaOmeD1Dg@!f4)!Lo!cI6A2VK};c6|%O@G&!&v1h!E zqSg*+89_Ga8J;J_-$Cm??$pQeNqS8FhpL>gd>^&p`K8@$(fy8tQT|^zaFG2BPB7UH zvYv=7-(f88ao|%Ot4en`H;c5mqj!?h`aAhXRrVb03)>@k?_!1Zq*FhVNAJLUB<+E? zUs+Y{2bJ%EVR5C>*~9L?dEcTLb-A9VySlP>oSrPWUGsrOQ}eV_U+N|I53ST`LxbJi z_3CCk;Eyc0)kVZNlfv>H6R8L5^|8iBX{flLao{uSCsx+dUB=VE)K9I{#GsJ=s0}Xn zdqyMv3~Iby=bsotjmjK|Vt0EEb{9IUknV(qZ+5^?{;efz4H3n?&#Or<_x5KBzB2 zBCWM$W>`O~wQzjye@@+39|oJR*jFop{q9#)g|iW@AvVm6sy47t zw*|G~H-Hkaw2b1mPH#E#Bn9Qv_HC>h`GRp)49EZPKw8)+WiSs+!rb-@bNM}>-EN2{ zxukkSk?RLQdw7-~_>yi2+Ugq8BTq{(xkA4DG<*%J^68xTqR{l9a7Y(F8DsOdFAHbw)4Zk#;SpbocjDQ+5QIW z^$Fq|9_BG&2w!BE1xR!UAD-d`0nYyae0325s2Bg;Q0&Cw#F%) zwBy0|3-iJS%k?*)v_}4TzsJ&4A{EYO|L{8r=@}8K<(oL4P_Z6Im2F$%7h~1rmrcPU zkl_+DY)H=tDu2ae`{goIkF4t4Q?0j>*`2R2*|%kQrYi3aCyMRK%m2ioRX=Ln@&7pF z)=0zVIY8U_wreyLq^dJuq+Wf%?OG2U+wI32wppAiKWh5z z4E%2n?uVPDOjUQiX{flXjz|hMZ#wmzL+-YDfy%q4O^Vw5mKg(2Ak{lg)xG8XNp^lo z<%3Vs3#oFKwMOjCyIKdTkatYS=n1?fxUBBm4yQ0$q;w#JRp_@XWxdDl)k?$3D(;wB zbaL|bWj%67X2hvJLvUBbwyyOfHb1?^b!v7$G+kz2bGT+P^7(^XBd?SqJV|4U%!=W9 zUG@o&6CQ0uO3MQf->IZ^v~u17F{Wv@kRC{9(s>s%|Uf*)%y{}IUZ!2kr(eh<3N z_HJu1s++yCbuYE@1m?n3({O6Ub~m_##)H-2b-41i?1a;s*Ddv3NVhr)iBo7#rrTzXD8Tu>J4_eY_kTEQP8Xgu5w{CEu?6Oj!KVHn zwe$1_19$T}BbU<`_cw69w0Zep9Iwn;W){sfAG^e<{hyhIA6E%oL0;*b60C1R@yM-v z{<$trRh%;VlZr7~{^QP;Uy2vioL1Fj+6_#y&-);j>SH*4XLGE?016w4C8XT6vhk0N zQ!7q;IOF3$I$*yKj`*4n!1Bcl8#jzQE)UGpTcHyv1K(=-5yi<5tK!?PT z8W2jvBk?sxv2vR+i5&Kf!#3mcFB(pv-N7c`AG2%iS*O13!9m>vIk1c$y&B5kfIq~9 zjP)nGlF2W%QL7%BpY4I}lsJ|1rLjo(?VjLYe5zA#^4G7-OEC%cic_n;a_WPqtZ45z z)#_{KLi^)oed1K?H|CHQXY9WGcB>bA&b-y^3_^ojXb1LnRZ|AOQ zjuaz>Q-_@Cw&IcYP&Hm98>wLLY{5HbiZQBG!D z;ASB!=Q4=4S4xmN*xIEv1lv3tSu5J5;9kC4sdYsQ?X9R$IiA-;Y%Z~mMSkGU*CkH?Kne2@SNtlw7zBPOr$mnPC@EJ z<_tGKI}53i!6|ZOd8Al1WIh{Z7ON)lL=d8%1iG?C+|hvMK)jcn#X?Gf%(Y0XZ^Z+( zsn@s$h@CFum9i`__>JZLQ_NLiTgP!wJO^_#;0i>!n1c9aX{jOdV( zKyO|H1G^AVW(VjTarF64HK#sV=Xua+ja=Gy&HqIp-Pi##0IB{KYg-(rBCc`ilUk&j z&m#Ff(IG;}q$VU=VAVwB&vq3^$>?^u2|SB>hIHU-Vl`>+~+Gnj#gnQID^3N*oI=M$Z9})9KX4ZKL+yP1o68uXZsnPEqo754?ol5l>J(;ZuAyk8+i!*OXPC(e6|jm{9V&+8>Z`# zabrXZ!c?uzFpX+!xeXd`qmIqa2-J*ic$tMqM5g>3zR|+B47PAiQX(!+Libwu?f_Sg zyJy@-DbJsSo{`$t2%iqS`VL*om>Hel;CRe~z_U7H7RLZZBqKN&q_k9An^~-~xll}QOd6Kh zLs$g{x%80`mg-@!$=-mI;t?}U^RokPK?e+W;ksOVm)=08XtqkC&Prvo4OvS0t^TOhYxfU*)hr0{S6jsW8JkZ|RZ+1;2Vqg*-; z5r^p>`QJ{RiZV){ja7;K{#{^4%9c*?gA?FM@eDS!7pu%pF1_&QfPNPA`CXtaOFNq; z;XL=8PVJ)n$`QSzViXpeyfGw2p)cpZa36GWVae>JN( zc5PK<@Ae4)MtYmJd%?nM?sn;O5|rYLNKMNQ2~bn!VI|7jWAXvkav!?nb*4+QW%p~+ zC{-41YC#?NlEollNhqF{SZg3w`+xsW}JWrrKzR6mDQS@`+upsvu5Va?|JbhyEL&_wA+)DE-hbN!eIOfgD?o_dtb{ zEmX;yfYP3XeXl=)>mnjB=6Wlx@dI&qE7$&cf%*Bjku&Me5Cm4fYFG0eKo?#NF%eum zpZqZWnfAJCB)b4I}GESAIm~O_Mn0G;1 z{b*Cf*5a)YnNxh*rWf zx?*K>gfCi{;iY09nu;CZl!7$-!P<{d`rfWm5fP7CevI!vliYf=^6QH$&bA$uPhh!CT&PJPB{-vh;bn%M3IO%N+!BHrnc8`WIqNoLLkXAA*6rUP6PMG)*#HL!gL zx;m&)16^`l=L*NzSxMB#?buJhU|#=AO}gqsQEYBXfZzSnK*4HHzV}s!qG+MYr>G^q zic{TEJv8{&sQIe9+H_PAA7Z4QNHyEY86f=z>#Q`Fc0?_}|8GIMIuYx{6KSq>iZ?Q( z1qgj-L0(HU#;SGcxSU=0PmVq>G#~i)apgl@kE^V{<_g$ir2T+AN0MNgSKaI1RFw{M zmD-*s{gEttq~MLTZxH9r<_7!;(8=+*sCOzIcReycnWX232f*|kI{t9iDJd8=$$5|t zn%FE^?YzOI{WVnWwU8G&+TK4iu}1vP>8mP-7wgp&M_e89v`oRu_iAs)g}BD$OK=oZ zEHj(4<7W($kuH7LhfVSeo8;`utDGI~arG-Qj56bjdh<7YHSZrBI~U%z_uqd9X{NCv zS^PiZ%CkKF{t4Lg|6g(C*#@SX{~K_&(O=B{4?eV~XRvX9*v|W11eE%HaCcn>n-}5| zEZZ?I-5c4HFSCAIay$H+*Wn7-@Z>w-N zCqy$AF5l*ake6ebtvN85p(<%vNPCsKBeaDs#fe{yFDx50Ex@|Acgk>8Inn4A=Je93 zp{jH$j`K^|#SvpWW#Y|GkG|z!eHK0j!-<%#Eds@H;qFB(<5lTo(=2SJ3j zWKYbO*cD=QR|pH*F@Evt;uNDzDQ16^cVu65e~D5d%Mbdpq#3BJS_A)KiXYAxPxDw% zwITDWTd|-@96d`E3HG2p_X+|?PIvThj5Rud93uo>2zJT-pGO^lR zei2~S>aSp963~kw#bm`J0gVpRO9ZzxIwI!EOqV{v#~MbF_o~}7I@%Tzosc!*iG~Y_ z&hcvUY@_wLLg)gjX#=o76=5w9q{w93m249SngxM;oyph5tNwEh2mX%+htmU$e1(eg z?G~>R3p{3*7*ItHbjurNk4TNfBG!_c%I*;+3mRhx+Aj3Rfx*UsDLAWgX9gxs@xXaH zGvYOFe7`n7MuLU{)Ydz(5^`&7w>n9*a5^(2l|3NIq?X@>n{vp6-8)ZzMt2RPJcTZO zdzHGohn4C(uzhE*)}-6GA`vk2eC!;c1p8oK>7AuhCP9hlMT+gP6QNxy{EQv=b8lQ z;DAsmUcK_4jMS538>UV{YD5)M>CzAm?59&nd%dNiY$uYZv83}e@Z^V7o|pVZa8zG8 z@#?2rjaiY3|G8k38|Q~tN$AdC3_*T@j*dKX8SN9)zuwa!T)6d^AFtN0F$-O~H>Q&| za(5u^MMRl0as2xSqj9R?awnrAINE1}>Q18# z*nj58e}p*rcdy1=fpO~cKAg3?+N)7u;HpA+?`*y=he7P_4XG&$=QOa*yWv8K&(6VP zZEo*|Gz%+NXrS`pDLyzB6VgQOcCDyy;d%8i4*hyMJc=xyrT4?>Yjb!Mdq4pq09&Bf z`4+q(#--nCW7Z+-Bvs`z3HdrsNM;5Vxs~*7h zMDb*7duE<~0!4-}T^)-_P!rwa|8#XM!3Q3mg*Axu$*Wx*H_I2k4*WM)$5P}C4{C2* z9k#=m%a|>HQE(TA=+(Zw*X2V2{p$- zE>h(nzm>I1+mvB04Z1%r+1PBiA=`FWSU1)3Nob#{025e~4v#hQs(O|mCbNw*r0m=MSK)sAY17AwSW?{pd$UUV1rJ&cBS6if?iF2iqc*ZbD~#!li@PFiOFLtWN1x zL@7wzVfx7^ByTp!h~B@$Y$VYRdI+B_9s@gQ)RX43l-9%gv!#u)@Hp#}t_jlfS>Y}5 zD&i?KH&6v{MfRF8@GoP}8Brz}fiZyV^JcIntMG?FSnzr9D>c=g%S#&n|U*v_(* zjcSZ|3*R8DM$t|)8*!WMk$81zr)fyeE02Q1v9T#Bs$xQ+R+>Bu^cYebjY~lc9WvOq zaI)@|{N&>r3Q!fdR@5ewMMo6Ua|#d<-EJXyjz5MVup+PRIOD zv&wHi+a!|&`eW?TJCUvO#+1IQu7uv>y~de363{am!u#ldH;GU`@ocs&y6P^Co7;og z<6%-FlrDPZDC|C#OftKV8Q`?rgR+x4baO6Fmg91~G%}r)dtm)MXS`!n+?Aj|J3gg9 zNl~aBv-J&0sVgn3y}-9kNC_p()oN%Glx8gKjkMqPPos^>IK zX!+(=VAu=6d2ziFAM`vC^IuJK9j`ZH$iqb`pJ3noAE0TGZMvv!&zr*@96`?`HS~0w zC>8vI$8P2e7&I>!pB5c5FS0qR{=j{Ri{lY4C84qaZg@|b?29ko=L%It6Vc>ZMKg8$ zDUsN4WZzFzhsWkwznAb$|4C4UsZYR|1$lF|JGfB$4B5&DU@`1>=>Qa!_p_%87 z53j^tgl!||2&8b(wMx9y9L+D|Gtf5kcj3Ag?riy~@I$WAB43WgSHSJUWNdh6pG9~G z=~jHCOK{fuRp1d*;MV+(k<$1fDL=>1Yb^hTCo?*zy7Crygzv0*;*Hqr1{1yww!Png z`lvG6iYCcXeB!{A6x`ls5fGwPyWcp1-z1wOPh}*lx{*s8Y*@Vo-TSi3scP{ckF*(2 z(!LGz=5<$Fb!BQw3swAzOTSgk)_kWPiB~bN8oTrcAjb?+_gtF<_4jKoea>H4W&%EL z;Ow2Sf&}(F5qd&U1lxSgxM5g|YDruxLZoSN5;d%O05UrFLStXm@+bqFdd5I_*TP3V zlMzthIwF`@nUZbThX2_aNOnGo?rQtQqx_&ks5gL40SW zG*hWjt-?*<*SAezSeh320QHE+Xsdk)~?Y>=bigza(;;HUn7h z3!phU8(Tc3vmwE%PmS1Ve*A1f37?s>P#l@R=+uE)1|=(Q*1V{Wf7KA47kbefH)c+` z@}wUV`VE3U4U@X&luHNXp}zlJ5(Cw`i9Vh5HG!<*W3;KrJbwrbH}Or~Q8GwOxKUz% z3ZlCiCHz;s@;l>ND>!H7zvVvyFno%$$8!GwjP19%{y|yK&V~70H5ZYivT&HG)+(gzj5h^ zk#r$6Ln&3#e$3huHZ5W@3|d=Yi{{`J@5K5H7ZYJ8dV?$RPMD{axyW@Qb9$0pQF$k*>hC;OD?Xq<>%>xT)%S)=B)GIh>i(x22dmoejdG7d zYAd9MxzOk1eln?~^+oEIO^t(bg9dZ;XXX|F5kJWAM{|gW|NRrxR_j0cwN6leKN$}& z@iySJ&6T1#(Mu-HO?4F@!a4x2SnAK13K=&`1j`z?O;GdBxk}|fo4=j>4^n>Tjf!{|xijcVN!-5`LG~aij-fW`5x;cp(E3If)YEK@a&VC+UtjZ}W>020svm zcKX$9Ua?f2P}ZO3;RHTvpzj#CtJv9mVzRr62zP}@4DOcd0s^(?*4Cz_zWeVYZ8q-& z->%?0uqfp*^}_F_|8Qhoht#u0De)@d>?A#AWH@omL~95y)BoYhkyI+tZb;p@8gIwP z{%QQdOpUQpONzO<#T!!FzM7&{Un=m}1eN%gnMbLm;}URk*vwN@jq%8T@?)QV%-TR+ z2??s^Z&!}03Y-=Y$X3Y(eWFOBXk1a&yjf!lW~vJ#5^&~kQd$$`eWtyaD!p+|Q+2pe zcFXeqg-x5MU7t3-duopixE62LM>p8%k`mO^e_Z+!{(SuJjqV*P2>kDXBJB>0 z9Evb>sLh#+W{JjCL^7bH1vtl?_0{CQ_VDZ}fHD_g!e6#`x%~!0Pawq$uqhdlhb5oH z=C$2l>;*h+K;QsXxobuP)4#Wd+AH6oc$G%*h$k1fWl`z5>BCkpdj+l`-%3oTws7)& z6B?I)*0{48*$bgPZGq(bfvMlg#<>3X<_bWbQG5CWs#w$l^G(b`4N33i7#V7Z{`xKN74DJTQmRVkL9-3Sc_ba;mc zq&J9UlydU9fhDeZo%g@?01WDU6zrTjPieB7E5H zqsV9b8))|vdvG5v!fT=ya5%8c1a;b{aJJBf;eQrs_xj@cA{M^AAIO`ml?E+?Y(kBW zW`s0KQxQwxXDAgwBDi=&nP zg?gKM4N@nd7=tp3T(i{bFTy*hvg+`rs_L4^35ci-N6(q1)AhPnxNyli5nRHTrX=Z8 zm^F`1)Y_1GZxW!arOZ3 zzx=1KW-3NQ+d_T8VHEyPL)OvD;E}0mTc{6raW$D^r4D*0BT?;cXIhsTaxExfc}fpL z@K+te@jae9kZMp9<$)-FdCE|Y^H#MKfW0Fhc1=*Bz6QWPG@T8VbPQeV;HYA(Xa1V6 z4+YWCodGtD1_j^@#t4oPB`Va?J559_^ zleB>(A9eaN!2+5DtDoM1SEl*|X4u>Wy!ys%3!&oNvfNOGse?5LY*Vvid6HaHm)=h{ zE6~0%g?jx(d90NG5#@uM_^P@`p`&UVXYUy)g1|Hd{Uu4ltEwZoeAnw>p%UncO@YHa-qKZ!TIHGSYF+Yav^>X3pe@` z9FW4dbc;#{WmBw0F&f>0Sy~`lFp+f$YGQKXVt1!mPc~cFY4W~Vf*Umh5SN7eFw(3X zIvWzy$dp2T-GIHU47~5T11ldpy?mSW8-b@i0wV{Vu(|TcB&?sE5&)H$|^>2eXVOLgQncE<;Z?Q&z%9c>3w%VE^?F4UKw zC=-QpWp?S>NhS0t)W^jNkh+O%1~!SzS9xFHu|ufy?y}d!ljF1=9z?X{zQ)jC?KXo; zYF2P(^;R9;h2bP7rR5~{5LxZr4muw3NH)%KPImDj&epPr#p zXlsHx-@mX_*i69xZDez)w|AD>Fxar6zOG165d#a?3K><%c9N}l&?{bjmxks)GhfH9 zy;211kp$I$P~r2^B-8N!Q5O0c@A0Z|C`fIQmLCIDdJopfpQjrA#J>1AQup4I5~pTg zo}`__!i(R1LPK5DnY6+irBW>P4$ORm3w59wmi9?d)ji`qP~{FZ<3I{djrbG`p0?R5 zLG2o18iHkhn#oE1yyvixTB)+=JHe=Um%HfM6STa^=o#iXQiGWRjQT3yDQ1!$2W%Hh zxen*AGlrRg%XQ6eq(Qa5h+e}sK`Sk=Y%|5pW3*_B0P7u=Pd z>n^x^Nd(l)6>wjYdoPOn0+y|ASjn`hSe9j}Xr<)_wwozfnwcAx?J8)CWhuYcYi92C zzUlKkzklGl?=#DpnKNgbGiQ=B`4z^=knu{97!S}p09CI`zMmkZIg1|-XQ%VszrDm` z*LS$oS36nLnnZ@QwhuU`=lO{>3j^>3(R{oX(0O419(g6}uGhw~MjBPagJYkd5#O<%LBi|Y7&tkdHL6eTAluW)IMuyh6E1ni zQ3VPXUgPlCLvf`(u9c{9D}1KlapI8|3e?^qksPzvDy;GF6h7H48;iBwha){j{oGTZ ztAhR_^cjdQ9`VZgp{jEn&Z5zIZA5iRGcVC$v}QXu+mrh83|F8#ooV$+?b%T zVa>CEXY1~1KYWAkBVTOhCyxCB)+6_Bb#|Uo>}gk>1AI#f>?+TL^O3pK`B|0osw#3U zKHJP&ly#g3&%+g)vLD#>1@QP>-8Jp5i@2DkN)%tkGwHRhESa*? z{s4XF1Zu5L`U^KL!}A7)TOhIG^T|$@&-dJN)mLHp*rsSYCL#QQw(_3kO^C_ZLUoNT zINx{98ug=sM~Vs8fc%BOfP8Vw`|I(=pV)TcHN+`T)0PL>%Fp^MvZP!a;wE+svmnXe zv^=FSaGif)fy;}q^pGC3|w(zSifL(TOj3?5?Er*u*xpuU3CayXP zRIkD|%q;S96{Vg~Ckqe2A#EsnO&3M#Fm6`wo5Phuf-7(xgWSElS-7xGhcq_4a#o{2 ztwiC>ya(k9P;;rZe@%TJDNqjH0GmzkFjl!?$(U}iv(7b)0V8Ls`*^G(Delsn@B+-z z!i}!J-&l6@22YbX{GeVKa`E5MMP_ZIRw8pzqsC(078oS!y3SRsgi`+x1xL1ZD&V#Q z6IBizi6VvJp9*5>Ctb7T`6nHo{%PZgW*;LAf)@H~xah)sm+?e8J&M%7C%&=F$Q%e@p||KT{wzGzY)$~%Ll`8#?K}=)uP51GzJHEt|tGTNm^e65SC{u?r zH7_OyiO}nKI$mABl4(gOANc6>KG59cIBsJ~KiYX>wSkC0D@9vRj6W>P}&< zN@ROqnx}}93*A`3+lVLU<&BcH=ooQRUkCk^&ZH4-Qhqtk+~obkH7y6bB}|RB*UHR9w&w_ z)c#5eZ4Xp0cSn}zoXw~y^^na!>p^i7cq({;uzfTK;&04YnH<~^dhQix*FcVJZy8*& zO%I$cd!y#%&0yxXKZbzH?gLY)3|yeL7xxWb9jsWa%>yp6FUm)Dbw>u}8+v!qU~I0w zhKcKc3~3>LS)%1DnL!J@S+X>5ww!7H_CwJ=*IEXODn31UxJ^MXqeH@9!6A@I(auqE z0f5w<_H3)hpW-@SEh;tKK!7s7@=PRQVfUrz*RQFcwglNP&r|mqsSgEVE=2q-a9>HJ zhPxB3&`jwYE}caA_gPI!inLH+2f=_eIEplMaf5k<(fM(@5P+&jdxePN1O42@g(?!{X2pKyu&Z~tuw?eDZW6(*Mf)5u}IoR>LdW}pJwXf-vZ}R{T zSMW4M0A_au|*)={c@2=WEj`r2g;Lu#blqNd(Xm+CDx z&IC=+^&w$ortLK>T&#-=aTA~YJRVBtJpI6JDhhN^L-P}5hkTofTE73d=@v3N6hEPf zl)Io~oXCDMFHoEqf>+z0Xs}2nWS85KJf)Ka-*fzQmcJt$ysVoz@!WJZTU$}Q3%axg zhHWfat+xoJa|}KR>bPa6()fvDiA^?PmjW@ zO3u73Nr8|8;6P||5&P(HcNMH>xQw5y3Pp$=uO)kmM+;J%m_g z{{8?0`J7!?UE@UO_1alPqp2IV?fN_w9^3}MyUX9L=)OuZ+RVxx}& z`3u`ygIzYyJ@LMaK{g zG#G_;zX2->YK8JLvFZ>`cxG!qh{a#ft*pkwBWXKwa?B_|BUnuAmnh|`bS@YU3NjTuoTdYkew@UQFk;MPm+AQ zK>5BL4Pld2yp*S+B`C~C$BF8#d0{YkU9=ax+@fR48D}#_gEPmtAfVwIoYkdaHWgk< zo&w!a)|TLiO?m2N4cQr~ahwDzcO-R~@fcggQI|*>F_Ogiaf15TQq+Fx(pbFHWH_>0 zBECazT&_(vS3K3tj59R5rY}i|upB!Ht@ybWVx-D?LmhZEPX&>aiRVIh-Ik}~ExE1} zHHk&{|7T(ZoDRWV>efC@Im0c{V_&-GlZKxlPS}C7v+snIKv=K*!v4 zU8K0F*+j$3_$oPProwd5x^@&5UE3r6KK( zySEUb$xd-LUQMEN&!YenirB1Gq>nHP|9BRtK}!Qa*dPtABC zI!Sp4M=1_*!FA%^T@)v(_U0i0#V3uMI=Kt@prNDB#kx${%z*`W zb8&Rxl!mtu1L<|5agtc~uIBqgP^XBazScV;RiyXN$6H+QX`PvV(G{q1`D&AP9GnUz zkZgcse+Zz6eXuL9H@5UMrmA>Yfx3#F`}H((JFdjn_4^Q|WZQ>u!-H>toS;qOAg8DH zH;+Q0M^vE0A;>=S^jDD`Tk+$p)g=}>%1^(R3rhqaAQ{8y=>y_9tT%bHjD9Bb3{Pt*_Sw+S8MVvmm@9CMz>+>?sXvA=bUG zwG|41r!g3x{qO+ze4v>r<<2vZ>mTN|<~$jdw+#{i-R!;*9-?wFo@~7IoerZ+L%k9S zRt%O_x{$x4Ia5)h?%;Ukz9B(9i++6%fHdHfR9`6po>N~Dc^vQRqr`joD>Ky8lj5JZ z!>i+QaDMN2)}S!G;8ZA7R7^vHEyL3pFWTV|Z@6O+pX8-WyPLkKjhH0m`QH*WFu>t4 zhw8Hr(tx@n??@2;O{%0QeKOb&ml=XxMQRz|>5`_1;c=eLsP|bOo}{_~`Pt`Q^~YhUq<-r4uc6)e z1Hr+fyiI{V7BS>}cpdN+Up2vlCx;-S?|D`4E*P&Gc@m9mSA`xHmm=mi5o=4b!RehZ zBD@hNysDu0*k-#n7w?%C;z^E5y;(^nZ%VK_0H&g`1I<}|+$1MQFz!)(>x(+jPJpTp zlGzJztnmmP3=9-1+(UPMvAg8^HhvDmC?7H!NuQs1&Lv&^d@BOx1GTa zu6SYBoyyCURKbOK2ZHu=9ugy*&KqF7&a+oSS~f_-ql&6g@PgkgiWg9C+(%we8Sb4V zioS9(s^5(h!_HvVn;o35p6Qc)QS!bgAxcvt=KFQY1MTs84ce&pV8a0{=hr&!A{E6C z%HOR;^$`fFg?9Cl%={3RC+vL~a7)i>9~UEy4y&(&#jH)(yoNWS%j3l2Z}QZe_uLy5 zD4Kt^QM9mnkLzv77kR`Esr?!bqbWV0cQSu zp1LJLVep~7Xz>T2>7@s7N+MkWh5-7H?1ggo5BC+B&$%LHo(*x0MWuT8WT2&w?S*Qm zh4_ln#^XG2Nw(;oU+fbFZ!2u=micQxBTh9W(qz}4Dmbk6OM0)%!#I4s@ga@~U-&RN zpkycH`er}e@uKc4YnoB>GgT(4WTR`iEOMz@+q-1k#}u@`DEw+8l(&9{+wp^_k4+FkZ z@L*BY9uG_w7mUSSr#(7x@j$@O7~oa6U0~M4g!pLQ(of$^VM|}DlAT0^8LtR3R!Xh` zE$Fif)LsQf~9rRTRuE93|#ebR{igm@1ONRFmK38(p>Tz1m-2;1xiYLLa*0nZ! zDgvlNs4UYsa1OaN=T+I>Bm!jta*JM2UvV4u)8rM{!`1z6`djq2{*=)Vsthw_fD%ci6kv z^FoyBV#tYkMM6|O0hP7y7y z6+WBc_s;!2Plfk$)U^tpApZGXTNNDD4GNUYZOzfjbl@R?JzVK#oH+4^4z{53@f%J` z{>)R?)HwmavuceQUbw&h)blhPpZ|b_xs~^<+}>2`|AY_dmNxFVc(>&5K(Xwvyb-cc z1U~+yKX%_cx0k`h{u>hU?>y)vo$`=Z+*$9xEg_01xC#G}zuStUio8BDWG1^s)p5v?t^u<}?%Lt< z5`fgwX&e4uyaEM@yNv>T#MQAl-if`V88xS?K|J)C6Ii*j6F4%q2ej2YQfuf`$X#^$ zc0q6Bkxe55eD-ckiaeJy5V4CY6zS9n61+|X`hB~!@NX)Z%K-UMIQSHemteGd~~JV^_nnE*9aph+8kh6BM6 zM$?S5wZ4-%MBMov$DI*paR_X1a=W8yOckQWr1{UN1Ub|64tTI`?b#+VA|zlb_JFff z%agF;&gS+a^S36>N-S-CM+;uM6q0%jf}A(pLHFh$Pa>a4or^{J2{|!7(ED92@OPYq z8UaVPdMsH|SUZ4yC0M0DqZQVi9fX_lVnEqY(18qOaz0;cfqDJ{@y<3k%W%0Y_o0%X z{5U|}v8lP4rL03X=g;OEWTH7K#T@apvMTpj0Fd5t*c&;Ozw(r#uAUSMRB*hQ#RvjJ za)p)()WLqR0grO^WQ0;o)(1N@Q-V~P03He8NR?rj2D_{k;C;$MBmT#sW-w;P-4ZIw zCbZOEL8*V0Z`D~$A3*aVvdktqoAhRc%HQFl&p~7|EcLLcYXVf4!Y~oa%8O5G(n?e% zxXHr-96PK)1eM|PNI2*0My$E5!6_O6OubHF=H-c`A=o7npqNw0>A&5}qGFcFJ6gw= zcw5wKl@#YuY_-#x#z+`Jrzs5VJ7M8lSX7)ar9)@=I|@%zE$w9t zDC|P4b|;uag>6(bO>%}^8lwxuD36B0&l8KztKNp6r>M6@sVnR(4qtNyAkXiA11#bJ zvUNr#zOqv6Kdc=#E#8Q7Y@Gzaea_$>MR79nzg!B`S{5~(L{+s1E+OF_C0Yc0kF=_iS}2qt{p80-e8h}~B%`=(s&3Q*-RhY6brR~b*b zn#4j%D^VkE;b$&_R}WNY2g&X#ew535ha5$f!$4)##su##*u zf|0&PIykwP*9GgbQseE3G84x&3e)Ek&K}!d0A2nXVT862%Ou$+`BrcBHG-iudgJHf zm2e5CO5F!6=`6xEcf>EQcZDzoHG|J0kAs|$nlBb9JAsX+#*3k07R3xX0ewjcJxpdq zYK7_Lj=<-BXkp!1I3{z$^;hnM{`gwz*BDXqoM^dooc(|U0IoUQkq|;VcT7@Z;1$3IYgnkr$Up5#z$Gy=?vBI zG+?*l9IgWY8>K_x$ja_hUroaMlk9qTD2B3e-|{9=PEHsD&8FimpEjCiIm5%y*5-(iWy4*= z;Q*a143Ma*>wR(_=<7ka(9-r6@H*8C9x5HKj6N5 z{bR(A4!ZALd>w_a`Fs7xi0qCQbvKA}{D7RIp5{n3MVC72UBI;&jT)CT8bxi0L!PMO zH99OK50EkJA*0bW;oaG3bYt-~_hNFJ|CS%8AmO4{jHO5_Vsdbt5+vL&?P5`J3T%3U z{2eKJ$5;xbLLp01cxT6p>{v^IBpph(9HO4p+uWAAfqI=)!l+B-61J>2)E7g{&qUzi zQ;m}ik9bY>)Ke$nYic$0RNSmpDFI}knE*k6*T!2)9KWZ;i{b?RJ00+;@ggqK@{ELM z;WzS7puC^RzKrJL{=nr01UBJx2+3h&89Mgy_Ova@vRW<|ah-wg*Y$6LoVv*xsWA<3 zBo^V@7%Hs!F1cAl;hl?(U|FN@4o*QA zv*ShQ9+vGA1$K!;t?C{YMCL`uY>~V));ZQc`vi0##;c`X!X((R~e>6u$QZ7K%q937-#}to4FuiRF ze3sEVP-Pn6^8nxSV{(qze-Z&#rHv;m_B<7!`2ZC^5}hrkr)nY2C0G!@?{KQ6w1Hfy z$tboE#diFR7bcU-#`YA|UyM`7j}rh|6fZ*hTeixg)Ik@cX#OfsEl4F1F#2D@X}K;@ za6E7~z)~RBg`N@^3U>!s)P0h1_`Qrn&M})uh@S^)#JGHLo~I7d%O~&~8Cfa@SuV<% z8V$sYP;K8vx3-y*craFk^U6L3pzw-#k!iN5_+_d-4*`?HHKCDW(^O~!hEypJg8`>m zR?2p{S63396Xy^Uf}lI4Z_tKI!px>%l8N#Ff-&#dV>9WevJsj&OAXi*l16pZg!1QMBzMf4)a>Y zYLUat>Ec+1Ma6w^u~+NQW|5a^878c|$F-1`=AQy=4PcVy(`QbZf=jdwT-!zppDas> zk;AStCg~HL{mg#aBT$Xj}E8euc5B$h*1+5sMb!$W0i)AAG_0|$I0%bX*|J)q|y6Q4`?x$lM0AtLt)+>JJnr$B2(y_#yriVO=h2!Lk+kNiD( zvb@*F-28G9HlWq#6lAR^I|*M{%2+K=$R3_oARH?F0V_B6z9!MM?iC$zVP0^84^f8y ze-BJwM4{tvgk*?_hxA)t^3*C`Y#OgAmA^M}Y*i!8!NNR2uMjD9Gkf?G8S`osetL)IBaDElWvc-3C0s#A0)Rfx`V*&p}U zHZblf7IjFTir=rri(jT#)cZSxzmEEzf2;k6&F$>`-wsg9-^j0*GEI}@OjLM7eeEiC z{Y{r2*D&^?etM0P;!SXm=@xY{j&|G*PV~kxX1qbxbUb3}XIK<+G>vvbV9&5TF9$>R zu?uY%-3D*J&>yafxS19er$eb<7B801w7f0Hbw7UZmcM(6K6{$95UXZs1gVGaK_g!6 zki+wTI|SYW*!v6~T9K^e()aW01vS47_{r(PgSntRMBI>E#lk0{<#*ym(QHd3&Wv!< z)I*$~tvS|c!1hX5fOz4Ids7tX`)D365c*`hiwYa;Aj30}`#2*f^4r1o)^~OjM9u+y z?g=K+dM>+B^K1nS#$u66~EwW z7P;??=vMN)HaXbfK|nSy#=~pWjtuP`VtW_Z-rf6=41@k}x++PphvP-Z0!`1Pzw&r- zxIhae9{g9ZYFUoCt5`Envl4QcBm8_V2OQ>Vjna9bVBiX_lpN$v@3X*)Tyr~-+YYKj z#sWzLZiywm1+^98 z`x$V69DtQsAa30B3J@8S%&y|455}h$og>GHYtAHduxlDps0m($ zz9Nk+4^U141)teoz1RnsAkR$51m(Ua{Ir5ci%TmkQ{|GU;_FwOnj2%>5e3>0H~4v_ zM#4x&XEZDl32AUcKqkXU={R2bPkH?LHJaSwY68gB)D*A1A{fLceFWa~arQ5ttzQVa z{8w1qMe&||gqMxcY?M>+jZ;a4nob|DgpL`Ya|(p}WehIc%1zM#Bd4;zISAKL4iWWo#pNUGFuyam#Qy&zvv()abtQ9JlwacXNagBsB@jtc)WxL z@*A7N#NFoJaw+vR8xjLot3oDq%2boApKRpXT~;7}k@Bm1n2Pr>Tqyq53GOD!es!92 zI9ZzN@D*;H@33)KS#asl%>+V>MC7j_LhgzL2Z@U-y^$}SB*(yYNkgfq{rx>~Q?Y9$ z#=_)u63k)#Ai)u0!s8w|pyV`qC*hFRkS6y>Sw_7IMq~CTl1j=U{zBS-dJO9nCf)R{nF5O4eOQ; z3hl?b3MC4hTGR+`W*R4o*6Xhv@C|$|stb(~ZeJ9^uy+nxxXE_nXLGkt2?`N0&uIBU z68KHS68O2h%)}tK8=L$cuno(RF~rfN+`H6>{y=Zlb)k6U-rk}FvBf_XD3J}yBO1Am zkhbr<7>{>t#ZfAQ7h8j@UAc+W>vCqiF@_rq{8ssTW$EBpo% z1TWDDM6VjWL=6GY-eQ0oE+;esJmXdi*|aiCsA{?-U=qUKj!qFW=cgl2vYd5>3c4l; zkBycWCGu7H-5AaIdYg`lrJFPz&{^chX8M-7AVKe5gjZ2&x^0~JmFg}zRqpyTo-)23j!Qzj zU3}OioEBFgDtOt}K{+1C`?p0)H!grD$FSKYLZqBU#Ne!#w93VScqJeZTzkDpMc%3? z*p9EsMa=&i4m1OM^a1>Gse4P&=dor8o8l9KGA*M)>GowK%}uRJ3e30)<0#`C0?=F) zu|HYqtc|x&AQR5x4~^|KcWc~zUZ9H>9O0=usIc?jq1y8#IzVw?5{Q3nfD zMezdLHcP%-ZF04i_OBK5%m`IHOOfiFayUrCOXfq#Qm<_~-$!c2HOkxS-dpjv@;j5% zsTf1Jg9)?EHxCt~wrfJ5zzac(`F6#sAhuI5VdP5I} z8bi4K>*n!DN@lOB1S$nZgdKijfnl8t`6VOmgDvzY~WTh3+d zBJ(5_KQ0qU7;+}G!!F*feK+K#?GuF89`MrCK=9Jer;%Ydq(D&r@P-;C@fcI?w;UN%7S0L^lZctDJQS96*<_?p1k zEn8-uRkG_rID4Frq7w;aYmhacr0&NNtI;#ve zfj8|Z*Lql=!O*bint(f!x+wQ?Hx!KxF(L2kL8XrKXskOvmVMm}ZkZ(un~IDOQ=n>T z^Q?SD)6{u;s6z1Qu#=QQ3)BYWPU}hBi~hk~Pzgh|1gXDDbA-M2LIIc~^QX?4D=zyl zhFTS_`cMJ=azZJxd!vsP!CtZ*mBChmIOaYI)B>?(78Tk>%v0=N-yGZBNBk1#qcM+t zP-C8oLa8WpHWYHWqQZ$i^u<@-Ft7(B&ay+|+z;@ei=J|(5Q7edSV{qc`X`9E_no{o z133N$)4Y(KecLi+5>q!Bs6XSKnvwUpiRF7df!(VMQ~_?rL5>2oA81LzdV|q(>R_*A zVy3h$F7gn3?YIh3MdHgvmZ)-(%>bQP2=}4`BvliF(%41D!)9bU;j-l{!O}3xm8?tM z>ORyxvc+AJ9hP-*zS_IwVnY)|%TFvT9W#=SqPruoa1rH5=&^Skh*_8OK>_>Sq>}--umd zTjMuwP$#Fm37L(K4QK{pg5&Lsy8`J(I_yA1wy;~ z;YRW2PBiiWFuJ_N!%uiU3O+7paMvt7@m!P93a;10LHCXs8w2>oE52P>RgtQj1g*_8&emNGjo0u@%jFRN;jk} zHiOmrJPH5ReluRXVOj^dR&pQ46sjz0O)@%RzAG<*{t-cGoV#>N-W~(HxtFpVy5J(PNVfZ3x1#qW9Q# zreM))?_71`S0dUPb)l~IIcHI?x_7{DZ1%Eq7MlY;CqZ;PueTvjNav!dBOkdUv%q;x z_550ZuNCb~@~hGLG!N3`f>wVyg!w4x(H<`WdS~L~k;gQ0bmVOdIEtXgM=kpDq9LBa7b z3Rma0>&mYPSVLTfM3jtrr5({rw=y~r4(Gxkt0HS|$4A&=VKMXr+jZ167>AFd*{bYz zcunDFjRR%lW2_Os-VC|N*6K#-M4e<@kE2HBZ0K?lRNiXbJvsjbK$|*aKn{?^kz;+* zj$_u!c7CGbVjH;jeznYytHa@~Vvmte!45`UH9kpt9N+2$QF>J?BB}Vj2HoU$p-^T~ zUeA`zFurvyo2h!rjF^(^nyHgxuEYMhZYh%3@OQBTn|H<0k8>#Pb=Hs?~V%_Xd=@7-NbP$9*QMm#*ZH z$fZgWMD-0Vlt`Q+L0E3;ped5ovk9X1rbRtdNTK%}$JJninIWziGhHLHEef>f6U5!$ zoFvu@EStXp5({PFa1Pch#C~4{O0jX^z7kYdqZMV2X(K?^#hy6L_(PK&`PU|V-Mph& z2a)-wWvHA#qO;jPe_O^(^A}Zd5bS7B<&beKTPgf8w=A=a_X)P3y>%Z&LIB;;>%;b5 zN)RXh(lf{3FQe3sc(~zm>zr0`Yl67=w?$?6VyRb9$~yt2fLp!e+gCAvxApvzF1A7K z{l}tmcJcRXs9Kr;ZXWlqmRp?l*Ew@*b6X;J!XKD9&^9z4m~?VT6cF2C!Q8Q^oO`6B zHxk6AJDRS@GTyY$OV-Vv$OXYh)-BQR-g23Dpxfd^a3^34Kj-<0ijiL&1}T!{Aosz`%oDd$P9gFzwL z4|vF1zR1ns0za(bwW9+7fxzJ^W~=0bvQ;8}5TLyGk=MWWKGZ7vcZ6c0WKV~HyyW$L zKc5mJ>dIP6f|ew3kG&m6{qkR1C##}09C;qPAI{Y$sIhZLBWw(2503EL@*g%gRer9eS4ru`|RUD!1<2&Nj4C_ z1$V?#&1l@P)?c9R+9s`Va1IV7=b2;q5?`~9wN4Zj-Sjf^>nXHT5{+{^^n<=ZnW zx%9KDU|I6#uM)&gKdTBm;WC^d3dR26c*5vDpg>=MGL3xu6)Adn}WSlL6FR^OuR>&zi|FJ!_T18E^IzjlfvZ_cO?uN@~D)mMy#Cu+TX zfx&wJ7{u9ks8RK>S3Pld1z&ypn4~zXcT(^@8?(KF(-t|MY)rB)%EliOM2}FbIyoik zKcbo9K47YCqNIew4h2({Vv|huXTX$3SrF9%N>r3T*@2`UvV&jHYD5ZD zV%aJgqFfn*er2m!Y4(vic9BD)xoU6Px3IOpSQcTOD(g~&Ut`_f54CB}c~<C8vxXML8R9D%G>LwSEzS4Oho2AtMZPJsWeIymd;jnfW=90 zNyKXVM0-d@07B2Xu4|%L+Qq7zJgnOo-9!$+T+0C&WRn{}nFFxeb{U~$6SsB~fL09P zk!uIN7dY6a__}+5NfM{pMUtMmi<%LoovGfPjVtYr8aD>DaH1MHlax>%NCX$Jw=Y}p zMA7jBq1;r*Ta{AFuU=?Zu6KiX6a2+6_vOK&w4qg*`Q*jk?Ds-Rn;5B*8N94MNR*an zRq7@Ag)chr8U!n_Hql8AG-m_Lc0!(D=cwB?5=9bA3vj_Ct9pWpO1&S;UUO*%y$#}D z7UQn*$8sfGl_5i-53u9h_h0{L5=K3}BaqFkcvtBx_w@Ic_G%4;cc3`@l()Ye6E9y# z^5<}a>}6IQ@$eUYc6M_UwJjzOG9F*HV*6dnXtqJ(Ipkn4#(#aVNm?LA?KLD(ly=wZ zD@1gGj+4tD26tNztBTSkGT~Uko>pZK@plBP)l zYU*GDt$|Ll*-n^pr#a_Ji30eB*}iieV7tN{jZohdt?#gtHf*go?$Q_(v$pseGsnkC zF>@?hyF}s9*Qx@?DCFA{%j&JIrK0Gh5;>-?1KJ9?57Q^rTSGKuM}SHv2kL!6<}175 z_Un`=V)|Q`%LU}`&ge$cbJqEOp~9z;HGn#+Do>raOQPsAz^Ys+ob#AOabkc~xvE${ z7G1>;bS_o11~kKg@R$Ikuyj_W zyP@-I>EuWTWnOm04cHwNn5I{qJFN%GS8Z+6!Ld;sMAwrD*X;w*In+c)E4>oM=R-BQ zQx)vZvXNcF63~pIPqM&1Xgf6nE^XR+h6`mWfY>tN*Cr#8t+JI=Y^-5gGH}lO5;vCO zCUlhQmspai3z5V0N1?b(6E+&vbV@2&lAr;J!YfNNA5z9ZVp)_4=d%>?WcrdeDKG}1 znM0X41(H0B$jWAj#$Zg@aJ}@D9YatwG7I{Qbepk9*$R6!12ilP$ViZZfYQKAM_QM$ zRr;pyL&+|+Eh6x68F;248Ocz9GM{dY9E~2!@J>pNwjE;RVCb@)A)Y*+fP(3aY5Ln+Z@CK zjn!L@PS;V8*<&?Q^oc%z<~Of)iQI5~j&hGl^vP{UCyLy0dZ8&8#}LWVL-2`ukJnZe z7j7&*Z-~Rg5w#N#5VXz%Z%{a2pc;qvy&Mym+Z{JQ3>o&vLr6^kx2f=RZ!Yc}1GV`) zI#W5<=uDd+!MVmW%xq%flO70v@OV@x-Zn$UqmWo~(BJyzZg}sv5r$s*O#PSHa{;d& zi6iZ6g{ms-JiI$BiH+N9BH&p!+Qy5ukAQ>>=@BOZRQXFgyz6xhc{Bdo^kl$OU%;)U z(urE0JcIgXS0K#!bZhH6a3hM#0r7nZnm(@->W6vEqfy^FqiOIQ}= zJWNj%hbLPLB;Ap=W^f9sDnKzgQ>-d05=TE1E$m_oMf*KKt_M6Ci^7OG-3_6Zvs4*` zT87%{S(pzdii1*ul89Y+v3acP=WMU|2SET}f!8YN!1741Pt z_YKA46zchaS|#*d$F{J6FVC>5ho*R5k6^%Mv?$%@~u9m@p5s68DLo71m!$3|D-JXp*{Rgpn&Ty&cgc z9W6us!(%`N#lN8IjD^~b#{)J%) z5p+{M))Xxcy`L|MR!%%iK8lv+>YB929)sItt~FQkcv*6KH;( zRh{A!ohLaj<6zLI{E2Dv@z4n{30ejHX@NCJ*gi4iO{+FGWMOQg?_9~|xLsG@E8_`w zE7;3_+1VOdra;!=QmvK0@vPngt4g>%~3u6k71w5rdB`1p#7)iYIMDz4)5(EpY>akUqS`>vzlY9MU2TREf``MK<@XS7(l zTn~w#Hv+GWY(^(}*^K9hn-ayaLQO3sgv~IR3iYgVRZ7|5lFzV#cDnE}OXplDThMCm zSB>$|W0CHQOZF1J-pe#kmU?iFJE z3j4kMz84}qf5pzMinXbABTlbsSRk?sy5q^TeaM>$K?9Gq^9AW`=&baarXf^S(wSkd zj(qwx)J-vq6w3*P|9 zCl?b7TB*mH-c+ELqS|V00a2~b!6=e;U@;%ne4W2{azJZ8$6`vRLM=p21P8l|umjV* zTk9^I6XcgcDn4elcyn-^wB2%WbH&NazQ_@I+1KrV(BU4igU2;rCP}{qG{YvE+G=XY zn#kqin!K$*L89sj>uATyyhDUe9>=aBG0C6}S%WjRmt$TY7%D8Iu^f!&aBSCo*siNI zVUbMUWiu%!kfq@2R%}cXgY1GGzeiZ)X`dEi<7(>!`I(l}e*GDd_2kyvnG(if93ZUX z1cE5lu61tiAYrL4o=8d0dNot^jwU@E0>&FBnIc7cTihb4sLN3b6FsiB$Mp1ihd_B?fF=3gHHEl=Ah3;ECQ%wz- z$|r>NJ_)fSVKpN)W?Y3&6UF{#v>f2?qhP`tth42U()Rca=&Y!OqoQmng3&5&&PR3! z=R9V|?C||j&4NU6Pu!L8c^^q|4R<9zw-<`}0(?X^!c!vI3GJfe3Xb#bn}`8HdKHBD zKByHM}NUvrH03(PbG@m&+DFv$!Ydn z_k}NlJH60zU1A%q#W_8G#`PchV$;=(ZIuQBGh9}(a#t8sTYw&huX zeB7FA(0|N`ZvZZzj`-E^M07~eEo+j3&jEB{I;1)pRh|LT$9bYrZ1zQv?|;z0K$!J3 zqdWTAdPpT-(j)6#)G#PWy`eoe%tpy8P9@V+_u-XOm65}izPH1xdwBMB z^n^w-^pg`4QRoM!Lat)%14EH``D+}I{THzx?ImZuYF#VG#QHzkzlLqIu9jbC;_J`$ zuT|Tu&&aRS@%0y{ukIqXn@2NI{i6p?q#WZjyoT|sDjp)Nx!(O*2-#MgU(2|v;87*p ztt$T`*W{Y|IDzGr<6{=SUQaA})7oC7?(_B(p4+W6g-<)3Eqex_HH7YzX!2*R9nC&n zn!1U&ojxhJIIz546xR*70C#APItZ0+LjBpHXMkJ!H#CxB1t|}$0$K2KjT9#t0R5gQ z{@rO+X%whh{E;YL++|hI3z3oi39>1(jy9Txx4_ZMtSa7w9Q-e|A7O*T!1J|Ph{AJD zmil*M$sX-nWXap4;@!z^;L9|YFBKyIjY)dLD07z^e{ql`)#*RHW@Q0xvF6bL4m zj~(uu7RW70tbJFT+EhB4u%#X2!lCKCi#-lYLvcK6L`^Z4+BwkAO5bxjF=)mbIhS!_ zQ1v68c4g$2BrL{gxg&q;e!Ukc);-Wd;anc5{fIQfg^g{B5mQWf0#r5^4sHqJ4OB_~ zbR~KLv~EX0qUc=*lq&Ax*(yV5s5jul{%RXR--`0FNd#yp`y`2uhqQu51<)5oFMZG~ zN*pRjQ?`7qu2Q%(2Pk5?S4V;rbCw8@Ik!-t5E0R65_od~%=4Wepnm0HE&VCDaJ(E= zZdI95sE7K?-+{u`p_zx+^*-8k&Wsj-CUdqnYD+V_UT=W_#ms}r1VogJg*sVFe4SqH z-&Wc6hMt3xMDY=)o?FS^fnvfHGqT*guN&b&g3-ufHVu4K)0AW(_9sXpcQp) zn^1i9oR3=+D2x2?YnI$2gfM`9eFA1VX2ECyg=4`#)Rr}yjzGy3^C2W;zE$l{Ov4`F?OwP{o!bN?nsg=PK)Li5U4`H&(U25*>rv<#FH5hq?6X zNF0~q>21e+bO(N^7hIx6&Ph$g+-^P8SM0Yh;eZBv*G^$R86;<&ZjGKvBJK;Tis&Sw zz1Umf7tyUnt}pne;U$UQNh0J+Ed*J%4|dg;+Ro$(rm*gWUoeF`-bd)9p`X+w@%SmL z3RLD^>YF5PpVF&MF53^t*81T&=QT&Mw=!30A|$APlDK+WcT1E9B*879QRX@fWVd;l zZ6d_bQJ7o9slXt@E@d{sLzNtV%DY;NBbg3Hmuo)&-51S*zr1ku5@pkH>V~k|-EX5K zg@wNKx~H_tP0Y=NRsM=73;5Y_aO?c|Y8slnycGLkT>$Q8@63V$Yj^g!(_fQR2K2*mm+P9S$^57kSYUw zIN-}4@r@97E?ZAaRNuqb5$fw6kr4rY#tM?|SYc$6IDgstr~J*L_a|*T|E;xwwPM|O)}EsDmB}jg#X%H*AZZ`K4eym7t;^qK`CUI~L+%KQj?s{H7T>0# z#?8O6xbv{-F_!5mmPY2OHs43VvCaj{R+?nLV5GI077wA!I9(>=XX_=2E0GzmKi1Ci zT~AzPodi+z#0ZsX>TQ&nK&rX?xA{#+0mXXQXtst$i96p~kI9n>IfZ2EIhew-qj(4X zVkBH1-1U+%kPGIrT2T;!`Q@G2%BOJw#95$DMAa3m;d+taU2XGW_rhZ@o2KzS3Gkg$ zePhKB6OrxK;CGs=K;dHMmV7TElU^Cv_!Kt1KDeDIZg0bTcFt+9rz%iqeRUg-_%2jp zDQl+zVesaG`ST{u&YV~n!!2n9q!Xwd6KJNnyEGu4?||6TJ;(nWM;ms&1N}tlJhJD@)JE? zwuPyqG6T1I5J?mMwyN7pWHGZ*K65RsNJtc@fWz`tFWioK0NK{!yj|L+Qgb(4)3*Q~ z-XTz?E`j&qZCnE~T>7*scuP^1?BXlD*MWHykD3ft)e?#245_tkC>i;B!zgieUs1gT z+WASM`d{l<EhoU`-AvY`Pw8h4u&rjO0$rdPLUl*Gz$n-!8L4hA)2^b@8eS`bycd~~`p9b_ z>Mw=L*~q4hULv&&T^(L;YAIs2xZxJ(+L`svc$X`9XEh!kp-7sc;#R0Y0itd_)=0Vg zoe7}`aNnmu2+|E^onTW`%AK}?7{&*O#fYCq=Boe#BJ&VxSe}A?2#o_737n$EA5Ib{ z8rxK`F0F)>C>i;-KWOFYEfc?=Kr>G~Z9)dRS7|R-JwtwNlF0V3 z&6mhgJg^hG$H&j>`FRUJ(;+1nl5FTHbmXxCD!=qcQMOAE zW&6{_SG%gz7L$#U5}#pX!~TiJyM={Xy0DK8NurmJO@$WlcL`dEY1Af4q-VmO!-2Ru zCY7j@6a@4wUz-Z3jKuF}QT~?=rdA>}3c4~6R72ha+WJAx3KYj+wK=6T|g-_bQf z9ar*PT;j-)y`W)!BC|)a?5HF%hTL}Um%3Ii!aHqz(5nrtb8sZG=v7#oT z^hJHZNg1<=EruTi(*dlzD4VRc&M?3>qgsRRjc_~EU(YU?LMgtUtUyMdHU2i0X^sZ# z7WEZ}1N8Cc9v!QcSDLvm0kr-aLgu72=CGi9yz*i3;!LV;PmWn zwfEKMh`aRs!gX>^$Ez@8>lHiv!HFIbs{ZCHIw~d=@K^2dF3LIlsY;w`pSHWdNlAhDS( zT?0N_C_!ArJqm>DEDef!%XoYV;?lpRKwVUNenXa@x9y;+X=D2sh21*}6h<{wJzha= zXs`Y2I?psWTpiYPOA)mXpaw5ML_yn8d!0=CUG+6kgtfM1JNEK>oYL}-@qB$zCQodf zk1X~liMy?BvnBfEH3!hbx)(4h-XG(1&Twt*Ae%Y)K0{od!cH?p(;UK7M?*MP+=Nv3 z`39FIsbI;4<3J8eunIV#LQ;-iCa!Y{5-Bg5WO>ATwb8yHnpPD~@Ma>l*&IYyIL9~~ zQSe9+yAJ75(4VYd@1Wy-RVW$}M#`pWz8vPWlRjzUg z2X4yhl(pF6QP!&RNekw|iwXdt-1`iEu}(JR;&`qvjNdo%doi>|&M&Fqn1rg(e~P-# z(fq|_zF|tP8y5U{l1S}h8)LMRPM~~|i$CrYZ8G7d`v07*R1&GRZ~7{UBj=GDtP0g4 zzf>nRN`}c{kP=U_#*IV1QtYUGDq*CtF9^HbJPpqR*n4s``6ZfM`eS`zc?w_0yET_b zLdc?q$G+syIn>ihC5f7TDHFaz$?DOn+j^^h2B54`__NTfia(MVksiM$M*p?|L$W=L z<5Po>Jd0|(2exV_QWLONhGV5~0P0d4jI$%wLiCDKC^*J*p!j54tCBvpQL-0Gm-FnU z#t)jCET8gWCE=v&ITr#&Bck&OY1|w+bOp93GF{qWu{R7WR%VGvE!;fq|ZcwYX z|6@=;fw+6wvJLMd|BN2jd>kCa`E`u`7kEng*pvg7bNVZc?mjkkia~O{nj|t)v>wIT zxyDXcm_qe>7!J*^6Xw~{zE*~*t3l^B$J7}P&TCoi@W^J!0G1Ay2afp^uV8mKGz_U# zd9G)0&fG+kMO&e%IAWRPtd2GHiMkjen5b7~19k^D|X{&B-)3tAEes{I3?>svwW z0I5I|NrvQ0e-e+Hap8yurH9`Tj&v(Yln&6UCmHu&IHDM+r<8X6-|{z>NF#9!qRW*vV1wz`-l64$owt)F_$}Rv4O= z;5hzJiU>*C9azV|jy#Mh=NswRFoQ?iBUuz@YP*IMN*zskGF4_}w41vbQ5c1ca`#TjVH6asdGO zu6Ai5ymzEF7IClG`bxSZ(xkgU1rC!a8sY$20@C+WywWFiB=U0ECRtw)Kv_#%pmH;+ z*|5!85wzL`FSN|>CKVNTzvPjHMneFZ^@b@;xg#8}J~{>x3i$2= z4I`8#!rQ}=AgJeuaiZ7XMnZ3Cdplwlco&2!ba=AJ9ILk>e@CDxuU&YBM22$E*(`hE zOh*D#ce`Pzib^&_K;z~|**IH)TyBznRI-Q}uT43ccF{!eO-B7sfVyXJinRgg(wngK zWmLH#ZniCPU(q@(Mg_Lhws0?wkSHX+5D)qFf~YNq%!ncj*M>TPRXJo*QS!w@^@!6*l{N=dqKCZ zXJT|o7F82%%ETbK#mL`yPhgVMo{2^MH!rwFG0<7D5-Oc>$)eX}TY+P=@kDD&bXqJO z3Ei0hSPnK5MHA3~?=HLd!4M^w2++g`JkyE5EQI(tKPsH0K*;zt)uw_VNxVp=T{Tr> z%w3C{-)YnIt|f7FB^triNK1-%Jmj=+eq^c}VVByZAzvgzy&U1WatvIR?ktenHZ2ej z1cCox>i{Rbk0+;=?4w7r=rcq63rKH0func6XQ+yDcRm;D1@1Z1Rv_nqoT@iU?koeR zk`ZVIXHB1E5%Hi6$y8_A)YFXeG)8_56erdvxQkXl=cmZ<8i@#-OO;^dQz7Pk!JHq| zf{oMAFIj}lwy9$%YSjHXvYqYHIx2<45RX3qsO{Ko*Uid5oTqeto_7yqHTU*u5Dko5 zt81;3{*V9$p<3mcAgxFmS{w|}gujBcw#Rrg$2<%HXzU()XVe)bhyun8(26~#5K+Er zCOX8qyIdIJkw!djIPfT+k0>(3k@$BD?f&rmM(L9t@yYKM|MRw}?G9n$deBa`k zE5k?)zOP|K=851ooG(T6oaPM3pE-J(`8!ko_7f3vZR){35=9oOZg~q*J^W|1%Ggdh z4pQ%MB2zdL$!s05oO1Z2tr4hJJi|MR6k68|&R@mT+dH_wYe;e8pdR2EPWUm~-Xn?M6!wY2`r{n7wd@cKu&!C0quOzCm_!|F{ z4|e22y(5XuIPA!UT1h9{8?Q>msUaHF$qB?UzuH^JVH#v=HmdD>uLT~zxGh>@8H7({NSb*6>yH>nsIxWA1TBPS(`p-Z)+ zmZ(n#4!L{b1jb|D@Tt#-x8IOWV+v9aEVHR>8Z@G(qWqD)I0BZLs%yU1PGfporV+VI zW8oN+4}%*#57P;Yk4ft+;tKV6*xC$!9&hzZ6bA~me~)gondoF#o_7Mj;xZNz*-9?J zdttMZ#fwF@m2%mrBtD3u*Z1+JHqSi`R@Q8QG9PUjC8}2FE~z!B1C^pVNfAi*EjIx6WUA53bc7Tn|*bI@$< zyC6g2AWbUbFc&S#rJ61B*R%=})w5b*Bm5YM$H`W}V`h*E1!$qqdx-gx;V!vB6YyTP zbHwR9z_a%wWr0MT{Frk?emb9c*TkjCF~Mns*ApFFeF3!M$23>x@E4*L&*j+rWsm7e z;r?8Nue-MgN6D#@by;>X^zg?u^>Z{!P|AB-NC$-h&1+edQ@1o(EPbN>wp~WNQWLOk z4J)u5&8$zw2IN;qXNB1Kt8`}>Xt4;Tau2}$Tw8;@Z}QEEb)|UW$e?PhP=zp>)p|5k ze;&fu-3O4LddE9~{=)W6L5ktJ*~1DPZmhHtUn>vbOio&S27U7pfYwh7i{`i?T6c_B zLWIlyD2Ld$)}yWH(-g)4wR8v9dQ8KDM9x9DYH*Q5<=vA7Xe~Uh3*gb~gAfh!#)-o( zFH2E*d%{twwAeO9?gvWACxKM$K`7QcYdt*Ky9!#7a1zBT1#hb^kQl-0?d!O?36^FKBe#lE?KeWQ0x(9A7>Qa50+ndnG-jq5|Lpj)>>6;4oGr&FPF!Cq- z{Vw0~a&{7L0BGazXoxk2_Zgg&B>){c=HFgS5Slx2aRt8C9LAnr`jw8gqE_}SzSg#c zn8Tdc+rrNQ6yfd>E{|9?6I0FDHxYjoRr%GTq8% zS41@O7hR5+f_So9PeXl07eIO~p86JkP z2%**NHOOpz?oTe+ZVM3JivoPbn%8YJWheCS z93VgiiBsJ;h}v~G1Cqsw?Rt@^4;z=qIfqNdhKx#wQ7yX`o*h(^fsb&mb}i#q3@L3s zAa4Ku;}*}paq`D!J`5K_K12hvKL*_vb%w))InQNZJURRb9)Vppl|_O3>|^Y+U3#C9 zDSpDXYbSUjBjLvsD9)jnpQ3D;ZK2#cT!*79d$(QlSP`)YcuKJQ;^i4B~>V0V4aOL!K)VTIUBe-C505aUvKMw!WTTTv#6DZpAQ#c&ZiqgDE z29@}_`&XRh#06l~hHY5|(7I0%Y>W!f?)N&fgifSe(>a&Ywy<=~#`g30t)U3GL)+#CEXUV}NG~tLYC0 zxW*lGW6*@Z0Y&V$t&|kPl{*I}z27MW<2=wg*gjIvonetL0Cb|n6Wj3^mds$sUIeK4 z7?h;LH*ve!$J_m?gth3}HZ&T`vIIa%BWS6Ft%ptP=Pvg8(G1zCUVQa1eyJ)3dP=|M#pG=f-d^LwuMN7Kci^j zagZSJRwT$?e!MGzvC$_}Z&poKhs4Lkw z#rm~m5%!TyWh$fPbe(;lfE||F3$ai&AL%Vj?WzVKUvDqGf%&oC!kpz=PD~dw+;$)( zSeqYW;D(IJH&D%|5-O5JPR^|1)rFf1?k8$Kv8hKBINIOvb!jD7R&lR+k4{ z+iYT<$ZD5~lqi+ZNadgm`{)l;F8jD8G#WQOfh3wg@io7Vdjb(v$EO_dEm&Hg*;GPG zlKWpA&-F8&I59e=he2~7eTU;9XaRUxs7)Fe&%bRGH^XPyKL+q81%<+_+2&A z%LC6ndx1`p^8-s7Uk@p9x?{g1ynnyl>A={$k=8i0=E)Vs?@4M*s@Hc@y z6=QZrs16X{55sWI*c~e(XWWp4`2S3zs8aL%RJ7E5a|FgLrpA}gw7r~j2djq&V>wm2IfzKlH(*n zH9?-QFEw)~*)>Jw+AraAMdQku!=kh~ngJ4V$`IFS*cR@9hke*ubE^NMw%FRany#ew$P49PiuvTN9EfexV*i@D*P8^;? zLBRH-jpMnemB4h6PIHs!b5^rma;O$)w(>NR6W`lL%A5Xr$0ZAl5#Aju8sfXAfa ztrNSnm$9T%mAw%#3MHT^yaNE*HP|ms>^P@SCaHP^62o&@X`RHGDjd`q=Ab2N zocszKyZpQ@-SK2rkj4XBH1bRkd=QsI5EwQzI5Iu!>{B#P|NLzu5!PD*Goo9JCs~01v!FFth)Xxa$;g&(AV(% ziiD*C)|s#)PpYY}mu|ER+CBU)jzK}^c)$d=+GI6q-!ZIqeNcO@O$AHlO!!vuLR?yS{ys}9%M%oev zoxYbzWY*eLyb#4xZWd%aP*2)8Dne!hJD7f&9eK z@0&D(mea(`G3a~5Fc5_%UIM-jI+3%;p$y_IUVaJbjTH=SJNDdQlkob}wpemKPSX&S z)r!eU&*pev?N4pfbJv*Va*W4v)PAnn0qdmkb6uHINVMievrQsEu5=#@+!zl52?W%R zk_fQPp^(9U+0;#YBAad!L;todl?c(9I?M^7={7$RciGfTom$e!lFiV-&M=8#x3$hp zo|efjBL+uF!B_86Y8FW2A8nRW&<)4b9l7GXUb0yZZG=gT{a0%UG@nMY+4Af(X~RI! z?U<_wpQF5=FrV2-?+KDod{oW=(HTY9_6q+vC#A@s={*1(8u-BKF1u?RDrF%DKN{7f zEUY<|=r4-H@tRsq;s71VX?S~R3_IBUy%*jVkSqk0#gKba)>wNZ@$U_8DZKscCLOIs zi6B+=ajYNv|LD5z_^6Af|MJjDCy-E5A)%8(yGwy2AT0?bp-4|I^qS-fy(B0$qy&U4 z(h?BG0t6ILkrM18kYIZ(2tg4QDZz?LdB5M?z2ko9^Ur-gw=+9CzuDc{+1c4y3(guQhymEerz+G#jr8BaEs+-wOW7ACXg zo(9SU7T?arF|Vu=U01)6!vL6xJ;ai_))i8%vX3W$y7e8rGLf+zKXL*2$G&c|!g1f% z(Li#Ea|+w591n3C*qF-VZ7DvmlbntP*F>0x+0nqNZU}HxPDc~rOZ}sbw*L&2ZcO!* zwm*{PxVz|P0<>h7zur92+K`{bAutP|ts)XCmd(obhgDtT$s=C{bDVrppG&@|&+~hT zu^v_xIzUTd+guLpY}+ykK3CZ_ za=qZ_k_0)g9I)!Wt!f|7sXiNKfVWizC~!wJ2X8j{SVKikPJxHW{0`eTUVoF-=?|PM z!Rl%O70f)g7jn0$b{QD%a?b~@uT^bL$lC(&9{Zzn%SVw)vJ}HYylmrVReFnKa#0VF z*x0I`y(WAys#g7oRqfta8B*sj0X*ZOpg1*O4evP>0+evsx%6CRTL^ zhn=$&s8_s{mb-5qeu&>$G!+h5$$`N)^f0RV+<^ZE9wjwN$nrAQ$bSW?VdXH9PbV6D zACI6~)=xO7mus$TQgE1YU3`@Fd!6-M?n9G`$L4Yme|Ei+Q_#vi>#h8H)MOR(w#O6N(9e&>CxlfE@QEXyFq8L_cxE}BP(lw|ReH~7e zig2q6c4e)#s#F^0_g$BG3ZSYx&I!eM3OP4u@vfspZ_G++4xW7nhF(d%%o zxYkq&A`2w|9b4@$eU$eZumYg08hjJo5mHMhqrwKXBi9+fC%XlU^J`m6uU}7N1ohLX zdi9~;R!Swp;!=EaPHiNgx%qIzx%8TgnN6tXaYH#w?_2vfV`jJ0eZc{=1*qFvwsvaO zhK_q$XloDAqP^BaG%*F9EgSyZTPi^6IETXuAMU`4vKwOSob4vKyfD z?OIE-29~M2`*jbQkAE&w_q#zsmSfTGYE}Ly>e9W$Epiz2ykWs2UzlUDrfz}KMo&DK1bMzsYx^)AQ8>mZ=aXr^Nsjz`=@W=HEt z<0IER*DAvp?A z^&i|5P_r6VfMWo8-K6&TL|gJ)t1I~39v$jQU3~92`gjkKo#;0C-bCe2S$>#&J+)hs zE8trwJ#!PD5b1H{gHz_-CeGjyc(;e}OtLDE z0QKQ}(7j1khwKZQYVTwBl5ABWZ`6q&^bo6(_43G3e*!Gk`U{RVH@8RJFqXT0hdi{$GF!?gS<> z3IJm%1kX1bX7A^K`&gR8DMEPVQos%IW?ull_ZAL+^0&d48}`f+9lrNN>31kSeWyo~n3JJ9o^H=eDD^3Clb~|- z8FEm3Pu}0J9nQ09)mP;02UXfcT-DjA74LG+5&k2;E9~76`Ca$L;$N1q!jN!&v%04t zzmZ4lKS7@jv8tGNPSPvH-CDs+R%>zAZiw8x3La|p3B=Jw?d}cM$C&E!(yNL?kRzJ) zuPK~P4DB~M>8|&vvp&bvp=1va<1gy%3LSqnsB+;B9y>E!uD+p4n~3VJKFxG`IXv+w z8)B`W0bf&!TH~12mw{l05%3GZE!TN=Cc?HjdBeMCr()y&vv*#nt?{MkdC9c3+%|I_jRuO4F2XzN;8a~S^rjhe^)V;UrD z3d@Fxs_GDcZuJm{$5>UU45!X*XzsCkZ_7sSplZtSAlax&kS2w|`E-{;h|TfoPqJWm zoVF&00``9<5tVJ-C^=v=wM-b1^McceszziQO8y>(0vC<3ehL_lt4Lv1keJ8GMd z`c!`h<9g}KzkX@dfDE;8u99eIz2X4Ch{cIDnd}8|0$V7@n7@+9Y9Bf0Ia@X0;jv zB$|NB>ADh^>!#q+`9B%|159Gc3=QYd2{Z}snbs9j&a{b|QCJtAOl>0WHZ&o>a-lYp z`Nhp$uwzfA1_{e74Wp6TLc?$gn&LD(>)aCG=zE|U-W|=Kuf`EoVGv4>G%`UI&YG_t z5tajwUlt6|s`r|i6v$v6h5*#r!-NHZ&75Bg3)ek&N+`fT_%&?9_2<5KPQ%Dy@=8p9 zvLU;fY~U1SgH<~N_`ek>-1Y!lvO~D|NmRCHbE%Lt(6d72Vcw^kk)b?{M_Xwc2M)ZCq};R)av)~#yPB0s> zs!{eXm8QugEK97#uHH7w|8PhYTCFUk%@PM1zJ4ZLvvHpn@%+Nlr&3{0PsXUD&rMQc z=OviL)*`Dqd?v4n#PwVUWrR;wlYxYLf^eDzH4IU$n( z%KV~LEP|M|IA~}kqcpEEric>iHvPyvEt?b!%ZIg7jXV0@@_$p|TkL@wt+W1B5B34h zGOY(`?mS=;d5>tl$ByZX<}&)@$b}yyzndS_4}UQgRLXG$#m@aO0eMnJq~s z*%|3j@lWVa#5Q zM2H}EI|IrBq{2lN54|gfD5W7-J6Bp&pb$0AQ28HG#`PM7>+bw$)P0GFhJjqw>#emG z;<_XGWA)+W@66LYTaPv2eXaS{p7KmwBCr=8)CdTHa&^8TierRHoLy~Iu@tN@5}jYH zmvL&#QE1#62yF=gs3T%2T#l2`Cb4Rbb%|>xjKTa|Yi%m71!7&1eGyu$Kb@4vN^pTO zxN96bXuWQZ9hQye)-=b_MqYDMgi#6PC=k|6=Ny#7fOF6SP_Nz*UECySI10|C6vk|E zYBz%)U_1yuZEYeB4-G&%nt!a(6C_v}1?<3ySfiiT!<^gpNoe|X3lpL%oQs@m@J+_! z(i`>Q;|6q!{NGfZ+=|6lE?-RZEzv3IsU~rHlkN#dK2C#r+H6(V+9{gp{D_Rwe;tc6 zz{Qo8rgq9>Tl(W{r9vp1nE(|XL3o_(k@Ca(8FWtJS*DUwe0k#H)<`n%6soH`^9a57 z%2qy`x0J!}Bj-w}`%Ci$Xk+zB-`;N1$dF3Q3V3L+a~(zMJH;M_VgqQ?kJy#V6k?&z z>dUMgUv_|&2175(2JdduyI6AW0I1gBaJZq_VTKLtY=AP_zWh}LGv+hpVfU!DKEe(@T{BmTZpQm zd*E!7T!g+dN_{a$&?11cLQSvsM zIrdpsxH_*`{%?hMS7Usz=vf;J=_HPBHCmZ)^eLv&l!8{U`6>0mgbADg-Pd@FP0#smr&U!bnr_1MxXjrqShw45$UgcWC z#j=DN;8;oXt|I?{_Jhzy5ulRzTS%<9c|aepFpOpc`tmhv^O6@4pvjM2)H8U)Bj4asSMx3EzMqvsI)+lREZH4r$PBu=sXLfUkj(fGc>i0I0G35T>* z=Q*Q*FX6=B;)JjKvUx`rQ6&P_-|B=H9CFrYdT$r~QYSp-4XX-5p)bG8Sz6Z3BtK8H zYH;{(b0Xyj;Le!ydk})<{sh8yg#>q->wE7G(#{a>#h(E@<)a`ZAuJ8*q>zps(LKxo zvqQm?)TbNTe-@zQvx1Y|NE_hKDLCG6I;MSH{Is3U(xOj;ajBGCg+om{&SkNcMO-pg)#a{I*qH-ylbO zLHYV}{j!_ksfT@-`R`a&f)?`kviy&yV>+5VnQ%(E0lDjV#Z+f~bSqqZWR`aKgc18e z+}hF9N@c++AX?DtIs-4Z^i!ZuFd( z*plELyd1AwhhB4H#&p6?N@o5(ABn3W26C?}Bphue`vKx9{Kf0LEf)3*7YqX zU^p92I*tJpUg?RH3vc2+uOWSIg#sazsk5oR$k>60k|Yuj{v~N@wBv3_5eiQ6NPZ}u zH=%=1TW88#ZLSN&Z(;25kSBlUw27_@#j-mM`5NB_?U*Q7r82pkbOai7_d9^EiGn^j zH8T`ko`GQ!nw+gvNgmpMSHUxdsIpFQO^Ww8@T>NxB5UACC@+H_`hAmFbVkojO7jCy z*nAG@I3)*gfhpg;q(yt?1Zia*z@>p{E3%N91-GUpjD7l{h6RhgMWGEvKY!%sHe|v$ z3F0>raIfabC*Xq@5v5dl9CJ#6nu@yzQk#jyPxN#rOQ+pHJ|g*GYCYB;B)zC?wo=Jf zoEe4pS+0MU-_tl;t57{Z#-w9iTrq~SWm*7d056NdCQ_b98&;!__(4}gz4U>rE8axb zW239ffdcmABIDWPXNlRAAqcCOr>XdZ0A#wee%^*JeR>3H-jVOGg4d-EJ4_4 z@`yL7;ZUW-=otMk>xNzPec)d37P;Zt`8h$S|G+!ENXmwEjaUQZpk%jw0T$0<-&FP* zt_r{RC8W=186=o);x8pQOgNWCqm%h72~|#Jx>e7bO1{>PWB&b`e^K`(9L_nHm{^(K zMbanAIfdfW&GbJ}&MTCPLvB=%XSzYQTD%8?O10!8RLB((>#%YZ5OP67M!x}5zySG1 zL!1jF0xH~{;NNO+W;+vJr03FZFc&Ur@XFn(?Ofk>A6MVOp#IhxE<*FLm>Jr>Bq8fu zo$$TE5TibnPi*b!f}s>Gl?82AyR;I-|6a5pf!nA8J?QE-`Ey{%Em11ck{ zA&BWGIAbsA1&FrO70}8ynPSD!ka_pzZ@G#c#`hYPE6_EdmYcAg?EYS_KJ@ooN9n$r zMgh2KlhaLjUXpWtdG()F&EpIs5{6 zc$9lq7b!xXRQ(ENf7z<6>qz|n4X9;i_yXkN6!R3hmiyl+qrJzl+#@}Q$~(s8{108+ zL4a*qfy%pYP>rjdpr(aNSW)y zaJ%xZ0>Kd%O#_<=I}9(F{-0Td)M$NA4?rz5R-AaOI5MLMZ6YZ^g8pL$bE}RcSA&EZ zo|mdkSh~d%TQKKSlu{|O=j(vvnbUYhsb~{k^N|T_n8riGIk@X8cpKfM`!f*r%);{* z%^la$`aqp^$+wF_HJm3l04O&B4yl-5ttvqmX*X1*nzkXSUL&%6<4pueS^mCXDjqP7 zf79*KSn?!`2~bw@H4U5(Dq~3wL!KXc0snU$Ou+x%xLfgu{?9F=58!`T)eSRFAz#qz zJhOq!b*HX;N>T**nZ?$d*0D18Y;$PxyQJ3`ywY5lZJV4s;od7fG6RWWMfC^afXmAaSy5kZn>RL848c@MT=U2xdRMP@Frr^E$eL8 zdLKxsg};l0!dGuut+JRqtY*5nrzoz>g8BZ`1l2ItAsp=Axvd?bY`6so=Z$C!7subQ z^rcC`TRf5$ol-4HD``t)fQY$kRl%3s-394lL^JUvJ8lkS73#EL!m{6kvpjRxLL6qh zCs_(HuRmc^WH{eLiJE)`Z%0S(Wuk4j2U^3xn6-bXKdwL6)JrPtkZ@Is2TY3aNPr<1 zMk@u1bfuoVFNbbxz;l|`N1DOf$Xme2A0)?g14NLGeedJKd`OG~93whR!q`)hgdJYe zAd2U|>T4v7+_WJq_yZ)bks$}PnWW)Eo2e~eJU$Me9tr2#ClSyvb!{qShRUNItG+Y> zFZw!1v3e#+ju2)VYHt?1>e*CANNTGN*j&`NseGAiyCXg){5|BHbKYiGv^mE+k#_Er zNcHBt#jZ3g=U`_IW12Vh60My29M@5xcy$rpWlB2XOprwjQo^a&q7}TYN@W+8IYNPG z(Z>K}=?#ls-qkml?Xe2fS{!X;Q`-o#(FI@QVRJ~@Jc#ZJtP8zOY2xcENaG)qV2d@z zgLH$Y^t3IIfsRyv-Ia8Qi>!}R@$j)T&8L(eS20E}dD+xWaJFaSXGfhxiWr6KeDhjX zpm@!w;B8a;J??Ab%;K=OO+_qGrtyIL*ygwzNWf6=(G5@;Cb9wFQwU6#4H%SLPYr|9 z_sGf2j~muM;df>GC-Xdw$z4*A}<(>!RQB2`LKTl z^#>@tpGntpkiXJ@oQVi`jsu7MaEfi~s$=D18H+L}rjc@Tjb<#Z;dvG`b} zE02f!iva0pdFx*-+ACSRA0H0_WZO=s%(!UDE@c-FM#M;S-NiJxGQdX*n{pO#-N{7N zqu&O0{3p>YK<@mBjIgqg47r(x0OYk3J!{CpH58>=kLw&==5<}?L&%fjrM=+oJwUZL(2-36OL*vc#y%zcv01j)t!KGPBBRMAe) z7Jhvm$rOL;)O5@SEVU+QeLi4VlO7Zm{ zke|g0^PHb`=C2k~ml$tl&qvK0|32jxV9H0kO?j9rVKHt~ei5o(7z9&u!OO_L?w-bc zu~`Jg+U7|iQtV4$DaP7Vt;WX@)>@8(vCue};M~ z+AAAu3RNn`FaaI{C_FF_UV_X(A7xSgQ@{@^q)3MEH4uDrM>0tHErXwQ5qs9u9hQ!ovA$FU!#$oA7 zl;&mP1TQqst#lPiS83e)q8L{LRF-K%_Lw(kB3kjKh45^k0!kwJa%rX67=g*pcq7p4t3>+I!b>^x1>;_8S< zz7(fy2G=;<2;3xX2+(rdgcgg2z;ctQrAaom6(ndgKo^F>36Q=Bhk?gZkcEkPS>$Y{ ze{G8se!HzlYr^sJ;(Bac442uqI+5^BbO@2uYC+MKIzjpC@x;2UpV`}{ywG%ul{w+* z2R$NG`^+sY+vnD0n-g9!6zj%6`P;7GCPiAZX1FTxjI(~u1GX8GYBH$qaF&i6hVGPH zRmBcL?v&R0REA5=vp`xp%%qbsW_W0+(UN@*pcjY1lD+rpkHJ*l33yag+ZetisgizB z*3V;)+TW%wE>L~Ez%EP}ibT%N?EyG&Uk_gdnVA+mEC_hKbW1o>D!*hFs|MJV7lt!- z7chI;^a~QkiqTt zsFoy_jzIRk;?WHo5(7^dK0D>UtAz(~P&HU@OKEt$%GRvkHx3c$myj^pJ@@ZxSZ*`) z{ILS{bu?}rX__u9TSh^jn3mwJ`1dBOC|4Wchs-5ew%6pR&&S_4NIAAV49h~}a6J9E zJL~~r`3775%5x4#X^2fp>3N_Y)~ID7X>wizvA6ALr3`qCe}pjKOSr@fuasGgKA5N6 z#p?h&ilH>j_CAtFj%_Su#8G(+Y_4h9sJPm5O&h_m+=GV93hs#z7R> zL||5cbW-YUyJOnq`I-4M=g*v$H%ZiL*$Tm0$CB%KIE#{#Q^dYjt%Akf;@J8k@{gQe za^A}irIB=;9OjN_2VZlWxmd3Q$Kf8=M`P6IjRV4)M6i4rI$RW4;iM={$6-e38U32# z0wBBvmQQ{hY8Pd@&y4ToNzsm$Zn0G%<56*+pRK-7=SB^Iq7 zID0-u&GH;5P|!nGLZ3xlgkhuc31niisSA*;fd47*ujIhBSCnf@_vfeyMd@c?D=B?a z-W11F-0hk@P*lZdDNWrU(Ep-N=DQxvg=M^+I{i@kxhf47x5nGlb7q53`h_a(CqgEG zpNtR3Dv!P#$%6s^(n)LY7dR#a66_l5Fg5&x0sjhepJ;ThPlp8rrvHnsyKkK;cDp^xZw7 z$tB(v=w|m6S>Kt(wQ08Ha;~xdC89>PZS{b7f4a6&cozCSzI=vFMfRoP{|{hmhHb5^ zM*089YBy?NgZWN`V|v9*-TG+2E^F8j79&UCxnar#ECcK(4MQ053r%tq!s?Y8VHOas zxDdjAMo#;S}0Tm`NID~XS-PpMZH*p!Ey=0%N0fE&`PXG#`|Z$Q{HC!115 z!^Bx}B6;l?)ejtcKPwQzEdEaQ6?c9~Z6sFPw86rK?H9mT)eq|}jy;mAs>&h4s=s0s z+HLD(m$LESfLb&K=Q^1V-F9D;{*KbUQ?PP|&aq)-#hNGyP#S;0K{MO7S#}N`zBkGJ ztlv@-h^yAYQ7HbC#TPoY$`T)-Sk`7&{)K(ZT-yrSd^Z07jfP^|v0({eGH5j2T9vOkG%I*{PHsIE_t&+v9kB}@odcIAm6RN*Euzcs+_QWRGRrXaC(fo_VYm;V1GA=U!+4ST6;`eMBK_e6;g0grBAdf_tEgdIos!yh zTZ&Z|;2SnXA~XV}T0Q;Z#fY~jBgmoSSgB8jgOCRxS!VAh!g339@7TIA$ZqOx*Lo^= z5AoQ!F~+!JF9m4<#nY$-!Er`=&f7_O@KoPO@!D>{^IK`N)q<9NobavrroPN6jg#!k zNoq}=0Jv`)M%Uik2=Q!vHW^1T0v5vcN3{&{f#4S>dOc=4C_9JSp2l(F&STp7z)9&( z0;`@uipQ58*As^dvT&1;SH^^lQul{ z7l6uDRxF$nM3=v-`M@}F_(_{h664y`OcEx&ytnRt4yK_>*lwTx&X_Ev|zC}Qd_!cDm$*7_!8qY{R+~X}0S{I1JIYKv|1RElUffZ`e+0cS1%E{B{wQpmLMmLZ)gb3* zI6rWocX+b!EzvzyfZ|rVc&gffR|JkzGRXm~HDNjR(?%dNf~MK^$*|on>WP~PgorqC zOW0J*0Xs1=PHf(wZHi~`zYTaQs~5Uils~O?7Y&cLBwN`aEm(Mu!wZ}lMRSx^A*@}T zShP`7;Q#iZP`UvA>B0{%Vhp$SIsoMTPQ7puyaKwZ;)k4A!4q#eD7cV!MBLCOo4WT% z1=5MlCf#Xl6RS4DRTxxvk<$2w0P74|zP0^dAqY06TL^y?AYqGeF`)Ey=z2cmCVPy$ zMFX^CDo$>`j+u}7>7F?}2B3mP*iFkv1`M~>V*x59!Q?@>MtCo+cwUlu;*p;v?9%-#a)GFv1!=rND;_zawb{f;3>rULS58*aJR57M+ zBsda5m=c^OaY?j68IMe zwQ(3Pl=f8M4(f@qj1P3kI`6T@2`-bvflK`Z zWOh$rtXT1)mIHSUeNno1b5p$Za}d25ngkVcY{wX-Lb%WEM+VB)g=C042N2MVnhcKv z);~^o@3Nhi%r1ut7y#T?FZ>btP%Yfd#$NICsIf8fcvy~kS_=cARrlD`k#;Knr=s-> z<9+(5(^*4OvowIR-U*2ozQ0Y!kar)w=>U~KjL{1acwRUWkLe&Yp8V1QpgKsw@xJ~3 zX}D2zJ_`X8bznLeoPdLYv~L-_<173zmivE!(_zD-vKdaeWf|OdTYewvB?_x2C@-9B ziM^a@K+5@Ls)u5SMvDAs?kcjNNnf$4JlTEme~7A-ASAxeel1a+WZ+@s?ETu$OM3~S zdFS@qR`_xIG8F%ZL&8aea%W6}#U0a6wDiTCH*ozRXn*|(X7BX}8J^{SEII-p-$&uD zywd?E1%^}mkpNjAHMJ`Fv}+yl>luGPk^HHDT`}u2V($zzMgcLbJN$lCiFAm#M>LKG zs4%=$yohT%NBPdU1sQ|VnkxThl5&%eK{@XT>~up-gcm#Ar! zPXv0%a#L>=>u~*q9XBusXwS!3z$XEov)nYCaBTPEvsFvntxpCh{7KVf5pyux4{6`w zfe5>rt(+zfz^5qqEVa=;U#+(|G_a>i{jthtkg8jBO7BeA;xr8##(L_!k{14S7O(LL z7%ZZD!V6VYh;5@mlh24lPITK$IVh=0XSxREwjFpvYH#;Y_^uv@{xADe-x{JHADfu5 z@9FJ5#g~WW@;#fnb?^ZGBWMZ#+aNO8=7C!WfnFl!19X%j{}KWc|9W3Jz;hUc}@1yEc|z9Os~+iOz~kMW)z3K z1kZ%CI1EQQCISnbdCdM>3t{-#JQ{tUvc|m+bK!rYv4R| z59g4`JdRVb!tJmQxfPclml}RI*f}a5|J8lnM72>hAYS;@_%cw z;!QmC2;vxZ^(H9(CIS2?o-hBeZI}FfZUi3V=U<6Tix4M2*Q+28gday2-1@d|nAkM{ zr!A!sn9GLz!%xJCjbGSQ8Z_D*%YnaiJ#32PFEy3cD18#8+ty=adFw$0ha`EiSEwUe$j?0Amoq;%Y3t|VW_QX_K2vFfon?vBvAp~--)Lbc#DfTDMez+uaIEUJd9 z8LQcB3GVd6QxGqB>4+|N&Z%Mu=bWvj$XgK6SY&VL*-*s183+yDF0GDu_iH^TxcOak z53wsPNjO}uk5*@LG!oWIunIDvZ{jKWzqP2b_V5v53nQ9~)#tTE#woE*G1R=IS|3qT zORPts&juJ=TdTFm*dHY*jop9)40!dh0m;#$k$ocI?!REWcV%LO{NGdz-c-90oqEV@ z@QpsthzI)9ptNQKZmVtHKU1B?a=33)rOlz0>iCKQBPQb7DV#fUJW$zfqTEwDqz%Rm z$O)<%*Wt~m7X6Z6J5li+*OUOeI>aSt3u<5qo+okx7Bm;-r~Dczq;eohvOL7xO0va= z{qPt93E&}am7PW&1yie(I;r?2rnMBwKiDA026%_Ykp8X=T%3CvmnB_X$i;_TYy+s% zMi(dv^N9mi(&u+?chWB!hOgM$+^)Wjo%sx^S_b%aQdBd3(B=pGXh)pb`hz}SAeYbb zGc5VF@r3Y?`f!-V&q?Cr#nQ%gT8pI1_`vGS(J_WczjvaM1&!A<8XtHZhRUpZM=fI74svLXl)q72KEDz z@PL!5A@J$|KuZk`$^pa%4+6AxE6$gtTnu5kuacL_80_NY(Ye9&eht;`1U8Eiq4D#T zw~yPT*IA=eY+4)9{_im!!f?)b2sQGiMy82F(_q)^{mrJ7GuN9pu$2C0+aV7g|b+lK=Dx)2X{?dsuzu}!<20$JDHZw zF(4!jnq;3U_B9HJ()LYkCffUk2PmHcA90lH3$;!~oGAWFZ#}7?j)VQ0KfNMF)Zbcd zG0Nvnl-_9W9U;z#5)vS0cFK(u)#16y z7fl)`*y`EtY3(F4E@ko|8Z3GS%A_C`n!mhz?!B}1lK>_D6^N7%0Ie_ADvkr_6lv9J zkIRa(R(?NSi*w^NYSr3-g9m_AH?g#mHLAL|K#tnMu(GJ?x@`x8ZDg%FcC|U>nEW_Stf*sG(N_Hb2~fkI z#ZkCK%_!1u@3A*OMW5HT+axm-(`RHR$qX?87guZCpYHiD$kx+j$@u3WYng+`3nf{D z?7je~>|bT~B^$VFPWw06eT7=d&uQ7o-ly!&!Xwhqt_%=<&DX&Cd_oKTHUAXXIlzxU zr^O|yP+aGs-y7N04H155HLyb8=R<)WdVwJA3n6Fwl2vzn1LEiwha&oY3e{zF3_>8 zTx|3Z>>$66%>97;fWG$m{LjVgN1z{h9tH;};cT_haJ8zC(q(|oyC|^{xo{Hn6L?D+ zG;N{?uiZFE_$-?N*9u$Wy+qj(<#q)FrmVtEh3Cy*|6hjx*8umopO6D;2mW8@ z2gg_)UAb||(M^%u zRi%-lQzJ*BTzX_zaOwHKc&Layrd&}|YXV$Oe{1jc(}2_hXz3o)T5&$io*>qR%~D4* z0-*oIi@{;`bCSwN{I3nvZRMt?h}uA`D#?x&V-Du2!LttVIylT|W&cuk^<(&7SN9kjz!p&5~P*i0HsB@>hu&x=FDm!^2X@r zzZp)^FdmW&?U$CSV0R+z>L7V2V2x1i%&Y)^VQFJmX+yG5>Jcw$+SoTpM$+)#6Id5s zMvk?;hY;LxzOB}P*X<>BDg%p0it9t}tmWpn1Lb!!kxed2|sy?veRTxv33 zvJ^WYtrMQo!MP9b=E-$>FkpUwB@N1-GfAv@pj(hAI~$3|M-RhOU^rH841smDt7B!> z@+ZZb0crh2+3LA!xR9MDVCUR^6E1kaVplVQ-_w-HMFZ3N=nsn3+ARaH0My7iU>cx7 z%;?2w?~30H)a;2hL*ts_nKP3QN8x=@N6kRw^Z~eqoC3Qqy>4^B7Y%Ejtl-gW=PDzR zpZp<#{H9;WzO}MEH1x^?Adb6OHUKUCi{p22m;B?y<7D+3Le)#i(~EU1hL1M$Od?$Ej&4n<0DQXr@t=UIT3ztJHEG2TO2d07{f7c~OOCRz*aH#3?QUNpZ??F`dn9PbypHj1DJ@i8 zNV08IX_#2O4KJRpHrbWQNz1=2TUav~hcsbR@k&9_;53}O_aCb6MM*xm2DH;yxM612 z66MHk&*JEqW)Fz!GCjR0#|~&N_mC+NTiSV^!cysj%M6_stD!s!@QxUG@%Az~9w^aH zAj9D*{2Z6v zk{S}%6ZIV91dfRpy?WYJt$MA6H3N3-7JUKH<#m4tUtXX8rU?cAqId0NT2?nVui3)J35KtM66-kDpFWA-r{k*-N){bql}~z zFk<`I*UBJeE}e;J3i20zgB(pnQSczRH_pHol##kcZ|Wh(q1_X0_SH&_|9i2`V~DMg zb5yd(l#i~dki>8B8IGwS5i+v0)Yt>bsO#ku(O*>WoSQC(0{baNu`)+k_w`4DmM>sT zSNGRLi*u(pYJJ@WbH|_?`^1Z?0lGKoBYuGGW<>;~sCEs?p)X-qk3%_N@F11uDD5HQ zXzK^+oyx;gjKjGE zLn4(h>&P^J5tQUt3G$v5Uy`L;V2eZI#a8G4O#B}jk2JLQhhzp(IXI~(hCzg3?;r-j!0hn&l3|)s zZy-GgO2h0beuABXLS#7!E$ zQ1u=4Y7Sxh((6QtvK8ok!`{aNOJ}rQ-6f#3vGuJQZC5XKP{QNE!qWF~P?~ueX}nzH zLQ0c@nV{fWYh2&0mp&ddPGqx|ms@1$YVLN%BMdRJ9r+l=#HLo>dOx)Lo z0+Ja#K25I;94wL(%E3z5vGkT&4z$fAj04w3SV$2vb~HN4uFhZi-+@LC zpEUIp$&YJg#eSNN(xq!W1}JIeJhxEo!Ki}g0F-pfX%3do)=g3y&t;R*uXrHDsT@HH zhNWa4s#TqWS6>ba!?>Q$YPoY->h;2qlyd>9)rgP)5mskhosy?Rf<*OaL*O}kV=@N6 zdldLWg@{l|e617$zitspZ<~aJb*1I7kC$VS7RqAO@V!~5nFzfB^PzgAy{QPBpA{?I zul6ocI3XhXo4Gi{#bej9_t?FK2oalUS03@U_+KRd2MOO<_8}699keuF%$#Lcc|55o z9*P(B^6cuh7Y@CLLEop!q)kEgt*h!X=#YH7N*_g+`XfMHU4_JSu305f*;|id!7ktr9gP*_BaLN`@MJ1d$Y`?>YhNgmESb%E0R<6#GzJPj?!~y z;IVb}sa*0n+n)q6Y5l}bnciGfig5Uu8EAO zY-1(bDEqY)>`r$>eHD34p6*n59BT5DIn-9ii)*v(DhQDjii!L6^fZiEcM@w@eEcux z(Bg6`NpL}43*9x>UMxk!RqrX{ZvCz{k}y92E7EX_VjUP<`Z%1~(ojO)Q(Ss?542gY z;LSzxeESgjSzMG$P+Ikg35)WJTA&;gg17_xn~7?#K6ORW%DJ#>Jp${M%tLC7>L`0p zVaTerL1F9NS1B_?8?HD!4R~mgzus*8!`9je18R}BwaC&Ypcl;a2TQO`T%y<(IH^|MDYrrJ)yWTw27D{v7e0f9bI)$1H6lgHR!S+#3v+#vIh5iNN-(;y z91lYQD+5dpnhSQ%<}Py$)+bc5Y=HA(8yeWR*;CJp@&avrad2-}ph(CI?;}vp7 z037nUN$rFrJ&x2JC@nl2skINJh*!D#s{9yt8PfDDYc;Ri0s(6a5CVy7WRl+~fSz;m zo7}H1?odX}@)DUd=BQ0{C%|{2>gw%vUR^N`HwJ*89i_iWJ8?#x$0O zFjvoAic4Yr$g`=ax#U?-6mPdrH{^KV9WO>drnLmA?E$rYqXR$tK5q!R1vl&BbpoY851f%SB4rpmG$k*Z0MXlTX-JxkkauASF&chK()X1_aYo5oJu6Uq*j4@5$8)zZ_ymM~I328_mdKmk@ zNr75T8`ilaZ1wsofA@V6Hyy44aTKJBwzbxq5*KON$y@@CDb_^RCsBAXJ21YU2y1H} zhTF|LK?GO43N})7+y@T}xJ&nVytq@MrOOHSCYp-9;9PhNB76&=f(sBK2}s^3vbRxM zYe8$RhYS_o0VsT3YgNJ3pYNi@r}b8i-Tt0t-ujX+VqVwzHWXExkt10u2*aJL_xS}& z7UDpb`zy%+r^E+nB`2^^n%c`4^01!(DDzvS-jaqEJ%Vzu(IfaFiCyT}5W$RF^w;q7 zPohT34*^=gu$NqXpMsNQt9`AU5Zoo4R(#J9;q6-^i{QHn4MoMz4a23KA(^ApD?xAR zMOaY=Z$cG@!go+bER-CP-WhUqJUXPGoZGTl&c=^mK5Vxm!*`{RkEpQP+l#Wqxmb!Y z$t4aa`NtAxt8A5ZK0&LYm#{Z+?FU_R?^Cw2xG*hQwjzsJ<1-dlm$Mt)&7}Xb*sBO; zlEL)+oW&VMPSaEFIG8_0%7*6u7eJ7@JnOSOzCki%_{+VBA2(< z%7mW4uK>#55P&msuDCn`lbm(}=q%e_Bbr7~uPW(vM)B7ywk}PR>J~9qKWE{thI_qp zL|DB=)q~p*s$6nHRem1L75sn`GpVXG+m3DG>g8(Zm*mcG&`eal;O1z*fF<-ry?0^< zej{0U2>Vx;*kub;Ki{H-j2|&~4A%5Tl&=2K=~b+Jc)l8+CRF&2#4;aFi@vvMlOo`z z?Gj7-gf;#BjCl*WobU*l1V>}!1(qb zHLR=1X#tVb*&^#vJeM^Lkwj0&!7?e=6NaAmpYRD+E@O&$?bUu~E(TXn8vYX;tWu!K zv!|%VjYH!qKsi4lFsq_@q3S-8xrQc6M)-Dj+Y%X4vt5Uq=4H1ndQH68ga1;#R3bM( zHS-EyFfr(+pHW(Iv9Z=oN&WP$lHdCazp3a|JZm6z1HE5zI#H0n#tZKQ_Ipo?|3+M| z$I@^Wh9?U?KMzq$P8;C<&W3N+L4?*pJ(t^}^bgipQ&~GyQ+& zrT}=COyoY1mLp1M&ryL76gzqNzXX8pcEi(6GOQ_j@3dHE7?AvPCsm8^8&AO3n>lDY zPWWqQE0_ha{}2|wJZ+>XeHMXLMV%($ecX44Dg;WTHW3P!r?nN;za!YNbO-`j3^8eS z62$1E_6j)$@=&2}0^H|~+D>sJ$S^|*=eO4byy9Zr;F1dcbzBGPvz2{MB8qzDXVV*$ zTpWjFq=-3?{W2Bs1_>hbO?#-wtQ!iOeJxzczQ^q<_dSEi8cJ|0VR^1@Px0q*ySmRr zZgB4y757U*1^Dlw31ytOM~I3kL4KlYYEXcf_m(zwW&_7FL7aZeepqtM&5Rc~Uh@kM z04?5AC1u*V-m0*rsG2Yv+tX!@aMt#vUET?j6?rGf2RPx=gPMt=cVjSNZ{ZmjkibnC z#+GJQjZvn>Xkhv}G4ofa)kjh{?>Z&>60!dsn2L)DZ`-p)S;A0-JrdY{ZrBL)LbGCT zhBj1k0dMREN46;cc^)Mza8EaEGCnu#UHf2OTyin#??i6Y#UoMihby#*&v0+Ci4&Rm2RiD0asK-b}%p3(u3oq4ym9Epp`dVJMM!G2BEs(B)Dwlzn(N{EiiZ^E zzhQ?1+wX5gG|pR($#ah+9r_&f!$Gl?6Q}wwVYlSpI4JQUZ%Q?d% zjEDqb`NXcSwowX^3bnZ?EeJ|->nGmIb_t~okfcze1w3)GpZ__m+B%WK{Cp56?>!Y3 z6z(!gA{`s2T35dxVqIfYM2#Y?tETT!MvbZi$ePAUa&M-;#{Kg#U>NBIY~$&yw+d_}>#e zt-0p^qN;!XlGZ-E0qO-%Y+dtn3M5%FxO>(nzARL%!TP1y3 z@ySl48ZW5*;PH(b3-2FwuhBwHQE&taj>)JiHq|pX6uX>oE*rfSe3%$3$X<{8Nd{x?0e}kpo8kUUCT;-LR7{8!OzZD*8uW$4k_JzjEB02Uw6B|zlG!&! zjP)HoSl)LJfR?x$j0XB8)RkC{{Jg#ec&%=Hk-sS2R@B#p(ElF7kV#V%Lghpqok~Mw z0A|J&yTuS^IuN{Nebc6c800(gK4S}4k_)bBOP-cN8uBn+v#SIFjd5EkJ)zF4QD@qC z)Di_XT70d6HV72+8k&Q|nA%hBOZq(+pfwY`^bt$p*2T)2j76;s@ODG5SV{Pw3Dolq zk*v?PDRo`!&!V`5-|j@Hg`e#z0YDt83`r2#zu47{u@w9t3KE@4n<0PMLTKrVRdIe| zZzFSEG3Ccu;LF|nJxpOHh_HvIDtnI`f#I^9<6kxl7F7o<6J`6f!XIS$`hCrVL~^PH z32I-`8)j}uMgS(tcRDvD86Os^PvmxOB)sdtYnOKq{2wL%2bKh6!f#RTTVK2pT-RGv zG?=3Db<6odLz@W9AbiDdjAiw{Z)ZjOr?`-du> z%+hfvUFvDZerLKxl~PRED9zl1N57<`OM4Gf+bde8IsBS=FE9$>FWtkm1}&%&nJ9kQIuzZ6QgiDJaSDq?0td1zZfbw1(k!WCDw~RkY}hdBvL!5uw?fIE$3JLiGxN$L@=1nJy*Iz<>blE49)zF}`dj^V=#I53$uPlUNMR z?fxdnf&_84fkT}+P{<33oBe#jmgHwe<& zl^RSErA(iY^=hTS(AXOXqggha(L*2j_5sFntP6e)bq30QeGDo24G;q6Q)!GJteCXM0^v+3MsW@{4a(| zXyQ;oca-89n7~clq_{RgTx;rBA-|WBdJ0rC0ued$Pa&?8d`7>WsfAd$XrAh5PRaGC zccwGmJ2WV$5^&SZ@u=%lg(R`fJ4lP#AkPgFnEOw8%IcKo(-Onyg+!$0iDyr_PwtK2 zV4qXCa#T>bn@~mE;G9?L03Gk@+s&wv+}ycx8pOT@po*1&ZgO``&aDYzPmrFRlz1sn z&owv8S=gYY2k#-fpV*ZFyxQi4G(?~0KxICw9}nXCzm2>tE^E|MUFs%GwnEtU1aUaT zp>iF!!~bVs`h_~w6@E^X9jse53dcc}p$;{FI-vMjv{v2~2{UUp%t4kS_v;4FC5W;x zO@XL8C57d6kj6UPF;WVQ4Lz@m!$ied#OoODx4!`Dw@d55Cw(3{HW>lt(&8_&+WcR9 zqebPbxVLCXPw*0|g=gcDXRwM)#)T8qp%&d;yA-I2IMfmrXs5r&sO!Q~4D9jU3F2mJ zhYG-?F5Sas&K&WN5l17ma-y0k2bs)ALy#Xa(xDbpnhkr2{Kg?HNtp-0lY1_MeW(x` z<=c%H?cE z)3tmcK_s?!s9=BoKS;_+3mQgfMUbUFgEvvH7Gfj)>M?o z=s7?Y_qGD}5Itjc=`xhQLwbuN7K@rR+_SyAKu26J0_a@@LVTxVJR+NxMBtjTA1vQ$ zKkWt}KkoslDqJ<%NR;gi!0Xq`B0NQLxEWC~U9|)k0P}r?X%_{1aZqcB4*USxxSK=8 zSkfVTLjFe-Uw4N?_S?fK{17Vt;RyM4_cheB8?DQ&@b=4k+4vVeVvVdU(aiVEDo*$YByTa;VxD&p?gSvc}7D{L-AROc0&o94hjJ z^Qj8vb(}-p>ENt8gActBVMgy=fb*;RXbwECxE3crgeK)jfFEswlw>mDgHNw)HA+b8 zV}P=QJz{Ruigj%!PO~d){>t&2(YhV zET_z*3Vl}eYNIB7e_>F?M7BL^O!eWN)C)u5qFjS3N zdW65n*111MZOrTN%;xoXs1QKmwr8!hEJKMbm3o0ESP_fIqHD!XcKV-6U z#vEUl(9QDW2LroC{%a!yb>0dN>b%w)J^y=Uh9Rjb_e%M;#?$>W12g@))U&`gHuS zBmd)Z6s{EvfXHU zC5ozSw}kZGpt=O>4oe=dwE?O7;$zlyH*3WRP198diODab z>((z()Z{ukxF)kyj_QLS1h}yTs~ZF)=MQq$9w2w`;|w`vn<#i&ah-Y^dQ=6C$>zvy z(?ndQ)JmT^Eg+Ff|8rO{zA^nhMCOf*dg53cKdBgD3L;URRCgAj25QuiN<}f$&Gi+{ z5=GwR|NRxsC0NBU@+(@9RK*v(!Ds9!ev3=Lw$xau#`PeG#oIc-m;LlqhdQ1t22Ero zshR5dOtQoyvJiCRG{-JUtpxu=SuOmB&{e$ghNLqy9BR2e2w0eg4G|SH9M8*vNY_y~ zgt>HvCr+dCd|HaAS;$yoa2B^x@K}PB(LWZoT9eqFvuVQ+4Fmt6&sRg0KYco25g06a zj=i!4ZlWSVp*Rui(Bcs=0holM9V}Tm}d2`%ZlKjLY_QgP|i-P-$>q&4;07)&sTu8gRfm)Ia zJfJtl7ASP6Z*laUMR%cZ|+DNd3_}QEV-8JZ20W>;sAU#RPX$ zO;oJXX>}=_2NY;9TflicKyttn)R)bq^a<8-qc>5)D1d&1-S{3hpcFvpCF~sB&t?0A zY)n71B&!^eawxIt0Dz7@AKY8j%>Q``hEk;tmEt5H2zX6D_zq`0>KGwwk(pFgs`)ge zph*L$XMZ?t3U@8SE!?2-O<9YpWIDM#`b=QB$lehc>>4WY$GF@5gESV-09}LIaKmXZ z8mJr-BEMCMTe3xQXDCny*SN+T0Ry$t;5^MF?Znpthl(T1bq_v*;w%;?Hw{^=&Ie1E zj6p{9+PRG-T`J2Vpu6g|z-(57c@wEs`{Mu&#e81jI4x0U;Quh7o;w^iQKhxK9*9R_ z49N?I1O8eKUW3hmjZPFti`^W^V?h0j%Z>0_$r`O0+5&4VN@F@U3KR1$;)(;` zP?xhL8K7~AqSspOq~x|OJ5iio>rmILxuefP)f;P?_fUM5UdQ&v5SM8Iydd4I;pGFh z*-BEm0PX&~d3RPt#1P&Xm7|)j!SVd0sunFw|BLJd)b$+n{n{I8ubiQYX<6NvojT;1w>+}{(i!c^Gom?fE&ok0QE*ml&6U@ZWCh)d3Pn2cNXLeUYp4$#ic z#9lM0RL?Sc%4d^8$8pGnBS-J~G1{9-?&hGU${Z?(0efmLaeaSHixHofX#p%o@jMnE z|254dst;r%^ThtVAhEjcP+U$f)CX2`fi+)a<%`UzNGBQ^0tCK^E8Wyw6BYm=Nvzo! z9Uzh?PG}&?4kFg8#Sn9VxVv3XN)FA1F#2~mnu;TDA~R`ODROQa&N~)KaDX^e&qIQd z9u_apOQCQ=E|%bjT>qD7YUhWTkpQNYH|VQ0I(aJ8;QZfV_Bp}oYLg^EAAXU>iGI#u zmqTSc3YS9I)}iK2V!%$lhoFV<5Eh=D+G1FP{|^JJ*K=XZMd#dXBnER2mRbhT-rtZ& z2Zo={UT?U=@d(9v-WS<;d-XwG2JFbjY0N!}x*>ky!$rl5x|`ag^fA^*O~xJrN-kcM z4$@<#17MG1ti0q<&na@OJVD%Z$)2sHLX?E55SN2+)PqofTg$bI;)Hz?r5RyCZCHx& zkAo~Jn<#)*z(m@uopk)a5~yp!gW4JitU_t2fq)#~tgxDl__k8Sm7CYaO);v~3JbcA zn>8rCZXhO)EJq$_8!Qj~J1dq$D zpP<38i)5o*sJA%b1$S`q7}b%?ZXNQ?8g4;sRY<9X$0KS-N7+q8{ZfD~j5d!GRbMk+ z93a_`1eNhyYxSr%R>6GQ>TDuY0oz7c(%gC$1!HPG*(s9&+ny*k9&+rG|HtG1GuS)7 zp?5p90d}CtePhkp!g3k+4lBPK3+HTv{v_6a7I5o0a}G%>bDLD2LuuuZ`eQ`QO?s8w zgLifURNAq=?6mtp&jVCtu6J)hNWsxj^8z6Ivdy@J_X^ewgVFw?0{IKi9C)7$1LP%s z_ujdxEw=$8KbBjgT^NzawM9)I!){jSRVRI*>N`WX>_Iy|7C7)+z3%j_mBTD}%c1tM z96x(WCAw~Unz;U+9y{DkG2XPsf=xP0WxuiXWg?%do8Dj1HEi2oL5(H3<}`wwBL(|w zS(gp%XWfc=x*-?I11$EdPm=HJmhymokj2^c(DWpo@QbdpK`NR#0}dCOT^^O0kj6g)ra zZAv)YD}9Q(H+cPHZHyB34s6L!90z5ScKm-A6fR6P&s3xPqAnBcx6aG3)pES zDfr59MSeX0SE7k+GtFD&fscGosiH}aQj;$LTm^8iS?2Bcz>Us1)Ue+P@ENw0;-6mX zgsbK7IeMI7g*MnT`MdmH&}HWqkdS_qtYsDf`bg$t70v@OwQc za~Vq<2XMYfgrncFQ(S*uZ`euqOIB>8vmRkCj0z2p|#TL^% zy`?zsz_n<@%MD+%L+psH`LY(W6uZa6oCCzqfg{(uKR9w^2hw~w?<@`d)x(#m?sZ~G zL&C;tg@h-3zQC5)c<@7z#lO8sYLOl+${t*RL{<&QsVTq({~HDGFDyS{dqn$FPBLz~ zzU60BTn_23(nvGfL+UD8iWdP(%CJqFlqa(O)2)f9j_u!4?EP;-U2*6PQZI%*KS%D$ zX{>!mG9NwxW9`GMT1iHu_!5gNTc#s2XbYpqGp1v&@sr-7@PzPta8_VKz*6QmoQfN^ zAwK|=JI5SRQdYa3j%R9Py)Rw*j|vC7W9RiFDN`CxmjRk{xGrvIh2x!1ZbYT!o(#}W z{4NnYvLHZI%`u0ID>Yij#sGK)^=jrYE-D<^p$!+lt}0MdQR!K?u_(v^oLe|K)VSZc zhI#q3L%n795dL51*B1w;FJP87IOE)3-mW2RUr2gj$vksQarzf+h;fT^!wuvwGJkap zlfA|5;LjQqAqwW1kq`eDF5k~<+k`6R7md(Svnc$XJ=<;XXiaTn~I#brME!|^Wni+3M)5|Ap-TQgC*e?La1T>oPt=&MS`*jc^h3-=&M4 z{~M=>Nw>70r8nSB(0=xE43enj-_{3a{%?w^MRO281G*JgSlTRQ`+SQ! zw{jAjr-0VeseYYBWFw1;`eAVkv@KYf+(kU_FD%(e*GCcE0rJhkH^b6vo(O%bU52&B z6u}0kQA<@RMnpEY+;N`rND((0Ta<5`~bhXDmmf~Qk?i( zDR>vQho{~(Fu@<7Ls$MMRRIdrKot}?oJMlm=rjsU5uPn9DxQvuA}B@dYGF|^KKvid z8EVuHiv%$~h{*V3rW!Gimm#ERH9j{W&)+xfY$ zvB}e-Vo*8&S0t+5E`|n-_p+#iAWNg<;I>3WiM;10{B|mXhR15#eJ|!oLD7;*c!H*V03j9L?|+ zxsOL7Wt)5#G7&b~jf1n5d9fN5+scYnqNGAU*^`69cI-Z(T0|P-)d#2xVG`MV*!=193Mp;rOh!qs28x z@hlW~XYu`S1m}q-Lbd*(InV<*>U!|*;ukhV^L(IlEKdQbCtDnPD{_o_v$EDtKO4hc zuvd!c*V^)qQ$BkGP5wH_XWwv*oO@?7N*`R8oFqC0&H!?x#*x!V6=EXt>iDeAV)YeR zMmcRP%1q_~qz_>?(A`Z#`iqoTS0PpJIIW!K0oq?d zs~Nr%WFQ5^+yFt4pv-xE<~psI4cbL)NzJwFji1fu~R zq@ZctvwEr5oFZd%7|0kv2NS9SU&#L<{2L~!+gnsz`#mTdnj(gGu&89UWce`iaZ7m2 zXm!j<>jcL>eHgMwE~Ts#QPWW~hkk@?`5$46oh&L0jY?-YIPBA@9qiQIvk`AML|@UR zVCE^ZtG~~S-nGn){Rtz-@HK96=Us&HDWW3D(pm*l zRaAT7rMLcCx0(T-Ai-)M=f zpOC9FWXF4+>(@ry=oZpi^}#y>a*9}ECiot;)xmcIXcj<;IUd2n*JLS@4alO|sLQZCMAC|y*R4C|qnMF{{!X1N^sx#eM@;zp)rz7xj^)XtsZW)c++ z%oMnlC_S(>3>khMlObhqI6$j7QGTz6Ax#H_S8|rZyISH;e6e4osBYXEu|2&kA%cf! z$q7<`T_$%BROF5MiLFFUdb+Q1g_cOLTA@@KYjD;WWVu`ROHFh)`(0!8NQ7J?PXA~9 zz1|GaJp?Vv$U?vj7$IAb5o=j|Fe3|oaQ~8qGseBJBZpd6O3Eqt>m=n{Uc!Bt+&8>k zHRHjic6I%Q;(nj3*64@_QqjHQfoi>xFf(U}jz{DG?+4SW9!`eq-oZ*UN*_S!-_Nw> zb$=IKxgMn}b~?66dBNJ?qJ5APuoEuY{NdVGNA!39tOT{@>WAySjKkQ7y&?xZ zPweP}6D#GCnX)aCRcAgtiMJ6kHo7bQ2r8KeNX+W z26?-cloSpGZ{-r&JYIhfq;7pka>_gmQB1f4=c)>+sHEg^g13=fyOMj#`6L2KD-mpm zG9G17{!eyR#?GoOp2(Y;Fhk8R_Zz|DErYTU<3_KcVJ>z6`N=mCOjT0hgL{$rmZ`E| zVt-hHdJEqJdeu~!2taACgzFv2a1Ce|K!+mQz!G#_3%ex) za$EPnJye@-q+*|;-5#{7AB}e4EcQcuE|%g*-B~UhM@5(54UL}>(1=~!U6h=kpKiRF zS^*5l%OPxB<&sp+Y9&ge-UME`ZmD1;-oXGpD)EkZv{{&_8=BSDD5Q^}UFyTW2r!v7 zNilc`nmkShmkvW^bG>i4DEbTO*3K?ps*D!7%qTfe=vw_71|#X=VDKF^g0dFp_!(WR z*MF78K7xF*vy!<#I;q(YYOas)K%$Z<9?isWcUf|bk9|FxA~sC5D1ZMjbZ`Lm>mN;) zV^-#YbIb<;TC~R_K%AatnI}uRFH|{7ClAlUGgZ^|q;aVoa)8Q*XT>N^m47u+amoz% za}N0D!?W5Ns6Om~L_LlZ&3}3PyaSHxAI(KoV#`Qy4HqYBmrcdvUoLN*zTi?ZN+CK} zdzT`CYxPCI4?T{XwNkDOL1r&uubgdZ$ie%v1JyzQfOzN2--zXNy{Pbtu7rFVGFJxP zl24d%M?A+7Rj0dQ#ILHt5K-}(hvrr7(|FOubudRA6d?HoQkP%%2vj4I_O>DR=@@EO z?nP$5k`Y;5#I=pQ)-lhbu4{1t9Cv}YM<(~ z;@W&mfdo+&s~w<)3oHv|DUFnqsuWw+e{|Pt07(I=|Io9Q*u7BWVmGflKqV7eQWW5q zb-k|sh69Yu4t&Yj3*ohemIApfXned$*U8-+frl$q zEe#o0@CAT}dwOZ?ISGrDCZ~JwB8AaB5xWbn&S6bO;%PuSp5&Oipu#1zEjoyx72KA? z!Yt7Xlb~9F{wc!^KqpAb9zk^}B7Thp;Wv0DTnau5FSD<+ld|H3*P~OJrV6i}xw?(N z1U~aZbyE@Q{Aivsoao`a0(@&NizQVg`zutvoE)URnye}x*`T(ZTHlbC^2v_x3bF!k8_E&T{qikNd+RJ#`JWEA?{IcqXK8SRelOtx z!e@oUZ|eK4e$#Y5s{a52PbS37pa(2j^ts=n*zhhuKeFr9d09x%&?IVuX#IdboRtH1 zRl|hvSPuuXAsN~=pqx_D3hb_eAGD^oeojw*RUyUK^}xlsO|9@Mke*JpeQ*TzXrH>o zUEX`t4|dZNELlSM4fb77kfmL@@c?U*v3k={mcKqr-)PdFQ=XV z|3D8V-Q4?&XSZmkvGh-3Ozq~L$zt5Zcm1&S7hzlW2H;EGgSc2*v-hqC4#C=?y=(9HjBD?z)k5^inz4h5-cWex5yWnZqsDCEen;0 zpd5~FAT%%NRz09Jnq|mL{I8|&C+J#5vHc7>+n6D(`HlRCR zM)m!Byz;Bd*evU*KJ5Wc`CzU-FSt z-3-VNt!UX^Y}lhsARg|TtI`nh<15LbV$x3SxuKQcB30Dw#<4N~7<@W_;5ODyJs4V; z?hRnr`LnV-MPvn3l;Mrhmby;K5qxh33KbnH*B8g@(Ex5H=I^qMlMwdp=?b~N%Q96$ z=*sZYkU!hOx{y@me2ax{Na~MN`Ji0g=;H@fANfB*lAr%JWt6a zi3lGw@VKRH&5D6=l~!s|Aa{e<`%!#)-s$5me9A3eBJELa!}B~EtiXxl#G{tsM3RKU z5ENFv=F{m8w#X5a-JuGOyr$cIn~7a>F=YoTF(-!C5W`$5$zdZ?3c}hY63SU?2fU`n zH&)a>uGa@?M#8M>CoFAwK5$1$>8z%T5E3rONy6L6I_d*8UA++$+tPVi*-A5%@6~m= zwMJsO?6s&cEVhke-R$8V5=6;7Xv4^R@pNX?M@toLg{Tk>+VT#!o2^nX_>4kyNXgqX zRT#=*z+)7Au!vfKO`znlG3pq?r44-*as*}US)c~6}w>hQmNku1s zDyos_N#Vmwa`IEynFEr<@Y>Zlce!*&}rhngW4p`M{xqW*djv5i{vWpI^m=wvhtdTLuZNKtF*8zM{yGJ zB^|cJ;O#HV9!bek6n2w^v7+yD+NNEC!tS!LGvZr(LYSxp~(3ZZ?U3{cU2H?v5*X9C3Mvc71{1!GDT z%U{xdNlIFuRN?)yMMdQEe_xUs`5C_PoOoF?lNkE}BP8^c1R_ig)J{pSBgYV+jPu@u_dpsWC?)!@;E$Uj_ zaQx3gt@y=w|7hn~Tyd@Lu5T+*t7mhhjTXnni~X#Ty!Ay!=-nSDQ+&FiU=;~GOu&fC4d0Q)08f>Fe#gw;o z90B#h7}oV&0oMo&bQRMfrHMLdtcC@M_%~J!k+D${#&+W*W5xsoiu!l-L6=R&>*82Z z{H~S^Zf_Gn?9Eg7RDE`1E4bH~qsb80G*Q9ZtIzq;3^kbUlZZBACA9Xx?`bQL-jT_= zI70NviVI@W!xm+a!rC+BqnU!;|9y*kbUO@<@>0dPQ(nT_Z+N z^O+j$DWefS8^oK>GH=toX~~B4#&bx+#noB3U-Q|WHrZT!1^K{Ych2)X)-5WAe4s95 zsa9Jd9Y$DndZFHs!Y7Q&V`YrM5pg{Ekemh1{Qfhb@wXtc* z-8e(brfD-*tL4YR*a_QrH`rU@sTo7_Wf?I3^8sSC6eAOKXC!PnK&Toqc7Ui_L_O=?$YClBy zg92ejrF3ydi=iEAbsQKuSR#90%dZVzYL!l&uSpdP>ntTwNJ!+}tmfAzMiWVH$mKBH~g>K^gRy2N`gN~1n@OmpND`l~;=w+=?i6>X%D()-xu z$nZD7~pm$Y zribb;&8+Vfaw(OfVcw@OQouU(xU?K6#d8a0;Cp>8C+D`pR{mbk2%A1cD%-eu%Snc^ zb$wQC8_@ckN1_`!!s163b(1c4ISXdA-41xa^9a8uXXPA|vt`6+I*6!7r$k8@;dS;XO0y}R?88PU*#M@ek#&NqnSDh${r|c6-WepQ-@}7M3q1jALgzl#L&Z|0G1;up}QAv6YbK^RYD$e|& zLqy1igQ;TDpPK)i@hWs_zUa^rh8^h;K#MM7FW>c-UecVv=TKVCQYstSJJB3w?`O@S z@RN`-ZYoaL^Mn-^Iba5@`vS0?ySrI~f@qkq7h&7~tJNph#Y;qUX|scS22x)Jn#8wa z6j4rTekE1xxuvCa+mU4p>yhgIjdqSlYsJECPc>@T$ydH`S z_paN<|bdmlUwUTL6u43&R1H<1%~(e;c4Rm+@(h%33ms`sp1~bLL!(i+Y#T z5ZSw_qDynD+V;rn_gL4bRb)F+(_L@$9QFG^obshZ193S?3>1h{u;yA=8^V@Pqv|7H zI#jV?@t;W*@3~vm>gVj8O%=Hj0?d+uW`{1X}9j>E`UIBKoSOlc>3({r~LsBQ&qQ;!vRpUV3ai=za{F&C9x8 zYIbt+6OBo%`8y1eblr0sG`othb_(XF97}Aqc%uTTWpUJwEUiGKjc7R4)B$@)ra#_LaNS5Rv)SdDv3lYR#?qH)G5H(N z!_hgV=1QtK6<}2dO^U)-5QRW33Z(aIVl27`GD{mvHY2OPfiV|kRnesU|1GN>dZQIy zxpS@d9jXOet&%5{>hD3rh`mT41{&_z34eel4snseABnMYC%#zLe+#dZ6hwm=dR14@ zGfN#S$)o{J&!GMatEF zz71O^&Kf9+esrBrIYu%Uk*pG2A8VZ@3yGqEmt;FvnH!rGmE#VM)YS|$lUR>6~oELYHkaW#){4v<7Ez?9)N2^`V3(j_+kt~s`6!}kL(fG%EhjFcO(lZ$v3J+ZTvQBveXYAY)pW`bb_`4W~kE!?3Sv1v;@ zB+oBRRCm$43AI@(bb0hTv&8OqBu^F;$IEO``v>-1C0VLS1 zrPy(eM3TW9pGEo-wWP861UojvC_xSJDxazZr#Lq3=6U?9;QsTs$&D^xLuk!pKP6BOeNf&WAG}EaWQ2j zo2U~y0Bz2Z7>9Z0N?}KU_Wum&HOyfrlpbC0s~b>fNM3QJcV<6RGTS28p{vs9f@PDU z_e-+0E2~vL4yB?_e?5_?@V6eV=rilybE<0T>4(ZT} zHd?1S&pDEKTB^N)>%!hxeTFjZ8ObDma~oGzk|9a53GmY2VA7DOlGh}*4}0vp&!NH$ z`$yljvH_ak)C2uk^~dAUU8H2o;c^;M(uC(gy&(}}D*7p39qUj8u9_qbGdRdaf0~KW z?~KDnF`R$Wf#&w_4hM;0OV3CX2L@|>!nw&r)rjzBdgKNy{Q)ZZ!-0h-Bj-jifSoUV z*0B*V&`OSpQHTSf@`hTK7n}-Y5YYJj2`8s>J;|!n4};T0-7u@VzCqnH1XaxswN-4E z^G2o&WvR8TW{N@Rh5>XaC)&X;*JDK%yX_p?-DrwrW0xAPcO`D?!_&%gwCPHMa?oh? zUydEYuvz2+wBHFbWLFviQ03EV3s>ftlTjndsK_cXN{*h*B#iobl$=3x1VVRRP20Qh zwBblAUM#xmBki?247|}2tPUA8KE@EsNi$@|pvA_5CUZfw)?xFkVB$r_{zzmTAKhhH(K+G9Zw`f{(;`7ttTgoQ#c8w7yh|(rA#K?h-q#0+N7At zJ~;(b2;20W!+q`Ck(?;=fO2@dX1Hwa@Uck{r8gg-iXeyd8f=IHfcE|C&>RNPRH*&q z{@Us@SX|T4sL?GPIJy7GV-i>CbWkWAV2jQUfs62PmOdl@*Z!S>_VGIcrTwc6K_{s* z0oruSu?_vV=`6sz>`_e{4hXX$uamUAlIe5O$|h^|NZ!pwqs@=Cbn(~A{y1Ip0Gf0g zmIj^t#yhX`0ZM-y7ZRnHKl1SShQs&*fJ)DL;4nTzD>{D2TF5D?cmYAj>bt-Tx&@h2 zh!%5Zbm$>wR20W z-|1S%a@pBYtGbctjv+4N5bIyU5SyFsQYiTmVL2)%G&Z#p-Dg_Wl}MJZVCBg#5 zDU)#VE>3^_nF-j+G|^&~b-(PMBU=Sd%+h+BACFeEZNw`Gx0^j%kCk8LiqTQ})DCgN z->X1%#L^O0Ui1n+s^!gb>3@wZp4p+TxQJpo<^05cH*lPr-(iYaIUlL|(bQlRkhb~Lf5g&sHq)d#BV5;U2=5y zq4G^POq`T5^C zEV8PgAhL85Zu%{S@?)O%pekXp>|K{y)TOn~rDA6D20xuXLu_mm`^n3fovAX?llgd;qm! z+PAZKcYcRhfn&2O=1i5bc*_Zh&JL?~#4ueU@9*r+Zf2gQ}%19aZ@timciepmJ!p71sHZ9sKHE!2w=6iF7IE z^N?T$={P5%5=wg%?%5^2o9!-+p0|47`ifP>=`cX>Q3;L~$4bx;QJ5+cg6w&WNN)@Y zjYIxH48!35c^tHPJ{H!#Y_*nYQuPE1_js5h#J^Pt;mcXBrGW(QMK7yy%PIS)9p7H? z0<0WL93W4o33IVkz4I1>|4#wQlqIp5V z+efk&y^dQCAX{w+oTL3|V$&KuN8E3p1;)*urjBCI4BVv8jn>Z!P-`DZ6C3WfZjjvN zT0KaNw~7Kgikx+maV^I+e7g#0qP$Fl#PxfuSu(d=AzBR&5=1&3?I^8X0Hd{#+P1% zjUmY6`ISnD*P+!bXjR~ADi+lXX5y8jbe-yH4nRi$s`WKZ6?vCu-Vs*tDly)C2Vn&= z1%b~CI*+a7GdGx{tdMh0U^j7fgVq_8ontI6c^AT+w^1wYX(&Dpk!od{BogK!3y>ik z^aN;F)5;XeI0J8SIy@iGAZ}ctM4p0Ot>A5i_h#JSHzMdwEN?rWkZvgwE>HhJos{Y z_HUzRmA}cQreqMidWU4(*n@Z~V1Z)UFi+lehyv(ci1$O*na)-J9co7iARi> zC_-XshpO)rOVX(+m=fTF=}GGvTnID z&)_Js-Ky@OaG+;_p*jHMmvlm)@8&YSGZ9pyKvCj$nN__A!YBvWFPE|ddV5wB8h7I!zfRb!@cFNG;0!k|%Z7mz9+`CfnNb*Iv+IDNlAsKlI z=f~YHnzR;8i@t;=&E2CZ;0|1e(!^j>FXupU4ne4m6dYo`4EUVY(G85a<5JR>fbR-6 znb`i0D7-5Gv_HoTjhM!(#)en#p8dc>540R48=o))C_j0(LBiP%u`i?bK z&2=>E5^BQ2VGopT**Rsq9-!P%6LJdd)k}>e-5|1~d;B9rU8SZ;wjfP6(c<#!INVNL zTyjT>p`UQZeA24!(Nh+FX4O!Wzc{fjxTO*{L$;z{6kHESrlB$%b$@lVzx@I9t{ijy zQ`$2{QhrMlsZU$y%Ed@u#qT6L{X-)HB@_(qv5A|v; zu2sMw*&K{Wl+b-vWotcyI)9=6YrnUDOnkmi@8LsH`Zpwgzg4{skd6QU$p0buiq<4S zS+fjql>DMb|CfUZ1}Jwkw)iN|$qGc9`j!F(i)Xe@#VZG{*9dO|lof93S(dW`xBah< zR*fir|7XU&8ji)g;QDO!EPYWY9r`#I&_)1Pgqx;`x`SH0$Dp*aD(xwJ2Q|fdtg@<0 z!&FC20LpEHGt%lqkg59RI{b-?!VRDW+ndA^lqEl4QkI*VrCBk5M!}L9!f)+_HtJb1 zL-O8cW-;_RYeVwh=4d0jeu|Tje21*|u(g}0|5EQ?(Lmb5EX;?kv*lEBy4=~o`~7we z4V1E{ShX~Z_~)%-jN7dTXstcc8XwhO(1Ofkv?tL-o`?OA_7X;CetfaofH`n4wD1mY z-%jMbsIBw}lzOwpjebpn#PJt319?>UL1p(ylfNN%Ro$JXjpm11W7eUFV=xq*LkO(~|?^#k%*9C)jZFAkHlAe_d-eGx}%?8WwhHZxa39&;mkHXou3Y zIBeb7D{*Awbt%a&T9oYp+A<}sySSLR46i26S(qehztTs#T);a3UJ&jvMBNlNTo&mF z(2Y1#qFD8o%Y=18X=Gb$_-!`e12P{jkPMp$T%FA#_igK=vTri53##5s4GtCc8Ezh; zW`J9q7~+A~m6@DdF1AUi)Rm(@m*f^IilT%4L|sHMbVOT-$&ooDl9^^&)+DL%y5?Z< z%=dHecvCU~U7dciakNW_u|X#!LKxq3(FsY!mD|)UR@{D1502`!8_-;AXG&_6E)uUG zPEFn}W1=fLMoMvav$*lTb*cPM`RyV92a3L)NQ@VJ3Z}8a``;4?OMmk2DBk)TG3|W5 zMGj{aTJ|CdTe5tduB|192JwDz%h zh=Wf}Rce*((xGQQa@ip>e)~A zrccC!p-9eaQ$GR!hoEXjXH&Q+y)Z|*7ILNVQ&5H~P&<+GnHDfIVi-!Je(=Cszy3&R zG^H5i^DH4-@hp+0;88sLqwip?CWtVb)cVfzX-kvuf8Br`t}BF!$S`ZO0^yPBGdinls}eFm={7jzCmgtHR&b;-9+%g2l?qT4Lq5 z2MxJ)**ZNNKzyL8`A~7%7dPb1yYm#{VE|1wi+{efc2^olj%`((`1YQVv`l`DU@{E2~-^?7M(7^sHM%q8R$MCW!}jbEZE zT@-zzl{}G6N9l+}IG_JQsqB&{XF%tEtKItK`b^Y1*ds7exPPZPPLZAk_4l2NpKrEV zT=-7Af+)vx&}vPapbnzC0}O<;xg*uer1;D=i_gE;aYnquGLK_OzlK!%^-&lD#kGFacAUW#@L(8c8>(*jZVqaGBs|3Z{zcQfH>25%%eHheWJ1gNr` zDM8G}gC%mNINlHSqev)WYIYmj~X22IgA78V6BKafme=$-*UAJD6 zT9*dR5|X$kwH5CA)oVIfycB3pA8v(*M(TOycz1%@LHU)!0??WsrXB?08o@~1M`hk* z*~37EoV?JLW~e;a#fbQ5=V_pB`hGk-w?LujHM2+PMr_7v4WX$qF1*)o(C^fWD& z?(A~Qh;G7=yk|M=pr5sjP@Atn_2}NFFtMPQ$wzqn0*#yUCzfPnTdnf%N2QgHN?m`o zRvMqITLn@5!`exl?hOx#v!xsqTdtPiSH+3Pb*9C!C@jVb|JxcT^8Xp$Oyu@4wP!;- zW881P^2BXGosv81AjbWhfN>)6Us!9LC^?aws5OeVKvCD%w1jYILOzmWsC2hWC0xwx zXPT#Ly5qQHcs$}Bay|dgtPGL+6OKBi6ZH&oJ*@?n$-n!ii{g)=QGA1u1Ge^qiHelT zfZq%FrtNXu^{cnX#i>lyg&UDL!I0cy9jf`h+in2icSOHG&choVPTECHDX>+%j3E z;cXis|3`_UbW@;uDw@hmGMVR<9k8stY+)kuR+hZProMbiFkN>KOK?NP?oM6Zf$fgl zb&qi9mA9}g)4Vmac$C|PQP-ryV5s@R4F-d^ZKUKbb?|Nl>Odh-+~xf3LFtB^w(Ui= zkDfoGDMzXKBezbh3yTwJE+o&1rh*->xs8GYsHG zvsfK&vydp#`w|e;oN@~j#pm5{qrQ!8gmWpstUz(1yp0wFF7Q`SdUF64c-L{0l~~E~ zQLK)b#a|IN6-P`%_El8f)HfgoubtyO8`*oAveGyWM*(_pAbbQ7QJQWJ@)+qp`A_3E zVoxzIZ8PsXGqg#PXu%yPNdryYM9qEB(gv&e1VD`jnW9A6VdQ4w^g0JpO;C9AIPv}` zIPmZ)y(F1zK8YHf8fYiDvJ?)6(7+ELJbXQ(5votm5`IC0?%r{J!}m zfEzcG-B7QeVmIk6vLnQ)&9JfzzOU1;A3NG+%R#a38T8m@C{{Jn?5M9MPS9C^7PN^3 z=m7|x!A)Ib76&@%l|t5^V~@GR+#^Lo7kq%@hfp~!T)-b-Rdu$_mH)}c4`FI_v8lT*gPA_agm*$p>F<}s0wFZa3#vi0s0)E>K4&UL}_CTdCzq1jkph> zFF3?q*Q1Kn45Yn?1b!1=T&Vilh6)!T2;FR-%WnJ{;bVo_?WMEBO)VS2KG$oGzMD(v z;_S>8_yGHb4JJ;>VuX3(o49Ot7vQyk`{bIkMctEnd~}P|sZt1cFPjQV&qC>Cl%~HI zG(c2Xb=_=~eu>iBdx8e4he&GMFTv}gc+XV!FbMD~fX9z8K~ElmR?8WoZ|)8U=qt=? zvTd7`ByNIVbI^x=gOjl?2h^4aOz$QPZ-RdVTy-N%gB7mwxww7EM7^ABdKC^!0O;08(*Qm+r1WDhN`EA> zMyHxf65#X3BO){isI#u(IF@2lp9lux|1}`kH_C($2mO{Hp)k@qN}kYUybhrn9h)vn z4=+(x(=Zg)qyEB4G5yux3_iRY?A7pBbasLDtG1$l9L(x_lQt zo0EE3O6q@RX|I?eBuSNWhJHb5;b;dF@VbyfGYggt7xPFh@lp)`7qTYy+N7Rp+RNd5UDMJ)B*-wNIn8Ef^Jxpn@7(vxGcBPU#j z`H^_INI9!)?kXy%_&npz7rbI2B3#WO&!w9iXTSZpaXs zqj}9uwxudXw(`?%u%jO7i(Ee2CxENxM|g^yo%$(G%BKeoL%DkCbF`kMc+Ep0gAq=# zL@hT8f)|UU!?XLVfkchd?<33u%$qQuwjR^|hj99k@<|Ti(bWrz2>@Z_#07~6FcdVXC9J%M3MO-vVkCCfC5_C_tt(RE2 zcB6o`a%NBuwQLQd5DoTB(LL>jvqCLb*s}=jG?3@rE^u<+QLkDP0VoZ7Y@2K;iF(#*Jp0 z29$lUP(=}wYuz2wS{XJqP~=Y@<}RwI!Z8l^D5semB5U?wFX#LaWg}sRBXdt|XLD`p zdMcd~z0g&iTR@`Xvf9gC_rTr?9w@TsyL*VUs~7l*ila8<*K+-goQz77+B>GI{!RT@ zEDbX_Fptl-sS6pLx<077d)A#hUSE`kZwwovo(tsrDv}rD(4Zec2c8QXA)$aZ*9~VwoEfX>2pka%PjxBKuH&;E?r8A2S*c`>r-;dV5DRM?l;%5KO46B{s869#h@WKFuh%~!VO3lo=a`&;wPtmAm(SO zZL1tUcl|+V8da!hfg{{?`xs0*51x;~IR?TcTTqyWpvCR1)|p28ZzxI&N@Dc>C+&cG zt3G%QK&xSZd(KBDD=FGexRfcvv)EZx*KAmATsHD?!;&Ti%I7x&6|%8a*lqh|0}f<3 z8&t$+?JZ%{-Z>gJUF~j$2MKc1#hK-LW{GD62s^g`243z_@XnABaim0( zklptVq9rRx^eD8=UFaB8-U=9^{8%jE6wbQ<-?$LR2CNgMrX`4KdNe@C7UDEmyGq;X zRIOuB8eIrysc&IUq%xV#J~T#Qm9j~!W3h3p)*{~l|Hq-xh(g3mEHBpQH%`-d)GF3% zo~n2PYOVR!-=SMD(GnpAa$>r;RbtyD*~g}nIPUyMTW5%>y&5C!>dDa1ciYy>YShqE zSgp8Q_F!#Ze4$s>T)^_s+s)Skr{NBrHk)Y+=d;C@Z@rPg!}aV?fXc0}xNF&wQ_uHg zWk)m7Y$}`8U+|dHFr9KU;(DJ3nB0uc^_+@WmmSd^jkfM|;LckfJw)7CXH&Y4ntO(B zRn{&kT$CJ!fl{4n>mUpk!pwAGegGCiN!Wm9xEhKmsM1)Z=Qo%Mvvi%Jp4mCdJYxD@ z$yjzXTY?+n3g&>A+e;v8wD@H$ZnbmM#p?C8t7O<66#4fnMXXOl z)C-b3T*33now{DQIV4T&`UmyQQu{7Q7pWVxs8GHZf`DV0KA2c8vig2FrKMHs&kX#= zTbM5LH`?|}(zvr0VgKHwcRVhZMd?UtsSnHBY8}ZNE>Jt0~0yFt+69^>$nATTVnUY1sSFi)We%ZKjoiP>e`6^F$;k(T@s$oe;TDT-u8mm_K%^ucZe9Kzo_$pmX>T-2Z zmVBkGuLd1H7uya}$cv8Yw01n96$9kA+_Bvh|5Kn28MHG?04iE;%F;?~hh_nHxivU{ ztS|)&-|ap=qO=cIfLwfrM`iDpb?`tikAAm1Oj3v`-S+@-#fBIprzYFVT@kMZC~cy{ z8$d%w4v<>oUJkG-6D(xGq>DYz=#7G0c^Gxe24ZKHb7o-nPHaE>Y+EH-R(*tM zw;1X=N3{!-CcG7^)S@iqRPILUp?e&MxB6$bTH`muJy2^0Y!$?jx+Q3OrX+w-p>ST(jQ~ zJMkrbvZK-QEOvsIZR!#^{~y3&dc_tYiozE*6SseK!>fmbz}fPrv=f$w$w661#jKNM zRp863_d8fdThl37htfs-5!)s?DjLJjp=#2(AeY0LtjYy?I32IiySNLV=TI6w>~I&> z*5Jud!^;XU0M~^FOt9jP+Op)kz_f>6L^H1Ff~CUz)Dq+k8=kE#uFXksI2tg#1PmkA zD-7yUFnasAOkc)1;kZpb8O8a11y<^Dm!s$rY^W#nQIzmkfvMkv4$&}ZxuXE>D+$tC z&T#lU21cILSjqh3=;fqMWtX9Q_XJr`^M_k+u`v-Be++@<)j(U_Gyoq3=uE{E0@(8V z%~v++2DCj1c>G2Ns-JI{BPx!pO2!vTeF^1fz+Y4Fs|2}3CP+!*X8$_--jd~ugfidY zi#4}jIaQqSH_}DKTeb~yR;lFQ1g?{kl%hLdnA%2Nui4-N*i<1`23o8=4L%Yo*&K6ZBob9qWz(eXfYmUT~# z&Thw(Knt&OU^v z67pT2`~Hx9HOh)>E$+Q{3bMbq*N-mGM%#}Fi{2N76YCoI$MasDq-2ENuaAMZVkar+8v6vGYkixUiTUSyP*~ zuoGYx2|F|)8=?50=xvrO=@L4r*^U#Md=Av`EKDsx)h$C1>dKcw`AJR6HC@bg>0-)x zn@U&1*s9A!zPWE0e9jHuS6l;RzXYzzGK5u0Tnv7eV#KZK3P9%`3rG5aFQ7jS7x2F# z+QLcM_^S7XrkU=nuhC+mfICaJFnr$p2B5RkaAO}J{hh~f8RA>O3wPi$MD9~7)Hgaw z2fv7Y2ha~a+lPtzOL`)Cd*b_aF|byD2kVIcKcJSmcl#Kz|5tdx>r?a>S!(+q0lK)u z6fHcPELRpwTa;c!>AySh*}rBeqD*UhEkHbTp>{UM0e%h3;j&HLOKgw-*HQoS$|h|@ zzYx3&!w)OuI=ik{kQkBhr9LsYL+K4w3M>98oRdlpE>YJ%}^uP|=DvMrXKa1H+^|Kn^} z*|e$H+JMrmq#g(yrjUOrgRv>&3{Swn3b;XusydL@w;I%su){yX> zW+5l;UzFbZ%`IB-#Bk~J7CZyr=})e)sCt{kMHFPW78Txz;lp$)eaM;qzYOH2_{|Lg z9#rVdx%&E=08O|jzGUYW*zr*p zAMfS*rcvOAX5wN&c9Md*hT$~L5Rmk#@RwJ6{ns(~i$oN2e(bl8y@Qx&rRSG9i54j1dY(HPVu>#A(@y z!u;1tW%_fadSr+_^){7Lil6B{S#5HaS2vuSCN^~K#p0T2*-#I1U)(o;fHYHhg7*e2 zZPbiug$qUH^GJ3p`%d@(0W5~>RyfjE-?XXhR8KS1`5?Tm;@_!qV|4~bXzu#Ok zz?h)171&j|2S$J-pm@(JWev&+OGI;jQXy_U7*P7!=QKITEZG~{$Z5*Kb-&V$nisR!OzAHv3P=crm#g|{h zEt1F#vEeUmz;P3Zq6DP%_wHpJLNqW8eZU)uXdQAPRSxAMCPT#kV_Pj*!L<=9Sy7!( zg0G2Bd&6zS7Uizb1IOV`$-g$0m?Z%%+Op61IoXK%`Wi1HB&6Zn?n%_}BqdIp6Gka9 z$}Vrb-Ve`T*nNZlhYa_E?rCmUu~b}&y*c)uJ(EPqlzhcU>V;&9=g=ES z8CbLzerF|Uj7sGTZn8U`#Z8WCokjkq=uSx&o*m>|h`tF_jfK zqQ1HUZs<{cp14)0V2t?aN7$Z)*`uZBbsB; zM*54AMe0PN1}R@UD)jLa=2kd? z(fJ@NadBs=O1%kEs*z`-{wO{93=YF{{L#ShK+XUKiV+nHAR&OOptA93H4r25w^zuK zO~n5}@;{6=pTq!>o8b=gZV9%qigKJ*a^BNX5!8l*fwQ11RLR-f&~{O|W0gTcRWd|@ zf<>*LN2thod6cqIx&kzm6O*(Q;gpd}5t)_{mfw&BWSEAh29UcJ4WcZ-uxrg) zFhd+#(ke>6+$Jh|&r-{RHL}s^v-_dd<+EhGwkXHMPKRfRs1Un~kmL%=fh336Rf<8v zb4hP258Sf}!yYZ)o{MySr)C7;6)8=FMBS!VNK)&3pw4h-YNV>vS`_Cu^>zt~Hblma zQkB}XCpB}l3GYH_V!!55;#hVOc8^dUuSJ*3X!4};R!evVjv?00`4La90-k`!u)^ut z(P^qeZJkbYPDkL_3{ewqSJ!Tbu2h=we~SE%^T?I) z9>QFKyL^Tl6M3Lw%d#e&gjcLxg?e&<=Bv`K;%s}&Nzt|OiOg~#N$&=5fdY3B9nE&! zR(E}Ub1Fb5?M+PL>ecb;m?c|L$xl+OudZBC zm(0-+r0QtWOyvKBmbdF+j`3hFY3Pnhb5Uu;Vcd{A+d+rE)9jt6O8s&A!d1mT4RV%Z-#FfS{F9ja~;FJ8SoUZvb{j$$!DmCs`ol0SuAaiPGl z(3YsC$R<`dKE;^Sa;XBf6}8tebq3a26ew2YKZ^&~4aZk2K(?ctZ$d^1gF%*_nfUH^U)=o)-;*;wOm8%szT9ljZxRTgT zzt2GK7b{SNs9oUZB}%*7m7sCME^z^Qi}(z?SJ{s)*5w)(u%CLNG%-i#J>dB6cEIcE z+**p7rx6R37Y5Z|_n3a2hTY$TN~Kra;zf-&&TMCk7Aub|yI%`oOSZQWMe~~mig*4R zb4L{Ty&7CGw`oh!x3^wtl+tw#kmm3uWg|1g$GE3O^F~TCje+}Q9c7qN^6v*7hqmH5 zBN?e=NPYPLK{oibqe=Fl0<|rBZgPr@MV2$n z^YsSRmA@#xJG;4fwvP+t8#8b(-X1RMu8vpl2vU`9(uGkQyEdnwdr-Tn&5C}d%^IBF z+Ki{aeu4^PE(1AGo~=tY_{1wFq|#vMVcq*hsx^1zEfTD5k5G4QmH!jP{QB{^5(ib+ zL$WYlEK7j&K-Z`_;`D8kkS>(|e!}qT=XQ+poM_UPOIPWC5?coSpJrFFsiaPjOQK0M z30M0YT`D_NX*k6-$4SM*8KNZJ-bK`|&2CCvE!AEFDLW-t-CUvgJtF^i5{VghH4+x? zl7->o_;XD>L{1xXGco?>@lb+z`AGIN7Ik)K;A)3`z1&yjq;X%}1Crv8nlgmXV5~jE zJKCLV(W*@;y4N3-uS zfU4b_^;aN%ZkP12)#K=IkX?PLpe^|Xs@^;XkN&~Ic4ZuKV)t?bE`G|rySlJ+@RwzG zd{TH4%?hne`iheJv5NIvJ5QnX>}xIBmt9?kn~mlv%4@L@kf(|8Tu%F$Ox1QrBCTgK zME)@Q21z{yW*@LDJb^p)Fcjbg{Are&CepP(L#)oSt6*+A)t*JwniHlN(KTD^M#{wj zl-@i6SKjAIpu=#Z>L5UV)wnsIRPphfJVB*oy3Km zy20=}-kzncV>;-b1E}9gxFipZuxEMGVN8{J7`W0#iKxlbL&a@(JY&^)q^6JR;(4@L zaT4C0@{zEe`BaxAg2vekB;xpSw@7g@6QW|cMEN3)3Zv~CWLPFj@ zJBBm-GK+mjBW<#6jMff|8{yMV#!F|$*k5iWC4r0e2v*ZLySjKwo%1T&HQMUlLF}3{ zT{#zJS6ov^3ET4<^37zmU8X*7NIZi9I|gMqK}%FN{vXHaA}Znriuwt9%=E#ZKxzFW z?S|>Y^~V!b2-_%tssZ}rwyoHa*zK55ZWYD}E64~@7TitL6n{o<~;LwoV2BHw|OB%H=| z`5Ogxl|PTu@Sd|m&N~nZc{I&=Umx9Ro4&6rv=%-y?UN<_M0`pY;{-j;K0y{!r=Hfu zcrs}aXy-XqE-b>%fNY-{*h=tCsxui8okg_ebZuIapc>RVbh!yq3$^Tu*xM{Op9Z2N zDiU=LmDaqA6D=H#YW)+{2dXq&RIGPvDsG%?>dU|tl_kwd#fOGUQM2q5oFe%Vl;>it+kR9q%^;7@?0-*mTL!h1O~P@Y)DPoAy`=0Amu%&{wdPRh>1 z-!#{bYdLkWdVPCiMf%Lpy93AknFOmd8m;8dfxff_QZrthYtNReng;C`;4M~lO~`;) zynyF~l@d)V(!I z9XDtg)S~x;?eX$jml)jcDPE@^aH3+VQy_$Oox+u736FeiHol3vWYvCuwU&K6D7uNAE4YE|B3oX z$iq^5!#&rl@_)22-}Z!G$=gegs*gB3Z-N>S71=d*cXKC#8_d}V9x9I3wi}%y*TI%8 zXA$0gFy9OI$2(A%hLkeM=0bRPk#HT%s<7IX(L`6%4OkI2d$8ElwH2<3zXPyARBlSJ z+6~R9{u3~*8RX+H5)OE^6H$LmSIdn@-=7_z`mwPchzcLzU@*wJqrvct1D-fACP3sb zv#Tp?6u@6KsO%gvs5ps`Eh(?RIa>O@F&z&+?85uk|3&{hDvkIU*Zwvg_k#cJgsKEr z>{{&uXGecHD#c$w9%XO?3flFy#-CUjtH6zjJrGw(NHJXGa#}gWza+S|Nc^iC0vMxk z+2M9OZ^X?0fw3Cy{{Jm2u9Xcr_J14=Tlq@XNG_}U@Ujj)7XQjRL1NEpyNWR8&~NF& z`u9V8u*yPOtIiBBk>1vI&J^3#S2T|F|1voe1iF;i)r|r(${J-N3`)x-|2NJQMQikb ze$8o;S$4Nwg){T7Tc%_wGL3u$-Ktpbt`FvfH+8^=SHj!)-zjP4fZs^(2DLrb)=X5a zwGTIj+%#8}1}b(NW~+q)1&W;Bh(wjsQ9eK?hUQ-H4!9J?0~y`?@z88exq>1C6}*aH)j$)=-Vkqz4G!-P7?j(*t&ubdA=5uS+Gh z>(JuzbCmkFY2C@V7F#($RUb|F5*7KwBlsNK2bSAZ(T zmt;z{;pMSFfXm$XkOC!$x+XzQ)xx5+=v>0VfUn7E9U$t{2H<;O z7ie#Y!W{&Ks7isz*fSkl;^J7%MXo`KiEA)aRSFTM1G~W*x)6g@f7|qYlPh5go`Sap zw6l&HxOHaPL-s(?=W~2GR@A?)H$+0i6}0P<-VP|nCKo2h)JC#XvO{|D+s1qv1BTdn1Wct_M$ z5>)OkJ)t}vbVGgUtbPg=gPA0il-%wL6e4n;MZy9nucaKC0q>D1s>|)6qVQgSs3-n7 zBn&&P>(kUdb;X)z5upP->WhfPLlSyPusXqTtLUw(=N>@Zlw_VE#wuBYgUTxPR|JxR z09%j}BoZ$}uAG!Q1*VT030|4=XbB~F8D~{lsbZC8Ds^UGmrC)fUxQfp1H93fP*1}j zv#YR2idKp$)uNT-`hh!Dfe?6DjgOd)sZ>r%N==#rVdP2aH-TvloAfQl*vW89_WDQj12M>X*og7M74lct(Y-GEm0bW zgB+Er<_5PAJJ;F~TEjD>K|c>xl>)_q?f!S{B|{vb;=@WlAZ@Nzz)%OgV!z%Isknxz zQml(-?em=rF-w)=kp|acWIHf)83j9`q}$x4qI$DE#~3y`+)?Sw^B}`HdlEIJI!D0+ zgx3q%4K|iqUF21+1D^A4Y-dqz(_4lapb-vG;{CyG6>7tEtC0?P%?sI`lw_1=O~6NJ zyi*|i=STTbj!Hm+8xZxrh|_qpFAgcY;Vk!EtKH(X8O>t=mwu{PipdoU46W&j#PL_5q_eyTZF5XV{l1KP&HjvsdE`k%oL?B z+SM&qF6c=P8m^mSeMMq&ED}|{q!l#hbu!@6$g6otTe4K!Q?LWPY*+6Nadz`iEB@bL zWa%9+4}sIJC&Bzo9Gr?@$%gN3z#T837ogRFoxz=)M_U!N;XFi6Wz(AhA<4++fHRjN zL+doOE&dVbl`})&gED;8n+}lgy&hqB1;r!K`A7ji{keWvouMjq;`0JZN70ipGt;f? zDvWJpFE$w-IG;sQ>W^S!$T0rJ2d#cWA3jLTeEC0IX2IvuhSOWVlFK2$gu!KGW#lFUl0xU$d(uJKPT!k<<~#vimWL zS#AED>cyGjuh(5ZZgB;gY~imfqo+UX>|^zo$(J zeg|HSszYx=0)5}t4p*|LnA9Bahiid9v_R?@9&e?ihwk+fz~C}EbQ+IkS9KchC2~(f zIXyTBiGd9nnAT*9U8n3*L{#|HK!zO?@7=QPjHRM@b#M4nV%*%k#-$HPACo z?=PZ$hRs&`#xm_wqgzSJAzeZ1fTHkcSQaPtHbeHWH{e?*9;s)D=e|s_x<)(a=~TNP z^&{@WI}Or(lK1Vz0Z#wd1G3BQ6)j_gxlv05rtyQR)UgJowjLOKe}SLX0dLq`9t0@o z7vv==d2EdGebY_20eD4GcFWGX&&;vPyF_v}61M0KgkP5Ko{e>sqdiX?+9nNaBh2Gk z2A0j3ri7now#}Ic)z*5C|4Y%aPic!j;@HPpiwsBU7L;!M6?dn0|BXnSs>o?-P6*lx zP~M>+4JwM5fiM8qE6xu=v3_b-QI_pce;a8D&5wi|^lNnKT8{n~G_sSTMaQn4TCF(~AYui;?%fXJ(gu%;)$1 zv)8qA&okxBnKNh3oH--Vij6<`^pi(3v^GkSOP;x#^|juNS7l`|p@LfdK6x)-_YiAO z^o|zgpKFsbIrCnKsn7LEL?8UWkJ*AegJXpw0ileN=1i9X=2*i0v!xiYkWUa3rH@%Xr59G*SbR;+&b--u2vd&xhn{^me9!F-Ja z69%`E49jNiP_S7{JfVk*EqDN_>3>3Yw|@t*&Kp1SgT8!SVaodoP7@qXHk`w`3wZrjzi8bA5KmS^ z;_c?Acf5n3YqHNx<7~<{PM&Tew*QBK7=;y9+131@t$h!Am0aPU$RgFL1PS!?IMMno zRQTGz5yDs&SUAJ3ID!3rM#AbSn3R7Og*2WDk%OuhWTrlc)LM_OdZ-x!P!0gO{&_%g z|3Ffn`F{08IkhN1_M)9~Y8|?IM65Xd!)g<5zaTa#15ZnuaTR!BoOtVp)vIMu8TkJq zirV(i=+WXt9D==46OwX{&`YvBQFkIcPMBVTou=>sY#Y8UgCm219bQHe=?`AkOVs_O zS0)a|KBT5xz<38YVZAWKKG{!#MxMMZMnr8~iPOsW%fn@x4w;DCwQwifEHZv07X66t9WmlyXeMRN7o?i`n z(l4Gq+LvpF2W@<=@$fXY+W!DMzQjhs#r?g*I<=v*Iscvn|fX4AFPh8 zlD1D)+dwJ`Z{Tj}`PJ&~9(ndn{QntSVe8MUm7fE-=q;J3d?d;AZ=<}1i#QoB_=U=w z`)!tY04@8e&rp%>0a=~=fKBlt4Lk1wO6hTTbj`T zeRy2`AFKO|prg=%o{ucWp{)CrnH+wfWlPjoSW) zs`PjfliDRfti8piJ7w^F`^YU*sH)v?ckg51drE_csgCh1NVcB)q)z}H@~~)#Hc#0y zj=`r8suxxd(=2oIHop``B!#wN9PKmX zM2xRZ`Fs}Q|5-4zudQCzxd8vqL8bDu-7RmD%0#kUJ9a7lw$3{y=dq~>u&He^#pcf- zK)a7+jA%c~lTmkW{RL2ouLYaZ+6B0G2X9*jWiyjH;!;IJpiS*6S=ev*qSaRw zcF8H`@BCuUF zB%5sNSd+)xe~%OUOg441mOi)tpsxB#MBJ07y;M78!^tEU*o-}2OdKfBbL+e6)ta*S zUqWRi!ElHDp?EYG5BU`rkBfxP9-oMtJUqUY2k+d8%M7fsgBglWz^GdnV2M;`#ZBFG z;k4}jUzW-+PUJRBLty5W3svcCz6Y_)(ZN`?ep{>#u4Tbohb|-5dIERg;R256bzU&2 z8EmkZBiL7Wn=(+)l;fENbHNZ?>}_7V4E)MV^#*C+1w8FJ6P~`4xO@BuZw2)b>HnDE zv`_pbub3NN;_^YJwm=IW!0CmZkl_g+Uj>DWzuT}GKO3oS1U$j>17v#NFPp3cZ@Miw zMd#p4F6H3+XNk5bn+lL;#s2_y`_RXTE>oHdJBz^%*ac;s4YFKXmLwU=3govtKJpu@ zh5?nS^_S%aWeIDnZHtS%!OWX{tM7>Vn=pe7E(0c1(;jRYDG>&Uk(^2kTnLaVlO;l= znJh!p?6qRDLMLg4=|*Ij|*zIx5>ubunifUo(e=b_|SQ5TBL5(j$Ql-CG57KN<$ zcD3MEBd=t|y8F;Z16p@HG*NW)(Nco#h(T(`*Iu#Wh4qN2Z@5Po3n*wHp5X21Yg2xC z{qcdBonCh~C|+Fg{53cMM|^bdMOao`7G9{fMG3#AYr3fCJ`9`2cu7`l8rx@?dWr@8 zZBr#RsPrbFD)T;{f$CE1?$gD%+5FHf6~Blsjft#u?PIuJf8PMzcxvdq@LBYH-(J#P zj+B>lVN-f%iOPYtrBWd!UHUldZa$ruBz8?+9n9d9DT0BmVO^%eGl`UfUsKkgn)d@$(~G7oN#JM9n@+u^-V zBcE$ik>u=t**jH;1pZAg-~X|DN@gKdAxPE-ix%1rOJkqq$g&bi;jMJGbCWFL%-F{mQLsb+8gFo+RK&PTiI5?hz zH~BV>uqi)rE;beg4HjQSV;UQRWTz@9OuRRyOE==9j9$W5IPsHTo3Igyu=uM?hO1~} zoSD{+vK1SZp)^1qJuKlOc(g4LcVfDDiH6azn;ERe=_*ry1(6{oYfx>=kOM2sXS}=P zc?)kJ5tRj#BY8s-n%m6GEK!}R7wj_pADboGQ*DpQxyQLR4yEsEOe_@RuYg(3{l0h> zkPNdQHuR4{e9ie-2Ls^VCYwxPHv7dXjMvy@g=|w!mPkvpRm%UI6uDrabQ@wMJrf)# zs+yrrwms@l%K)?INjO@Jf6OsP&;eg|j#43Cb#9OF3m2_V!!&sAOFY%en^&@Ia?O{9Px978_U2GK6p`IOqj?n6 zvIfpuYnHe;&bCzkC;J!3|2;(Gk;{FBNSbm1Q7LiF$}`^*14ewYnrnP5|=R~(8Gl(0Jn zVu0rlvd>g9=dgsdm%N8wasVMS&~{L&GsOS@o`Y!gF8R|Q3xR(%%ofSVsRNtd{k!tNy)U+`(9{!lO=v3~3E0CH}5I;hd z6TEntn&50&B_zlcy^Uc9tFlDxRGYGnP_$Re|6w9(noYU;vGQdoudZL*P-892k$N`X zNdUxuR0QA$*8nPd3m+I7yF#UQ6VZ>1_ZP8Q5dosE)P_(G^~=3ZJBAiQBI)@ z{9lzNPR!H|CVnlngIOA9hgN5a3$twMjYd{&LnV!g7APUbdizW!w3B@8Wf4ersq^&o z=6vCX%R#*LZya4=$dpb;)~rEcnY}E-#gW~*Fe+}>G4t-=AZUy%jAJP09mjADz6k!_ zA9u1U03|KaM9BcO7SJYt6CSm#)MLf=*CI8rH#Ucz4KwUB)&URN9HI9a>2_TphxvLy zF>gj_4m1qF4S>$a2MlF1aKh#Oh}ex;V*AxLbqRu{+=Q%gp9bnuI(Fy^*!`OU?R+O> zxHvo4jk|6^>bgG8aZk_DA@xY+TNM-`GG+z9VSk=&z9bB_xZ9ji>_vF!qCgdHRE{%u z!ncDl=j;76H|gtpHjTKif?infKi#jJZH zWWj*MRCt>&>pX7Vgsd6;U_+9GH&Cw*(D+eg-|3St*~@PRbSkt*=WwYh%8|SiY_T=P zI#Lz5d$FF`Nc-X7(0h1763>M)Opp1Su^zJGT-W^mJUZoJca6y{u<) z%TUNYPJ5ZmkrYgB9v42`MYBsTY~PK>hYWyFgfvrx$iQd!AT^(<2EM!(sp|$f`O?71 z_W}Chn;tlIt#Tvs{YY&efbEMEl?FTHHb6zK@v>dY#4Xu^m2U@>*{i3jT=s;dY+z49 zZi^eKdvZcMk&>2;_#&hYvA6I0ATCIh-eZdpN$&U#g+owM_%h6atc(PD!1?LyYiw06 zvOS2(>d(Y=QcVphIqe|@LH}9bGhU6WApqmUS+Ji%f|WG&9PBoM4X(wH$UHa7>NrOp zW#2YOL~2pwikmAlaL^xPhN7E-5QCprII)w}0r0=7ScW|x^eOorMb8t&4tiPj z^1%=-a(1!IC!yT0*18rc@f0Yr;Z>L_C6QX5)3b~1+X<*L3G+fiCA=#%lGrZ%_;v=itPoI zyeD9olHoA@bk6Y?NaI7k&Ph&jEE`9fzL+JBthK3uW@BGs-s*R|O3uKuk!eoP)a`^joI~kn}u&HtrhYlwNG393W1fgMdyA!OWGTz_}q= zgmb5v)voimA~G76C$3zw`K;kkmWa7g8~->$uQ2Z!f9o(cLwS2ye5g8*J1I-(!dW85X7W zLc@x17)?8x89cO850o@zu}An>W1tm}4LY>}*OY7_b}SH6(zVFQ=CS%#>=icX>iPdD z^RBN99nvX4WjP#+_lea8VJxIbF!)t`fTo3vv~&|kf~+ybm1O6v|4>14v@g*DKFX@! zq%FT>jE}R#hE3WQ#TDige0jgOzm&h1tbm^aT6V0LUI7hHw0y>9uI<1x%NPx;?}*tV z2bXK`=lCph6qW~rN_Y&Zdq!D$FbJ=&sJzpreCXNdFBF7`DR=2LE(58@S;YmDRdRQy zqILqAqenxWfahW6bU)iU38-i^6bUs1rEbqcx3RF7$Hu|AluNJS3fQSEaqm`J=M}Io zvxK?c%{=lIirw){RH)cjZ&N`F*yyjZAr`huxl3_z$TzI!Xpd;kAqJuGE!5fjY&S|W zk(<6lv2*r1Rd+*}t?yY(@YqBtUe%z;DmdqVAl7z`Nh=>z$|T0Q@*}aL$>CC+!7+*Z zcHpNhF?hSyuG#+6ut{#$Vw!W}44RWN1`}Np&(QL-*uFRD>BN?w!v?KU|L5A-j@@*l zHmK8-a2`c@q`}}VpNMSRwniCLDJg#jRFsCNYR`sSqdKRUizvS+T&t*tR{WYJx;&_b zE;qNov9onO5_+p@lHb(Ta60^sVos%DGeu42N44t2M*oY{ zb3cUlQEhI#QMZ}ZUIbKSjnmaq*Od+D9PP-ef81@k`M+$m`+N`U;7<7=8_yPcz-2i& zCuX>U4C={cc*^PtT}%eDUe026432@Zj9XsjmBp||PZV?La4?>5C1gET+6~@!Qm~2tG&o)536=yYxX`e5F z71v$ojYWOU&w3}ZJ_Ncb&`)1cT$ll^$%9adT7%a(4d={pX!-%$t&$~q<`mC?9{6e~ z&fnXilP3LJp@cN0V*;_b1Z%vu0>fiZ_Lj%CiKt>izNH^8^-BY+Izf;)t;D^6R}Ts9 zE6z1ziYzqV#o}y7|bi z56Tu_yl$(OrIq9VV18Cz)TfNW9?-t$;IVA*pi0UXrZ;W#L^F+ z))bb~ydkig*!`AGJ=M;p4#Br?>$88ZT0^r%@;kP0v2npdZ_zXX#>>MGELR?`t`%aK z%u{Lf;T{emrA)N+frhUoIY~PLsUE`-oeKdX;5QUEOPzM|KzSsfnu(S)WwC{8oEm)E zrAM)?%lcYjC4bL0LlS~r8m*J#l?Pk$42N1WIT{v}wr*;~NYOM0mxm30GpPzn6l;&z z$|RFe23&#E2?f|N96YMWg3@jbr2qTi``UpazT(1ac$Vy*j=0Hu(@A!pE*EJs&ldTg ztxeAsX&-2DL=l&Pl22^%>myElpa-9}_e?fEZU9tLdBDxX0a*gAtYfpq6Cdi$4c!99 zWsAWd*;Y%OC1xQo`$x7Fk`QF@Y?gI&fVGc^wLo^!JyGVRkvu0`?D|-bJI@t!nYSvS zSE8_fq6db_d1z!>k);JhWj9C&LIS) zPf(u-GX#@lecG?FoRB zlswNvEm<_J@($*#m*oyxpa6|aYhLDxNdc$`8oA(m=ihM^oo^(uI zsoy@`hJ15mzOUtXjrdD5o!I(=cDR`WYyq*j;nsu}Ju#o>`n;;ivu43i*LORFpSmBExNZ0uKV);>v1qXdG3p9SX7`%yJlh^le)QR^wv^+ z?ST45C03)eTHoM$UX@*cPH*$r&uV;VuJVz(*(i~=L?7+11++|dNHjmSFcL5D<>O71 z+&)EekdofMPMKr4g<6ghN{dmoJaRW!fv#b*JL-K?JbtA&ti-5p*)=u@v7dPg?!HDjP;TZNOPEMH zz6xHTC9qMLzH;Ct%pc}L?zo3RthqEN-f#R}O9ZZV*C`wdb_qOk(27f}$ZOp2DAkhM zmsX`nUppymc*3w&WrBI>E5{YawYgS7X{=SArXtX-dzkul3J-%vm(BeD>csVKnU={3 zo`YI%z!M1UrHP&3@b~-Y8+aJVur+jYr~5C~12sbdkD$H4!MnZbs^w+qxxC zExRPp+AUz>i?&L+>);Z7D@FwZ(c|u%P+=;AW5dRQG=lzxQ)0sn+S^cjhvo#TFSnzL z#v3DgckTh?K$DHvbD*2ML<|yVeC*1}LH3c;Z$tLh@9oN?oqz8@DJ^A|{<0KC!ju^~ z$v0+;(;jxU&~stm1hxAzy9(2ri2rr?vNg@Gr&v731|N~{wVutZ1Dh2Tqhi8y10~7D z-rlL8FmdK`y9#?tCE_mh&C|X@*3HSZC0iWvw5!u<;#=|6_rCD%-yUMel{4IFky+WS zdaTJ__PNqQiis2i0@>p}K!o4#23O#I=#Yv_l<>QOm&~=`Mu3w%vVEk-J%D!2g^jZA z?d}K+@H1kgp8FYQbxvrRiEG%sDpNeaS9>Hj@;;=t%!QVTof8D}u&WT$9(H6o(A3}V zR|6eq^$!<8^RT&-m!(cVewCW-9Jp=SVuGJtMMtGD-Ohom&4e($;HRG|AbA>4)6sdb z!8B)DVQlr+jUd#hpzh+Vzg^kqn7Tuy4it5P(9#cj>c>IY;Rh5HEn=^P!}^@k051|5 znflSsAMKZvX8Rum<94yv%Kz-|L)qfTE_S8)@&ChYY{ytDf-c`1gjc!(?P|rKO81Dy z66FXQBCbA)2K0;QI)MGaQvmC$;U?*J6Xr3%jS<1URnrSgCU4j z9AVFt8$H=a67zZXaaRs>0u-P~$71*5v*2=zE|xXQW)gZKTbzy1lblP}i^y76j@>y5 zfXNgDl!HhDz67Yb95w`J2ObVB?Sac$vkh&*1uGSiKKwth?B?7}9xXl%8!x_^uhHwME5Ti4c)G zwHT>~SjRZ?6mcOM(u$)kWdOIZub?mp`^-NPJL|nayL7p%{2Ed8$Z|Z-3l}Qc9d3MI z%@!wO?8@n#7US2lMRlxQosQ5N^Llo@*?zq&lV5TH z+^NKfX zSiD&g25s07DEVW^%mMbra+a{ipOBJmR%?Qo{r)OtvXn)WH$FvC1MTW~flU7ynv!Ym z9wn-iJxwBDkX_x8A>}^@^inmpy|@wv<5X`3=Ccj5&`!&#ygFnj&68%Qnr?`aE4+=^aFOIY;%NdLL5rSuwU0w9yoc;;9 z(pTV2ZDs|$X-(hh?dwuNr?W-R(e~{!PhSMUP3crwe5bGo)k$ws@@ zS(KUF>g}8$?t0caKtb=o$GFo@Z7{{OXN&hNc6FTz<#~FGs*mwhDm6a2Ii<$;ho+1;^(lN+lj9M8vhKJ8l!(I} z#d8*{9fnKKe{$?SrotdqxgW2Gw5-F^2nMI_znEb#Gc@Jly@#a2tCY2mwxGX>-7(b~ zjQ~&Hk}gW8A<6#H7~*g8yOwW?ha$qqWaUj?feaUzp?RuxKBRjQ^vLeuZBA3kmXuEa z>I^X=@M_rfru*YbJBkmm5IYe@S7ztb8ni1 zn>6cZE1Wv`|K;F{!?l(e@yA%rl~qXe$Pw0Y_6Ll2SS~}>i(g}VdL-M8<1a^Q!O&PR zSB@Q59Nq7cd*+~HrJsiKO$3nJu*viyc77$EKyUnTfg%>=q&Jg;>yd2k&`*w%hCVr> zInS=V)7ffYR9b4sG)oIZEby8EFmiSro2j%E@`j(n3<2 z@7kXT7_d-lUo5eE4s5J$HAG+}itLc^*FmSOyh?YT>rf9Al5!oCFImXiWN@+Jz@;bf z((5qOS-tTzJ(AQocQb^WZmQ5UR8S1JMyRJH=qw>XRedX4%!PMv}a5j;JlRuaRmC z``Ax5QPkeOD7OCbC|t6h8VVhL#Y_Z#T&-X5VYdFL<&MjHrizRJ{j?SJiUCO7wGKz2 z7hhfqseaEQCEi%iK$5ieYWTc#1_I$xuar<^uR%Hk9?RJggusapPA!&=qW(TuHL7f| z7v71OYsHqC!8SVPXeOifodj`HYFFAf7lmXrW&8D(BzY`l@N`Ydsh?|)5bb9!2QzKK z#p>pdgG@T|i2HVFh+DpluMnIaO^Of=&P(a8xjdBlVnoXpL}$oou;&`Lvcrg~W>J<7 zN3+u6{owO4S1Zocx<|0@r$$7NR9h@VNXC))!sAAq(8xV}dVg4!4Nvus;v99Xkv4YBAC5=e-5{>5E;p$mD^lYY*B=5`*3KyXic=k#czRqp6nTiGc`wivA}+# z90pFID^%{xzj2ITczd*u3N>!H_%sHsZ4JQL-hc1Mr2(qG3F|%&JA^dE8LnEU=ZL~` zJ-fL7%s~G3n_;|Yy9oz0RZC%BGHkaqHD2EoV8fQ;bKgq+NAk#i*gUgGZD?qs1F+| zr;WkO!N#EbeAsIQf~q(FOYDy7;_vmvuy|7O*7moEtD|4KkCg0 zlXe?cke%O6#W~quI?j+4MHHd#l-przD!9iyOnk9Imp&fQRSJUB|FszBwJWvuM`)6Q zBE*K zbJ*3g&Tf}rxw{UFZ1Q(Qalq4Tj#j-6PFt*c%_8UMJZ&|0b^DtimdeBpk`^RQ89VJ! zjRVON3yutUn?_<8z^*2?L&U;aW{n;#^8Q?qJaZ#1TNHnelRCqMoTs4B`p*_2c1Ku3 zj4suE#O8PA@e(HjauH6W5u`ohi&0CTvO=k7G%PGY##5Utq4k@fGQ?m=kB)KQX2bDm6Y;))0QRlr-B@9}9OMwuokbIZHX56^fJM?u|_l zru(28auP^tlhZ5t<(zA*13jl)Gk>|*bFVp+MJur=St0|us&Yik&31M3g(kXcv}eaw zgqG?&#^H>Y`8in2azw^0cIEF%y>~fl+3pJ+{md;|)Ny&f2E`oP43pWSd#0%-F?EIP zOkHd;B!X@!jmayS+`ajdx+Q0sGO)$D3T#J(LxDyMUid=w)KIJe{ zX;=-Y`A!V(aBDdZ_$QYs$$m2+8wASrK5NCdi8C#tah`tp;T9lviF6m$ci5koy`-w- zfbzOgZ$jD28kAf#JRn|)N}9yvAW$Y>hgQOHmK2p6E2<)syu|)3*d;WDlq>e)dc6i; zw{Edu^rWuoZbe-S9kI@iaP{Zt>-LK?oPmMlwK9*gv{F}DC;xZD{bm11zB+VC6!Pn# zvTe32LBSEdA*cQ>dz`2}S>!3E*&T((!O1ek}Co66sf0R}F3{frOYVTs}JFtnkTeCIuZbUI< zcSFY1-miIyq}ilWp`PsR+C@0-vCnsPsm=}Z7v@!?pcjMc- z*eA0V zEvnUqz`>1x3h&2eLbi_VB}atpwF5236g{*&th;iqG_49l*+aoO@ytIbmk`ykuo0Z;3X=FnLfp#7EMmphp~HoSes=z z^pE6-Bahe<@c<{{1FWrw_EYr zKLY;OJQ>_ZpN9+j43gvUM2;wVOxx$V?RhdsoPA86lyOL&lK*>&mhF~cygZE;%J>$e z)$GC72O2P#=f;(s=Sb*=v!>xH20;4Pt1dAnJdXkz z8evR$=RO1{ala+M7f8zQq46$@W(rIsMQR$dWiPPk%$qQVFiNT209U;i@mY@@aLBA{ z(jtlNcnPVQJJ9p!Jz5K(k@sa5aqK2*Khc0>*=HI7_pwbo;2vIl4$s3eR+OX@`EI|W z)9zlL-9%b%&+ekN2-Z$+1tq6beK?R)|BPLQb)b55kae_Hg-43Eal>$sRvGRoVr#;K z5qeTD%KWHV=gNTlX?dsqcnE6XbM|iHsNNPthroGKQi@eGLH%%L3+H7E-q{I2^CR$^?J-j_YG(2$p>**-X5{IbFqC> zLGUIzR1TZz0DYUAy3|{Q9`qR_3R4~G18!>HRv$!)O~)pfgvm76TdaA}UMz`Bwfr58 z*T1A2K#}q;664n3$&rmOX;V54!0#co=wWO>2fl1q(Iq&G4y#l=QT5CUFuS|={SnyQ z_UV%q&W{#k^>_qR*)%&oM6_pw!+^nVaEsz1Yb#W+{q_yAf$Y{%=SNjy zsnrj5;C&}v_XxNR<*8vKbw9|dKd7sx2>uW`(W?m2u^v|_Ts6y@IG`UXJXAEjjhbpt z*^7+I#K$?JsoAcArL#Mq$p1K>eAJStiaYpWxzZ>&il3smqmNor>tpozWgzi0Bo;k| zK2)#D4Hc!Q?cm4mi#vT*KX=2ihhKuoe(lUucvy20D?SFgzNXJ9si1rz|3h|Pn;R@L zhB!(kF-d{r5|V+Sly-m6`bZ8wnZm(mfAP;u_9^S=4|EJ z$FZ9;Fz6`-g^69Ob3??zu8v5No-j>Og@f>=6W?aSDan7E^c9HumL7h}_^(y|Zld^Y z&7c`b{l=M^J~tv!nBLJMh8u%#olsL9>}#?D!?1kk#7|5Z2G5^KIG?Y6Me`FE`|lOr zL+pB2pLB6X{NPNj^bPP6O%t#66iHr=Jfrv2k17-1v-u?$`^>kCaX8Y|k#F4e{iHIb zsiqlb@M#6XN$0SArE7+sai*HLL_~&((s)NX<2H$}CCDXM>W z!n?3S7?3|CvQg&bi2f=6!(-z^`%>5Ef9b^b!eO`qaU4Xg`$%glEd6f{Cl@*4rh|G0 zQdIn-;p!r+B~m6yMuQmze)#S$N?vf5bE+JXR*%~!7~jUIT}1QaID>3C7K8Rl z`Q{$r#82gfoA+y)wB&04Tv2xtaxUq`Fh9}uVi*GHy{lm^XaN#duWXa3UF3f}K#>s+ zPa$`;AW$OV!f`KD7b646#$SVvc5gT z#M+!N+%|C^rw}KUyf_R?dqo(w9Q&bh&{8JL=i1QKnW-%*tgouyAag>Uc>3WU{fylU z%N3>HK%r^-TJL+QPjr)s8(rmxqXJNW)Jzw5MG;V_wkYyZfEmtZ7m!qCKPviPHc=u65Ae~(EKT^Z(KbMEV zEPjcYiPC8LM{&Ik@+Z0z@D@!c;t)m8U4q59!-uz9;|(9jYBc$`atu8nF$JI`xtnm7YdM>y3d-zRX86!ofK! z)RvpJ>^`~Bz0WsVai(!bZ@KwJU!9EOr{8*-I(c92S~8HKpUw~}VvC1$?UZMd`KaFX zN1c_o1w@FJmtaJfL@_8E0~9_?LR$|yRFk+S3}n%}I}%11w6j6Tzs=tVrx#EoU9}8G zYSSBjVdCJ!T_L4Odczb-QaIwLT?B(cZ^Uao?hACX#_RLp${=Bb`ozD6coeo$tRiAFc3kQSbMCXjJ0=*V=n`x=&oOqtCVoYgtm!_~BG1%-)iKjGbAme0YLFnK)32^V}H zpJ3qgTqm;o!N^`LSTQ?yv3Y>d9b5_gxGO>>xbL*`6_jpVg|1Yo;D^f{)l!U*YU6W- z+1n8&&R*_NIu|GW1f94x+{dt9;^igzA&<@`>O4uuVEHsG+g6G66d9()P#Ir=lqb*1 z4_SDDgw^hh9VwLmBSc9EvR5BNtR=bKr@kb=;nH5D^X!_3tw8LL_VMySls+C@R<6=$ zVND!XPHd{>cU%M~X{6TIq0WlPNRxBx{T#}9Gy{L9@Go9x^K+UuO3U!ql;UU8 znASs7T!;jVxNCsZ3E)xD0LKbBq?{)+Kf zvcRm|`ap+rZ6r;K@%iTAQ3%oDZc~`8QsGP)E)W&|LNXhs@o-ML`7g46kB=gNAGN(~hU-r*8tP-^AJO zY=FuRoJ@I{wklhJgFzZ>%9ynC0+bFvvP))8#PH^BLJKmuVvMCa-lZg@Hy~1$p zzx@BF`n5*p*&qwzH*0l4rAHtb3=+K-wP$X`c4=v%mc`7!4*C0_;>I1hIL~{TIWVTYHQD4+$R4=irtQcIOCVMOC&-ZZ?$MG#W3jW z6;YrNJcJH9ckrE@xk2GtxEOpW?obd;CFp7J4w)v`E3 z%|}D*uPsiz@kyr|XP7WsUHBs;5cj2qv#NT9hl%PZ{V;#|$lN6M~nd#jRaMkU< z_}vP}>%?OghfB+)Y(J&@JqpqTU=TI;0^0E*6#k3@xH>OOH8lM`h3mO)`1F2NDu#U& z-t%@gNXZg;xIm`c2JIIIYIW1&;*~UI#0sf5b6~eqH;y}q<1qO-i=|jraCI*xN-(Rj-nerv1F)L z7`E|GU9V<;Th&FehqgsI;$|e$M7sOYda0`V)+K z6Sx=6m$xf;eOZoLAK=gD3jc8q<;2VUds$Y{>zEUrw{i@pgfF1LL!V=%b>b>IL0^3P zBEL;ZHJM#kIdJE}{Sxy_%#i#E-kOWa*2L(A?_Oqd%xPS!YRf|-gMYkMaa97aeYxVt zY=;W`#bdtxxuPV;p>DSF_Q(N<=p099kduS*KQ_7}@gitJDnT;2*_T=Y=O8+WZzb z#AL{RL+rTsbD`ci)C~kO!w1N}G~KV4iWqJPuJU27I8x-;;+mEpA#2J>Ov}bedQV2~ z`WUwDs~qZvH7D;UDp!~oxXTOS%#l?Vz^tD-p@7K_6#SU>U+Bs=zD)JG zhU#1J*hume$7ER)52BAL1d%>*y+WpP+JAx6iZ;YEa9%Zr{B*xpd0gRUlm}<)zu_bx z4WEqQBiWU%04GS?nD<~bmG1!X^-dQnpClG{hhLPaTwMVXgy-YPdz_GMC_4BQ7Dog^ zg2%>b?IO!LekxaNoUZp~{Qo7gPB;biNctcf!V-K1sP1bYvuK#1Da2*sYn3`&Y;a(f z7%rN91L*iu*o@U(hNvmAF}UnwaG3ZOc>A9|!x@VQ0sP&gFnk9n{YyMjBHzty-dLt2 z;1pE&J)omsTCB=1i5GMwi8)$707?l72!|(MA?)6DgJ7fk$`XbbRHVm+%E(6lcxlE+ z@%bFhHyo>-Sf8$0RB5D2D%nq+h5deF6Ue z4N1SqQ7>^;_79fDMUFe=;4%LNRI~0o3tmv?!!ga}I@S-V^KY&QTI^8%W*okYs?NBb zev#tZDd9M)>yLYDT&88|oar68V(=1&ga7HK@PB!DhxL1(zOoPL53E$IKo3d}Af?}9 zy_XLHq*ps&mCV%Foy&n=2E6@y2;`qWM4T5HfNXcha`ioxn#+MN8y1UzQb#At%P6u# z9Ai&~#BZ%G^AxYYy(UfGWtLaxxKeoKk#O@@)b~jZum9NUBSu&0q2Yjf=ZU&1hl)PH zb=?QKMaIXlHiA5!U3Wo>%2+wepspXSZ zN{gVB3ds}OS86SXHFafQLq0tJzz+%F5vRSpz@J4jE zJhAawhu!6c6^^{%^NPuRV#VIoj!Z+`gzjjsX>uPtx4v5UpUQg#QvaFki+AlhaGyiE zd>QU%^Z-8i46L|Ghe3tbhDA#CqH@raEzA5I@sg#7Z2HfOl^UG^EK;?k?3Y~Ja{k=v zC4RI!N?l4e?s2l65V%bttZ?XR``10=RS<&fO7?&p& zU*}k1^bLz=*V?}H8Nd%l8=dzO@`P!vHdS(>C+3O9wR!{1$<_;P+I&vFi3Nv5_ZQxK zD+I^3mo14DYx>i~$EVU_9rIE|VITdLnml-7{VB(B@O1w=O&L!>G6~9d`v= z9zv)jgZF4Z;OFlNh3Dx#pOJ%S`IaRpzOhT1c{%o z8D*tU+IpgAf{3zOAXCF_IIp^Hp2|VBFGc34KNT1+cAg6K5rZZob|*ul$vOuh$B;bX zf16{uWUop1KU97p_PjMLS+p-%eU&l##xS-%|G8et;`=7Fp6+?FXPl$Mi3M)%HCQZO zuL;04dqkdSUhk-tVv$W6DJwu6%0nx{V?^Z!$2j9-!K2XM^j~pj;DbjUX~x&SMguDS z75Aqme`HHH29B_>^7Oo*D1|X>ql@IJ#5~IU5_ty<(H9<^t{P1-do5sA zLU0moa8w_I;oIa;cLT_!$vj zH9<)6Ow7a0dI!YCZ)~3UVzX8a_`;i=1hF!nI752tAS9U7HxoBC*L7)D-qHhs=@PiD&+Vg-E9!ne} zf(}64GlYo92Nd@Q_DeN?>rRrPuT-hwqUsxKuoxJRr+*9~qQ(Q?@duXu#&4`Wg~?-y z+`5xI6BII3J)_sYNk5}UIWQ4&K{#%7%?zvj54+E|xU*GpJN8S+o?<)>EI@Iy|Ae%K zHmbatB~x>v6oM3xnN6un;ziY2A6x))DXbLOlGGWsbo>uwFl)lOY-=b`gtV&^P)QXnInFAc)W+SRTpz|gL`^d-2lw#}h} zQc|^;ioS&WjYxm^MzI-1*fbW^_B|F2B!gS0F6mT_r{jyVX};L0?*b#Qy@)6k?#Bl+ z^05ABLCaB_iR`8Kc86PM{0cP6{Q}%9Kuw?Gd8%`bj&do6^6){iO3f61<}Oiu#E#EK z>h^!2T*(G8h=HURRg4lgHoeU!MofRpw<`&Q7vOLMT$acTbMS4|89e<_y4_7omm+od zQ{joYyyELE{ypjACwlC11d5Xr=MNEdGLgiiQK?K3ujnFNR+}zh9XmMz;c2TiS~yaw;IEC&czz^h+~W7E$XTB^2Dx3w4NG||MSs;qyJj4FCT*o?y$h9^^VYj zJhAUlN9VIu3)#nz3V)nXJO&MDPL)=15|MWiiaGT@98ddBTph?_l8p*%IavB}Xzh)= zyhHc`loCv`RTNB%C0rwWOI2ox>g}l!d9XA7J=#l5;L~D;8keHR9sjLS7D1J{LKU%Q zbYA!Roj4kBRVs6^(#p;p!h9AXTdH5dE#{Ud_4zVK4RInwN(T%Bg-_{Gn}F16q?UEy z$;{KvRPxL+l^RulLqIQaU^V=?+a^v@vX|c3%SpX;ji#~WS!MU3LVk^EM1!>tuBPnL z14V<#iaarRw-$^vG_U0M?dPmx#NK|B@vJTsTo-e#Le+6=WB@_jADF8*#4-5l+C0(L zZ4KrnrR#7-p>=4&aJLVW|J#Yd=BK(e~=KG3?k3nHKyvtXE z+Sh&ACBpbT9OM-uZ^|Z8K3}IY#i&Q(nl6+nMSzR&nmkeXtm9spm2FsythJY=_HwZ# zo>6*R2g{@BwiZjqa}E`xh)c{mMZ%Qaya;i}W8QwWaQTYE#ac7q?&Ny6ESrAD?&OZ3 z8r*mIQa_j*hb%Xs)=c5qQ(1H5)oE%>l3Pfr8&wgzbMqp_r$0luH=KrTskTuE#^9dt zCKR#c^3*&r<^^qx;*7glrN$r(LuzmF86Y_tl(M(viR2d@D!>5$--@i}hD3|__eEU* z8S6Hr7JS$(Q~aBHmEPVMf}Y$CJUu;fkT`y0G44*T#m$J0g<9$1JYElay{zXk>9qkp zsh{MJ(?~uF7z|ZdlAc-bKv5>I)JWPIRm@e{R(`b+oU+fMV%c&%+k~vvbzN~GYrmcv zT*~T@y4EXoq*_o75^yu1^W}K@dG@yu=jNvuLynG~tRy7kUEB%$cv)0$ar||?@KYbS z3yl>`e@FKbv)^#2Sg%y)x2VymyU90Jm}+DYQ5msO#fX;uA-iuynVY;*2VkQ-TE3?# zhm(p~y(+-N=4%#>ha9*egM0F_vy`?XPpo;xpSAs5&5s`;Uibw1F7|@ zKFOc}z8A{hYmQ!`ZKe+{;q4#fCyq?RictIfEG#-Oe7R=feKMmm6H|KLpC`t??odI8 zNQ!N!Zm=)7g`^0fGEGu!mrb6(Vyfsk=ui(AkrWN2(C$C2{iHSEzi$9GqOda#-%c7x z>W~IIfE@Ns9jN!M&fuYPQR6QC0hU~PQ%J0s{gxh*JCXdLP7W7?{ZdzmjJKgwRqvVA zRhG*P53%|Fsp|=i7TYRHRL|=HJ)9>>-gRJ-U9heqJW9-Lbtt;`^}wl2-t#SvN)JgW z{(m&Dz6Iw+4ex{n?hSe`%)>*}^k0K3Z|*j~$FS@j)w^eo!sBe-sSc|ZL0FXyTo%Xu z;uCoy=mWsjwXXccH&&rqm@2#-p?aPMF2MqDC zE|Gifr;*DPk~%^p|BKL7GO!Te6O}FCm_L&z-uXmZg4v>Hk-stoA~WsDnW_b3qvup= zylDTNw!;sxEl>ZX2$!4OPk^3Bri|+Y22s_4#S`a%avUj+_5v#U$IBwx*W#Mne=m8x zpzvs%WCVDN+Rq)zFN|yJi-fwS4i>E~pls5N3T5qJ+h2k>I;PvsIr=iP9_^YMFZN$v z;3qPESgtz2@!F@LKH~l_w2Wcuex&YeA2f!h_TRdsLRkw**jH2{ZVrZ~28r!!VTqxjkR+m@ zcoq1W(9{g<9_OQcuGXz}tD^>{puQsTRFM zzHav}7)aBq@}nwxYU&;i)}Dk}r7R{}Idh zsLEvK_qa&weo^*)mYuSy=LkOYAp;e;rpgCUfPZwXmV%#r@*%R)qV$&!E^YsX-I2>& z?~;2Tv9x7=1+mIr&nLA*DTKKad@MhozhYYXa*=s1H^P(8N{IADi*k*@dp|*A&veI1 z+H}{|qvd)m2cC=Ur-07igO_7*ZgdIs8KAfb_pf9>_|@lm;`C|lXh(7$gCsqp-PVYI zfls$Tj#B?y;c-B9EcL$?o&ePTnEO|*3Qs}-J?Bj$--3xU`*-9H%rnW>l?Q zF5}6C^nesP@-r0a;ItC;hP9+CdGkwTt{)ILK(zk?#beB*NlNkHZ2b!DDD8pSdg46B z*04kST0ywb7=RJMr^*r|$vFa_e&dG2a@->ou^;}E>04w9?wN`s4?>2ggTLcwHyuvE zEw5j+m4o8odnT{xQqYgdP<#8WMob8(RkAlE_z%pmvkSQXzZCxw)jicyjia*N@_>QE z_7lDW`|Y`*YlNtdNc9mdzvfSP&y7 zep}(YwY@@Jh~OG?4yBx{fDt>r`Eoz8^AAUXBo-@ZS5TbjvPfSjW9oUNwts~vKB!hn zUl#K-QZ0)SmLDPFe8rJ}=A_6Sm}C%YEx$nG{^d{+C}~#zl`VR_`0{8mVdN}q*zeV6 zrL5sMl(pv$9HYvP8QvNB9Z;7iBjC;d+^S2$`TYT?^mNZb!Zf~eRQ&}V$Fu_F{fPsG z3ywH(at#7pHP;qld0I1Ts4!2Tq=M=%L7u;4o~LDdEAjtt`9D@H`PZ>kCN4$dKRU7P z+CK0Qxu_QuA{R8W%T$nC-%xW2SN@BpwZ^0l6&=CoYx4t$Wo~$0`67w2Bc!0WIItXs z5QB-XLt{Neati_rwjc4t@t#Lbr~k$O<>TYQ$96b49xc9)2lkouyW?nSgu&nG#bciAFzN zRz6bwRBB%l>tC}_wvRHvKVQ`O*Qog0HvA7jt|N)5%SGL3gizcRP@@zDPOvWdhz3)m zT#Gm0e_*~S>{9c-Eb15h50d}!`e~1Xy`o1@joMf7WIi}wR0q{4eVYGGsHE*joYXf4 z&BRIlhzccm$h0AVF7{515hqu`j%M(#=$bE%1lKH;^)YLx{EzrlUA??Tc_=u-u)hgI zNvHatBy$$RmL2b1fxX&(M4vZAwCm=U$*KCxuG?aWN&C+=$S8?Lu=Oa zKc#3SGVSS$y+B>JngA7I+;E;BrSN1C6^;Xi-NSVNPih6xfILq64&tY(D>R+Ns8j?t z4A)Gt2&u71ZI3dSA>1-H;7#}GI4+!TW(AEF8{RS_V8`MtFL7@q;us;PVq4DpIAk(M znHMk>>{0+Fqho7{V#R9U@d`)yET3Y3ksN7;V{3Pe)a(UwRi>MeDFK;U`=_oE*F^$4 zQKue&?x+QnnD1GCo5?1^j$DQP@88;hK}PK*5k%8&?q19fmHI=z=*=0{u%mbM*0W&Y zjyrWE4lZkb2rU~Lh8W}T;aw;`I3p+KLSTJ~6?Ti8qs*wAyx>sqF9L)^9t_G94^E90C;Guq%A*sxa!|=j0%RYYnkaTBVB~ma z&QkbMGLYm4!`wcSedI?e>|;s)umpak7?v}82);0RLhIP|9VQ)HB)?#bhVqLu@dZ6Z z;9E;C(GrK@vyde06b_GDpeFl3d@(#nQ9y+BF_~v&lX8+PN^|y$ob^d zWKq>$lZ~5=G^CnSu-SO=8$53r(@WdLZUvMM-WX7GL>6#6{%0WT$&}O^MB4zcc*{Kt zRb3AO%FGw0K{Y?htdHRTSY$05k~&5tC)W(z8fzY*qM9_NmL!WW2G^)v$y3NU4jI#j zrD8K&J~lu!zZVxPN|I`nkz^O}EQLSHIzd=>+fFv1nopv8GkAx^5QRAh$a@-r{6+8* zIdWV;a`Q!dN{zaudI10PAn!-Oo4nL9HcHec$C*UCBi2XE`N513&%@Ep!@>Fm9=0a` zlH4Jp%di?1gJ})mDBi4-D!lnGB5es4}EjLB0l3wyhZs}v#E zBjW^_@qNj<i^Vv0v5ZWPv(ND6a0TG7Y*MuBgC8i`i&7*Y0-Vz(e)w2rD# zjuBP(U&u0cJllJrI5=91m?9(>>Ez*JqNPS1oX{qD6$)-0ojOW%A2KghmI!0n*fFz;sKv*W3$Da4hm z-;K{^A+z1k1O%8DX&vELoZ_PLHdpll>(@ z%TP~cS}MHPH^I!&(sxRyQ^(c$!kk^BE*nt}%|+IhG|12W*?L5FB6S{8O=adss8!)@ zgfVl}TVB%U$FJt2S?QCi7tI%ie~yK!-&Cxo4;?fI0bh_WHs;o>kPV{QY9Y!ynV#C0 z8Z*|%?hyE~iQ46E0q{laX7j7DeQ}T4+*Pa{UXUmnpMnsMZJDox@Lc37*STh?7;Q?m zPSid+EM_sPIhcX%n(0htps4nX3%x8QMZ_)6LJT8$^fgJ#N3O|B^6?r#&GVAyxzI1o z7nS2{o|dF16IICCMi!t=BMYun1u&wP%utLBl{&*@g*9%`5zc1-s{#j3tZ9^aXW@S} z^DY}zFiF&oLGaB``9&y?!PB(P9gF`pAn&xA1#;}DNneM>W4hL?xd&W>8s@AEj20(ju0W($*6l+8qP!)Ll`!9q=i(ZB8U$5<}97ZCekhBOgmdYi}nh33v%hEzFIXe8Lm`l>Svz!1GLA_fA~_pkMkDEmb(skFaYVSulsm znMd1|YhWKGcBlbYFC<9}uC0$@m$OtG1lZokQM$@LMZ6T91#jips$+$_lE zQwQ0DhMu%S+s@dD)rVIgouj=(to7lDJ_<9u zvh^~W(pCtSNPb4Hl>)LC|jQfiND9Kuc15;TN zRuZKOa_kNQN-61~p_ZM+N-%I3o3S-qt(7PKKZLBCf9nCQ=GGcjCEeX#kxi)`UX~_rEY|JNvegm#!#M(C=@^RgYJ$j9=#^N&PmrZcqv6LxK~v;Br7S_SdZAy!AhXJD{49wwDKCbp>u?*DJER_T%^PWRlr>4iqX*5cl z-MvU{oM_}|CkYyh3erS#bi9v9@Q8~P7m5lH8R<*hFfqKrd>*B&d*`wQ*W)tEfW?FA zpK;`HSB>&kS&9F@z-Dn*%|1zZZg_r`|06|4Mvv(teM?QMFdv<)mMw1YeuIX-wWe8S zUxoj_%j^hpGAnf|v%_WS?ixG+vsN#-*CNj!;OqLD1FmoW)ZauGyM3ctNRs@OFV@_x zNkW6e-{{PyVi>bqc4HIp|Iu~bVO1SZ`)9$56qP1Ou^S7Zv4R4&7}~wW5<6hmSil;U zTTF~87BGoyVpJf;R3n%kO%&6sVtTP)dNGRW#qz!H?B2^gCf`5jdCt!6?Ck99?Ck99 zE)T?t7}2x#4_UIvzgxU|{61JP z9Pf?Nak+4j4x;B+Y)o9PPg-|T7hiV4`hUKtk19wqh5-!$KNQ$mv|YBi*UZig%V8}popufMUy*gA;F+bn$Pm}^s?=&xeG_770y4;go{RK|jBB@e=Dz6%UuWqM)A(jfxT zN71O4L(^d^yw$J^yJL`bjE1o%c1*UdP&2D=mH4(tjMJeO#llf+^Tp1es-HT-mr;H% zo;xWUZ6bc@7>P(%-#uD%zX&(~+YwjJRNGi%65?qTA$BBSk&v=IMH@wJ@IMN;4@U>% zvEz>rRaluk7Q3gZ(^?CzMS!;_uEc4!*^Ff!tYUrLKhY>#b08*{J{Ih1_}+dVldfv_ z7U|p1X8}ztvhJOgG))EchJ))+zh!VpVOvG2q4J1hHH*K9EYwzSakN=u4g;)1y7J#w zq_5K`z<*<#kdpCgAM&bm{(QZNAmHX&q4H^Yd zcMVEWC3iv3#B?_7UIWlo>3A2tXsGP(i$Ktm)*;d&qCQ}u>x&&L(6=pLb7qEDYiTyKxnn|A6)*R8JaoSoP;`qzYP;G>VxqPOHtLl~c?B zjjAp@0neQ5!|VWPqV+7xK#}!%(6BxJ%!MMyITvXmw^0Y73Asz-dJ}{bK)%~}g8^zV zqCMX5zBYWCYTg&h@1Whh#>FNkdEoE9T=a;rkg3V(%KeI&k2t{$Nmrv^HH%jM4@Iey z1(qWiOTi0Sq0cobTr_J z%aYotOH1L$FW7&bzg|ak6G8eV8=0>4$%OYmj)Kz5SthJ81Mu{8RsL?#osxev{xfub zZ1Mlp7!QG8gXb+7j3sbRD4BpC{kjouQDX$ip{LB_)9jHffPzw*_cXyFnRxTuA_v)o zFGt)#z2m(y*tCa}y$$fS%agj}3LNZ&T0nLU3RQ=eqrC#R?3CsJer!4HzLo3e>L=1g z+i`ljjzW8?z}8*eunSS8yk%Y*LyE_sSnSBIJyoT@0}qM%?l6u`SH0ga(ubrt@r)yy zwS_j^&*6I}e>^apUV-?h>W}C6sl3j9t<}lj5xR3|L%yB@Ib&XrW4|91>BF#`-6!Zm zcn4sKttFx)s7-ti=}(k#w#0Uo_0`2mLc$VTAKi^Sy(q1`7APkZw&jzcG@M{!WRB+v zHOWBwryxV=Qjj0t0>Ts&hI#GW!;{*G4b0&`74Q_bC@f)wvd2d>RVCN9k5k!OFq;qQ zfamjg>lUqHs^Dp8&L>41y>;iZ?w^+0(!B5AKyTn2h79F&6v#_2{mg7CQpEDp)wa*f zCMN%6S**5^f~Lgi5W&lD=tmWJctYDS*jav9hxps76* zmZEB&8l%mERFs$v%j$%&toT1CT}7WPS}(*8#{XOrpR)o?WihFU+2%<_)x+H}SleGw z(R@^txhCm~if+KnSnGp$0TCbG7>1ED0uSiYgD#bF{jiX*wJWje+W8n2?vZ~QyGzs6#;<*RGRuI6$yK^>tPy5MHvAC~ClGsoT< z>4qX3Zb|0Apu&<)=I}29EJekCZ^z7V^Ni`j5b8JwWag(LeI}DNJBeenyA@tR-3a~3 z=pyM#nw>7fwm;RPH!UD-rf|Y{gQDJLtq@7fT3tmq>QyyOthFpr@nMU0dXuHR8V@^^ zZS z`fJ3-#HU~n-S?p0EwGb+hS&Y_!DNJf-!@~WG@GniFA#0mr{e!slQZ{FGT!d`H4t|^ zeUqa{7Ta-~0jGop1*z&X5EC>%Pjes*;M>80GyB7FUluvQ$8x+qc?Up?uWAWX5iAo< zl4To6c$V8X!0>FRqmQ&qx{E7`jna1KQ5=p){ccIB^>p(t(wS5(?U4!Kc6a^i-zidP5!#$W^=agfmW@yab||DQ$cgng8f zJJZ#rKZ|rcHzngP;C9`D_*YTzmfI2TPFDec7dfS698p`PWwqIE__zNy!3G?er9g2a zxV3a8jDDPr5+0IU3ZVG)h(qu;Rj_f5caLB@|4uTZt)Fj+OL}RV$Df>|+eQJumu(9Q z48$QjGOBFZBt2P?CEL=~(2FKKf)(Azyj3@Qx`j*?9$}gNmSL_W@yo) zBvhoU77ZLLgbudnexgl#z(3Knm1hDh7bWZhn&8*{BXD>uu$8LWIs=TlzgC&`Zq zFs)fDoO1KS%3Gw2dRhdMQnd$atf@oCeNx>$j!J??1;aiwiUHHK?<3TE0k6Fy3AgF^ z_CO_4vs-peRQ+$8SwC2-P&*DJLn`DlqRW{%IC|iFI&~k4<%~yQ@{ldl;kaqx(9Gs` z(h~qR_yU)1m1kj-dFRO+lC3wCJ*nXsWLsQcQ$xoYP z?nf3hQ3?JIyKsRnrDx#!@povK61nm$^Iq=NsHb{v3Ksu#6Nwzo2JAW1T>fU8TvgW& z#<+!<&tokg;Lwjuadht|n$3RQdaI)51^OJjP|0C?fUpK1HTDTamMZAregRdaZbHC* z?eBOJi9=e7aiqP-V(0E4PkH(}y1`z7vIuc(m)6;l_f^yt^JXYqiR)MD;Z1|%HKZn%Bw;k|M}$n- z{6e&AVve>wIW%5J4JmkKZqX3ses8806z;ygfpS$PNf|7sJDY?3O<~h-IM=O%&@(6D zb+r#CY47J4WOxfoC)}}3_{y$&n*|E~ZU!yyMvwD3NP*Gze21{&Vq2`kw7E)&OgKu|;u0H9n$B=&E{sKT4O-)Vt7(=> z-LO=*hU)ehGY4+Lnp#@3coWXmpZe0oi&8bbm#+4-cj)*h_R;%5b9PL}k(j%U0->S& z0aERgJB-%k< zTt5lC(f2T5{e?v+hNnTK7zfrV7CW~UPWa8|aUMb#ZJEbvO@_W+ybvkJby}n-!Kblm z@8;0^z+~JPs4VTCq$qjbNFC}B*vZR7bYHEx{b?Fuq_ z#Hp^PLn$`jVQBXvbPznA||mGV7$NiLOeT`)^lHt+`nF5iZO#Zo`S1NNBB%rE)K z^P|bLDBf|W@S4-o8Pc%jSmSUNADI=Xsy=VrN~I*9e0+V4SOzy0hu3 zI?>Tq-TQPnP7Go2h)_}V&Iv>xSZ8n7L2};+M+nXlxC$&MBhG_0h1=m#^opJ!;UM`r zUG44b&=-jH#s4qqD*I|PK@#b&K$?pq4ubUZKGTbIbN(if2o;!#SOMV{V#l@YQRII| zoyX2}Y@_Zy9+q6MCS{xv{)dLQRRN#lnFX&J^}_$uaD)|5!&;QtUl>gT9lGJ{x4%7A z_1;8BI*Z`o9eH_31KOZ`@vpp=ufC4KiSC9QG)rvU|)8o|Nl` z8EW$o$K65!Wvvmi7Q}}dH3X^E_i#wJF$TMFhN>Cr(2wqMIQy~M2D3v4_)Gwd+VD21 z2@uBJk0x34Z&RHbq4JhTT>T%MZkC~jrkQDjWHo0s6Lv?)^n*ZAU!B?lxX!;7fq0%> zh+L(73$>K*mKiE&xTF3tK7UWv$0HDvUq5A8C~l=QWz>`99|d~U&_@E7pkkE47GEVL$C1Jlwcsf#t0EP+8Q`F??zO+jO@ySrP`$D zKx@NRyLa0%RrM`K$y0r_#UlpSI<%XR@(=aBU^T~`Y5)qwPV0Kh@@?eb4Hk2JYYmBw9RJ&ZDt z)KzhqUOC75s?ci;G-$3_>$AH@gXr^5B3w45V_=|)oebkFVd&C6rB962^9-$mYjqye z<(H?JTU|(7x~F*A`vcmNbc-xy0DAf_biKuf&qV5{PkAh3ER*yw00|Wv zA4?YQauUwgpfI)QX?VZ%MC|)wtqq!c5x9G4fkR(Wz$w^n688@a?5pK~y2snwIRe%m zV>079`NuKK6@#!}(Hp!-br?gHOO!Dnq3~I3%+$Hsn4w`Y$;Nu%KRtsvN9;0-TYH=b zp7JbC_sSqWm1xMJ^)enKr_k7K6vpdetQ0ylc~|3qLWXiKF?JkVHxc*}m*8>u*p-1s zOTy&otICtiQRNB9G$})sFLmq`K2q6CMxjH`;T(-ccBkmnt5iZ?M}o4L$Wvj)EH{%X z#cvw2mOYQj0S%D*5CnB1Ai02i|9sL_KC26^qGd=@Yv*xDrd!ObBu9q`Kio*6`pbj!RmL&Nccc(p$~U)>#ZMfO>qaypY$AKZX7 zov_`D-#Y|nWvF{r8&iS`a(0FqyvDIj;G7%gWT=yCjA20D&qaNOFJOy8@X_uQQVE); zL3jlBAZ!M2Hw&EIJl_MaeJbi|4L^GGs`@GX26VgA5vq>9$k_IOEdUpw_{w(e5!(hs zR(Q)^S;z*a9=5?J;G4XN$t6rKUtz2ilDHT+P9O464{m6~h=>YUV)08TK`k!r=g=sG z)wtfsCi$}j{8;Z;FDJRY<7sJz^1sb-w-80`xs13D&kPf;G8Q*?9tR8{+^p_!B3D%d_79Qu?W z+pt>xL)7Apja}~ZDG)9wi z?(vnn!#HwoG8k+c7C6hSlDKzqGw2D5j!vRJg-@)xxgDJCUI$RzYiPHn6K+AOBR$S^ z0#|6#Y#P_=0ebv3kAZHPv2O*a=R=*%j7^;;rP#jP09yOHN7?If=j(jBozx%R6=zl- z|ITm+Gh7uPZOP!P88)DXX>a`hnqi}c<6@|YRxJ<&5Y(JXE>Bhx!eFYGO{}H*ZJe3( zj%TkdY$h!59h;E_O`<*O8*%WF<8W*jig=F(3Lp^%GV*#AbJ~siDWvGL@%q+%bR5{9L{1JM!SwhhrX^9u9 z$WUh=@X`18Bmbp$JsMMtQp;A?t_;=fL5E&oWts$D-;nQY{bg1pynR-w$ZtP)z+&6~|A!BjM&UbU;5e;gq zDj#ty^mfIgtUc#_bj8IXQwNp4FhyIJ?8V2xtw$X?B(xv?_b}_e_u;6W`Up0GE$+CLETi|4b5AsymYsoIw+Zy8LIAa*iz^E&x4Kbdrxzf zKo(k~LiUk>v=8AJy{gizVA>z0<0|sMVh#?D>zNpGy`l*8_wW>9b6ZJ=SS}SBP=l^<(CpnFM5b z4h=uBwsCh={iN~!QFNbYvHVYLA*$C?j+>2>2gynsTb$RW}q1XeA8x6hJ*DVD$rT|CT{Y8F&lG-G$Au*t!{aytpnHk0?QJ zYfxC}VTV4#N7;La)gCEo9-)Gk;N^EIRe5?$y>Pw@JLx5de#C-tGDl#ezvR%zN8|B- z6j;+f!v?JD&E;1FlGkLYZ7(}k2yNYw^%$~NeTKlS#qSJksfN5_*vzhbPlK?ao4P;; zPe@}~!TX-Z*1oZPs`WJF2PjoMu5Aa_4JP|uEB7Hlv7aaPP$lKq?XcXm^pOUIsNBz! zf>isB%bTgxXJ%+0Kj)5*J@CqlZ9+A?+JB`Ea;Mh$1dEK<9XhIcJ^nvM{_?hMU8-&> zb~&gQMRv*E&oa~xZ#eD{4zTFw$eM5*hnXLL(=ozQ){ZX~szn$7P3an^NqaQ%JqxRorTdR9vTr;7K8d!IQNaD&QT5z7B|4 zPa|vLiKLDy<~ImjP4Ohe(fNdweF4|SJC2P)J7K%3o1_8mw!T zsLE@BCHIJ#lsV?U&QN=gIM&IaVBT+-xA04w$s6;7*`T9m>RZC%&%oONJlHGpc?I*k z43%Bu(4nLAk@b5Ja=u-MPU>=vV}p>#5%+^m%~rP*7uH`b`y-Y{?->^YXQMO7b@)(= zA*BI%+Qt;_&|di|L*>11CQjhraA29XbC(&cJv5hf7Wd7z%Pi{${m7tu(|E;rS0B zhv&A!$x2F$?K&JFvYtPI!}PdAA3))Fya@B;xUuCpeP2TPsITCasQC#ZZn;6@GC;L| zYhk>?*!W*2o_PBT*2~Z@%uG$K+aOcboHRQFv;!L=*Xgei!3^9Y%~!l`)JVhORyk#S z)D-l_p46gRhdy~lFOXj*Mfyi<#R#F6aOftPs_3+lY1&pzf#JswgRh{jS*D8m(t(FF zvzGa*gw_5ysQQKDIuTh)O>+V3SeTypZ$UDyYSr00pz#3A8FP0L$6rgp_g)i#kU28G z>`n#)`^L@rli)pPK(LHKi#~0IvUST^_RwvyT%8&KP~5jkc%`cr{lOVoD5P2s%v7o0 zn69KD6of5|ZyYysqlCY~nJV*JhYm$zZ?q->2j2(?RdwGw^lb~hk=!N|kNG&3iD*%a zwnc>r-yyEjD?qdo1xzb31mLOP!Pb{PlxA9XtV3DLnLhsUIs?^^5KJ``mZ=u~;Lrz{ z*oWcF8+6Xohn6GT5$MAojRTK;*edfkfkWlS^hmF(*Cni z;H03vPQ|r|LzcBF{WYVha-VXvQnf!j66Lu^X(mn8Xk@H%1;L;(idUnTrjQ&Qiby?Y zM2w;xi~JQoK(t?YWGXC!K4xc`BPuRa+3OtoMAAh3Bm4mWcxBpA!&hZE9x?Pb7n2^x=^NX&lY2E&!eX zF=;SIs~Lw39+SqU0^SvZ^ov6u&SGP`ft+7WW2yAIqkQ5i+%k{3EvqecC0TYAxB zSu@q?hwwn)_TL=SMAcI5^wY?5VB+lLv}-X7XRCA4wEG1SLA z9l`0XFUU=;OB$-4k65gyOHL+NXR0$7jIl?Pv0tWgUo>o{aQ4T8)0fQfpx_K(4d)kh zMeN`=Cc;CykYxv=hMe<=h6D(4At|PE_U<5nYBzTsY(R6&Yq=aXg8@2x9%H*z&|*A* z==*+D5Gt)WhT<5C3gPI!lHGP=KkQx99(PD4mn-e0z!rGJ4eDl*B+*<@&@n$pmz zcVx)L6kx6RIq7PR_416l+Q284RAPBC7^f%d{xE2ybwrbv*|_ue-lBX>u!zQliUAVBJ~^yM{#Z5Z3O z2N|R7Y@vk~@@O<6?N^xKUdD*=OqAE6SjyN{1?u!2k+{A2A1v~a3Gvdt*J!6_s`6%r zB~*GD?1NvE;)QvZJN7dHIrl46eQn}m?HFLQvXFXgW*aSbF_rW)$+)IpQXc^z`54Ui`A9W5}RG7r{I* z2+@th7UQg^Z!5=Qs&8dTVS#bL;`$w@bii!g7N(9zY9&*>Y!WteUb!BiQ@?XwA?=sX zEz-188YTcV?Ut|}s=D)HO%kWGi6kknBnT6YNbE4vTEZrwME)PR+D<^lYvyQVtYMGV6 z0fCkiOsL}x0Of(&t=aY&asqK{3hr~_=o4+jp37%rHur1ZOXc==>K6#;C76!nlyRye z$c`(K7}Q)A;?(rgFq)C6&W1R3Ja!)bXJP<^Iv*6IT!PFZ(zJ$wxN#uNP}vp9vq5F8 zzkjUCdS(e;cfn;y(k$9IbJ(oE5vvIh)@{)@Vl&Ct;+UJMwuL+Onv+9v9`aB98wZ%q zjW0C$iz1xbSa==q`GCh}#a64y?VNhm&t<|5NZqox%Mb*e8nQ_30;IbC!GbC1Z9EHM zxw&v5=A%fb-u_|>7G>gkBBzdG;QwM2E&IeTR=J{_gVYO8nyb6E0aTEwvfDcssftta zZItU`Y#TMV39gnsRbq~dxRIw2hElZiHIak4_+J9cFUGl7__!ATOUcK7{Oqgvm;fgC zIkWK~MWg)Iau#|PGAz^GlpCHCs;Xn1PHPD7au#=wY!s`ucXV3cJ6r+6D@Hf!ArQIS zSBRilwlcG{qf=XDTwdOYoDD`Gx)?b07+~ruqIL{xgGlsFhLx1g)#S(EM!odGZ2NO~ ztiy6){2IV_UQS9T^>DD&&Cvb&m8in$wEcP95%r>4!(4n^THr{lG3iL<#K z_F5x|-nk`aG;>(p1YD;-1(z;eW!lcsc{59_wa3J(i~DiyeNBsTTJCxPwhkrAgJL@A zM<A;w7K4XCQ9kL_41AaU3Wj98Ye06W<1R;Q>?$D{-)xl^S?cw4@IKe4% z0}+;`$MjW?95(NO^g{B+Ow~KV2n1EbCSW+y7*q3xA@CPlo{rdz`8v_LL@<(5#l%<{ z69!HtI`x&Z?B5b5y8`UpVXm6dxf;n^fVk>X>!oV*w&?>^lwXlvaO?-@&di6(`#SZt z&op}PB8Kds=pO3q>H@fdw-;#T#IC*@^!9UVKMqyPR-g@TnvCbM{(&>u@`iC~rb_5< zva)FkSt}}FVT;N;I>6Z%p;_kC_)@^{VYPKZ_8U~*j)^dS1~|1_`e+(QdEBE>IhZ&Zg2~p9ndqn1xhHkw@osl5yqJDLos^~sax74BVvUON=Y?4uCzSfhhCa!T9k_a50JI4O)`#}FE>hn za`GWraxofmf@Dkv6HF)2?)eBM>}_FA0J(lnOB4Z{^D#g{*R(dxxm;yNmcS{tp8#Ib z7UQ~lnxU2?eTvj$ZIf|x{4yZ4yr%w{2DMQ^A;}$7>_StDto$6H#E|43D%N56!*O^V zo3_);Ohw!$kjp)>V=qmt#SeCpZLE&9<0)t1QnX<{8F&iCdXDbkVIcZN`k6gY3x$(s z%nL4JPZN!Eu+Do0KfWN1cf!>xHSql-5l{|=FIntZ9OiaiyKmQjg<{7-llzu#@X?81 zBXRHWCfL#>+bws6d;`$g9&uO-upwTydso5VqOO$dv0s)}+N!N`&UE6Gy6^Ql-@&e& z<#ft56Lge*Z}Klw=hMR5s+if%S<-y6{fA7odA3tW8ZhsV7~*rB`l(xT?u@6d)5lu0 zDeYvB(nOVgzkH<7!!kel9-2Q*jCWvb+Pre`Ul zb!hap5tunRK19OUJLi#F7y;HjYJ`GCf6i3z%r`|TB)=e2O++$YE~UU(+*rQ?6u7@R z9wo4_Qo4;2{2NGDJ1c{6BQ!)l37n$5o7$7CcfPYxx#lzu! z{$n&Rb|OGI#JD4qfna$U^I7{U%GkwH)-cRQWQL#_K z#5?y&aJY5&Hv#<9j8;9YdaG#`h;J~wMK`uy-`!7S&5$eupfEffcfEZ+u&Jtg76O;| zi+L}Heb^ir51)z_HKYZKMOz@X{L7951fg)5sV#x9^bdrTfk!$od&?hrD?&RNt#R&c zBhB;%w#rfotDM?lOA!pnQq`+WbJ=ZyD4IK{ll1cy6AL!cYgU^MVkZQ%jd6qHJbKGl zIknDGwQGE)hc-Z4RuF-`c3;)m7OBCpMsI`>%4=}-83lh)N(EMBr^)2BMUpnTSj>MEcNJGXOU3N{)q;=Z!%UQsf>|-M4+UE zHdTju+wsb(XReVl98a+(PhD?&oV8!$P-WpxoY0XG;kwTp7WLMKP}RJ3rZzUQW0pF* z&Z&27S$ijx59(+pY46zXOeS57_l#{p%P=uP%N2JQg#52}*6$y8Wr4y3yG#L?)_o_e zZm7aGx}71Byp(##%kC_8I>9az_7ythDin(;iR)+W^By=5db@MAs4=pml8v7F78Olcbo4e86e4~%+>a5buP92)SIjJ8q zW_C$7N|+KS{lLcc&r;FFW~|c68Gx*hbxHP_)g>!u^?_NcTZvJK96f`O^>UZwUKU{; zoTYkiaTW=Ci7%<1y@|mqaA16kbDA`ObR`4B_UoD$x~PqX=^Tnf$j!@Fdkq+0MKCl= zrQc<&6beBK^DgM;5rTg+Di!qv&gp2(3ybePjb*Esb~N^}#he@l(ckKMe}Bmaumt5Nl+W8sRLZCY>2J4zMF4#MijY$Wkq~87;$corwXu&1hQ|%K|wI z?}>zY`?;|V$ogy|i`^D&D6zELM%b2to_160ykp8vSoJ`RDWVg05M2%`N$inqR3ke> zK$N!Y017_Sp_i5FF%Y+%Mto^FjYY26=UR8KA84YRsRGBLN|`6mFN_OUqnF`hnGD8b z)+#fL2R8V6pjsQ&6jM2-7a0V!3MOQ!cglTC#EDrdyTZpIH;D~9I2elyix!v+WIy$U zmh`G-X&C960#HmZm^uQoD9Nc=YSAvEBuUaVB0tt2hb-82f`MZ=7Z~!=u;=Z?ARwyQ zJPm@SvD)wrbYVh||%g=70dV`etHMdB~|_ zOUe9M%-ep5y&E-xkihwJHY#fo54WpT9tBQf<^Y~Hv5j6g2_5Y2xiG9A@mZYCL$2Fz zZPUYodVW^p(kcG+n6+ACr8i`$n`T&4;6j|A+T(1iTm^wR$>?hz zED}il=G0;kQJ#=2)3djI1xP%S;Ax*_fnAtYy4UCrwsZ-Js7%4g11st+tEE6N?Z&2N zdb8LA%K$oiA08vMkhL5-yUOr}Q}PPr8XDBXAmBn<*vL`5660f^8O78$HzI3h^H8Hy zXvIl8XvM7psIX5m94@rfxu%iPN~ zP|`t6$}4a?SpIS@BEO|x%PPQ$Vuv1&@|hc@VVqZ7Fx8$mJfQ5kk@cXfb9ak;tVQCq zkd8hA;_bDY0NT0H1F|dxZieQ27S0aK`}6CN|K!z}YeD4|3*lP;3h3vVGA-<14=Fxi zv;({2RyMY7e}_IKQ`k#ky^X0oMtUtubfCak;&#HOjkKF3k0sRp4ph8lcxS^^@3bdn zDQ_EC_Si_sn^)@y|EP5~W~n<4`s`0{V&1e-SI!EXv((N*M!yneF{+$00Bo>$R7zO* z^e8Aj>Lsb4dT9%j1Fy07=90;~V80WoqKDTOx51#>)}|?q0=-`G4*$Ed)ZmwlWlmIg zXQ_iP`OvWybevk#UQ8dYoM{=CLOH)|^d*Htk${}h9#0RqjDSI0thx+X(guUemX%F8QVR!TWy6`pDSp3^~%8`s7BY1^`*=<3sg4#@htWGQKvpzK&tl=O~S5dGaFb&e`QwbF`pW%n6)s=QzNr_ z8@~@VULF}@cxYL&JOR+bAs#0sm8q1XGJO)DeP;rFj9xFor!**34T)@pH+MaH*jw~z zz;lLrj7+dpW2GQI1JKTV+y^VAER)Ey0L8~Njkj?2IjmkjG}29d`aCji85Uyfehb0- z0m^@}qaoPB^8)}?uZ}j;iMNlv8t4Ub@0^WV#n*^>5%cONMi@C34+71Z6i?TkQ`7Z+ zkyn@xWvOMKnvH)N2i3?wBo$6oPK#0*QxCJU;)88)9RSzA7E9_Stn@!Knt|i=W#-Mv zvE$s0ul@N75WDXQG4`jI5a|Gps#gK;_Gk!pu6#?qhE(THPbmw7UI*yAEg^g83L?_o@TX-qSA+xw)g;DLrcRZ%pqx8%D= zl9!?XemGjh1(AKh9E@ zzIHAU%DJ8W3CJrPk=#Yy_l?<-CWcRmp@JAZY{Yaw!(4|JjSQbLLxZau;qvuw4fnZC z_c^ew&FB`b{Jt|g*P$%Xi+Jt*W(q!&H zoWz3Sd*?Fwr@AsTcuMe>Qvdas5=W1ng-tS_Sb?wDXQQwcvFVIYGrmS@aVDCv;_z5)vvQ&K4N~2s zuu#Kmq=UAWOl~XDD20H0iwaUEsNlN>&GBxKBVgtA+4=)e_Y5KVv|0NQyCTzF^7ChB>PsXs!R>dXYp_Mbs6|4q^T)lM}R z!7`Wfw2EZbp8$&QgbOC+R%YIwBeG)y=q$N%c#=I-6~u)FtMUP1u_|xOOqi;ja2>z9 ze5w{RW<6)knlR8OD>fB&Y-nC}LSzc>?>C5ZlK5Sp_v zx~D4s6@xYCo;=M2S~$M|RC{gm0N#wMnZRlBS4{T5J8zOc;KJ-T)R2;^D*ESjz*P^f z=~6Yh^E<0fo5DC3UPM{^iMg?bqxlaA+8+i9$^26zDgG|3lXCy*oGu-~g~DH;AtojQ zXDsB_&zH?MJVAeFsiMD}`nKOx{QtxH3a4V5m$&^28BD&wwqHc7trS z=%RC-w15%B4YOgT9d3s=Eu{sxHc~)ja3c+BtI8YUSyF{cbt zg8Z^o)n#K%v%)6XXyCq^BMqM|m!CByY)e&SUyY~I??zoa%QZt4uI5qwHOO*LXmj3H z;3|^%DE4s+ph?LcF>A@Z6>3XG|0tF4w=-ILRM^ZOZ7DV7bCQd2>2|OZe^j!gb9++> z>z8&A)GAxq{ajjm@IL^I#J{j05C4HET4~27wf%?o=6x^AiX=$G4QgHpB*f6>3MMf* zGvLtkik_e)p{-GDpFJ9o%d1 zgDyr>dA|4XPO7}fr3W2x#sX*D*kpr~H=PP++0r;td2UKOa2%Ad?vSl&f?c61 zdspwaDlgcj<2lIKjsmG?Y$p)a+Ax;g(ODC*Wo#tI83!A0N|w%IbzMM7;W(%uUXSYS z!mdb--v@z@y=ksaW!`Q`&214S>PlbaEaXuech6R_Aub)E#ooLMSyRR*H{ zi{SDrft7A44&%$XKC~nx4^wivhXjOfk z=~Z_3)gY&MLNe}>@TgK@C_AYiKt-S6UcP@fygxtVyAirk>=Xf|0yIp6a4$_0=yHn=91c)Qmq;HS=oPsU z(w@bhZjQnHCKyT0NEFMR0w0-hkgC($$D>dTb8g@w^f9#2bOEr;H5zctFDh2WPBW7+ zHR5Pibg00i5p6g&E)`KQu0=(OQ@w(L;qDdI^lUY>o2$@z(Bk4tbS7{G zPxBDX_ISCQ#bQciULay(ikg7N+ z#_OtVH|BMrC1bNjMO=@9r{g@L&ru<)rf5%qqE9f2oiaL+S@WQyz+|mGm^umSG|@-> zPe!hNA9@g4#={hVHs>eHc#sBjJWOSSw}iG6Eua}hw9_=z_GNgRk6O6&lLcA{Q>b!* zXy|lLpL$1y;N+;tBhIx=B12U5EA8;UG6y`zhGa-ZhJ2F&Mz*hQFdca7rYHN13vY+b z(Da;N4gm)}HtQW4Gf^;ReWcIGkp8C*p9RpC86KRJKf%dn%+@&TRwU@LA!Rr==AcYk zN)!%1ka7+UuYAlU<)`nA^2mqp*q8?sVXzq+(u=HQJ}iMGmo`fIe*+O5&h-^~FF^}T zBX7;a#puaquueyzg)9`e+CGWzPSvZYMND41+M}K!`V2?6rfGEG0_98 z3a*eC64!Vz&?J|paw1+r*rGKs;3#my97@JgAUZJ{V#rBMNGJMb$V{VuV_wXEOhL;v zG;vd&kNM--E?$wXQir+p<148^wUYH#6kTby-^k=sOt!o{unOe#p6fA9EYr$rfMWM~ zhJr;ouK}ohE@JbDOB%ruSER|T`zZp)Mkr*Kt4JIu6FkqO6)i*6nXQhDGP4x9;et(b zjj?2D4Y{+`*=t<-4kil0TKNxE2`)Q^7ln+R7Z#KAH=(9uong+<$Pwz8db6f(-#A~x z$g7ywWvhgAQzdiX0{b=HrH_D-r1fN93>=X5C zWv7%GdaP-)f#JZ%k@j#<`rcb#3@XwV!e>o=B0M3%CE3cIV|bE^|1H@n;5uXf5`Jg4 z+IgL^(y9FK(o`4j#4Egg4-+=^E?@QSmE=c&vTh0HaEn{uQKU{=>Z!;we>?_c(0ThN z4v$qqSNRxSdo-Yq0P1lW!qZXx;~KDK((p*{B32J@wrQ^y8mkg|7(<;FPbFxWvIUO| ziK^i8%S&_>K!>T;ElY}hFbZZGZqY;V1ac)V_c#wM;`k&$+si#Kw^)qIr!+l<$Mm9A z4+rX&r?XXyIW8TAMXo*r9C>wEziRq?&A(>>N?Y-t?0Zh5KW&eQ#dRXa#ghtj&!bHI z#t58i@_pNBf42H;p3g*j0L4|s`w>Q3EDNF+H0tvQ@_ZaN-U0O@OU7-+fU>N&4>CD_ zGbR{|QE~{%>PCzJhq z&7zlD>J1c4Snbh7mbw2;)|gryrJXn45&Kql=`w?mbiJLeHv9g+ldYnbn*p5%_`AeX zc&B}WelC;F8gHA97#N-?q&64Hq02@ZMkDMfsJpxdPJHkDC}dFnYO+cL=WGU&|c3&^xSy`;eFCjcNYYKqsQBKA22!LZaZ#~^R-St1VKX_ z9!cO;WKx0(_9K?q;ql-|6H<}I6uF|Eo^Iga6!HoB zOq1WB2Ch^XB-$ZE%%7u7iPP9GQXjJ)M{0wgJq8c$R$&>3=n2eVPGe*->q$~vQRgym1Y(xp?2*>f#iI@Kei%K3>axEN6c3m zS~G7rmT`Z=iNQB}gu_JnHBsi=1C$m;_YD-?%|;^W*ZCGE%Q_#w&UfHQ{1&Kt_M{-A z+-;UtH{FFN18 z55lpJKxZQjHZPxl)zI>P!i}X~{zSfZ7SWRVO+%-h3Hz_r>hB=^SO<@QSw!^@L)=$+ z9ZENwxr?^OpD5Dc7AzOg+IrLSuWWT`v$0Spj(@|yTq3ZETt~K8A`#+ncUSZKR zKx)vf9z9^0F&o-M-MFuh{I6ANBNSYHtA_{{^vXnIfcAND(#nv|WpDb~@Pw7IJ*gL) z*i>Gr%PH;V$Zo3ZEYAwUxXHvEz_Bc)DU!`>sz{kNsnkZ4%~4x?_x2v|p6`6!0-&C^ zduSvf-cl_AI=mbed?f>R>elY4g7uFKj-0ZTqR#`h-`tUvX4O?qa zH}x)3g@K$F+h_(R97qg<)r4a_LMSf*l`O>BR%gjQkQky0-b}{(ExeE*=E;eA3gFIY z2-{Nm2c8YgD>K_2-0KZRm4y!ncTqJxvD>zIyc;JXeQ!<*13Y*moL2EKCN@_!p#e=* zZM^#$>-{9*o=nBRw7_+Gp34w~#q8t=4Ig0je6`c5VagpE)WK@hMgrv8=;>we`=Qca zDy}Hq-o1~Gh$`*bvbEe~>D8X?+0tXXJub@ly)MltHaA)qtbP~I_EkUV!u#ZGF=L*E zSVjxPpzhR7h!&{lh-LFW7Vwk~t<1d7Cmf|_7L8-gJ07(6FsGxRo~Uc49n?YByk*ab z_A0lL8*EGm8_OOx?r9#&?a1N>;)7#Lv)$V2=9JM%7u@%7K)9OrSOE6k`0S(QW|z)3 zJWlS?H@k43xC=-u*o^bsHT~UJgnD$fshtm-DS^`8P5$wuGt68~HQ(kFMiboKP~ANc z%c@7rE(yiyDm)MKsBz!0M|z;jsl{C2ZsQdWQ7dK7o-FSP$fjaM6Il5v`_HS?1| z>;U;v+Fyg@JxLJddz4PvbUV%k|MiSn2`XzP9RVmp;2p77s-iJ#AEcHvUtf;-7KdEzG0dN(G;vb?MuG z*rjPUb?I3%1<;}xrlsieUvrXl%Ph|!4o96;y*dxVWAczzi}Pj#K&S48EF6E{m`Ci! zk>u5dy@*8F*%WH-P`k-`2$IxMgvD1v(@Tg$<+Dc9ldNmlxDWhr%;IgoAQk(zUlUx@ zj8D};7rdz9xc7ysPg+IUAB|QYFe1)@c&(@S;ule5#d_Q3ySVyHXYx}&;?Y(fJOPeEb#+17Eydqv>#RDh=4i+R8^-Ga$I%o!~T{WP1h zzixz{EzO0vylxhk?8rRUxUT@mF-2ih2xOw5B<2%V93Eh{`)Ol~K&1jU-G(XL3^r<} z8Me~5%vzFMn`vvjbuSL22~~`N@M^bNAoysrg31B{&boD(*9D5pU{zq#l|Cw}-_|bpuk} zPkN$PENkEez>YO=ZGOAX${h>Qu73DN9lbIdl`OQWMMquwy)CkN5oxJy>hU%3DPLhL zS-zOCswt6RIW)3&h!)sX^f5C;$cwe?_HBeOIWx(H3~tVzMk8q$h>a{ zCN?e=1tn1|!}RyQOW$vnivQ&{_0IdQ^)hd=msaRD9eXh&1erQ1Vn>d+mAmQnT7t`jK&^Q&Fv9OVak^_z)v> zggd0Eh{>nMMw`$Y%k<}f8St@@GHNj=TV3WgihZBibrDU>0mS@@2wGP0Zm{K3C?rBGQ#Y{0sTb{6TQ>MNvf}%bhggl_O-Oy~e&dP}ys5yj8L1Id;Ayn@e~O7W zC@{)rt3*n$?@LgkU>D*5L8P{kxc1zF)SbJq$hEi`?j)YTS3S(M_-*e(iC)vO2g1Q1 zEuxioH%nYPgotG#68R(7wqg$a%1k0`cqzv0*G8JSFjhcz`~lNsDV2)Udu(d+H^$Gz z|9g?kz8fsC_%OEF)ZTB6&w>rS54jHRzOsSak$PtLl?~iMa$W!3z?~>j=4qhN#0Hi@ z`Tk&xQMRlcxeh<*5k(7!Dga6t60U;{h5sC6_aimCFxgCcdSm|dBm$)!~z@~ct zR1{vB-a|^tUonnXNoIAAGdZyb6q5`N zi<@_^O;!JFYUAWrX;az17#>qvs!*HjUxQ~KF&erU31To ztaJY@QqQAA@C-9%gcyp{ex%0lF3yW*gvjv$)At^L$=1He}+v`moK`KRPB3{TMHwn z0#a>L7cRISkPe)T|HH!Wx9nq>SKCKSehC$R9uzf}skmqU!%2FbJO-eb0ouD4mz=SH z7tbriQ#`Z1;6ZGA5B-S6I23pl1u`p>vo$sL8goVLB!FIHDfc_}>j-P6oKvSHS|jFP zN7?f;u;{M&Vk|6vEV@O~x#)faWg+SNdM4bsULz2># ziSI4USdHBE+t6>5lKchX@U4HgpnmRrN0N`&+mwa{;JI%SVVNA>^}to$`4&w}Hzqe# zwru|ZO-tEVW`U9lT8@~Uuwwh07c8iukD6qhgl`iRplqQ*O>u9ad$icP(rau;4SY#W z+&a39!{Hc9MIW^%tDq6fwYpD5@_T4+;uDbn)cf)^{)P0KzA>(w6RxUZwCU zsf!&M&`&eWBIKWe#Lu3D!X_7_FUf_^HHk4pmm}ik=E!hmKV>?B(tn)zTpO_eEFEC6 zJWklu@m6mASOYPggdz`cYp)y2onl)d34~M=4X0g}5fO-ouOv(hhFR?BNcHimkv#;boL=7FaJV3=Zj%=pvP2J&E zFX-1;b+vZuNMd%?H=s85jsV<@*v759l^W_>q#k(&2W)4-#lm&6aGI*^JJOdo0lV0s zPrrOAJf;5mUN^65SY{agM@?}ps{2~Gt4fN~^!@A(DnBy0ZLu={(0Tevm-)t2|4Aku{RAVodd6z4XE@^iVsieWD7Z=~gqF>h zzcsbBJ29oZkK%lb@R+`4>1*nTe{>dmqW;S)8T>YU@Ue5IXu;))g8$j!V*ezl zZK<=Mi3)l%tf^Xjf0HGB9(!L9!rB*2(seL|sgVW^ z7k+v*T4TEk>TCvQDsuVRRk`P%a?!-D61o`m&mq>-jt1;Iu9r^Q%PP#};j(6cL4Q`9 zTM4f}o$R$g;Py$%FstLpXbu``|7c~5KX~Y|j>i3ILJdzZwXiFDH@Egjvbimh-+c%X zE7c(Z_0DyOVTpN^g(UfVGA-`zo+X^3ptRDd;VR_^I4s5JvV=_pX!w6c*$3K7d-y2z zAiFB*;jRx?4c5&({8=jmhaAUtgZtd|T0^r5tu@q9J_zqQJ?I)DibSZQB58wKoz-wJ z!q;3rs0Lw`Ji^4w(*G5P)X(CB z@gTBy!@}ZP31>a0AN9CaEdKcj!WP$h{PT-{#z{cBs!0v~#q9_`-?}v%=R{uixT}v< zhee|DpqHq_F0E{?3SOl*-q-gVrQvc{YZys?)epK=xANcaGKWAzII|c6F(*TVMFj4vAyls4wi|U%x{2cL0nMyURZc zuiWTup?Y1@ktMgh;onXa*yU}>W@pKA9;cJZB<^fXob(M*^LmGTXJ&}~5>Y7@Iq71m zZhE80iCsFJ;@lN1IzrvT<%*=z9PWnH)1L-mU_Cg< zOW`Vj&b{LGHo$2irO53b8Wf4^T3eZYj>^Gq%~g8SdxEQj-FHf>SW7RqDlfiyn(iG+ zk>H}i*&DbH?{3ypEm#N7dv5!@L|I&ko?u(zb<^WIto|==cLGWec@4H1yxqF}G*=Rl zS{~m#soqy8)v?3-095%J;!N%x>eeoKjli|n-`H`!!iD2~ zWe=~`L{p^tX*k@{^yyko{`Ci_;B~C1V=tQ{E41wfAhkBy4_DAMn}d6N?;5XhQ4tQ* zpjcJ+tA7(!v(we8v}$5~D8wN0uKZgQ3Lz||jWU?YwcX)cvG~`LfGqb7ERVSAmqs&l zGE$4*Fs?=6EK`SQ&gRz^nb3gP(+hNH0O$On8hXCAC_>eigyB{fv`2I=+mm8PtfpJL zlJ!6uYD8u(QvC&8bIwZj3rVY2{zinyH_ezkjKOAs^A*8 z4rykUBY@_7SU`vh8tvBSi`a;fcI6&zY`s+ck3z1B&>#qe@A0l{>}uz=ZXJZf#*Id< z;GQiZYIw9y2xYHdi`2`@Z`soyt3eT0Y-&dNr8{QkJ`pdjsOX?@s)#mHm`j{4(J`sg$-?cEElamNAc{&Mb zppZ8;a720)G6t&e!vT}+s@+)kI*~fc;uO|=;SE@zptXLfHr1}G#+g~1HBYmv)bYl& zpzP(61Hb*}R%f1F)m-lmRmHEsp4O**eYZOE1*{$Jl-KDbprQ`juHL;)FL5&r46mSY z+qW#s^fY&1WzXu=ro>?bO&AxCHv#hA~?Hq5xZJ_xoQ;zRG{xjrjCR zccRK&Yz~Wc0(2Q@Nq#T6y&8Ncwx@Xmv<%x=fR@|U-}&y@(t7SNt^gvnb8wd!+_i+h zIQrW%XeJ*5R}y}N1fEcUqb^M#kN)3C*qL8&-yh0q^%{fZRZI^06{}4Y!6`g!oQr&L zO}!d0(0y!68p2p-x^)Cp765BNa$&!)QL3aAJE5ntX7y2)$J~nmKYg_aF1zZM;2a0m z2eaM!_#PKfxE~k)ajN#ZUa<6rG{)&T1;qc}UUvcUg7>kdD%UCGH84Wc5jflcRUB#< zr5x{}9ehhk1cT(R1t^E))E}R>JRNtFhU2}0zF}CB&NHf>l-;aBAN!(O`OnR!wM)_L+s`~(!=2p#j>z5Fjx?ZPVLxRxdc$rUVrAcxtK&b;8cU0$U zR`{u!L5!Vw=Yc^_7gev(4_9z}dXjb~w)C9R?Kq%uS1DlX1c5ve`Ro4x(y!{4u zC*DM$uNRO^bogx6nUcM%O0;%GG2rJuLSWMsNGx{(m*`A`HIhS{vC$r%J3-caR7_g{ z&Og*JTCKSeAvwHKU7Ad3zSFK=C^Q3x`uHx6%3~>Ez0~9-?)r$YyMZBfRO4>yb5AO# zm90oE?2VnK&l)V$*SJfws9H)j2uE|`8{-AvQH`6a^jJJGYZc*rUi@G`sl zb)_Mh;$F`Dt-kleq;jL-7bl|%)VaM!qXDY-DkE>yM)%v*x>fE)!hBi^yX@-1Dq|_| z{{fUrd!b=_b@_t5nR4F&DYiUGvRlJDsdsNhn=S8kJP4a^jeCipX7e7B|7aE8wjfZY z{5lCsv}ecYZl{8M*skUlxu?sX1OFeP;+i>Y!Lt0UdFq&QBlcvkJE8P~xy5B6vOJ2J z+u?pnR4d~O9+S>`xnWmTeFIcaywfaG*g1Rb%IS2^6>9kZI937p4+9e~N8^95u5QIV zybCiXe~vmo-95r88kLycUB((^zf_@iw|lO1DlL?KU`0w#9PU0o434&=n~ZaY7V{Gt z6soHFHEyeNe}mLfNeeMl(oaI<*ShZvBr*Jd3Jpn_wMcD=3+t%dH@WAFI!^`UX@RJ< zcOU|$re|Yl+Rxzx5$#g5JV&d2=6l8`Uuu8ks~!xEEcxIeV^ivZ*ybf7>7w6AQ7HzqYMUnkzYV7fbSzEhJLI_t^ z^TZ+*<^c^ErZL@d*4%(W*1e!XJ=Nx0-2Q6QEz|J2=ScI+6j}WuJ2tpsP6WkFtAI!( zA4IX7KO44HgKjhHJBsWfcx7%gaz}kqZCC!cyJt&3QS}@~DcgzUXtmRY5U65>WqArh zfCSF`F9E*em4@MJNGn8p@HJc!Fi!I?la88(IfzWS!%Pqy&aa?E*$MOrs2i-}?=i^Q z1N16D=l^IJs^0!-S-tLh4XH;@;Fg<@?<^c_wZvWr=&g?$c2JWynl_PlZ@~Sv$$b^H z1L($tgNFup7mfOkMb z@Tuf*)wS5DA^Q5?W!v{P&KaU!M6w7Zok~Y2XmeEj*T|nx;vOq9O^@uXB7vcvj}`Ht8kADpmB`9(z@^e=7&-EGc7h*VKj*#}qs?Tt=3B_* zeQwQFcGGvPI;~kwJ9Y9tW9m{(f6wInW_Xb-;6}_vmeKhGVGh<&z1&YhR2Kr{XgNn% zt6IM>bzwUAox2y74qGM}*s?li*vx*~R7DeF`UJ5H*qZZpwZF{NLmBv)Sr<3YiDXvY zrLjo(1y!#UCG~%yUv?R><+S{_UA?r+tq%;+fcVF*dOu(c2%0(<{!l zk*knhoKl+Up=KKD`}V(p!{grj-)rr?_PTs){pb8U%H&8?gLdbmJA&QaZJt%^MgkqD zR~=JWcNqD%!SP#$gt~X~*^p?n1mAMS?tJg#c2=Th#wL2iNOI074^xlGYkurOQFo$@ zR5ogULfX*=S<5xN3)*_rAeRl+$QdMaC>r4u>Kvr9lZPH-r0X+1bSlA2BiWy8+l%%r zgWoTxAZahNjm&RSedF=P8hNKlpXXZQjEpxqe*5uu+Dtqa;HxUFDXozAK6DajG;a9u zVsH)?9@FeeknxEemx?l%Yi^zaMVT~LD>kyfi(}ROaCyvsKPcJcs&65N5{zWFI03t} z)o3Rgx!B^oWsw%W@ZFHOuWGmCh8z5wqE+J;V^Wfl!B+K!RE~K(o0(StF2~9NE)Pc5 zyESm`cfOI!Z3+ch{7A+Y{s=?y!~ccC1oHBtw_p_6<=$GH2rrYdJ0Ji`A@S0gCPONY zDZpXr@*d3icoY@hePw$hWc-+x0*I#X<&6JA!NZ2rq2aw#C7T79mO}QwA6J`VKuV?1 zYowOQNH3rXL{Omp+CEqV{!v|q`mgGuF2NaNk{FBNOkZWbBI zX;)RG&==#;>f;{#K)*dGEx*t0O_G@F05wwz&xm9NLleE9N4s*wlPv$7)Zj`1FELVh zN`og&aw#7>Gux!gU#HaemZCd@-DV^_O>UirqTl%0xV03P{W3XrWF$P5WA57lc4ryM z>u~Dq0!ZQkJ{B@ML82?K$R$Cp4{~hZNH~fPN*>M#g**!-xy*;K#ljzv&`ktAcP>r& zVaoTdxzUD}iCR6hX)R2zpPqcos7ob>jmO@1s_G;>-ee zN63LUO_q&OR_keR%Z+%?slDc)y~4=5=N$KQY}B*IY&%)1GEu51{8}Z*d^($?Q}r{v&fH#4=iWp%du5dp;n;4L)Yq&?FTE)6P&ic6N)o$ z+ZzR(K}WP&d*@0;5*9ggeF+%q!gZp!~8}aB8fbM1L;zSN$-|#NJc41S!hz={T4Y$W%R`W?FTEu0%YCp1L{E-5<-zl7aq5{;Dz zva9@!u5)GuuN1w`tEVe@ETLGRl5(9}R1@a43Z0cA!yE<5gZfTX<@PZr$3 zpDT{!+NxSF|ZqWY*2~PEcd{Oh8*wzq0|Tw diff --git a/C3d/Lib/x64/Release/c3d.lib b/C3d/Lib/x64/Release/c3d.lib index 1fb5ce1a33357d8cbf5b0f62395460ba2c94e511..73ad20ee3cee61724c3e65bbf53abbaee6e4f124 100644 GIT binary patch delta 1224240 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ΜA7nYm)CF@1iebL3_{gPM$lFoZg&PNTRd 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%0A6e%LIfh`hq20sf^-$&4_|Wds{Ts{A6M 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

LfPJi~M|M07DapV<*vEwHkB)inin2aW_z2Og-f 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